Initial Contribution

Signed-off-by: Jan Supol <jan.supol@oracle.com>
diff --git a/examples/LICENSE.md b/examples/LICENSE.md
new file mode 100644
index 0000000..d5ad335
--- /dev/null
+++ b/examples/LICENSE.md
@@ -0,0 +1,29 @@
+

+    Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.

+   

+    Redistribution and use in source and binary forms, with or without

+    modification, are permitted provided that the following conditions

+    are met:

+   

+      - Redistributions of source code must retain the above copyright

+        notice, this list of conditions and the following disclaimer.

+   

+      - Redistributions in binary form must reproduce the above copyright

+        notice, this list of conditions and the following disclaimer in the

+        documentation and/or other materials provided with the distribution.

+   

+      - Neither the name of the Eclipse Foundation, Inc. nor the names of its

+        contributors may be used to endorse or promote products derived

+        from this software without specific prior written permission.

+   

+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS

+    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,

+    THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR

+    PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR

+    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,

+    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,

+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR

+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF

+    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING

+    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS

+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

diff --git a/examples/assemblies/pom.xml b/examples/assemblies/pom.xml
new file mode 100644
index 0000000..527177c
--- /dev/null
+++ b/examples/assemblies/pom.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>assemblies</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-assemblies</name>
+
+    <description>Jersey examples shared assembly types.</description>
+
+</project>
diff --git a/examples/assemblies/src/main/resources/assemblies/glassfish-src-zip.xml b/examples/assemblies/src/main/resources/assemblies/glassfish-src-zip.xml
new file mode 100644
index 0000000..44bf0bf
--- /dev/null
+++ b/examples/assemblies/src/main/resources/assemblies/glassfish-src-zip.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<assembly>
+    <!--
+     An assembly that produces an example project source zip bundle with
+     the jersey dependencies in the original project pom.xml file transformed
+     to the "provided" scope.
+     The resulting zipped example project sources are thus able to build example
+     binaries that are suitable for a deployment on containers which host Jersey
+     runtime by default (e.g. GlassFish).
+    -->
+    <id>gf-project-src</id>
+    <formats>
+        <format>zip</format>
+    </formats>
+    <fileSets>
+        <fileSet>
+            <directory>${project.basedir}</directory>
+            <outputDirectory>/</outputDirectory>
+            <useDefaultExcludes>true</useDefaultExcludes>
+            <excludes>
+                <exclude>pom.xml</exclude>
+                <exclude>**/target/**</exclude>
+                <exclude>**/*.iml</exclude>
+            </excludes>
+        </fileSet>
+        <fileSet>
+            <directory>${project.basedir}/target/gf-pom-file</directory>
+            <outputDirectory>/</outputDirectory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/examples/assemblies/src/main/resources/assemblies/src-zip.xml b/examples/assemblies/src/main/resources/assemblies/src-zip.xml
new file mode 100644
index 0000000..6f99a65
--- /dev/null
+++ b/examples/assemblies/src/main/resources/assemblies/src-zip.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<assembly>
+    <!--
+     An assembly that produces an example project source zip bundle with
+     the jersey dependencies in the original project pom.xml file transformed
+     to the "provided" scope.
+     The resulting zipped example project sources are thus able to build example
+     binaries that are suitable for a deployment on containers which host Jersey
+     runtime by default (e.g. GlassFish).
+    -->
+    <id>project-src</id>
+    <formats>
+        <format>zip</format>
+    </formats>
+    <fileSets>
+        <fileSet>
+            <directory>${project.basedir}</directory>
+            <outputDirectory>/</outputDirectory>
+            <useDefaultExcludes>true</useDefaultExcludes>
+            <excludes>
+                <exclude>**/target/**</exclude>
+                <exclude>**/*.iml</exclude>
+                <exclude>**/weblogic.xml</exclude>
+                <exclude>**/glassfish-web.xml</exclude>
+                <exclude>**/sun-web.xml</exclude>
+            </excludes>
+        </fileSet>
+    </fileSets>
+</assembly>
+
diff --git a/examples/assemblies/src/main/resources/assemblies/weblogic-src-zip.xml b/examples/assemblies/src/main/resources/assemblies/weblogic-src-zip.xml
new file mode 100644
index 0000000..3aafa40
--- /dev/null
+++ b/examples/assemblies/src/main/resources/assemblies/weblogic-src-zip.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<assembly>
+    <!--
+     An assembly that produces an example project source zip bundle with
+     the jersey dependencies in the original project pom.xml file transformed
+     to the "provided" scope.
+     The resulting zipped example project sources are thus able to build example
+     binaries that are suitable for a deployment on containers which host Jersey
+     runtime by default (e.g. GlassFish).
+    -->
+    <id>wls-project-src</id>
+    <formats>
+        <format>zip</format>
+    </formats>
+    <fileSets>
+        <fileSet>
+            <directory>${project.basedir}</directory>
+            <outputDirectory>/</outputDirectory>
+            <useDefaultExcludes>true</useDefaultExcludes>
+            <excludes>
+                <exclude>pom.xml</exclude>
+                <exclude>**/WEB-INF/web.xml</exclude>
+                <exclude>**/WEB-INF/weblogic.xml</exclude>
+                <exclude>**/target/**</exclude>
+                <exclude>**/*.iml</exclude>
+                <exclude>**/glassfish-web.xml</exclude>
+                <exclude>**/sun-web.xml</exclude>
+            </excludes>
+        </fileSet>
+        <fileSet>
+            <directory>${project.basedir}/target/wls-pom-file</directory>
+            <outputDirectory>/</outputDirectory>
+        </fileSet>
+        <fileSet>
+            <!-- will copy everything from /examples/etc/wls/main/webapp/WEB-INF -->
+            <!-- currently the directory is empty -->
+            <directory>${project.basedir}/target/wls-web-xml-file/src/main/webapp/WEB-INF</directory>
+            <outputDirectory>/src/main/webapp/WEB-INF</outputDirectory>
+        </fileSet>
+        <fileSet>
+            <directory>${project.basedir}/../etc/wls</directory>
+            <outputDirectory>/</outputDirectory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/examples/assemblies/src/main/resources/assemblies/weblogic1213-src-zip.xml b/examples/assemblies/src/main/resources/assemblies/weblogic1213-src-zip.xml
new file mode 100644
index 0000000..7adfa57
--- /dev/null
+++ b/examples/assemblies/src/main/resources/assemblies/weblogic1213-src-zip.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<assembly>
+    <!--
+     An assembly that produces an example project source zip bundle with
+     the jersey dependencies in the original project pom.xml file transformed
+     to the "provided" scope.
+     The resulting zipped example project sources are thus able to build example
+     binaries that are suitable for a deployment on containers which host Jersey
+     runtime by default (e.g. GlassFish).
+    -->
+    <id>wls1213-project-src</id>
+    <formats>
+        <format>zip</format>
+    </formats>
+    <fileSets>
+        <fileSet>
+            <directory>${project.basedir}</directory>
+            <outputDirectory>/</outputDirectory>
+            <useDefaultExcludes>true</useDefaultExcludes>
+            <excludes>
+                <exclude>pom.xml</exclude>
+                <exclude>**/WEB-INF/web.xml</exclude>
+                <exclude>**/target/**</exclude>
+                <exclude>**/*.iml</exclude>
+                <exclude>**/glassfish-web.xml</exclude>
+                <exclude>**/sun-web.xml</exclude>
+            </excludes>
+        </fileSet>
+        <fileSet>
+            <directory>${project.basedir}/target/wls1213-pom-file</directory>
+            <outputDirectory>/</outputDirectory>
+        </fileSet>
+        <fileSet>
+            <directory>${project.basedir}/target/wls1213-web-xml-file/src/main/webapp/WEB-INF</directory>
+            <outputDirectory>/src/main/webapp/WEB-INF</outputDirectory>
+        </fileSet>
+        <fileSet>
+            <directory>${project.basedir}/../etc/wls1213</directory>
+            <outputDirectory>/</outputDirectory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/examples/bookmark-em/README.MD b/examples/bookmark-em/README.MD
new file mode 100644
index 0000000..ab9b57d
--- /dev/null
+++ b/examples/bookmark-em/README.MD
@@ -0,0 +1,60 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Bookmark EM Example
+===================
+
+This example demonstrates how to use JPA in the backend. The example is
+based on bookmark example from the [RESTful Web
+Services](http://www.oreilly.com/catalog/9780596529260/) book, which was
+inspired by the [http://del.icio.us/](http://del.icio.us/v1) web
+service.
+
+A bookmark web application is presented that is capable of maintaining
+users and their bookmarks.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                                 | Resource class      | HTTP methods                                          | Notes
+---------------------------------------- | ------------------- | ----------------------------------------------------- | --------------------------------------------------------
+**_/users_**                             | UsersResource       | GET                                                   | Returns a list of users.
+**_/users/{userid}_**                    | UserResource        | GET, PUT (used also for creating new users), DELETE   | Returns user details
+**_/users/{userid}/bookmarks_**          | BookmarksResource   | GET, POST                                             | Returns a list of bookmarks for actual user resource.
+**_/users/{userid}/bookmarks/{bmid}_**   | BookmarkResource    | GET, PUT, DELETE                                      | Returns bookmark uri and a long and short description.
+
+Running the Example
+-------------------
+
+Bookmark example runs on Glassfish 3.1 application server
+([https://javaee.github.io/glassfish/](https://javaee.github.io/glassfish/)) and needs a running JavaDB
+(<http://www.oracle.com/technetwork/java/javadb/overview/index.html>)
+instance for underlying data (it comes along with GlassFish 3.1).
+
+Presuming, you have installed Glassfish 3.1. `AS_HOME` variable should
+point to your glassfish installation directory.
+
+#### Building And Starting The Bookmark Service
+
+You then build and run the example by
+
+    mvn package
+    $AS_HOME/bin/asadmin start-domain
+    $AS_HOME/bin/asadmin start-database
+    $AS_HOME/bin/asadmin deploy target/bookmark-em.war
+
+#### Test Client Running
+
+    mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.external.ExternalTestContainerFactory -Djersey.config.test.logging.enable=true -Djersey.config.test.logging.dumpEntity=true -Djersey.config.test.container.port=8080 -Ptest
+
+Links
+-----
+
+<http://java.net/projects/bpcatalog/sources/svn/content/trunk/ee5/docs/persistence/webonlyapp.html?raw=true>
diff --git a/examples/bookmark-em/pom.xml b/examples/bookmark-em/pom.xml
new file mode 100644
index 0000000..12927d3
--- /dev/null
+++ b/examples/bookmark-em/pom.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>bookmark-em</artifactId>
+    <name>jersey-examples-bookmark-em</name>
+    <packaging>war</packaging>
+
+    <description>Jersey Bookmark example using EntityManager.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jettison</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.persistence</groupId>
+            <artifactId>persistence-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.ejb</groupId>
+            <artifactId>javax.ejb-api</artifactId>
+            <version>${ejb.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.transaction</groupId>
+            <artifactId>jta</artifactId>
+            <version>1.1</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Run the application using "mvn embedded-glassfish:run" -->
+            <plugin>
+                <groupId>org.glassfish.embedded</groupId>
+                <artifactId>maven-embedded-glassfish-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>run-external-tests</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <jersey.config.test.container.factory>${external.container.factory}</jersey.config.test.container.factory>
+                                <jersey.config.test.container.port>${external.container.port}</jersey.config.test.container.port>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <properties>
+                <!-- External test container configuration is done via properties to allow overriding via command line. -->
+                <external.container.factory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</external.container.factory>
+                <external.container.port>8080</external.container.port>
+                <maven.test.skip>false</maven.test.skip>
+            </properties>
+        </profile>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <maven.test.skip>true</maven.test.skip>
+    </properties>
+
+</project>
diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/MyApplication.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/MyApplication.java
new file mode 100644
index 0000000..2cd0138
--- /dev/null
+++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/MyApplication.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark_em;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.examples.bookmark_em.resource.UsersResource;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * @author Michal Gajdos
+ */
+@ApplicationPath("/")
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        registerClasses(UsersResource.class);
+        register(new JettisonFeature());
+    }
+}
diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntity.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntity.java
new file mode 100644
index 0000000..e4d4d77
--- /dev/null
+++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntity.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark_em.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.annotation.ManagedBean;
+import javax.persistence.Column;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+/**
+ * Entity class BookmarkEntity.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Entity
+@Table(name = "BOOKMARKS")
+@NamedQueries({
+        @NamedQuery(name = "BookmarkEntity.findByUserid",
+                query = "SELECT b FROM BookmarkEntity b WHERE b.bookmarkEntityPK.userid = :userid"),
+        @NamedQuery(name = "BookmarkEntity.findByBmid",
+                query = "SELECT b FROM BookmarkEntity b WHERE b.bookmarkEntityPK.bmid = :bmid"),
+        @NamedQuery(name = "BookmarkEntity.findByUri", query = "SELECT b FROM BookmarkEntity b WHERE b.uri = :uri"),
+        @NamedQuery(name = "BookmarkEntity.findByUpdated", query = "SELECT b FROM BookmarkEntity b WHERE b.updated = :updated"),
+        @NamedQuery(name = "BookmarkEntity.findByLdesc", query = "SELECT b FROM BookmarkEntity b WHERE b.ldesc = :ldesc"),
+        @NamedQuery(name = "BookmarkEntity.findBySdesc", query = "SELECT b FROM BookmarkEntity b WHERE b.sdesc = :sdesc")
+})
+@ManagedBean
+@SuppressWarnings("UnusedDeclaration")
+public class BookmarkEntity implements Serializable {
+
+    /**
+     * EmbeddedId primary key field
+     */
+    @EmbeddedId
+    protected BookmarkEntityPK bookmarkEntityPK;
+
+    @Column(name = "URI", nullable = false)
+    private String uri;
+
+    @Column(name = "UPDATED")
+    @Temporal(TemporalType.TIMESTAMP)
+    private Date updated;
+
+    @Column(name = "LDESC")
+    private String ldesc;
+
+    @Column(name = "SDESC")
+    private String sdesc;
+
+    @JoinColumn(name = "USERID", referencedColumnName = "USERID", insertable = false, updatable = false)
+    @ManyToOne
+    private UserEntity userEntity;
+
+    /**
+     * Creates a new instance of BookmarkEntity
+     */
+    public BookmarkEntity() {
+    }
+
+    /**
+     * Creates a new instance of BookmarkEntity with the specified values.
+     *
+     * @param bookmarkEntityPK the bookmarkEntityPK of the BookmarkEntity
+     */
+    public BookmarkEntity(BookmarkEntityPK bookmarkEntityPK) {
+        this.bookmarkEntityPK = bookmarkEntityPK;
+    }
+
+    /**
+     * Creates a new instance of BookmarkEntity with the specified values.
+     *
+     * @param bookmarkEntityPK the bookmarkEntityPK of the BookmarkEntity
+     * @param uri the uri of the BookmarkEntity
+     */
+    public BookmarkEntity(BookmarkEntityPK bookmarkEntityPK, String uri) {
+        this.bookmarkEntityPK = bookmarkEntityPK;
+        this.uri = uri;
+    }
+
+    /**
+     * Creates a new instance of BookmarkEntityPK with the specified values.
+     *
+     * @param bmid the bmid of the BookmarkEntityPK
+     * @param userid the userid of the BookmarkEntityPK
+     */
+    public BookmarkEntity(String bmid, String userid) {
+        this.bookmarkEntityPK = new BookmarkEntityPK(bmid, userid);
+    }
+
+    /**
+     * Gets the bookmarkEntityPK of this BookmarkEntity.
+     *
+     * @return the bookmarkEntityPK
+     */
+    public BookmarkEntityPK getBookmarkEntityPK() {
+        return this.bookmarkEntityPK;
+    }
+
+    /**
+     * Sets the bookmarkEntityPK of this BookmarkEntity to the specified value.
+     *
+     * @param bookmarkEntityPK the new bookmarkEntityPK
+     */
+    public void setBookmarkEntityPK(BookmarkEntityPK bookmarkEntityPK) {
+        this.bookmarkEntityPK = bookmarkEntityPK;
+    }
+
+    /**
+     * Gets the uri of this BookmarkEntity.
+     *
+     * @return the uri
+     */
+    public String getUri() {
+        return this.uri;
+    }
+
+    /**
+     * Sets the uri of this BookmarkEntity to the specified value.
+     *
+     * @param uri the new uri
+     */
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    /**
+     * Gets the updated of this BookmarkEntity.
+     *
+     * @return the updated
+     */
+    public Date getUpdated() {
+        return this.updated;
+    }
+
+    /**
+     * Sets the updated of this BookmarkEntity to the specified value.
+     *
+     * @param updated the new updated
+     */
+    public void setUpdated(Date updated) {
+        this.updated = updated;
+    }
+
+    /**
+     * Gets the ldesc of this BookmarkEntity.
+     *
+     * @return the ldesc
+     */
+    public String getLdesc() {
+        return this.ldesc;
+    }
+
+    /**
+     * Sets the ldesc of this BookmarkEntity to the specified value.
+     *
+     * @param ldesc the new ldesc
+     */
+    public void setLdesc(String ldesc) {
+        this.ldesc = ldesc;
+    }
+
+    /**
+     * Gets the sdesc of this BookmarkEntity.
+     *
+     * @return the sdesc
+     */
+    public String getSdesc() {
+        return this.sdesc;
+    }
+
+    /**
+     * Sets the sdesc of this BookmarkEntity to the specified value.
+     *
+     * @param sdesc the new sdesc
+     */
+    public void setSdesc(String sdesc) {
+        this.sdesc = sdesc;
+    }
+
+    /**
+     * Gets the userEntity of this BookmarkEntity.
+     *
+     * @return the userEntity
+     */
+    public UserEntity getUserEntity() {
+        return this.userEntity;
+    }
+
+    /**
+     * Sets the userEntity of this BookmarkEntity to the specified value.
+     *
+     * @param userEntity the new userEntity
+     */
+    public void setUserEntity(UserEntity userEntity) {
+        this.userEntity = userEntity;
+    }
+
+    /**
+     * Returns a hash code value for the object.  This implementation computes
+     * a hash code value based on the id fields in this object.
+     *
+     * @return a hash code value for this object.
+     */
+    @Override
+    public int hashCode() {
+        int hash = 0;
+        hash += (this.bookmarkEntityPK != null ? this.bookmarkEntityPK.hashCode() : 0);
+        return hash;
+    }
+
+    /**
+     * Determines whether another object is equal to this BookmarkEntity.  The result is
+     * <code>true</code> if and only if the argument is not null and is a BookmarkEntity object that
+     * has the same id field values as this object.
+     *
+     * @param object the reference object with which to compare
+     * @return <code>true</code> if this object is the same as the argument;
+     *         <code>false</code> otherwise.
+     */
+    @Override
+    public boolean equals(Object object) {
+        // TODO: Warning - this method won't work in the case the id fields are not set
+        if (!(object instanceof BookmarkEntity)) {
+            return false;
+        }
+        BookmarkEntity other = (BookmarkEntity) object;
+        return !(this.bookmarkEntityPK != other.bookmarkEntityPK && (this.bookmarkEntityPK == null || !this.bookmarkEntityPK
+                .equals(other.bookmarkEntityPK)));
+    }
+
+    /**
+     * Returns a string representation of the object.  This implementation constructs
+     * that representation based on the id fields.
+     *
+     * @return a string representation of the object.
+     */
+    @Override
+    public String toString() {
+        return "BookmarkEntity{"
+               + "bookmarkEntityPK=" + bookmarkEntityPK
+               + '}';
+    }
+}
diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntityPK.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntityPK.java
new file mode 100644
index 0000000..ff2c36c
--- /dev/null
+++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntityPK.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark_em.entity;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Embeddable;
+
+/**
+ * Primary Key class BookmarkEntityPK for entity class BookmarkEntity.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("UnusedDeclaration")
+@Embeddable
+public class BookmarkEntityPK implements Serializable {
+
+    @Column(name = "USERID", nullable = false)
+    private String userid;
+
+    @Column(name = "BMID", nullable = false)
+    private String bmid;
+
+    /**
+     * Creates a new instance of BookmarkEntityPK
+     */
+    public BookmarkEntityPK() {
+    }
+
+    /**
+     * Creates a new instance of BookmarkEntityPK with the specified values.
+     *
+     * @param bmid the bmid of the BookmarkEntityPK
+     * @param userid the userid of the BookmarkEntityPK
+     */
+    public BookmarkEntityPK(String bmid, String userid) {
+        this.bmid = bmid;
+        this.userid = userid;
+    }
+
+    /**
+     * Gets the userid of this BookmarkEntityPK.
+     *
+     * @return the userid
+     */
+    public String getUserid() {
+        return this.userid;
+    }
+
+    /**
+     * Sets the userid of this BookmarkEntityPK to the specified value.
+     *
+     * @param userid the new userid
+     */
+    public void setUserid(String userid) {
+        this.userid = userid;
+    }
+
+    /**
+     * Gets the bmid of this BookmarkEntityPK.
+     *
+     * @return the bmid
+     */
+    public String getBmid() {
+        return this.bmid;
+    }
+
+    /**
+     * Sets the bmid of this BookmarkEntityPK to the specified value.
+     *
+     * @param bmid the new bmid
+     */
+    public void setBmid(String bmid) {
+        this.bmid = bmid;
+    }
+
+    /**
+     * Returns a hash code value for the object.  This implementation computes
+     * a hash code value based on the id fields in this object.
+     *
+     * @return a hash code value for this object.
+     */
+    @Override
+    public int hashCode() {
+        int hash = 0;
+        hash += (this.bmid != null ? this.bmid.hashCode() : 0);
+        hash += (this.userid != null ? this.userid.hashCode() : 0);
+        return hash;
+    }
+
+    /**
+     * Determines whether another object is equal to this BookmarkEntityPK.  The result is
+     * <code>true</code> if and only if the argument is not null and is a BookmarkEntityPK object that
+     * has the same id field values as this object.
+     *
+     * @param object the reference object with which to compare
+     * @return <code>true</code> if this object is the same as the argument;
+     *         <code>false</code> otherwise.
+     */
+    @SuppressWarnings("StringEquality")
+    @Override
+    public boolean equals(Object object) {
+        // TODO: Warning - this method won't work in the case the id fields are not set
+        if (!(object instanceof BookmarkEntityPK)) {
+            return false;
+        }
+
+        BookmarkEntityPK other = (BookmarkEntityPK) object;
+        if (this.bmid != other.bmid && (this.bmid == null || !this.bmid.equals(other.bmid))) {
+            return false;
+        }
+        if (this.userid != other.userid && (this.userid == null || !this.userid.equals(other.userid))) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a string representation of the object.  This implementation constructs
+     * that representation based on the id fields.
+     *
+     * @return a string representation of the object.
+     */
+    @Override
+    public String toString() {
+        return "BookmarkEntityPK{"
+               + "userid='" + userid + '\''
+               + ", bmid='" + bmid + '\''
+               + '}';
+    }
+}
diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/UserEntity.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/UserEntity.java
new file mode 100644
index 0000000..06a2e69
--- /dev/null
+++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/UserEntity.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark_em.entity;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+
+/**
+ * Entity class UserEntity.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Entity
+@Table(name = "USERS")
+@NamedQueries({
+        @NamedQuery(name = "UserEntity.findByUserid", query = "SELECT u FROM UserEntity u WHERE u.userid = :userid"),
+        @NamedQuery(name = "UserEntity.findByPassword", query = "SELECT u FROM UserEntity u WHERE u.password = :password"),
+        @NamedQuery(name = "UserEntity.findByUsername", query = "SELECT u FROM UserEntity u WHERE u.username = :username"),
+        @NamedQuery(name = "UserEntity.findByEmail", query = "SELECT u FROM UserEntity u WHERE u.email = :email")
+})
+@SuppressWarnings("UnusedDeclaration")
+public class UserEntity implements Serializable {
+
+    @Id
+    @Column(name = "USERID", nullable = false)
+    private String userid;
+
+    @Column(name = "PASSWORD", nullable = false)
+    private String password;
+
+    @Column(name = "USERNAME")
+    private String username;
+
+    @Column(name = "EMAIL")
+    private String email;
+
+    @OneToMany(cascade = CascadeType.ALL, mappedBy = "userEntity")
+    private Collection<BookmarkEntity> bookmarkEntityCollection;
+
+    /**
+     * Creates a new instance of UserEntity
+     */
+    public UserEntity() {
+    }
+
+    /**
+     * Creates a new instance of UserEntity with the specified values.
+     *
+     * @param userid the userid of the UserEntity
+     */
+    public UserEntity(String userid) {
+        this.userid = userid;
+    }
+
+    /**
+     * Creates a new instance of UserEntity with the specified values.
+     *
+     * @param userid the userid of the UserEntity
+     * @param password the password of the UserEntity
+     */
+    public UserEntity(String userid, String password) {
+        this.userid = userid;
+        this.password = password;
+    }
+
+    /**
+     * Gets the userid of this UserEntity.
+     *
+     * @return the userid
+     */
+    public String getUserid() {
+        return this.userid;
+    }
+
+    /**
+     * Sets the userid of this UserEntity to the specified value.
+     *
+     * @param userid the new userid
+     */
+    public void setUserid(String userid) {
+        this.userid = userid;
+    }
+
+    /**
+     * Gets the password of this UserEntity.
+     *
+     * @return the password
+     */
+    public String getPassword() {
+        return this.password;
+    }
+
+    /**
+     * Sets the password of this UserEntity to the specified value.
+     *
+     * @param password the new password
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    /**
+     * Gets the username of this UserEntity.
+     *
+     * @return the username
+     */
+    public String getUsername() {
+        return this.username;
+    }
+
+    /**
+     * Sets the username of this UserEntity to the specified value.
+     *
+     * @param username the new username
+     */
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    /**
+     * Gets the email of this UserEntity.
+     *
+     * @return the email
+     */
+    public String getEmail() {
+        return this.email;
+    }
+
+    /**
+     * Sets the email of this UserEntity to the specified value.
+     *
+     * @param email the new email
+     */
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    /**
+     * Gets the bookmarkEntityCollection of this UserEntity.
+     *
+     * @return the bookmarkEntityCollection
+     */
+    public Collection<BookmarkEntity> getBookmarkEntityCollection() {
+        return this.bookmarkEntityCollection;
+    }
+
+    /**
+     * Sets the bookmarkEntityCollection of this UserEntity to the specified value.
+     *
+     * @param bookmarkEntityCollection the new bookmarkEntityCollection
+     */
+    public void setBookmarkEntityCollection(Collection<BookmarkEntity> bookmarkEntityCollection) {
+        this.bookmarkEntityCollection = bookmarkEntityCollection;
+    }
+
+    /**
+     * Returns a hash code value for the object.  This implementation computes
+     * a hash code value based on the id fields in this object.
+     *
+     * @return a hash code value for this object.
+     */
+    @Override
+    public int hashCode() {
+        int hash = 0;
+        hash += (this.userid != null ? this.userid.hashCode() : 0);
+        return hash;
+    }
+
+    /**
+     * Determines whether another object is equal to this UserEntity.  The result is
+     * <code>true</code> if and only if the argument is not null and is a UserEntity object that
+     * has the same id field values as this object.
+     *
+     * @param object the reference object with which to compare
+     * @return <code>true</code> if this object is the same as the argument;
+     *         <code>false</code> otherwise.
+     */
+    @SuppressWarnings("StringEquality")
+    @Override
+    public boolean equals(Object object) {
+        // TODO: Warning - this method won't work in the case the id fields are not set
+        if (!(object instanceof UserEntity)) {
+            return false;
+        }
+        UserEntity other = (UserEntity) object;
+        return !(this.userid != other.userid && (this.userid == null || !this.userid.equals(other.userid)));
+    }
+
+    /**
+     * Returns a string representation of the object.  This implementation constructs
+     * that representation based on the id fields.
+     *
+     * @return a string representation of the object.
+     */
+    @Override
+    public String toString() {
+        return "UserEntity{"
+               + "userid='" + userid + '\''
+               + '}';
+    }
+}
diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/exception/ExtendedNotFoundException.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/exception/ExtendedNotFoundException.java
new file mode 100644
index 0000000..8a2926e
--- /dev/null
+++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/exception/ExtendedNotFoundException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark_em.exception;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ExtendedNotFoundException extends NotFoundException {
+
+    public ExtendedNotFoundException(final String message) {
+        super(Response.status(Response.Status.NOT_FOUND).entity(message).build());
+    }
+}
diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/resource/BookmarkResource.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/resource/BookmarkResource.java
new file mode 100644
index 0000000..3f1bc40
--- /dev/null
+++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/resource/BookmarkResource.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark_em.resource;
+
+import java.util.Date;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.UriInfo;
+
+import javax.persistence.EntityManager;
+import javax.transaction.UserTransaction;
+
+import org.glassfish.jersey.examples.bookmark_em.entity.BookmarkEntity;
+import org.glassfish.jersey.examples.bookmark_em.entity.BookmarkEntityPK;
+import org.glassfish.jersey.examples.bookmark_em.entity.UserEntity;
+import org.glassfish.jersey.examples.bookmark_em.exception.ExtendedNotFoundException;
+import org.glassfish.jersey.examples.bookmark_em.util.tx.TransactionManager;
+import org.glassfish.jersey.examples.bookmark_em.util.tx.Transactional;
+
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class BookmarkResource {
+
+    UriInfo uriInfo; // actual uri info provided by parent resource
+    EntityManager em; // entity manager provided by parent resource
+    UserTransaction utx; //user transaction provided by parent resource
+
+    BookmarkEntity bookmarkEntity;
+
+    /**
+     * Creates a new instance of UserResource
+     */
+    public BookmarkResource(UriInfo uriInfo, EntityManager em, UserTransaction utx, UserEntity userEntity, String bmid) {
+        this.uriInfo = uriInfo;
+        this.em = em;
+        this.utx = utx;
+
+        bookmarkEntity = em.find(BookmarkEntity.class, new BookmarkEntityPK(bmid, userEntity.getUserid()));
+        if (null == bookmarkEntity) {
+            throw new ExtendedNotFoundException("bookmark with userid="
+                    + userEntity.getUserid() + " and bmid="
+                    + bmid + " not found\n");
+        }
+        bookmarkEntity.setUserEntity(userEntity);
+    }
+
+    @GET
+    @Produces("application/json")
+    public JSONObject getBookmark() {
+        return asJson();
+    }
+
+    @PUT
+    @Consumes("application/json")
+    public void putBookmark(JSONObject jsonEntity) throws JSONException {
+
+        bookmarkEntity.setLdesc(jsonEntity.getString("ldesc"));
+        bookmarkEntity.setSdesc(jsonEntity.getString("sdesc"));
+        bookmarkEntity.setUpdated(new Date());
+
+        TransactionManager.manage(utx, new Transactional(em) {
+            public void transact() {
+                em.merge(bookmarkEntity);
+            }
+        });
+    }
+
+    @DELETE
+    public void deleteBookmark() {
+        TransactionManager.manage(utx, new Transactional(em) {
+            public void transact() {
+                em.persist(bookmarkEntity);
+                UserEntity userEntity = bookmarkEntity.getUserEntity();
+                userEntity.getBookmarkEntityCollection().remove(bookmarkEntity);
+                em.merge(userEntity);
+                em.remove(bookmarkEntity);
+            }
+        });
+    }
+
+    public String asString() {
+        return toString();
+    }
+
+    public JSONObject asJson() {
+        try {
+            return new JSONObject()
+                    .put("userid", bookmarkEntity.getBookmarkEntityPK().getUserid())
+                    .put("sdesc", bookmarkEntity.getSdesc())
+                    .put("ldesc", bookmarkEntity.getLdesc())
+                    .put("uri", bookmarkEntity.getUri());
+        } catch (JSONException je) {
+            return null;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return bookmarkEntity.getBookmarkEntityPK().getUserid();
+    }
+}
diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/resource/BookmarksResource.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/resource/BookmarksResource.java
new file mode 100644
index 0000000..7b566e3
--- /dev/null
+++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/resource/BookmarksResource.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark_em.resource;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Date;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import javax.persistence.EntityManager;
+import javax.transaction.UserTransaction;
+
+import org.glassfish.jersey.examples.bookmark_em.entity.BookmarkEntity;
+import org.glassfish.jersey.examples.bookmark_em.util.tx.TransactionManager;
+import org.glassfish.jersey.examples.bookmark_em.util.tx.Transactional;
+
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class BookmarksResource {
+
+    UriInfo uriInfo; // actual uri info
+    EntityManager em; // entity manager provided by parent resource
+    UserTransaction utx; // user transaction provided by parent resource
+
+    UserResource userResource; // parent user resource
+
+    /**
+     * Creates a new instance of BookmarksResource
+     */
+    public BookmarksResource(UriInfo uriInfo, EntityManager em, UserTransaction utx, UserResource userResource) {
+        this.uriInfo = uriInfo;
+        this.em = em;
+        this.utx = utx;
+        this.userResource = userResource;
+    }
+
+    public Collection<BookmarkEntity> getBookmarks() {
+        return userResource.getUserEntity().getBookmarkEntityCollection();
+    }
+
+    @Path("{bmid: .+}")
+    public BookmarkResource getBookmark(@PathParam("bmid") String bmid) {
+        return new BookmarkResource(uriInfo, em, utx, userResource.getUserEntity(), bmid);
+    }
+
+    @GET
+    @Produces("application/json")
+    public JSONArray getBookmarksAsJsonArray() {
+        JSONArray uriArray = new JSONArray();
+        for (BookmarkEntity bookmarkEntity : getBookmarks()) {
+            UriBuilder ub = uriInfo.getAbsolutePathBuilder();
+            URI bookmarkUri = ub
+                    .path(bookmarkEntity.getBookmarkEntityPK().getBmid())
+                    .build();
+            uriArray.put(bookmarkUri.toASCIIString());
+        }
+        return uriArray;
+    }
+
+    @POST
+    @Consumes("application/json")
+    public Response postForm(JSONObject bookmark) throws JSONException {
+        final BookmarkEntity bookmarkEntity = new BookmarkEntity(getBookmarkId(bookmark.getString("uri")),
+                userResource.getUserEntity().getUserid());
+
+        bookmarkEntity.setUri(bookmark.getString("uri"));
+        bookmarkEntity.setUpdated(new Date());
+        bookmarkEntity.setSdesc(bookmark.getString("sdesc"));
+        bookmarkEntity.setLdesc(bookmark.getString("ldesc"));
+        userResource.getUserEntity().getBookmarkEntityCollection().add(bookmarkEntity);
+
+        TransactionManager.manage(utx, new Transactional(em) {
+            public void transact() {
+                em.merge(userResource.getUserEntity());
+            }
+        });
+
+        URI bookmarkUri = uriInfo.getAbsolutePathBuilder()
+                .path(bookmarkEntity.getBookmarkEntityPK().getBmid())
+                .build();
+        return Response.created(bookmarkUri).build();
+    }
+
+    private String getBookmarkId(String uri) {
+        return uri;
+    }
+}
diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/resource/UserResource.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/resource/UserResource.java
new file mode 100644
index 0000000..3ffb842
--- /dev/null
+++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/resource/UserResource.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark_em.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import javax.persistence.EntityManager;
+import javax.transaction.UserTransaction;
+
+import org.glassfish.jersey.examples.bookmark_em.entity.UserEntity;
+import org.glassfish.jersey.examples.bookmark_em.exception.ExtendedNotFoundException;
+import org.glassfish.jersey.examples.bookmark_em.util.tx.TransactionManager;
+import org.glassfish.jersey.examples.bookmark_em.util.tx.Transactional;
+
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class UserResource {
+
+    String userid; // userid from url
+    UserEntity userEntity; // appropriate jpa user entity
+
+    UriInfo uriInfo; // actual uri info provided by parent resource
+    EntityManager em; // entity manager provided by parent resource
+    UserTransaction utx; // user transaction provided by parent resource
+
+    /**
+     * Creates a new instance of UserResource
+     */
+    public UserResource(UriInfo uriInfo, EntityManager em, UserTransaction utx, String userid) {
+        this.uriInfo = uriInfo;
+        this.userid = userid;
+        this.em = em;
+        this.utx = utx;
+        userEntity = em.find(UserEntity.class, userid);
+    }
+
+    @Path("bookmarks/")
+    public BookmarksResource getBookmarksResource() {
+        if (null == userEntity) {
+            throw new ExtendedNotFoundException("userid " + userid + " does not exist!");
+        }
+        return new BookmarksResource(uriInfo, em, utx, this);
+    }
+
+    @GET
+    @Produces("application/json")
+    public JSONObject getUser() throws JSONException {
+        if (null == userEntity) {
+            throw new ExtendedNotFoundException("userid " + userid + "does not exist!");
+        }
+        return new JSONObject()
+                .put("userid", userEntity.getUserid())
+                .put("username", userEntity.getUsername())
+                .put("email", userEntity.getEmail())
+                .put("password", userEntity.getPassword())
+                .put("bookmarks", uriInfo.getAbsolutePathBuilder().path("bookmarks").build());
+    }
+
+    @PUT
+    @Consumes("application/json")
+    public Response putUser(JSONObject jsonEntity) throws JSONException {
+
+        String jsonUserid = jsonEntity.getString("userid");
+
+        if ((null != jsonUserid) && !jsonUserid.equals(userid)) {
+            return Response.status(409).entity("userids differ!\n").build();
+        }
+
+        final boolean newRecord = (null == userEntity); // insert or update ?
+
+        if (newRecord) { // new user record to be inserted
+            userEntity = new UserEntity();
+            userEntity.setUserid(userid);
+        }
+        userEntity.setUsername(jsonEntity.getString("username"));
+        userEntity.setEmail(jsonEntity.getString("email"));
+        userEntity.setPassword(jsonEntity.getString("password"));
+
+        if (newRecord) {
+            TransactionManager.manage(utx, new Transactional(em) {
+                public void transact() {
+                    em.joinTransaction();
+                    em.persist(userEntity);
+                }
+            });
+            return Response.created(uriInfo.getAbsolutePath()).build();
+        } else {
+            TransactionManager.manage(utx, new Transactional(em) {
+                public void transact() {
+                    em.merge(userEntity);
+                }
+            });
+            return Response.noContent().build();
+        }
+    }
+
+    @DELETE
+    public void deleteUser() {
+        if (null == userEntity) {
+            throw new ExtendedNotFoundException("userid " + userid + "does not exist!");
+        }
+
+        TransactionManager.manage(utx, new Transactional(em) {
+            public void transact() {
+                em.persist(userEntity);
+                em.remove(userEntity);
+            }
+        });
+    }
+
+    @Override
+    public String toString() {
+        return userEntity.getUserid();
+    }
+
+    public UserEntity getUserEntity() {
+        return userEntity;
+    }
+}
diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/resource/UsersResource.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/resource/UsersResource.java
new file mode 100644
index 0000000..5c7025a
--- /dev/null
+++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/resource/UsersResource.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark_em.resource;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import javax.annotation.ManagedBean;
+import javax.annotation.Resource;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.transaction.UserTransaction;
+
+import org.glassfish.jersey.examples.bookmark_em.entity.UserEntity;
+
+import org.codehaus.jettison.json.JSONArray;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/users/")
+@ManagedBean
+public class UsersResource {
+
+    // injected EntityManager property
+    @PersistenceContext(unitName = "BookmarkPU")
+    private EntityManager em;
+
+    // injected UserTransaction
+    @Resource
+    private UserTransaction utx;
+
+    @Context
+    UriInfo uriInfo;
+
+    /**
+     * Creates a new instance of Users
+     */
+    public UsersResource() {
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<UserEntity> getUsers() {
+        return em.createQuery("SELECT u from UserEntity u").getResultList();
+    }
+
+    @Path("{userid}/")
+    public UserResource getUser(@PathParam("userid") String userid) {
+        return new UserResource(uriInfo, em, utx, userid);
+    }
+
+    @GET
+    @Produces("application/json")
+    public JSONArray getUsersAsJsonArray() {
+        JSONArray uriArray = new JSONArray();
+        for (UserEntity userEntity : getUsers()) {
+            UriBuilder ub = uriInfo.getAbsolutePathBuilder();
+            URI userUri = ub
+                    .path(userEntity.getUserid())
+                    .build();
+            uriArray.put(userUri.toASCIIString());
+        }
+        return uriArray;
+    }
+}
diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/util/tx/TransactionManager.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/util/tx/TransactionManager.java
new file mode 100644
index 0000000..5f4d844
--- /dev/null
+++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/util/tx/TransactionManager.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark_em.util.tx;
+
+import javax.ws.rs.WebApplicationException;
+
+import javax.transaction.SystemException;
+import javax.transaction.UserTransaction;
+
+/**
+ * @author Paul Sandoz
+ */
+public final class TransactionManager {
+
+    public static void manage(UserTransaction utx, Transactional t) {
+        try {
+            utx.begin();
+            if (t.joinTransaction) {
+                t.em.joinTransaction();
+            }
+            t.transact();
+            utx.commit();
+        } catch (Exception e) {
+            try {
+                utx.rollback();
+            } catch (SystemException se) {
+                throw new WebApplicationException(se);
+            }
+            throw new WebApplicationException(e);
+        }
+    }
+}
diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/util/tx/Transactional.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/util/tx/Transactional.java
new file mode 100644
index 0000000..78a4d42
--- /dev/null
+++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/util/tx/Transactional.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark_em.util.tx;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author Paul Sandoz
+ */
+public abstract class Transactional {
+
+    EntityManager em;
+    boolean joinTransaction;
+
+    public Transactional(EntityManager em, boolean joinTransaction) {
+        this.em = em;
+        this.joinTransaction = joinTransaction;
+    }
+
+    public Transactional(EntityManager em) {
+        this(em, true);
+    }
+
+    public abstract void transact();
+}
diff --git a/examples/bookmark-em/src/main/resources/META-INF/persistence.xml b/examples/bookmark-em/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000..6df08eb
--- /dev/null
+++ b/examples/bookmark-em/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
+    <persistence-unit name="BookmarkPU" transaction-type="JTA">
+        <jta-data-source>jdbc/__default</jta-data-source>
+        <properties>
+            <!-- drop and create tables at deployment -->
+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
+            <!-- log sqls executed in server.log -->
+            <property name="eclipselink.logging.level" value="FINE"/>
+            <!-- Instruct EclipseLink to not log execeptions it throws. Lets the application decide about it-->
+            <property name="eclipselink.logging.exceptions" value="false"/>
+        </properties>
+    </persistence-unit>
+</persistence>
diff --git a/examples/bookmark-em/src/main/webapp/WEB-INF/glassfish-web.xml b/examples/bookmark-em/src/main/webapp/WEB-INF/glassfish-web.xml
new file mode 100644
index 0000000..4ed5567
--- /dev/null
+++ b/examples/bookmark-em/src/main/webapp/WEB-INF/glassfish-web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
+<glassfish-web-app error-url="">
+    <context-root>/Bookmark-EM</context-root>
+    <class-loader delegate="true" />
+    <jsp-config>
+        <property name="classdebuginfo" value="true">
+            <description>Enable debug info compilation in the generated servlet class</description>
+        </property>
+        <property name="keepgenerated" value="true">
+            <description>Keep a copy of the generated servlet class' java code.</description>
+        </property>
+        <property name="mappedfile" value="true">
+            <description>Maintain a one-to-one correspondence between static content and the generated servlet class' java code</description>
+        </property>
+    </jsp-config>
+</glassfish-web-app>
diff --git a/examples/bookmark-em/src/main/webapp/WEB-INF/web.xml b/examples/bookmark-em/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..e09e223
--- /dev/null
+++ b/examples/bookmark-em/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.examples.bookmark_em.MyApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.examples.bookmark_em.MyApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.examples.bookmark_em.MyApplication</servlet-name>
+        <url-pattern>/resources/*</url-pattern>
+    </servlet-mapping>
+
+    <persistence-unit-ref>
+        <persistence-unit-ref-name>persistence/bookmark</persistence-unit-ref-name>
+        <persistence-unit-name>BookmarkPU</persistence-unit-name>
+    </persistence-unit-ref>
+    <resource-ref>
+        <res-ref-name>UserTransaction</res-ref-name>
+        <res-type>javax.transaction.UserTransaction</res-type>
+        <res-auth>Container</res-auth>
+    </resource-ref>
+</web-app>
diff --git a/examples/bookmark-em/src/test/java/org/glassfish/jersey/examples/bookmark_em/BookmarkTest.java b/examples/bookmark-em/src/test/java/org/glassfish/jersey/examples/bookmark_em/BookmarkTest.java
new file mode 100644
index 0000000..0dab7fa
--- /dev/null
+++ b/examples/bookmark-em/src/test/java/org/glassfish/jersey/examples/bookmark_em/BookmarkTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark_em;
+
+import java.net.URI;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.FixMethodOrder;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * TODO un-ignore once Jersey supports @ManagedBean
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Michal Gajdos
+ */
+@Ignore("un-ignore once Jersey supports @ManagedBean")
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class BookmarkTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return URI.create(super.getBaseUri().toString() + "Bookmark-EM");
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(new JettisonFeature());
+    }
+
+    @Test
+    public void step1_getUsers() {
+        JSONArray users = target().path("resources/users/").request("application/json").get(JSONArray.class);
+        assertTrue(users != null);
+    }
+
+    @Test
+    public void step2_createUser() {
+        boolean thrown = false;
+        JSONObject user = new JSONObject();
+
+        try {
+            user.put("userid", "testuid").put("password", "test").put("email", "test@test.net").put("username", "Test User");
+            target().path("resources/users/testuid").request().put(Entity.entity(user, "application/json"));
+        } catch (Exception e) {
+            e.printStackTrace();
+            thrown = true;
+        }
+
+        assertFalse(thrown);
+    }
+
+    @Test
+    public void step3_getUsers2() {
+        JSONArray users = target().path("resources/users/").request("application/json").get(JSONArray.class);
+        assertTrue(users != null);
+        assertTrue(users.length() == 1);
+    }
+
+    @Test
+    public void step4_updateUser() {
+        boolean thrown = false;
+
+        try {
+            JSONObject user = target().path("resources/users/testuid").request("application/json").get(JSONObject.class);
+
+            user.put("password", "NEW PASSWORD").put("email", "NEW@EMAIL.NET").put("username", "UPDATED TEST USER");
+            target().path("resources/users/testuid").request().put(Entity.entity(user, "application/json"));
+
+            user = target().path("resources/users/testuid").request("application/json").get(JSONObject.class);
+
+            assertEquals(user.get("username"), "UPDATED TEST USER");
+            assertEquals(user.get("email"), "NEW@EMAIL.NET");
+            assertEquals(user.get("password"), "NEW PASSWORD");
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            thrown = true;
+        }
+
+        assertFalse(thrown);
+    }
+
+    // this is ugly but it would be probably uglier when divided into separate
+    // test cases
+    @Test
+    public void step5_getUserBookmarkList() {
+        boolean thrown = false;
+
+        try {
+            JSONObject user = target().path("resources/users/testuid").request("application/json").get(JSONObject.class);
+            assertTrue(user != null);
+
+            final WebTarget webTarget = target(user.getString("bookmarks"));
+
+            JSONObject bookmark = new JSONObject();
+            bookmark.put("uri", "http://java.sun.com").put("sdesc", "test desc").put("ldesc", "long test description");
+            webTarget.request().post(Entity.entity(bookmark, "application/json"));
+
+            JSONArray bookmarks = webTarget.request("application/json").get(JSONArray.class);
+            assertTrue(bookmarks != null);
+            int bookmarksSize = bookmarks.length();
+
+            String testBookmarkUrl = bookmarks.getString(0);
+
+            final WebTarget bookmarkResource = target().path(testBookmarkUrl);
+            bookmark = bookmarkResource.request("application/json").get(JSONObject.class);
+            assertTrue(bookmark != null);
+
+            bookmarkResource.request().delete();
+
+            bookmarks = target().path("resources/users/testuid/bookmarks").request("application/json").get(JSONArray.class);
+            assertTrue(bookmarks != null);
+            assertTrue(bookmarks.length() == (bookmarksSize - 1));
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            thrown = true;
+        }
+
+        assertFalse(thrown);
+    }
+
+    @Test
+    public void step6_deleteUser() {
+        boolean thrown = false;
+
+        try {
+            target().path("resources/users/testuid").request().delete();
+        } catch (Exception e) {
+            e.printStackTrace();
+            thrown = true;
+        }
+
+        assertFalse(thrown);
+    }
+}
diff --git a/examples/bookmark/README.MD b/examples/bookmark/README.MD
new file mode 100644
index 0000000..dca924a
--- /dev/null
+++ b/examples/bookmark/README.MD
@@ -0,0 +1,81 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Bookmark Example
+================
+
+This example demonstrates how to use JPA in the backend. The example is
+based on bookmark example from the [RESTful Web
+Services](http://www.oreilly.com/catalog/9780596529260/) book, which was
+inspired by the [http://del.icio.us/](http://del.icio.us/v1) web
+service.
+
+A bookmark web application is presented that is capable of maintaining
+users and their bookmarks.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                                | Resource class      | HTTP methods                                          | Notes
+--------------------------------------- | ------------------- | ----------------------------------------------------- | --------------------------------------------------------
+**_/users_**                            |  UsersResource      |  GET                                                  |  Returns a list of users.
+**_/users/{userid}_**                   |  UserResource       |  GET, PUT (used also for creating new users), DELETE  |  Returns user details
+**_/users/{userid}/bookmarks_**         |  BookmarksResource  |  GET, POST                                            |  Returns a list of bookmarks for actual user resource.
+**_/users/{userid}/bookmarks/{bmid}_**  |  BookmarkResource   |  GET, PUT, DELETE                                     |  Returns bookmark uri and a long and short description.
+
+Running the Example
+-------------------
+
+Bookmark example runs on Glassfish 3.x application servers
+([https://javaee.github.io/glassfish/](https://javaee.github.io/glassfish/)) and needs a
+running JavaDB
+(<http://www.oracle.com/technetwork/java/javadb/overview/index.html>)
+instance for underlying data (it comes along with GlassFish).
+
+Presuming, you have installed Glassfish 3.1 `AS_HOME` variable should
+point to your glassfish installation directory.
+
+#### Building And Starting The Bookmark Service
+
+If `.asadminpass` file is missing at your home directory, you will need
+to run the following command to get it created
+
+    $AS_HOME/bin/asadmin start-domain
+    $AS_HOME/bin/asadmin login
+
+You then build and run the example by
+
+    mvn package
+    $AS_HOME/bin/asadmin start-domain
+    $AS_HOME/bin/asadmin start-database
+    $AS_HOME/bin/asadmin create-jdbc-connection-pool \
+             --datasourceclassname org.apache.derby.jdbc.ClientDataSource \
+             --restype javax.sql.DataSource \
+             --property "portnumber=1527:password=REST:user=REST:serverName=localhost:databaseName=BookmarkDB:connectionAttributes=;create\=true" bookmarkPool
+    $AS_HOME/bin/asadmin create-jdbc-resource --connectionpoolid bookmarkPool jdbc/bookmarkSample
+    $AS_HOME/bin/asadmin deploy target/bookmark.war
+
+**Notice:** <span style="color: #f00">On Windows</span>: you might need
+to replace asadmin with asadmin.bat. Also, because it seems there are
+some issues with copy-pasting backslashes, you will also need to use the
+following one-liner instead of the above mentioned complex command:
+
+    asadmin create-jdbc-connection-pool --datasourceclassname org.apache.derby.jdbc.ClientDataSource --restype javax.sql.DataSource --property "portnumber=1527:password=REST:user=REST:serverName=localhost:databaseName=BookmarkDB:connectionAttributes=;create\=true" bookmarkPool
+
+and make sure you do not miss the backslash (\\) in `create\=true` part.
+
+#### Test Client Running
+
+    mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.external.ExternalTestContainerFactory -Djersey.config.test.logging.enable=true -Djersey.config.test.logging.dumpEntity=true -Djersey.config.test.container.port=8080 -Ptest
+
+Links
+-----
+
+<http://java.net/projects/bpcatalog/sources/svn/content/trunk/ee5/docs/persistence/webonlyapp.html?raw=true>
\ No newline at end of file
diff --git a/examples/bookmark/pom.xml b/examples/bookmark/pom.xml
new file mode 100644
index 0000000..4db7fc9
--- /dev/null
+++ b/examples/bookmark/pom.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>bookmark</artifactId>
+    <name>jersey-examples-bookmark</name>
+    <packaging>war</packaging>
+
+    <description>Jersey Bookmark example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jettison</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.persistence</groupId>
+            <artifactId>persistence-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.transaction</groupId>
+            <artifactId>jta</artifactId>
+            <version>1.1</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Run the application using "mvn embedded-glassfish:run" -->
+            <plugin>
+                <groupId>org.glassfish.embedded</groupId>
+                <artifactId>maven-embedded-glassfish-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>run-external-tests</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <jersey.config.test.container.factory>${external.container.factory}</jersey.config.test.container.factory>
+                                <jersey.config.test.container.port>${external.container.port}</jersey.config.test.container.port>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <properties>
+                <!-- External test container configuration is done via properties to allow overriding via command line. -->
+                <external.container.factory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</external.container.factory>
+                <external.container.port>8080</external.container.port>
+                <maven.test.skip>false</maven.test.skip>
+            </properties>
+        </profile>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <maven.test.skip>true</maven.test.skip>
+    </properties>
+
+</project>
diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/MyApplication.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/MyApplication.java
new file mode 100644
index 0000000..6d875a8
--- /dev/null
+++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/MyApplication.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.examples.bookmark.resource.UsersResource;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * @author Michal Gajdos
+ */
+@ApplicationPath("/")
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        registerClasses(UsersResource.class);
+        register(new JettisonFeature());
+    }
+}
diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntity.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntity.java
new file mode 100644
index 0000000..2c692bf
--- /dev/null
+++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntity.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+/**
+ * Entity class BookmarkEntity.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Entity
+@Table(name = "BOOKMARKS")
+@NamedQueries({
+        @NamedQuery(name = "BookmarkEntity.findByUserid",
+                query = "SELECT b FROM BookmarkEntity b WHERE b.bookmarkEntityPK.userid = :userid"),
+        @NamedQuery(name = "BookmarkEntity.findByBmid",
+                query = "SELECT b FROM BookmarkEntity b WHERE b.bookmarkEntityPK.bmid = :bmid"),
+        @NamedQuery(name = "BookmarkEntity.findByUri", query = "SELECT b FROM BookmarkEntity b WHERE b.uri = :uri"),
+        @NamedQuery(name = "BookmarkEntity.findByUpdated", query = "SELECT b FROM BookmarkEntity b WHERE b.updated = :updated"),
+        @NamedQuery(name = "BookmarkEntity.findByLdesc", query = "SELECT b FROM BookmarkEntity b WHERE b.ldesc = :ldesc"),
+        @NamedQuery(name = "BookmarkEntity.findBySdesc", query = "SELECT b FROM BookmarkEntity b WHERE b.sdesc = :sdesc")
+})
+@SuppressWarnings("UnusedDeclaration")
+public class BookmarkEntity implements Serializable {
+
+    /**
+     * EmbeddedId primary key field
+     */
+    @EmbeddedId
+    protected BookmarkEntityPK bookmarkEntityPK;
+
+    @Column(name = "URI", nullable = false)
+    private String uri;
+
+    @Column(name = "UPDATED")
+    @Temporal(TemporalType.TIMESTAMP)
+    private Date updated;
+
+    @Column(name = "LDESC")
+    private String ldesc;
+
+    @Column(name = "SDESC")
+    private String sdesc;
+
+    @JoinColumn(name = "USERID", referencedColumnName = "USERID", insertable = false, updatable = false)
+    @ManyToOne
+    private UserEntity userEntity;
+
+    /**
+     * Creates a new instance of BookmarkEntity
+     */
+    public BookmarkEntity() {
+    }
+
+    /**
+     * Creates a new instance of BookmarkEntity with the specified values.
+     *
+     * @param bookmarkEntityPK the bookmarkEntityPK of the BookmarkEntity
+     */
+    public BookmarkEntity(BookmarkEntityPK bookmarkEntityPK) {
+        this.bookmarkEntityPK = bookmarkEntityPK;
+    }
+
+    /**
+     * Creates a new instance of BookmarkEntity with the specified values.
+     *
+     * @param bookmarkEntityPK the bookmarkEntityPK of the BookmarkEntity
+     * @param uri the uri of the BookmarkEntity
+     */
+    public BookmarkEntity(BookmarkEntityPK bookmarkEntityPK, String uri) {
+        this.bookmarkEntityPK = bookmarkEntityPK;
+        this.uri = uri;
+    }
+
+    /**
+     * Creates a new instance of BookmarkEntityPK with the specified values.
+     *
+     * @param bmid the bmid of the BookmarkEntityPK
+     * @param userid the userid of the BookmarkEntityPK
+     */
+    public BookmarkEntity(String bmid, String userid) {
+        this.bookmarkEntityPK = new BookmarkEntityPK(bmid, userid);
+    }
+
+    /**
+     * Gets the bookmarkEntityPK of this BookmarkEntity.
+     *
+     * @return the bookmarkEntityPK
+     */
+    public BookmarkEntityPK getBookmarkEntityPK() {
+        return this.bookmarkEntityPK;
+    }
+
+    /**
+     * Sets the bookmarkEntityPK of this BookmarkEntity to the specified value.
+     *
+     * @param bookmarkEntityPK the new bookmarkEntityPK
+     */
+    public void setBookmarkEntityPK(BookmarkEntityPK bookmarkEntityPK) {
+        this.bookmarkEntityPK = bookmarkEntityPK;
+    }
+
+    /**
+     * Gets the uri of this BookmarkEntity.
+     *
+     * @return the uri
+     */
+    public String getUri() {
+        return this.uri;
+    }
+
+    /**
+     * Sets the uri of this BookmarkEntity to the specified value.
+     *
+     * @param uri the new uri
+     */
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    /**
+     * Gets the updated of this BookmarkEntity.
+     *
+     * @return the updated
+     */
+    public Date getUpdated() {
+        return this.updated;
+    }
+
+    /**
+     * Sets the updated of this BookmarkEntity to the specified value.
+     *
+     * @param updated the new updated
+     */
+    public void setUpdated(Date updated) {
+        this.updated = updated;
+    }
+
+    /**
+     * Gets the ldesc of this BookmarkEntity.
+     *
+     * @return the ldesc
+     */
+    public String getLdesc() {
+        return this.ldesc;
+    }
+
+    /**
+     * Sets the ldesc of this BookmarkEntity to the specified value.
+     *
+     * @param ldesc the new ldesc
+     */
+    public void setLdesc(String ldesc) {
+        this.ldesc = ldesc;
+    }
+
+    /**
+     * Gets the sdesc of this BookmarkEntity.
+     *
+     * @return the sdesc
+     */
+    public String getSdesc() {
+        return this.sdesc;
+    }
+
+    /**
+     * Sets the sdesc of this BookmarkEntity to the specified value.
+     *
+     * @param sdesc the new sdesc
+     */
+    public void setSdesc(String sdesc) {
+        this.sdesc = sdesc;
+    }
+
+    /**
+     * Gets the userEntity of this BookmarkEntity.
+     *
+     * @return the userEntity
+     */
+    public UserEntity getUserEntity() {
+        return this.userEntity;
+    }
+
+    /**
+     * Sets the userEntity of this BookmarkEntity to the specified value.
+     *
+     * @param userEntity the new userEntity
+     */
+    public void setUserEntity(UserEntity userEntity) {
+        this.userEntity = userEntity;
+    }
+
+    /**
+     * Returns a hash code value for the object.  This implementation computes
+     * a hash code value based on the id fields in this object.
+     *
+     * @return a hash code value for this object.
+     */
+    @Override
+    public int hashCode() {
+        int hash = 0;
+        hash += (this.bookmarkEntityPK != null ? this.bookmarkEntityPK.hashCode() : 0);
+        return hash;
+    }
+
+    /**
+     * Determines whether another object is equal to this BookmarkEntity.  The result is
+     * <code>true</code> if and only if the argument is not null and is a BookmarkEntity object that
+     * has the same id field values as this object.
+     *
+     * @param object the reference object with which to compare
+     * @return <code>true</code> if this object is the same as the argument;
+     *         <code>false</code> otherwise.
+     */
+    @Override
+    public boolean equals(Object object) {
+        // TODO: Warning - this method won't work in the case the id fields are not set
+        if (!(object instanceof BookmarkEntity)) {
+            return false;
+        }
+        BookmarkEntity other = (BookmarkEntity) object;
+        return !(this.bookmarkEntityPK != other.bookmarkEntityPK && (this.bookmarkEntityPK == null || !this.bookmarkEntityPK
+                .equals(other.bookmarkEntityPK)));
+    }
+
+    /**
+     * Returns a string representation of the object.  This implementation constructs
+     * that representation based on the id fields.
+     *
+     * @return a string representation of the object.
+     */
+    @Override
+    public String toString() {
+        return "BookmarkEntity{"
+               + "bookmarkEntityPK=" + bookmarkEntityPK
+               + '}';
+    }
+}
diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntityPK.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntityPK.java
new file mode 100644
index 0000000..144bc8a
--- /dev/null
+++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntityPK.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark.entity;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Embeddable;
+
+/**
+ * Primary Key class BookmarkEntityPK for entity class BookmarkEntity.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("UnusedDeclaration")
+@Embeddable
+public class BookmarkEntityPK implements Serializable {
+
+    @Column(name = "USERID", nullable = false)
+    private String userid;
+
+    @Column(name = "BMID", nullable = false)
+    private String bmid;
+
+    /**
+     * Creates a new instance of BookmarkEntityPK
+     */
+    public BookmarkEntityPK() {
+    }
+
+    /**
+     * Creates a new instance of BookmarkEntityPK with the specified values.
+     *
+     * @param bmid the bmid of the BookmarkEntityPK
+     * @param userid the userid of the BookmarkEntityPK
+     */
+    public BookmarkEntityPK(String bmid, String userid) {
+        this.bmid = bmid;
+        this.userid = userid;
+    }
+
+    /**
+     * Gets the userid of this BookmarkEntityPK.
+     *
+     * @return the userid
+     */
+    public String getUserid() {
+        return this.userid;
+    }
+
+    /**
+     * Sets the userid of this BookmarkEntityPK to the specified value.
+     *
+     * @param userid the new userid
+     */
+    public void setUserid(String userid) {
+        this.userid = userid;
+    }
+
+    /**
+     * Gets the bmid of this BookmarkEntityPK.
+     *
+     * @return the bmid
+     */
+    public String getBmid() {
+        return this.bmid;
+    }
+
+    /**
+     * Sets the bmid of this BookmarkEntityPK to the specified value.
+     *
+     * @param bmid the new bmid
+     */
+    public void setBmid(String bmid) {
+        this.bmid = bmid;
+    }
+
+    /**
+     * Returns a hash code value for the object.  This implementation computes
+     * a hash code value based on the id fields in this object.
+     *
+     * @return a hash code value for this object.
+     */
+    @Override
+    public int hashCode() {
+        int hash = 0;
+        hash += (this.bmid != null ? this.bmid.hashCode() : 0);
+        hash += (this.userid != null ? this.userid.hashCode() : 0);
+        return hash;
+    }
+
+    /**
+     * Determines whether another object is equal to this BookmarkEntityPK.  The result is
+     * <code>true</code> if and only if the argument is not null and is a BookmarkEntityPK object that
+     * has the same id field values as this object.
+     *
+     * @param object the reference object with which to compare
+     * @return <code>true</code> if this object is the same as the argument;
+     *         <code>false</code> otherwise.
+     */
+    @Override
+    @SuppressWarnings("StringEquality")
+    public boolean equals(Object object) {
+        // TODO: Warning - this method won't work in the case the id fields are not set
+        if (!(object instanceof BookmarkEntityPK)) {
+            return false;
+        }
+
+        BookmarkEntityPK other = (BookmarkEntityPK) object;
+        if (this.bmid != other.bmid && (this.bmid == null || !this.bmid.equals(other.bmid))) {
+            return false;
+        }
+        if (this.userid != other.userid && (this.userid == null || !this.userid.equals(other.userid))) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a string representation of the object.  This implementation constructs
+     * that representation based on the id fields.
+     *
+     * @return a string representation of the object.
+     */
+    @Override
+    public String toString() {
+        return "BookmarkEntityPK{"
+               + "userid='" + userid + '\''
+               + ", bmid='" + bmid + '\''
+               + '}';
+    }
+}
diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/UserEntity.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/UserEntity.java
new file mode 100644
index 0000000..425abe2
--- /dev/null
+++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/UserEntity.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark.entity;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+
+/**
+ * Entity class UserEntity.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("UnusedDeclaration")
+@Entity
+@Table(name = "USERS")
+@NamedQueries({
+        @NamedQuery(name = "UserEntity.findByUserid", query = "SELECT u FROM UserEntity u WHERE u.userid = :userid"),
+        @NamedQuery(name = "UserEntity.findByPassword", query = "SELECT u FROM UserEntity u WHERE u.password = :password"),
+        @NamedQuery(name = "UserEntity.findByUsername", query = "SELECT u FROM UserEntity u WHERE u.username = :username"),
+        @NamedQuery(name = "UserEntity.findByEmail", query = "SELECT u FROM UserEntity u WHERE u.email = :email")
+})
+public class UserEntity implements Serializable {
+
+    @Id
+    @Column(name = "USERID", nullable = false)
+    private String userid;
+
+    @Column(name = "PASSWORD", nullable = false)
+    private String password;
+
+    @Column(name = "USERNAME")
+    private String username;
+
+    @Column(name = "EMAIL")
+    private String email;
+
+    @OneToMany(cascade = CascadeType.ALL, mappedBy = "userEntity")
+    private Collection<BookmarkEntity> bookmarkEntityCollection;
+
+    /**
+     * Creates a new instance of UserEntity
+     */
+    public UserEntity() {
+    }
+
+    /**
+     * Creates a new instance of UserEntity with the specified values.
+     *
+     * @param userid the userid of the UserEntity
+     */
+    public UserEntity(String userid) {
+        this.userid = userid;
+    }
+
+    /**
+     * Creates a new instance of UserEntity with the specified values.
+     *
+     * @param userid the userid of the UserEntity
+     * @param password the password of the UserEntity
+     */
+    public UserEntity(String userid, String password) {
+        this.userid = userid;
+        this.password = password;
+    }
+
+    /**
+     * Gets the userid of this UserEntity.
+     *
+     * @return the userid
+     */
+    public String getUserid() {
+        return this.userid;
+    }
+
+    /**
+     * Sets the userid of this UserEntity to the specified value.
+     *
+     * @param userid the new userid
+     */
+    public void setUserid(String userid) {
+        this.userid = userid;
+    }
+
+    /**
+     * Gets the password of this UserEntity.
+     *
+     * @return the password
+     */
+    public String getPassword() {
+        return this.password;
+    }
+
+    /**
+     * Sets the password of this UserEntity to the specified value.
+     *
+     * @param password the new password
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    /**
+     * Gets the username of this UserEntity.
+     *
+     * @return the username
+     */
+    public String getUsername() {
+        return this.username;
+    }
+
+    /**
+     * Sets the username of this UserEntity to the specified value.
+     *
+     * @param username the new username
+     */
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    /**
+     * Gets the email of this UserEntity.
+     *
+     * @return the email
+     */
+    public String getEmail() {
+        return this.email;
+    }
+
+    /**
+     * Sets the email of this UserEntity to the specified value.
+     *
+     * @param email the new email
+     */
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    /**
+     * Gets the bookmarkEntityCollection of this UserEntity.
+     *
+     * @return the bookmarkEntityCollection
+     */
+    public Collection<BookmarkEntity> getBookmarkEntityCollection() {
+        return this.bookmarkEntityCollection;
+    }
+
+    /**
+     * Sets the bookmarkEntityCollection of this UserEntity to the specified value.
+     *
+     * @param bookmarkEntityCollection the new bookmarkEntityCollection
+     */
+    public void setBookmarkEntityCollection(Collection<BookmarkEntity> bookmarkEntityCollection) {
+        this.bookmarkEntityCollection = bookmarkEntityCollection;
+    }
+
+    /**
+     * Returns a hash code value for the object.  This implementation computes
+     * a hash code value based on the id fields in this object.
+     *
+     * @return a hash code value for this object.
+     */
+    @Override
+    public int hashCode() {
+        int hash = 0;
+        hash += (this.userid != null ? this.userid.hashCode() : 0);
+        return hash;
+    }
+
+    /**
+     * Determines whether another object is equal to this UserEntity.  The result is
+     * <code>true</code> if and only if the argument is not null and is a UserEntity object that
+     * has the same id field values as this object.
+     *
+     * @param object the reference object with which to compare
+     * @return <code>true</code> if this object is the same as the argument;
+     *         <code>false</code> otherwise.
+     */
+    @SuppressWarnings("StringEquality")
+    @Override
+    public boolean equals(Object object) {
+        // TODO: Warning - this method won't work in the case the id fields are not set
+        if (!(object instanceof UserEntity)) {
+            return false;
+        }
+
+        UserEntity other = (UserEntity) object;
+
+        return !(this.userid != other.userid && (this.userid == null || !this.userid.equals(other.userid)));
+    }
+
+    /**
+     * Returns a string representation of the object.  This implementation constructs
+     * that representation based on the id fields.
+     *
+     * @return a string representation of the object.
+     */
+    @Override
+    public String toString() {
+        return "UserEntity{"
+               + "userid='" + userid + '\''
+               + '}';
+    }
+}
diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/exception/ExtendedNotFoundException.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/exception/ExtendedNotFoundException.java
new file mode 100644
index 0000000..3417426
--- /dev/null
+++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/exception/ExtendedNotFoundException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark.exception;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ExtendedNotFoundException extends NotFoundException {
+
+    public ExtendedNotFoundException(final String message) {
+        super(Response.status(Response.Status.NOT_FOUND).entity(message).build());
+    }
+}
diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/resource/BookmarkResource.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/resource/BookmarkResource.java
new file mode 100644
index 0000000..6ed3f0d
--- /dev/null
+++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/resource/BookmarkResource.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark.resource;
+
+import java.util.Date;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
+import javax.persistence.EntityManager;
+
+import org.glassfish.jersey.examples.bookmark.entity.BookmarkEntity;
+import org.glassfish.jersey.examples.bookmark.entity.BookmarkEntityPK;
+import org.glassfish.jersey.examples.bookmark.entity.UserEntity;
+import org.glassfish.jersey.examples.bookmark.exception.ExtendedNotFoundException;
+import org.glassfish.jersey.examples.bookmark.util.tx.TransactionManager;
+import org.glassfish.jersey.examples.bookmark.util.tx.Transactional;
+
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class BookmarkResource {
+
+    UriInfo uriInfo; // actual uri info provided by parent resource
+    EntityManager em; // entity manager provided by parent resource
+
+    BookmarkEntity bookmarkEntity;
+
+    /**
+     * Creates a new instance of UserResource
+     */
+    public BookmarkResource(UriInfo uriInfo, EntityManager em, UserEntity userEntity, String bmid) {
+        this.uriInfo = uriInfo;
+        this.em = em;
+        bookmarkEntity = em.find(BookmarkEntity.class, new BookmarkEntityPK(bmid, userEntity.getUserid()));
+        if (null == bookmarkEntity) {
+            throw new ExtendedNotFoundException("bookmark with userid="
+                    + userEntity.getUserid() + " and bmid="
+                    + bmid + " not found\n");
+        }
+        bookmarkEntity.setUserEntity(userEntity);
+    }
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public JSONObject getBookmark() {
+        return asJson();
+    }
+
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void putBookmark(JSONObject jsonEntity) throws JSONException {
+
+        bookmarkEntity.setLdesc(jsonEntity.getString("ldesc"));
+        bookmarkEntity.setSdesc(jsonEntity.getString("sdesc"));
+        bookmarkEntity.setUpdated(new Date());
+
+        TransactionManager.manage(new Transactional(em) {
+            public void transact() {
+                em.merge(bookmarkEntity);
+            }
+        });
+    }
+
+    @DELETE
+    public void deleteBookmark() {
+        TransactionManager.manage(new Transactional(em) {
+            public void transact() {
+                UserEntity userEntity = bookmarkEntity.getUserEntity();
+                userEntity.getBookmarkEntityCollection().remove(bookmarkEntity);
+                em.merge(userEntity);
+                em.remove(bookmarkEntity);
+            }
+        });
+    }
+
+    public JSONObject asJson() {
+        try {
+            return new JSONObject().put("userid", bookmarkEntity.getBookmarkEntityPK().getUserid())
+                    .put("sdesc", bookmarkEntity.getSdesc())
+                    .put("ldesc", bookmarkEntity.getLdesc())
+                    .put("uri", bookmarkEntity.getUri());
+        } catch (JSONException je) {
+            return null;
+        }
+    }
+
+    public String toString() {
+        return bookmarkEntity.getBookmarkEntityPK().getUserid();
+    }
+}
diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/resource/BookmarksResource.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/resource/BookmarksResource.java
new file mode 100644
index 0000000..c03aaa5
--- /dev/null
+++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/resource/BookmarksResource.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark.resource;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Date;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import javax.persistence.EntityManager;
+
+import org.glassfish.jersey.examples.bookmark.entity.BookmarkEntity;
+import org.glassfish.jersey.examples.bookmark.util.tx.TransactionManager;
+import org.glassfish.jersey.examples.bookmark.util.tx.Transactional;
+
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class BookmarksResource {
+
+    UriInfo uriInfo; // actual uri info
+    EntityManager em; // entity manager provided by parent resource
+
+    UserResource userResource; // parent user resource
+
+    /**
+     * Creates a new instance of BookmarksResource
+     */
+    public BookmarksResource(UriInfo uriInfo, EntityManager em, UserResource userResource) {
+        this.uriInfo = uriInfo;
+        this.em = em;
+        this.userResource = userResource;
+    }
+
+    public Collection<BookmarkEntity> getBookmarks() {
+        return userResource.getUserEntity().getBookmarkEntityCollection();
+    }
+
+    @Path("{bmid: .+}")
+    public BookmarkResource getBookmark(@PathParam("bmid") String bmid) {
+        return new BookmarkResource(uriInfo, em, userResource.getUserEntity(), bmid);
+    }
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public JSONArray getBookmarksAsJsonArray() {
+        JSONArray uriArray = new JSONArray();
+        for (BookmarkEntity bookmarkEntity : getBookmarks()) {
+            UriBuilder ub = uriInfo.getAbsolutePathBuilder();
+            URI bookmarkUri = ub
+                    .path(bookmarkEntity.getBookmarkEntityPK().getBmid())
+                    .build();
+            uriArray.put(bookmarkUri.toASCIIString());
+        }
+        return uriArray;
+    }
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response postForm(JSONObject bookmark) throws JSONException {
+        final BookmarkEntity bookmarkEntity = new BookmarkEntity(getBookmarkId(bookmark.getString("uri")),
+                userResource.getUserEntity().getUserid());
+
+        bookmarkEntity.setUri(bookmark.getString("uri"));
+        bookmarkEntity.setUpdated(new Date());
+        bookmarkEntity.setSdesc(bookmark.getString("sdesc"));
+        bookmarkEntity.setLdesc(bookmark.getString("ldesc"));
+        userResource.getUserEntity().getBookmarkEntityCollection().add(bookmarkEntity);
+
+        TransactionManager.manage(new Transactional(em) {
+            public void transact() {
+                em.merge(userResource.getUserEntity());
+            }
+        });
+
+        URI bookmarkUri = uriInfo.getAbsolutePathBuilder()
+                .path(bookmarkEntity.getBookmarkEntityPK().getBmid())
+                .build();
+        return Response.created(bookmarkUri).build();
+    }
+
+    private String getBookmarkId(String uri) {
+        return uri;
+    }
+}
diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/resource/UserResource.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/resource/UserResource.java
new file mode 100644
index 0000000..1de92f8
--- /dev/null
+++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/resource/UserResource.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import javax.persistence.EntityManager;
+
+import org.glassfish.jersey.examples.bookmark.entity.UserEntity;
+import org.glassfish.jersey.examples.bookmark.exception.ExtendedNotFoundException;
+import org.glassfish.jersey.examples.bookmark.util.tx.TransactionManager;
+import org.glassfish.jersey.examples.bookmark.util.tx.Transactional;
+
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class UserResource {
+
+    String userid; // userid from url
+    UserEntity userEntity; // appropriate jpa user entity
+
+    UriInfo uriInfo; // actual uri info provided by parent resource
+    EntityManager em; // entity manager provided by parent resource
+
+    /**
+     * Creates a new instance of UserResource
+     */
+    public UserResource(UriInfo uriInfo, EntityManager em, String userid) {
+        this.uriInfo = uriInfo;
+        this.userid = userid;
+        this.em = em;
+        userEntity = em.find(UserEntity.class, userid);
+    }
+
+    @Path("bookmarks/")
+    public BookmarksResource getBookmarksResource() {
+        if (null == userEntity) {
+            throw new ExtendedNotFoundException("userid " + userid + " does not exist!");
+        }
+        return new BookmarksResource(uriInfo, em, this);
+    }
+
+    @GET
+    @Produces("application/json")
+    public JSONObject getUser() throws JSONException {
+        if (null == userEntity) {
+            throw new ExtendedNotFoundException("userid " + userid + "does not exist!");
+        }
+        return new JSONObject().put("userid", userEntity.getUserid())
+                .put("username", userEntity.getUsername())
+                .put("email", userEntity.getEmail())
+                .put("password", userEntity.getPassword())
+                .put("bookmarks", uriInfo.getAbsolutePathBuilder().path("bookmarks").build());
+    }
+
+    @PUT
+    @Consumes("application/json")
+    public Response putUser(JSONObject jsonEntity) throws JSONException {
+
+        String jsonUserid = jsonEntity.getString("userid");
+
+        if ((null != jsonUserid) && !jsonUserid.equals(userid)) {
+            return Response.status(409).entity("userids differ!\n").build();
+        }
+
+        final boolean newRecord = (null == userEntity); // insert or update ?
+
+        if (newRecord) { // new user record to be inserted
+            userEntity = new UserEntity();
+            userEntity.setUserid(userid);
+        }
+        userEntity.setUsername(jsonEntity.getString("username"));
+        userEntity.setEmail(jsonEntity.getString("email"));
+        userEntity.setPassword(jsonEntity.getString("password"));
+
+        if (newRecord) {
+            TransactionManager.manage(new Transactional(em) {
+                public void transact() {
+                    em.persist(userEntity);
+                }
+            });
+            return Response.created(uriInfo.getAbsolutePath()).build();
+        } else {
+            TransactionManager.manage(new Transactional(em) {
+                public void transact() {
+                    em.merge(userEntity);
+                }
+            });
+            return Response.noContent().build();
+        }
+    }
+
+    @DELETE
+    public void deleteUser() {
+        if (null == userEntity) {
+            throw new ExtendedNotFoundException("userid " + userid + "does not exist!");
+        }
+        TransactionManager.manage(new Transactional(em) {
+            public void transact() {
+                em.remove(userEntity);
+            }
+        });
+    }
+
+    public String toString() {
+        return userEntity.getUserid();
+    }
+
+    public UserEntity getUserEntity() {
+        return userEntity;
+    }
+}
diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/resource/UsersResource.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/resource/UsersResource.java
new file mode 100644
index 0000000..69b4b0c
--- /dev/null
+++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/resource/UsersResource.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark.resource;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.PersistenceUnit;
+
+import org.glassfish.jersey.examples.bookmark.entity.UserEntity;
+
+import org.codehaus.jettison.json.JSONArray;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/users/")
+public class UsersResource {
+
+    @Context
+    UriInfo uriInfo;
+
+    @PersistenceUnit(unitName = "BookmarkPU")
+    EntityManagerFactory emf;
+
+    /**
+     * Creates a new instance of Users.
+     */
+    public UsersResource() {
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<UserEntity> getUsers() {
+        return emf.createEntityManager().createQuery("SELECT u from UserEntity u").getResultList();
+    }
+
+    @Path("{userid}/")
+    public UserResource getUser(@PathParam("userid") String userid) {
+        return new UserResource(uriInfo, emf.createEntityManager(), userid);
+    }
+
+    @GET
+    @Produces("application/json")
+    public JSONArray getUsersAsJsonArray() {
+        JSONArray uriArray = new JSONArray();
+        for (UserEntity userEntity : getUsers()) {
+            UriBuilder ub = uriInfo.getAbsolutePathBuilder();
+            URI userUri = ub
+                    .path(userEntity.getUserid())
+                    .build();
+            uriArray.put(userUri.toASCIIString());
+        }
+        return uriArray;
+    }
+}
diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/util/tx/TransactionManager.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/util/tx/TransactionManager.java
new file mode 100644
index 0000000..856a90d
--- /dev/null
+++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/util/tx/TransactionManager.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark.util.tx;
+
+import javax.ws.rs.WebApplicationException;
+
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.transaction.SystemException;
+import javax.transaction.UserTransaction;
+
+/**
+ * @author Paul Sandoz
+ */
+public final class TransactionManager {
+
+    public static void manage(Transactional t) {
+        UserTransaction utx = getUtx();
+        try {
+            utx.begin();
+            if (t.joinTransaction) {
+                t.em.joinTransaction();
+            }
+            t.transact();
+            utx.commit();
+        } catch (Exception e) {
+            try {
+                utx.rollback();
+            } catch (SystemException se) {
+                throw new WebApplicationException(se);
+            }
+            throw new WebApplicationException(e);
+        } finally {
+            t.em.close();
+        }
+    }
+
+    private static UserTransaction getUtx() {
+        try {
+            InitialContext ic = new InitialContext();
+            return (UserTransaction) ic.lookup("java:comp/UserTransaction");
+        } catch (NamingException ne) {
+            throw new WebApplicationException(ne);
+        }
+    }
+}
diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/util/tx/Transactional.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/util/tx/Transactional.java
new file mode 100644
index 0000000..4c88d06
--- /dev/null
+++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/util/tx/Transactional.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark.util.tx;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author Paul Sandoz
+ */
+public abstract class Transactional {
+
+    EntityManager em;
+    boolean joinTransaction;
+
+    public Transactional(EntityManager em, boolean joinTransaction) {
+        this.em = em;
+        this.joinTransaction = joinTransaction;
+    }
+
+    public Transactional(EntityManager em) {
+        this(em, true);
+    }
+
+    public abstract void transact();
+}
diff --git a/examples/bookmark/src/main/resources/META-INF/persistence.xml b/examples/bookmark/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000..5708414
--- /dev/null
+++ b/examples/bookmark/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
+    <persistence-unit name="BookmarkPU" transaction-type="JTA">
+        <provider>oracle.toplink.essentials.PersistenceProvider</provider>
+        <properties>
+            <property name="toplink.jdbc.url" value="jdbc:derby://localhost:1527/BookmarkDB"/>
+            <property name="toplink.jdbc.user" value="REST"/>
+            <property name="toplink.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
+            <property name="toplink.jdbc.password" value="REST"/>
+            <property name="toplink.ddl-generation" value="drop-and-create-tables"/>
+        </properties>
+    </persistence-unit>
+</persistence>
diff --git a/examples/bookmark/src/main/webapp/WEB-INF/glassfish-web.xml b/examples/bookmark/src/main/webapp/WEB-INF/glassfish-web.xml
new file mode 100644
index 0000000..485abd8
--- /dev/null
+++ b/examples/bookmark/src/main/webapp/WEB-INF/glassfish-web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
+<glassfish-web-app error-url="">
+    <context-root>/bookmark</context-root>
+    <class-loader delegate="true" />
+    <jsp-config>
+        <property name="classdebuginfo" value="true">
+            <description>Enable debug info compilation in the generated servlet class</description>
+        </property>
+        <property name="keepgenerated" value="true">
+            <description>Keep a copy of the generated servlet class' java code.</description>
+        </property>
+        <property name="mappedfile" value="true">
+            <description>Maintain a one-to-one correspondence between static content and the generated servlet class' java code</description>
+        </property>
+    </jsp-config>
+</glassfish-web-app>
diff --git a/examples/bookmark/src/main/webapp/WEB-INF/web.xml b/examples/bookmark/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..d64c5f1
--- /dev/null
+++ b/examples/bookmark/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.examples.bookmark.MyApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.examples.bookmark.MyApplication</param-value>
+        </init-param>
+        <init-param>
+            <param-name>unit:BookmarkPU</param-name>
+            <param-value>persistence/bookmark</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.examples.bookmark.MyApplication</servlet-name>
+        <url-pattern>/resources/*</url-pattern>
+    </servlet-mapping>
+
+    <persistence-unit-ref>
+        <persistence-unit-ref-name>persistence/bookmark</persistence-unit-ref-name>
+        <persistence-unit-name>BookmarkPU</persistence-unit-name>
+    </persistence-unit-ref>
+    <resource-ref>
+        <res-ref-name>UserTransaction</res-ref-name>
+        <res-type>javax.transaction.UserTransaction</res-type>
+        <res-auth>Container</res-auth>
+    </resource-ref>
+</web-app>
diff --git a/examples/bookmark/src/test/java/org/glassfish/jersey/examples/bookmark/BookmarkTest.java b/examples/bookmark/src/test/java/org/glassfish/jersey/examples/bookmark/BookmarkTest.java
new file mode 100644
index 0000000..80e8637
--- /dev/null
+++ b/examples/bookmark/src/test/java/org/glassfish/jersey/examples/bookmark/BookmarkTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookmark;
+
+import java.net.URI;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Michal Gajdos
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class BookmarkTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("bookmark").build();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(JettisonFeature.class);
+    }
+
+    @Test
+    public void step1_getUsers() {
+        final JSONArray users = target().path("resources/users/").request("application/json").get(JSONArray.class);
+        assertTrue(users != null);
+    }
+
+    @Test
+    public void step2_createUser() {
+        boolean thrown = false;
+        JSONObject user = new JSONObject();
+
+        try {
+            user.put("userid", "testuid").put("password", "test").put("email", "test@test.net").put("username", "Test User");
+            target().path("resources/users/testuid").request().put(Entity.entity(user, "application/json"));
+        } catch (Exception e) {
+            e.printStackTrace();
+            thrown = true;
+        }
+
+        assertFalse(thrown);
+    }
+
+    @Test
+    public void step3_getUsers2() {
+        final JSONArray users = target().path("resources/users/").request("application/json").get(JSONArray.class);
+        assertTrue(users != null);
+        assertTrue(users.length() == 1);
+    }
+
+    @Test
+    public void step4_updateUser() {
+        boolean thrown = false;
+
+        try {
+            JSONObject user = target().path("resources/users/testuid").request("application/json").get(JSONObject.class);
+
+            user.put("password", "NEW PASSWORD").put("email", "NEW@EMAIL.NET").put("username", "UPDATED TEST USER");
+            target().path("resources/users/testuid").request().put(Entity.entity(user, "application/json"));
+
+            user = target().path("resources/users/testuid").request("application/json").get(JSONObject.class);
+
+            assertEquals(user.get("username"), "UPDATED TEST USER");
+            assertEquals(user.get("email"), "NEW@EMAIL.NET");
+            assertEquals(user.get("password"), "NEW PASSWORD");
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            thrown = true;
+        }
+
+        assertFalse(thrown);
+    }
+
+    // ugly.. but separation into separate test cases would be probably uglier
+    @Test
+    public void step5_getUserBookmarkList() {
+        boolean thrown = false;
+
+        try {
+            JSONObject user = target().path("resources/users/testuid").request("application/json").get(JSONObject.class);
+            assertTrue(user != null);
+
+            final WebTarget webTarget = client().target(user.getString("bookmarks"));
+
+            JSONObject bookmark = new JSONObject();
+            bookmark.put("uri", "http://java.sun.com").put("sdesc", "test desc").put("ldesc", "long test description");
+            webTarget.request().post(Entity.entity(bookmark, "application/json"));
+
+            JSONArray bookmarks = webTarget.request("application/json").get(JSONArray.class);
+            assertTrue(bookmarks != null);
+            int bookmarksSize = bookmarks.length();
+
+            String testBookmarkUrl = bookmarks.getString(0);
+            final WebTarget bookmarkResource = client().target(testBookmarkUrl);
+
+            bookmark = bookmarkResource.request("application/json").get(JSONObject.class);
+            assertTrue(bookmark != null);
+
+            bookmarkResource.request().delete();
+
+            bookmarks = target().path("resources/users/testuid/bookmarks").request("application/json").get(JSONArray.class);
+            assertTrue(bookmarks != null);
+            assertTrue(bookmarks.length() == (bookmarksSize - 1));
+        } catch (Exception e) {
+            e.printStackTrace();
+            thrown = true;
+        }
+
+        assertFalse(thrown);
+    }
+
+    @Test
+    public void step6_deleteUser() {
+        boolean thrown = false;
+
+        try {
+            target().path("resources/users/testuid").request().delete();
+        } catch (Exception e) {
+            e.printStackTrace();
+            thrown = true;
+        }
+
+        assertFalse(thrown);
+    }
+}
diff --git a/examples/bookstore-webapp/README.MD b/examples/bookstore-webapp/README.MD
new file mode 100644
index 0000000..3921d7f
--- /dev/null
+++ b/examples/bookstore-webapp/README.MD
@@ -0,0 +1,125 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Bookstore
+=========
+
+This example demonstrates how to connect JSP pages to resources. The
+example is copied from the
+[Bookstore](http://stapler.kohsuke.org/getting-started.html) example
+presented by the [Stapler](http://stapler.kohsuke.org/) project that
+provides a way to staple URIs to Java classes to build RESTful Web
+applications such as [Hudson](http://java.net/projects/hudson/).
+
+A bookstore Web application is presented that is capable of presenting
+books, CDs and tracks of CDs.
+
+Contents
+--------
+
+The example consists of four web resources implemented by the following:
+
+`org.glassfish.jersey.examples.bookstore.webapp.resource.Bookstore`
+
+The bookstore resource that returns a list of items, either CDs
+or Books. The resource dynamically references a Book or CD resource
+using the getItem method that is annotated with Path. The Book and
+CD resource both inherit from the Item class and thus the resources
+can be managed polymorphically.
+
+`org.glassfish.jersey.examples.bookstore.webapp.resource.Book`
+
+The book resource that has a title and an author.
+
+`org.glassfish.jersey.examples.bookstore.webapp.resource.CD`
+
+The CD resource that has a title, author and a list of tracks. The
+resource dynamically references the Track resource using the
+getTrack method that is annotated with Path.
+
+`org.glassfish.jersey.examples.bookstore.webapp.resource.Track`
+
+The Tracks resource that has a name and the length of the track.
+
+The mapping of the URI path space is presented in the following table:
+
+
+URI path                            | Resource class   | HTTP methods   | Notes
+----------------------------------- | ---------------- | -------------- | ------------------------------------------------------------------------------
+**_/_**                             | Bookstore        | GET            | webapp/resource/Bookstore/index.jsp
+**_/count_**                        | Bookstore        | GET            | webapp/resource/Bookstore/count.jsp
+**_/time_**                         | Bookstore        | GET            | webapp/resource/Bookstore/time.jsp
+**_/items/{itemid}_**               | Book, CD         | GET            | webapp/resource/Book/index.jsp, webapp/resource/CD/index.jsp
+**_/items/{itemid}/tracks/{num}_**  | Track            | GET            | webapp/resource/Track/index.jsp
+
+Running the Example
+-------------------
+
+Bookstore example runs on Glassfish 4.0 application server
+([https://javaee.github.io/glassfish/](https://javaee.github.io/glassfish/)), which can be
+run as an embedded container or Jetty.
+
+If you are working with Jersey GlassFish update center module installed
+into your existing GlassFish instance, you will need to follow
+instructions at [the module README file](../../README.html) in order to
+deploy the example.
+
+Otherwise, you can run the example using embedded GlassFish as follows:
+
+Build and deploy the project by executing the following command from the
+project directory
+
+>   mvn clean package embedded-glassfish:run (TODO does not work at the moment)
+
+or you can run the example using Jetty as follows:
+
+>   mvn clean package jetty:run
+
+Go to the URL:
+
+<http://localhost:8080/bookstore-webapp/>
+
+#### Test Client Running
+
+>   mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.external.ExternalTestContainerFactory -Djersey.config.test.logging.enable=true -Djersey.config.test.logging.dumpEntity=true -Djersey.config.test.container.port=8080 -Dmaven.test.skip=false
+
+or
+
+>   mvn test -Prun-external-tests
+
+How it works
+------------
+
+This example shows how to support polymorphism of resources and JSP
+pages. Hence it is possible to add another resource, such as a DVD
+resource with associated JSP pages, which extends Item without having to
+change the logic of Bookstore or the existing JSP pages.
+
+JSP pages are associated with resource classes. Such JSP pages are
+resolved by converting the fully qualified class name of the resource
+class into a path and appending last path segment of the request URI
+path to that path. For example, when a GET is performed on the URI path
+"/" then the path to the JSP page is
+"/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore/",
+and in this case since the last path segment is empty "index.jsp" is
+appended to the path. Then the request us forwarded to the JSP page at
+that path. Similarly when a GET is performed on the URI path "/count"
+then the path to the JSP page is
+"//org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore/count.jsp".
+
+The JSP variable "it" is set to the instance of Bookstore so that the
+index.jsp, or count.jsp, has access to the Bookstore instance as a Java
+bean.
+
+If a resource class inherits from another resource class then it will
+automatically inherit the JSPs from the super class.
+
+A JSP page may also include JSPs using the inheritance mechanism, for
+example the index.jsp page associated with the Book resource class
+includes a footer.jsp page whose location is specified by the super
+class, Item.
diff --git a/examples/bookstore-webapp/pom.xml b/examples/bookstore-webapp/pom.xml
new file mode 100644
index 0000000..8b910bb
--- /dev/null
+++ b/examples/bookstore-webapp/pom.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>bookstore-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-bookstore-webapp</name>
+
+    <description>Jersey MVC Bookstore example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-jsp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>jstl</artifactId>
+            <version>${jstl.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Run the application using "mvn jetty:run" -->
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+                <configuration>
+                    <webApp>
+                        <contextPath>/bookstore-webapp</contextPath>
+                    </webApp>
+                    <connectors>
+                        <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
+                            <port>8080</port>
+                            <responseHeaderSize>16192</responseHeaderSize>
+                        </connector>
+                    </connectors>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <!-- mvn test -Prun-external-tests -->
+            <id>run-external-tests</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <jersey.config.test.container.factory>${external.container.factory}</jersey.config.test.container.factory>
+                                <jersey.config.test.container.port>${external.container.port}</jersey.config.test.container.port>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <properties>
+                <!-- External test container configuration is done via properties to allow overriding via command line. -->
+                <external.container.factory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</external.container.factory>
+                <external.container.port>8080</external.container.port>
+                <maven.test.skip>false</maven.test.skip>
+            </properties>
+        </profile>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <maven.test.skip>true</maven.test.skip>
+    </properties>
+
+</project>
diff --git a/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/MyApplication.java b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/MyApplication.java
new file mode 100644
index 0000000..fc591f6
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/MyApplication.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookstore.webapp;
+
+import org.glassfish.jersey.examples.bookstore.webapp.resource.Bookstore;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.TracingConfig;
+import org.glassfish.jersey.server.mvc.jsp.JspMvcFeature;
+
+/**
+ * @author Michal Gajdos
+ */
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        // Resources.
+        packages(Bookstore.class.getPackage().getName());
+
+        // MVC.
+        register(JspMvcFeature.class);
+
+        // Logging.
+        register(LoggingFeature.class);
+
+        // Tracing support.
+        property(ServerProperties.TRACING, TracingConfig.ON_DEMAND.name());
+    }
+}
diff --git a/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Book.java b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Book.java
new file mode 100644
index 0000000..4d0ff1b
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Book.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookstore.webapp.resource;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Book extends Item {
+
+    public Book() {
+    }
+
+    public Book(final String title, final String author) {
+        super(title, author);
+    }
+
+
+}
diff --git a/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore.java b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore.java
new file mode 100644
index 0000000..367eccb
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookstore.webapp.resource;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Singleton;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.server.mvc.Template;
+
+@Path("/")
+@Singleton
+@Template
+@Produces(MediaType.TEXT_HTML)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Bookstore {
+
+    private final Map<String, Item> items = new TreeMap<>();
+    private String name;
+
+    public Bookstore() {
+        setName("Czech Bookstore");
+        getItems().put("1", new Book("Svejk", "Jaroslav Hasek"));
+        getItems().put("2", new Book("Krakatit", "Karel Capek"));
+        getItems().put("3", new CD("Ma Vlast 1", "Bedrich Smetana", new Track[] {
+                new Track("Vysehrad", 180),
+                new Track("Vltava", 172),
+                new Track("Sarka", 32)}));
+    }
+
+    @Path("items/{itemid}/")
+    public Item getItem(@PathParam("itemid") String itemid) {
+        Item i = getItems().get(itemid);
+        if (i == null) {
+            throw new NotFoundException(Response
+                    .status(Response.Status.NOT_FOUND)
+                    .entity("Item, " + itemid + ", is not found")
+                    .build());
+        }
+
+        return i;
+    }
+
+    @GET
+    @Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON})
+    public Bookstore getXml() {
+        return this;
+    }
+
+    public long getSystemTime() {
+        return System.currentTimeMillis();
+    }
+
+    public Map<String, Item> getItems() {
+        return items;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/CD.java b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/CD.java
new file mode 100644
index 0000000..1e357e7
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/CD.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookstore.webapp.resource;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Response;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class CD extends Item {
+
+    private Track[] tracks;
+
+    public CD() {
+    }
+
+    public CD(final String title, final String author, final Track[] tracks) {
+        super(title, author);
+        this.tracks = tracks;
+    }
+
+    public Track[] getTracks() {
+        return tracks;
+    }
+
+    @Path("tracks/{num}/")
+    public Track getTrack(@PathParam("num") int num) {
+        if (num >= tracks.length) {
+            throw new NotFoundException(Response
+                    .status(Response.Status.NOT_FOUND)
+                    .entity("Track, " + num + ", of CD, " + getTitle() + ", is not found")
+                    .build());
+        }
+        return tracks[num];
+    }
+}
diff --git a/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Happy.java b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Happy.java
new file mode 100644
index 0000000..f9314d6
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Happy.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookstore.webapp.resource;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.server.mvc.Template;
+
+/**
+ * Generates a header display resource which is useful for displaying the
+ * various headers browsers use for making test cases
+ */
+@Template
+@Path("/happy")
+@Singleton
+public class Happy {
+
+    @Context
+    private HttpHeaders headers;
+
+    public String getHeaders() {
+        StringBuilder buf = new StringBuilder();
+        for (String header : headers.getRequestHeaders().keySet()) {
+            buf.append("<li>");
+            buf.append(header);
+            buf.append(" = ");
+            buf.append(headers.getRequestHeader(header));
+            buf.append("</li>\n");
+        }
+        return buf.toString();
+    }
+}
diff --git a/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Item.java b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Item.java
new file mode 100644
index 0000000..77bfbd9
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Item.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookstore.webapp.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.server.mvc.Template;
+
+@Template
+@Produces(MediaType.TEXT_HTML)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Item {
+
+    private String title;
+    private String author;
+
+    public Item() {
+    }
+
+    public Item(final String title, final String author) {
+        this.title = title;
+        this.author = author;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public String getAuthor() {
+        return author;
+    }
+
+    @GET
+    @Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON})
+    public Item getXml() {
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return "Item{"
+               + "title='" + title + '\''
+               + ", author='" + author + '\''
+               + '}';
+    }
+}
diff --git a/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Track.java b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Track.java
new file mode 100644
index 0000000..86888f1
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Track.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookstore.webapp.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.server.mvc.Template;
+
+@Template
+@Produces(MediaType.TEXT_HTML)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Track {
+
+    private String name;
+    private int length;
+
+    public Track() {
+    }
+
+    public Track(String name, int length) {
+        this.name = name;
+        this.length = length;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getLength() {
+        return length;
+    }
+
+    @GET
+    @Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON})
+    public Track getXml() {
+        return this;
+    }
+}
diff --git a/examples/bookstore-webapp/src/main/webapp/WEB-INF/web.xml b/examples/bookstore-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..1bba9e7
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <filter>
+        <filter-name>org.glassfish.jersey.examples.bookstore.webapp.MyApplication</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.examples.bookstore.webapp.MyApplication</param-value>
+        </init-param>
+        <!-- pass to next filter if Jersey/App returns 404 -->
+        <init-param>
+            <param-name>jersey.config.servlet.filter.forwardOn404</param-name>
+            <param-value>true</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>org.glassfish.jersey.examples.bookstore.webapp.MyApplication</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/examples/bookstore-webapp/src/main/webapp/css/style.css b/examples/bookstore-webapp/src/main/webapp/css/style.css
new file mode 100644
index 0000000..412a223
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/webapp/css/style.css
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+body {
+  font-family: Verdana, "Bitstream Vera Sans", sans-serif;
+}
+
+h1, h2, h3, h4, h5, h6 {
+  color: #111;
+}
+
+a {
+  color: #125AA7;
+}
+
+a:hover {
+  color: #000;
+}
+
+blockquote {
+  color: #666;
+}
+
+pre {
+  background: #eee;
+  border: 1px solid #ddd;
+}
+
+hr {
+  background: #B2CCFF;
+  color: #B2CCFF;
+}
+
+table {
+  border-top: 1px solid #ddd;
+  border-left: 1px solid #ddd;
+}
+
+th, td {
+  border-bottom: 1px solid #ddd;
+  border-right: 1px solid #ddd;
+}
+
+a img {
+  text-decoration: none;
+}
+
diff --git a/examples/bookstore-webapp/src/main/webapp/jsp/help.jsp b/examples/bookstore-webapp/src/main/webapp/jsp/help.jsp
new file mode 100644
index 0000000..8ac9f84
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/webapp/jsp/help.jsp
@@ -0,0 +1,26 @@
+<%--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+<html>
+  <head>
+    <title></title>
+  </head>
+  <body>
+    Help web page
+  </body>
+</html>
diff --git a/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Book/index.jsp b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Book/index.jsp
new file mode 100644
index 0000000..5ec592e
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Book/index.jsp
@@ -0,0 +1,40 @@
+<%--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+<%--
+The taglib directive below imports the JSTL library. If you uncomment it,
+you must also add the JSTL library to the project. The Add Library... action
+on Libraries node in Projects view can be used to add the JSTL 1.1 library.
+--%>
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@taglib prefix="rbt" uri="urn:org:glassfish:jersey:servlet:mvc" %>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>Book</title>
+    </head>
+    <body>
+
+        <h1>${it.title}</h1>
+
+        Book from ${it.author}
+
+        <rbt:include page="footer.jsp"/>
+
+    </body>
+</html>
diff --git a/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore/count.jsp b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore/count.jsp
new file mode 100644
index 0000000..b6ff454
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore/count.jsp
@@ -0,0 +1,21 @@
+<%--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+--%>
+
+<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
+<%--
+  An example of another side JSP.
+--%>
+<html>
+  <body>
+   # of items: ${fn:length(it.items)} 
+  </body>
+</html>
diff --git a/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore/index.jsp b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore/index.jsp
new file mode 100644
index 0000000..21afccf
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore/index.jsp
@@ -0,0 +1,50 @@
+<%--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <style type="text/css" media="screen">
+          @import url( <c:url value="/css/style.css"/> );
+        </style>
+        <title>REST Bookstore Sample</title>
+    </head>
+    <body>
+
+    <h1>${it.name}</h1>
+    
+    <h2>Item List</h2>
+
+    <ul>
+        <c:forEach var="i" items="${it.items}">
+            <li><a href="items/${i.key}/">${i.value.title}</a>
+        </c:forEach>
+    </ul>
+    
+    <h2>Others</h2>
+    <p>
+      <a href="count">count inventory</a>
+    <p>
+      <a href="time">get the system time</a>
+    <p>
+      <a href="jsp/help.jsp">regular resources</a>
+    </p>    
+    </body>
+</html>
diff --git a/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore/time.jsp b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore/time.jsp
new file mode 100644
index 0000000..88ccced
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Bookstore/time.jsp
@@ -0,0 +1,18 @@
+<%--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+--%>
+
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<html>
+  <body>
+    Current system time is ${it.systemTime}
+  </body>
+</html>
diff --git a/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/CD/index.jsp b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/CD/index.jsp
new file mode 100644
index 0000000..6e6a896
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/CD/index.jsp
@@ -0,0 +1,44 @@
+<%--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 
+<%@taglib prefix="rbt" uri="urn:org:glassfish:jersey:servlet:mvc" %>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>CD</title>
+    </head>
+    <body>
+
+    <h1>${it.title}</h1>
+    
+    <p>CD from ${it.author}</p>
+    
+    <h2>Track List</h2>
+
+    <ul>
+        <c:forEach var="t" items="${it.tracks}" varStatus="loop">
+            <li><a href="tracks/${loop.index}/">${t.name}</a></li>
+        </c:forEach>
+    </ul>
+    
+    <rbt:include page="footer.jsp"/> 
+    
+    </body>
+</html>
diff --git a/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Happy/index.jsp b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Happy/index.jsp
new file mode 100644
index 0000000..feef3bf
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Happy/index.jsp
@@ -0,0 +1,36 @@
+<%--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>REST Bookstore Happy Page!</title>
+    </head>
+    <body>
+
+    <h1>Happy Page!</h1>
+
+    <h2>Headers</h2>
+    <ul>
+      ${it.headers}
+    </ul>
+
+    </body>
+</html>
diff --git a/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Item/footer.jsp b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Item/footer.jsp
new file mode 100644
index 0000000..1cab567
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Item/footer.jsp
@@ -0,0 +1,18 @@
+<%--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+--%>
+
+<p>
+<a href="../../">Back</a>
+<hr>
+<div align="right">
+    Common footer (title ${it.title})
+</div>
diff --git a/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Track/index.jsp b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Track/index.jsp
new file mode 100644
index 0000000..e941f29
--- /dev/null
+++ b/examples/bookstore-webapp/src/main/webapp/org/glassfish/jersey/examples/bookstore/webapp/resource/Track/index.jsp
@@ -0,0 +1,35 @@
+<%--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>CD Track</title>
+    </head>
+    <body>
+
+    <h1>${it.name}</h1>
+    
+    <br>Track length: ${it.length} sec.
+    
+    <p><a href="../../">back</a>
+    
+    </body>
+</html>
diff --git a/examples/bookstore-webapp/src/test/java/org/glassfish/jersey/examples/bookstore/webapp/resource/BookstoreTest.java b/examples/bookstore-webapp/src/test/java/org/glassfish/jersey/examples/bookstore/webapp/resource/BookstoreTest.java
new file mode 100644
index 0000000..3e2d2cf
--- /dev/null
+++ b/examples/bookstore-webapp/src/test/java/org/glassfish/jersey/examples/bookstore/webapp/resource/BookstoreTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookstore.webapp.resource;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author James Strachan
+ * @author Naresh
+ * @author Michal Gajdos
+ */
+public class BookstoreTest extends TestSupport {
+
+    @Test
+    public void testResourceAsHtml() throws Exception {
+        assertBookstoreHtmlResponse(target("bookstore-webapp").request().get(String.class));
+    }
+
+    @Test
+    public void testHappyResourceAsHtml() throws Exception {
+        final String response = target("bookstore-webapp/happy").request().get(String.class);
+
+        assertHtmlResponse(response);
+        assertResponseContains(response, "Happy");
+    }
+
+    @Test
+    public void testResourceAsXml() throws Exception {
+        final Bookstore response = target("bookstore-webapp").request("application/xml").get(Bookstore.class);
+
+        assertNotNull("Should have returned a bookstore!", response);
+        assertEquals("bookstore name", "Czech Bookstore", response.getName());
+    }
+
+    @Test
+    public void testResourceAsHtmlUsingWebKitAcceptHeaders() throws Exception {
+        final String response = target("bookstore-webapp").request(
+                "text/html",
+                "application/xhtml+xml",
+                "application/xml;q=0.9",
+                "*/*;q=0.8").get(String.class);
+
+        assertBookstoreHtmlResponse(response);
+    }
+
+    protected void assertBookstoreHtmlResponse(String response) {
+        assertHtmlResponse(response);
+        assertResponseContains(response, "Bookstore");
+        assertResponseContains(response, "Item List");
+    }
+}
diff --git a/examples/bookstore-webapp/src/test/java/org/glassfish/jersey/examples/bookstore/webapp/resource/ItemTest.java b/examples/bookstore-webapp/src/test/java/org/glassfish/jersey/examples/bookstore/webapp/resource/ItemTest.java
new file mode 100644
index 0000000..0e14ab4
--- /dev/null
+++ b/examples/bookstore-webapp/src/test/java/org/glassfish/jersey/examples/bookstore/webapp/resource/ItemTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookstore.webapp.resource;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author James Strachan
+ * @author Naresh
+ * @author Michal Gajdos
+ */
+public class ItemTest extends TestSupport {
+
+    @Test
+    public void testResourceAsHtml() throws Exception {
+        final String response = item1resource().request().get(String.class);
+        assertItemHtmlResponse(response);
+    }
+
+    @Test
+    public void testResourceAsXml() throws Exception {
+        final String text = item1resource().request("application/xml").get(String.class);
+        System.out.println("Item XML is: " + text);
+
+        final Book response = item1resource().request("application/xml").get(Book.class);
+        assertNotNull("Should have returned an item!", response);
+        assertEquals("item title", "Svejk", response.getTitle());
+    }
+
+    @Test
+    public void testResourceAsHtmlUsingWebKitAcceptHeaders() throws Exception {
+        final String response = item1resource().request(
+                "text/html",
+                "application/xhtml+xml",
+                "application/xml;q=0.9",
+                "*/*;q=0.8").get(String.class);
+
+        assertItemHtmlResponse(response);
+    }
+
+    protected void assertItemHtmlResponse(String response) {
+        assertHtmlResponse(response);
+        assertResponseContains(response, "<title>Book</title>");
+        assertResponseContains(response, "<h1>Svejk</h1>");
+    }
+
+    protected WebTarget item1resource() {
+        return target("bookstore-webapp").path("/items/1");
+    }
+
+}
diff --git a/examples/bookstore-webapp/src/test/java/org/glassfish/jersey/examples/bookstore/webapp/resource/TestSupport.java b/examples/bookstore-webapp/src/test/java/org/glassfish/jersey/examples/bookstore/webapp/resource/TestSupport.java
new file mode 100644
index 0000000..f454e85
--- /dev/null
+++ b/examples/bookstore-webapp/src/test/java/org/glassfish/jersey/examples/bookstore/webapp/resource/TestSupport.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.bookstore.webapp.resource;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.examples.bookstore.webapp.MyApplication;
+import org.glassfish.jersey.server.mvc.jsp.JspMvcFeature;
+import org.glassfish.jersey.servlet.ServletProperties;
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author James Strachan
+ * @author Naresh
+ * @author Michal Gajdos
+ */
+public abstract class TestSupport extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        final MyApplication application = new MyApplication();
+        application.register(JspMvcFeature.class);
+        application.property(ServletProperties.FILTER_FORWARD_ON_404, true);
+        return application;
+    }
+
+    protected void assertHtmlResponse(String response) {
+        assertNotNull("No text returned!", response);
+
+        assertResponseContains(response, "<html>");
+        assertResponseContains(response, "</html>");
+    }
+
+    protected void assertResponseContains(String response, String text) {
+        assertTrue("Response should contain " + text + " but was: " + response, response.contains(text));
+    }
+}
diff --git a/examples/build.readme b/examples/build.readme
new file mode 100644
index 0000000..dc8b3b3
--- /dev/null
+++ b/examples/build.readme
@@ -0,0 +1,42 @@
+This read-me file briefly summarizes options for building Jersey examples.
+
+To build examples without generating individual example project source ZIP
+bundles, run:
+
+  > mvn clean install
+
+
+To build examples and generate individual example project source ZIP bundles,
+run the build with "examples-source-zip" profile enabled:
+
+  > mvn clean install -P release
+
+
+Some examples contain tests that depend on external container to be up and
+running. Tests in these examples are disabled by default. To enable the tests
+to be run against a properly set-up external container version, run the build
+with "run-external-tests" profile enabled:
+
+  > mvn clean install -P run-external-tests
+
+When running the build with the "run-external-tests" profile, following command
+line properties can be used to customize the build:
+
+  external.container.factory
+
+     - defines external test container factory class to be used for the tests
+       executed as part of the "run-external-tests" profile
+
+     - defaults to "org.glassfish.jersey.test.external.ExternalTestContainerFactory"
+
+
+  external.container.port
+
+     - defines the TCP/IP port of the external test container to be used for
+       the tests executed as part of the "run-external-tests" profile
+
+     - defaults to "8080"
+
+For example:
+
+  > mvn clean install -P run-external-tests -Dexternal.container.port=9090
diff --git a/examples/cdi-webapp/README.MD b/examples/cdi-webapp/README.MD
new file mode 100644
index 0000000..763a299
--- /dev/null
+++ b/examples/cdi-webapp/README.MD
@@ -0,0 +1,62 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Jersey CDI Integration CDI Beans WebApp Example
+===============================================
+
+This example demonstrates how to develop RESTful web service with CDI
+managed beans and a Servlet 3.0 Web container.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                              | Description                                                                                 | Expected Results
+------------------------------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------
+**_/cdi-webapp/helloworld_**          | A managed bean with no use of injection whatsoever                                          | Hello World
+**_/cdi-webapp/singleton_**           | Shows injection of context objects into the fields of a managed bean.                       | OK GET /cdi-webapp/singleton
+**_/cdi-webapp/singleton/counter_**   | Shows injection of context objects into the fields of a managed bean.                       | standalone Grizzly (no resource injection support) - 0 initial increment value, that gets incremented with each other request
+                                      |                                                                                             | Java EE compliant AS - 42 initial increment value
+**_/cdi-webapp/other/C/D_**           | Shows injection of context objects and path parameters into the fields of a managed bean.   | INTERCEPTED: OK GET /cdi-webapp/other, c=C, d=D
+**_/cdi-webapp/echofield/b?a=a_**     | Shows injection of path and query parameters into the fields of a managed bean.             | ECHO a b
+**_/cdi-webapp/echo/a_**              | A managed bean that uses (but does not inject) a path parameter.                            | ECHO a
+
+Running the Example
+-------------------
+
+This example should work on any Java EE 7 compliant application server.
+It has been tested on a standalone GlassFish 4 instance. The easiest way
+to get the application running there is to build it and deploy as
+follows:
+
+    mvn clean package
+    $AS_HOME/asadmin deploy target/cdi-webapp.war
+
+Another option, introduced in Jersey 2.15, is to run this example in
+Grizzly HTTP server. To get the application running there you just
+invoke the following command:
+
+    mvn clean compile exec:java
+
+Since Weld Servlet support is provided also for Apache Tomcat server,
+there is yet another way how to deploy the application. Use the
+`tomcat-packaging` maven profile to get the WAR archive packaged in a
+way that makes it ready for Tomcat 7+ deployment:
+
+    mvn -Ptomcat-packaging clean package
+    cp target/cdi-webapp.war $CATALINA_HOME/webapps
+
+After you successfully deploy the application, visit the following URLs:
+
+-   <http://localhost:8080/cdi-webapp/helloworld>
+-   <http://localhost:8080/cdi-webapp/singleton>
+-   <http://localhost:8080/cdi-webapp/singleton/counter>
+-   <http://localhost:8080/cdi-webapp/other/C/D>
+-   <http://localhost:8080/cdi-webapp/echofield/b?a=a>
+-   <http://localhost:8080/cdi-webapp/echo/a>
\ No newline at end of file
diff --git a/examples/cdi-webapp/pom.xml b/examples/cdi-webapp/pom.xml
new file mode 100644
index 0000000..078e6b0
--- /dev/null
+++ b/examples/cdi-webapp/pom.xml
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>cdi-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-cdi-webapp</name>
+
+    <description>Jersey CDI example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency> <!-- this is to avoid Jersey jars to be bundled with the WAR -->
+           <groupId>org.glassfish.jersey.containers</groupId>
+           <artifactId>jersey-container-servlet-core</artifactId>
+           <scope>provided</scope>
+        </dependency>
+        <dependency>
+           <groupId>org.glassfish.jersey.containers</groupId>
+           <artifactId>jersey-container-grizzly2-http</artifactId>
+           <scope>provided</scope>
+        </dependency>
+        <dependency>
+           <groupId>org.glassfish.jersey.core</groupId>
+           <artifactId>jersey-server</artifactId>
+           <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-weld2-se</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>run-external-tests</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <jersey.config.test.container.factory>${external.container.factory}</jersey.config.test.container.factory>
+                                <jersey.config.test.container.port>${external.container.port}</jersey.config.test.container.port>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <properties>
+                <!-- External test container configuration is done via properties to allow overriding via command line. -->
+                <external.container.factory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</external.container.factory>
+                <external.container.port>8080</external.container.port>
+                <maven.test.skip>false</maven.test.skip>
+            </properties>
+        </profile>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <profile>
+            <id>tomcat-packaging</id>
+
+            <dependencies>
+                <dependency>
+                    <groupId>org.glassfish.jersey.containers</groupId>
+                    <artifactId>jersey-container-servlet</artifactId>
+                    <scope>compile</scope>
+                </dependency>
+                <dependency>
+                    <groupId>javax.ws.rs</groupId>
+                    <artifactId>javax.ws.rs-api</artifactId>
+                    <scope>compile</scope>
+                </dependency>
+                <dependency>
+                    <groupId>org.glassfish.jersey.containers</groupId>
+                    <artifactId>jersey-container-servlet-core</artifactId>
+                    <scope>compile</scope>
+                </dependency>
+                <dependency>
+                    <groupId>org.glassfish.hk2.external</groupId>
+                    <artifactId>javax.inject</artifactId>
+                    <scope>provided</scope>
+                </dependency>
+                <dependency>
+                    <groupId>org.glassfish.jersey.core</groupId>
+                    <artifactId>jersey-server</artifactId>
+                    <scope>compile</scope>
+                </dependency>
+                <dependency>
+                    <groupId>org.glassfish.jersey.ext.cdi</groupId>
+                    <artifactId>jersey-cdi1x</artifactId>
+                    <scope>compile</scope>
+                </dependency>
+                <dependency>
+                    <groupId>org.glassfish.jersey.ext.cdi</groupId>
+                    <artifactId>jersey-cdi1x-servlet</artifactId>
+                    <scope>compile</scope>
+                </dependency>
+                <dependency>
+                    <groupId>org.jboss.weld.servlet</groupId>
+                    <artifactId>weld-servlet</artifactId>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.cdi.App</mainClass>
+                    <classpathScope>test</classpathScope>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/App.java b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/App.java
new file mode 100644
index 0000000..1d970b9
--- /dev/null
+++ b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/App.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi;
+
+import java.io.IOException;
+
+import java.net.URI;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jboss.weld.environment.se.Weld;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.jersey.examples.cdi.resources.MyApplication;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Hello world!
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/cdi-webapp/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("Jersey CDI Example App");
+
+            final Weld weld = new Weld();
+            weld.initialize();
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, createJaxRsApp(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                    weld.shutdown();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.\nTry out %s%s\nStop the application using CTRL+C",
+                    BASE_URI, "application.wadl"));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+
+    public static ResourceConfig createJaxRsApp() {
+        return new ResourceConfig(new MyApplication().getClasses());
+    }
+}
diff --git a/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/EchoParamConstructorResource.java b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/EchoParamConstructorResource.java
new file mode 100644
index 0000000..4efde86
--- /dev/null
+++ b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/EchoParamConstructorResource.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.annotation.ManagedBean;
+import javax.annotation.PostConstruct;
+
+import javax.inject.Inject;
+
+/**
+ * Shows constructor injection of a path parameter in a managed bean.
+ *
+ * @author Roberto Chinnici
+ */
+@ManagedBean
+@Path("echoparamconstructor/{a}")
+public class EchoParamConstructorResource {
+
+    static final Logger LOGGER = Logger.getLogger(EchoParamConstructorResource.class.getName());
+
+    String a;
+
+    // no-arg ctor is required by WLS
+    public EchoParamConstructorResource() {
+    }
+
+    @Inject
+    public EchoParamConstructorResource(@PathParam("a") String a) {
+        this.a = a;
+    }
+
+    @PostConstruct
+    @SuppressWarnings("unused")
+    private void postConstruct() {
+        LOGGER.info(String.format("in post construct, a=%s", a));
+    }
+
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String get() {
+        return "ECHO " + a;
+    }
+}
diff --git a/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/EchoParamFieldResource.java b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/EchoParamFieldResource.java
new file mode 100644
index 0000000..0526731
--- /dev/null
+++ b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/EchoParamFieldResource.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+
+import javax.annotation.ManagedBean;
+import javax.annotation.PostConstruct;
+
+/**
+ * Shows injection of path and query parameter into a managed bean.
+ *
+ * @author Roberto Chinnici
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ManagedBean
+@Path("echofield/{b}")
+public class EchoParamFieldResource {
+
+    @PathParam("b") String bInjected;
+
+    String b;
+
+    /**
+     * Ensure we got path parameter value injected.
+     */
+    @PostConstruct
+    @SuppressWarnings("unused")
+    private void postConstruct() {
+        if (bInjected == null) {
+            throw new IllegalStateException("Field b has not been injected!");
+        }
+        b = bInjected;
+    }
+
+    /**
+     * Return a string containing injected values.
+     *
+     * @param a value of a query parameter a.
+     * @return message containing injected values.
+     */
+    @GET
+    @Produces("text/plain")
+    public String get(@QueryParam("a") String a) {
+        return String.format("ECHO %s %s", a, b);
+    }
+}
diff --git a/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/EchoParamResource.java b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/EchoParamResource.java
new file mode 100644
index 0000000..6e02543
--- /dev/null
+++ b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/EchoParamResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+
+import javax.annotation.ManagedBean;
+
+/**
+ * A managed bean that uses (but does not inject) a path parameter.
+ *
+ * @author Roberto Chinnici
+ */
+@ManagedBean
+@Path("echo/{a}")
+public class EchoParamResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get(@PathParam("a") String param) {
+        return "ECHO " + param;
+    }
+}
diff --git a/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/HelloWorldResource.java b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/HelloWorldResource.java
new file mode 100644
index 0000000..f2210ab
--- /dev/null
+++ b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/HelloWorldResource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.Path;
+
+import javax.annotation.ManagedBean;
+
+/**
+ * A managed bean with no use of injection whatsoever.
+ *
+ * @author Roberto Chinnici.
+ */
+@ManagedBean
+@Path("/helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String getClichedMessage() {
+        return "Hello World";
+    }
+}
diff --git a/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MyApplication.java b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MyApplication.java
new file mode 100644
index 0000000..608799b
--- /dev/null
+++ b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MyApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application.
+ *
+ * @author Jonathan Benoit (jonathan.benoit at oracle.com)
+ */
+@ApplicationPath("/*")
+public class MyApplication extends Application {
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> classes = new HashSet<>();
+        classes.add(MySingletonResource.class);
+        classes.add(MyOtherResource.class);
+        classes.add(HelloWorldResource.class);
+        classes.add(EchoParamResource.class);
+        classes.add(EchoParamFieldResource.class);
+        classes.add(EchoParamConstructorResource.class);
+        classes.add(ProxyInjectedAppScopedResource.class);
+        classes.add(RequestScopedResource.class);
+        return classes;
+    }
+}
diff --git a/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MyOtherResource.java b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MyOtherResource.java
new file mode 100644
index 0000000..1bbb829
--- /dev/null
+++ b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MyOtherResource.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.UriInfo;
+
+import javax.annotation.ManagedBean;
+
+import javax.enterprise.context.RequestScoped;
+
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptors;
+import javax.interceptor.InvocationContext;
+
+/**
+ * Shows injection of context objects and path parameters into the fields of a managed bean.
+ *
+ * @author Roberto Chinnici
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ManagedBean
+@RequestScoped
+@Path("/other/{c}/{d}")
+public class MyOtherResource {
+
+    public static class MyInterceptor {
+
+        @AroundInvoke
+        public Object around(InvocationContext ctx) throws Exception {
+            return String.format("INTERCEPTED: %s", ctx.proceed());
+        }
+    }
+
+    @Context UriInfo uriInfo;
+    @Context Request request;
+
+    @PathParam("c") String c;
+    @PathParam("d") String d;
+
+    @GET
+    @Produces("text/plain")
+    @Interceptors(MyInterceptor.class)
+    public String get() {
+        return String.format("OK %s %s, c=%s, d=%s", request.getMethod(), uriInfo.getRequestUri(), c, d);
+    }
+}
diff --git a/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MySingletonResource.java b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MySingletonResource.java
new file mode 100644
index 0000000..eb6c410
--- /dev/null
+++ b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MySingletonResource.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.UriInfo;
+
+import javax.annotation.ManagedBean;
+import javax.annotation.Resource;
+import javax.enterprise.context.ApplicationScoped;
+
+/**
+ * Shows injection of context objects into the fields of a managed bean.
+ *
+ * @author Roberto Chinnici
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ManagedBean
+@ApplicationScoped
+@Path("/singleton")
+public class MySingletonResource {
+
+    @Context
+    UriInfo uriInfo;
+    @Context
+    Request request;
+
+    @Resource(name = "injectedResource")
+    int counter = 0;
+
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String get() {
+        return String.format("OK %s uri=%s",
+                request.getMethod(), uriInfo.getRequestUri());
+    }
+
+    @Path("counter")
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public synchronized String getCount() {
+        return String.format("%d", counter++);
+    }
+
+    @Path("counter")
+    @PUT
+    @Consumes(MediaType.TEXT_PLAIN)
+    public synchronized void setCount(String c) {
+        counter = Integer.decode(c);
+    }
+}
diff --git a/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/ProxyInjectedAppScopedResource.java b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/ProxyInjectedAppScopedResource.java
new file mode 100644
index 0000000..7b1eebe
--- /dev/null
+++ b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/ProxyInjectedAppScopedResource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * Application scoped CDI bean to demonstrate a dynamic proxy is being injected
+ * for URI info so that actual request information is available.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oralcle.com)
+ */
+@ApplicationScoped
+@Path("ui-app")
+public class ProxyInjectedAppScopedResource {
+
+    @Context UriInfo uiField;
+
+    @Path("{p}")
+    @GET
+    public String getUri(@Context UriInfo uiParam) {
+        if (uiParam == uiField) {
+            throw new IllegalStateException("Dynamic proxy expected in the uiField");
+        }
+        return uiField.getRequestUri().getPath();
+    }
+}
diff --git a/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/RequestScopedResource.java b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/RequestScopedResource.java
new file mode 100644
index 0000000..5c127f3
--- /dev/null
+++ b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/RequestScopedResource.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.RequestScoped;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * Request scoped CDI bean to demonstrate no dynamic proxy is being injected
+ * for JAX-RS request scoped URI info.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oralcle.com)
+ */
+@RequestScoped
+@Path("ui-req")
+public class RequestScopedResource {
+
+    @Context UriInfo uiField;
+
+    @Path("{p}")
+    @GET
+    public String getUri(@Context UriInfo uiParam) {
+        if (uiParam != uiField) {
+            throw new IllegalStateException("No dynamic proxy expected in the uiField");
+        }
+        return uiField.getRequestUri().getPath();
+    }
+}
diff --git a/examples/cdi-webapp/src/main/resources/META-INF/beans.xml b/examples/cdi-webapp/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..5d361e9
--- /dev/null
+++ b/examples/cdi-webapp/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<beans/>
diff --git a/examples/cdi-webapp/src/main/webapp/WEB-INF/beans.xml b/examples/cdi-webapp/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..5d361e9
--- /dev/null
+++ b/examples/cdi-webapp/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<beans/>
diff --git a/examples/cdi-webapp/src/main/webapp/WEB-INF/web.xml b/examples/cdi-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..3090928
--- /dev/null
+++ b/examples/cdi-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+    <env-entry>
+        <env-entry-name>injectedResource</env-entry-name>
+        <env-entry-type>java.lang.Integer</env-entry-type>
+        <env-entry-value>42</env-entry-value>
+    </env-entry>
+</web-app>
diff --git a/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/CdiTest.java b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/CdiTest.java
new file mode 100644
index 0000000..e85295e
--- /dev/null
+++ b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/CdiTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.jboss.weld.environment.se.Weld;
+import org.junit.Assume;
+import org.junit.Before;
+
+/**
+ * Test for CDI web application resources.
+ * Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy target/cdi-webapp
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CdiTest extends JerseyTest {
+
+    Weld weld;
+
+    @Before
+    public void setup() {
+        Assume.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy());
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        weld = new Weld();
+        weld.initialize();
+        super.setUp();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        weld.shutdown();
+        super.tearDown();
+    }
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("cdi-webapp").build();
+    }
+}
diff --git a/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/EchoParamBeanTest.java b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/EchoParamBeanTest.java
new file mode 100644
index 0000000..619671a
--- /dev/null
+++ b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/EchoParamBeanTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for the echo with injected param managed bean resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class EchoParamBeanTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"alpha", "beta"},
+                {"AAA", "BBB"},
+                {"b", "a"},
+                {"1$s", "2&d"}
+        });
+    }
+
+    final String a, b;
+
+    /**
+     * Create a new test case based on the above defined parameters.
+     *
+     * @param a query parameter value
+     * @param b path parameter value
+     */
+    public EchoParamBeanTest(String a, String b) {
+        this.a = a;
+        this.b = b;
+    }
+
+    @Test
+    public void testEchoParamResource() {
+
+        final WebTarget target = target().path("echofield").path(b);
+
+        String s = target.queryParam("a", a).request().get(String.class);
+
+        assertThat(s, containsString("ECHO"));
+        assertThat(s, containsString(String.format("%s %s", a, b)));
+    }
+}
diff --git a/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/EchoResourceTest.java b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/EchoResourceTest.java
new file mode 100644
index 0000000..90664a7
--- /dev/null
+++ b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/EchoResourceTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for the echo resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class EchoResourceTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"alpha"},
+                {"AAA"},
+                {"b"},
+                {"1"}
+        });
+    }
+
+    final String a;
+
+    /**
+     * Create a new test case based on the above defined parameters.
+     *
+     * @param a path parameter value
+     */
+    public EchoResourceTest(String a) {
+        this.a = a;
+    }
+
+    @Test
+    public void testEchoResource() {
+
+        final WebTarget target = target().path("echo").path(a);
+
+        String s = target.request().get(String.class);
+
+        assertThat(s, containsString("ECHO"));
+        assertThat(s, containsString(a));
+    }
+
+    @Test
+    public void testEchoParamCtorResource() {
+
+        final WebTarget target = target().path("echoparamconstructor").path(a);
+
+        String s = target.request().get(String.class);
+
+        assertThat(s, containsString("ECHO"));
+        assertThat(s, containsString(a));
+    }
+}
diff --git a/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/HelloworldTest.java b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/HelloworldTest.java
new file mode 100644
index 0000000..4db5323
--- /dev/null
+++ b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/HelloworldTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.containsString;
+
+/**
+ * Test for the helloworld resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class HelloworldTest extends CdiTest {
+
+    @Test
+    public void testHelloworldResource() {
+        String s = target().path("helloworld").request().get(String.class);
+        assertThat(s, containsString("Hello World"));
+    }
+}
diff --git a/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/PerApplicationBeanTest.java b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/PerApplicationBeanTest.java
new file mode 100644
index 0000000..f3ec4d2
--- /dev/null
+++ b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/PerApplicationBeanTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.CoreMatchers.containsString;
+
+/**
+ * Test for the application scoped managed bean resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class PerApplicationBeanTest extends CdiTest {
+
+    @Test
+    public void testApplicationScopedResource() {
+
+        final WebTarget singleton = target().path("singleton");
+
+        String s = singleton.request().get(String.class);
+        assertThat(s, containsString(singleton.getUri().toString()));
+        assertThat(s, containsString("GET"));
+
+        final WebTarget counter = singleton.path("counter");
+
+        // TODO: JERSEY-2744:
+        // TODO: @Resource injection will not work on SE
+        // TODO: add a custom extension to make this work with Grizzly
+//        String c42 = counter.request().get(String.class);
+//        assertThat(c42, containsString("42"));
+//
+//        String c43 = counter.request().get(String.class);
+//        assertThat(c43, containsString("43"));
+
+        counter.request().put(Entity.text("12"));
+
+        String c12 = counter.request().get(String.class);
+        assertThat(c12, containsString("12"));
+
+        counter.request().put(Entity.text("42"));
+    }
+}
diff --git a/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/PerRequestBeanTest.java b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/PerRequestBeanTest.java
new file mode 100644
index 0000000..1532cc6
--- /dev/null
+++ b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/PerRequestBeanTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for the request scoped managed bean resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class PerRequestBeanTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"gamma", "delta"},
+                {"CC C", "D DD"},
+                {"d", "c"},
+                {"@^&", "?!:"}
+        });
+    }
+
+    final String c, d;
+
+    /**
+     * Create a new test case based on the above defined parameters.
+     *
+     * @param c first path parameter value.
+     * @param d second path parameter value.
+     */
+    public PerRequestBeanTest(String c, String d) {
+        this.c = c;
+        this.d = d;
+    }
+
+    @Test
+    public void testTheOtherResource() {
+        final WebTarget target = target().path("other").path(c).path(d);
+
+        String s = target.request().get(String.class);
+        assertThat(s, containsString(target.getUri().toString()));
+        assertThat(s, containsString("GET"));
+        assertThat(s, containsString(String.format("c=%s", c)));
+        assertThat(s, containsString(String.format("d=%s", d)));
+        assertThat(s, startsWith("INTERCEPTED"));
+    }
+}
diff --git a/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/ProxyScopeAlignmentTest.java b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/ProxyScopeAlignmentTest.java
new file mode 100644
index 0000000..b963e3d
--- /dev/null
+++ b/examples/cdi-webapp/src/test/java/org/glassfish/jersey/examples/cdi/resources/ProxyScopeAlignmentTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Ensure CDI and JAX-RS scopes are well aligned, so that dynamic proxies
+ * are only created when needed.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class ProxyScopeAlignmentTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"one"},
+                {"too"},
+                {"much"}
+        });
+    }
+
+    final String p;
+
+    /**
+     * Create a new test case based on the above defined parameters.
+     *
+     * @param p path parameter value
+     */
+    public ProxyScopeAlignmentTest(String p) {
+        this.p = p;
+    }
+
+    @Test
+    public void testUiInjection() {
+
+        final WebTarget app = target().path("ui-app").path(p);
+        final WebTarget req = target().path("ui-req").path(p);
+
+        String ar = app.request().get(String.class);
+        String rr = req.request().get(String.class);
+
+        assertThat(ar, containsString(p));
+        assertThat(rr, containsString(p));
+    }
+}
diff --git a/examples/clipboard-programmatic/README.MD b/examples/clipboard-programmatic/README.MD
new file mode 100644
index 0000000..e3f096d
--- /dev/null
+++ b/examples/clipboard-programmatic/README.MD
@@ -0,0 +1,45 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Clipboard Example
+=================
+
+Very basic programmatically created resource example showcases CRUD functionality
+implemented via HTTP POST, GET, PUT and DELETE methods.
+A simple clipboard is simulated which is capable of handling text data only.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                  | Resource class   | HTTP methods
+------------------------- | ---------------- | --------------
+**_/clipboard_**          | programmatically | GET
+**_/clipboard_**          | programmatically | POST
+**_/clipboard_**          | programmatically | PUT
+**_/clipboard_**          | programmatically | DELETE
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the Clipboard example using [Grizzly](http://grizzly.java.net/)
+
+You can post any text:
+
+```
+curl -v -X POST http://localhost:8080/base/clipboard -H "Content-Type:text/plain" -d 'The urgent task'
+```
+
+After you successfully deploy the application and post entities, visit the following URLs:
+
+-   <http://localhost:8080/base/clipboard>
\ No newline at end of file
diff --git a/examples/clipboard-programmatic/pom.xml b/examples/clipboard-programmatic/pom.xml
new file mode 100644
index 0000000..f1321db
--- /dev/null
+++ b/examples/clipboard-programmatic/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>clipboard-programmatic</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-clipboard-programmatic</name>
+
+    <description>Jersey programmatic resource API clipboard example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.clipboard.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/clipboard-programmatic/src/main/java/org/glassfish/jersey/examples/clipboard/App.java b/examples/clipboard-programmatic/src/main/java/org/glassfish/jersey/examples/clipboard/App.java
new file mode 100644
index 0000000..e96397f
--- /dev/null
+++ b/examples/clipboard-programmatic/src/main/java/org/glassfish/jersey/examples/clipboard/App.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.clipboard;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * This is the example entry point, where Jersey application for the example
+ * gets populated and published using the Grizzly 2 HTTP container.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/base/");
+    /**
+     * Clipboard root resource path.
+     */
+    public static final String ROOT_PATH = "clipboard";
+
+    /**
+     * Main application entry point.
+     *
+     * @param args application arguments.
+     */
+    public static void main(String[] args) {
+        try {
+            System.out.println("Clipboard Jersey Example App");
+
+            final ResourceConfig config = createProgrammaticClipboardApp();
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, config, false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(
+                    String.format("Application started.%n"
+                            + "Try out %s%s%n"
+                            + "Stop the application using CTRL+C",
+                            BASE_URI, ROOT_PATH));
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+
+    /**
+     * Create example application resource configuration.
+     *
+     * @return initialized resource configuration of the example application.
+     */
+    public static ResourceConfig createProgrammaticClipboardApp() {
+        final Resource.Builder resourceBuilder = Resource.builder(ROOT_PATH);
+        final Clipboard clipboard = new Clipboard();
+
+        resourceBuilder.addMethod("GET").handledBy(new Inflector<ContainerRequestContext, Response>() {
+
+            @Override
+            public Response apply(ContainerRequestContext data) {
+                final String content = clipboard.content();
+                if (content.isEmpty()) {
+                    return Response.noContent().build();
+                }
+                return Response.ok(content).build();
+            }
+        });
+
+        resourceBuilder.addMethod("PUT").handledBy(new Inflector<ContainerRequestContext, Response>() {
+
+            @Override
+            public Response apply(ContainerRequestContext data) {
+                if (data != null) {
+                    clipboard.setContent(((ContainerRequest) data).readEntity(String.class));
+                }
+                return Response.noContent().build();
+            }
+        });
+
+        resourceBuilder.addMethod("POST").handledBy(new Inflector<ContainerRequestContext, Response>() {
+
+            @Override
+            public Response apply(ContainerRequestContext data) {
+                String newContent = (data != null)
+                        ? clipboard.append(((ContainerRequest) data).readEntity(String.class)) : "";
+                return Response.ok(newContent).build();
+            }
+        });
+
+        resourceBuilder.addMethod("DELETE").handledBy(new Inflector<ContainerRequestContext, Response>() {
+
+            @Override
+            public Response apply(ContainerRequestContext data) {
+                clipboard.clear();
+                return Response.noContent().build();
+            }
+        });
+
+        return new ResourceConfig().registerResources(resourceBuilder.build());
+    }
+}
diff --git a/examples/clipboard-programmatic/src/main/java/org/glassfish/jersey/examples/clipboard/Clipboard.java b/examples/clipboard-programmatic/src/main/java/org/glassfish/jersey/examples/clipboard/Clipboard.java
new file mode 100644
index 0000000..2788c72
--- /dev/null
+++ b/examples/clipboard-programmatic/src/main/java/org/glassfish/jersey/examples/clipboard/Clipboard.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.clipboard;
+
+/**
+ * Simple clipboard implementation.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class Clipboard {
+    private final StringBuffer content = new StringBuffer();
+
+    public String content() { // GET
+        return content.toString();
+    }
+
+    public void setContent(String replacement) { // PUT
+        content.delete(0, content.length()).append(replacement);
+    }
+
+    public String append(String append) { // POST
+        return content.append(append).toString();
+    }
+
+    public void clear() { // DELETE
+        content.delete(0, content.length());
+    }
+
+}
diff --git a/examples/clipboard-programmatic/src/test/java/org/glassfish/jersey/examples/clipboard/ClipboardTest.java b/examples/clipboard-programmatic/src/test/java/org/glassfish/jersey/examples/clipboard/ClipboardTest.java
new file mode 100644
index 0000000..c708156
--- /dev/null
+++ b/examples/clipboard-programmatic/src/test/java/org/glassfish/jersey/examples/clipboard/ClipboardTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.clipboard;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+public class ClipboardTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return App.createProgrammaticClipboardApp();
+    }
+
+    @Test
+    public void testClipboard() throws Exception {
+        final Client client = client();
+        final WebTarget clipboard = client.target(getBaseUri()).path(App.ROOT_PATH);
+
+        Response response;
+
+        response = clipboard.request("text/plain").get();
+        assertEquals(204, response.getStatus());
+
+        response = clipboard.request("text/plain").put(Entity.text("Hello"));
+        assertEquals(204, response.getStatus());
+        assertEquals("Hello", clipboard.request("text/plain").get(String.class));
+
+        response = clipboard.request("text/plain").post(Entity.text(" World!"));
+        assertEquals(200, response.getStatus());
+        assertEquals("Hello World!", clipboard.request("text/plain").get(String.class));
+
+        response = clipboard.request("text/plain").delete();
+        assertEquals(204, response.getStatus());
+        assertEquals(204, clipboard.request("text/plain").get().getStatus());
+    }
+}
diff --git a/examples/clipboard/README.MD b/examples/clipboard/README.MD
new file mode 100644
index 0000000..0f1df89
--- /dev/null
+++ b/examples/clipboard/README.MD
@@ -0,0 +1,57 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Clipboard Example
+=================
+
+Very basic resource example showcases CRUD functionality
+implemented via HTTP POST, GET, PUT and DELETE methods.
+A simple clipboard is simulated which is capable of handling text data only.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                  | Resource class    | HTTP methods
+------------------------- | ----------------  | --------------
+**_/clipboard_**          | ClipboardResource | GET
+**_/clipboard_**          | ClipboardResource | POST
+**_/clipboard_**          | ClipboardResource | PUT
+**_/clipboard_**          | ClipboardResource | DELETE
+**_/clipboard/history_**  | ClipboardResource | GET
+**_/clipboard/history_**  | ClipboardResource | DELETE
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the Clipboard example using [Grizzly](http://grizzly.java.net/)
+
+You can post a JSON entity:
+
+```
+curl -X POST http://localhost:8080/base/clipboard -H "Content-Type:application/json" -d '
+{
+   content: "The urgent task."
+}'
+```
+
+or plain text entity:
+
+```
+curl -v -X POST http://localhost:8080/base/clipboard -H "Content-Type:text/plain" -d 'The urgent task'
+```
+
+After you successfully deploy the application and post entities, visit the following URLs:
+
+-   <http://localhost:8080/base/clipboard>
+-   <http://localhost:8080/base/clipboard/history>
\ No newline at end of file
diff --git a/examples/clipboard/pom.xml b/examples/clipboard/pom.xml
new file mode 100644
index 0000000..1655949
--- /dev/null
+++ b/examples/clipboard/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>clipboard</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-clipboard</name>
+
+    <description>Jersey clipboard example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.clipboard.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/clipboard/src/main/java/org/glassfish/jersey/examples/clipboard/App.java b/examples/clipboard/src/main/java/org/glassfish/jersey/examples/clipboard/App.java
new file mode 100644
index 0000000..49aed24
--- /dev/null
+++ b/examples/clipboard/src/main/java/org/glassfish/jersey/examples/clipboard/App.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.clipboard;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.server.model.ResourceMethod;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * This is the example entry point, where Jersey application for the example
+ * gets populated and published using the Grizzly 2 HTTP container.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/base/");
+    /**
+     * Clipboard root resource path.
+     */
+    public static final String ROOT_PATH = "clipboard";
+
+    /**
+     * Main application entry point.
+     *
+     * @param args application arguments.
+     */
+    public static void main(String[] args) {
+        try {
+            System.out.println("Clipboard Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, createApp(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(
+                    String.format("Application started.%n"
+                                    + "Try out %s%s%n"
+                                    + "Stop the application using CTRL+C",
+                            BASE_URI, ROOT_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    /**
+     * Create example application resource configuration.
+     *
+     * @return initialized resource configuration of the example application.
+     */
+    public static ResourceConfig createApp() {
+
+        MediaType[] jsonAndTextTypes = new MediaType[] {MediaType.APPLICATION_JSON_TYPE, MediaType.TEXT_PLAIN_TYPE};
+
+        final ResourceConfig resourceConfig = new ResourceConfig(
+                ClipboardResource.class,
+                ClipboardDataProvider.ApplicationJson.class,
+                ClipboardDataProvider.TextPlain.class);
+
+        final Resource.Builder resourceBuilder = Resource.builder("echo");
+        ResourceMethod.Builder rmBuilder = resourceBuilder.addMethod("POST");
+        rmBuilder.consumes(jsonAndTextTypes).produces(jsonAndTextTypes)
+                .handledBy(new Inflector<ContainerRequestContext, Response>() {
+
+                    @Override
+                    public Response apply(ContainerRequestContext request) {
+                        ClipboardData data = (request != null)
+                                ? ((ContainerRequest) request).readEntity(ClipboardData.class) : null;
+                        return Response.ok(data).build();
+                    }
+                });
+
+        resourceConfig.registerResources(resourceBuilder.build());
+        return resourceConfig;
+    }
+}
diff --git a/examples/clipboard/src/main/java/org/glassfish/jersey/examples/clipboard/ClipboardData.java b/examples/clipboard/src/main/java/org/glassfish/jersey/examples/clipboard/ClipboardData.java
new file mode 100644
index 0000000..6b5b4d5
--- /dev/null
+++ b/examples/clipboard/src/main/java/org/glassfish/jersey/examples/clipboard/ClipboardData.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.clipboard;
+
+import java.util.Objects;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class ClipboardData {
+
+    private String content;
+
+    public ClipboardData(String content) {
+        this.content = content;
+    }
+
+    @Override
+    public String toString() {
+        return content;
+    }
+
+    boolean isEmpty() {
+        return "".equals(content);
+    }
+
+    ClipboardData append(ClipboardData addition) {
+        content = content + addition.content;
+        return this;
+    }
+
+    void clear() {
+        content = "";
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof ClipboardData)) {
+            return false;
+        }
+        final ClipboardData that = (ClipboardData) o;
+        return Objects.equals(content, that.content);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(content);
+    }
+}
diff --git a/examples/clipboard/src/main/java/org/glassfish/jersey/examples/clipboard/ClipboardDataProvider.java b/examples/clipboard/src/main/java/org/glassfish/jersey/examples/clipboard/ClipboardDataProvider.java
new file mode 100644
index 0000000..d24eb25
--- /dev/null
+++ b/examples/clipboard/src/main/java/org/glassfish/jersey/examples/clipboard/ClipboardDataProvider.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.clipboard;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.message.MessageUtils;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public abstract class ClipboardDataProvider implements MessageBodyWriter, MessageBodyReader {
+
+    @Provider
+    @Consumes("text/plain")
+    @Produces("text/plain")
+    public static class TextPlain extends ClipboardDataProvider {
+
+        @Override
+        public void writeTo(final Object t, final Class type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType, final MultivaluedMap httpHeaders, final OutputStream entityStream)
+                throws IOException, WebApplicationException {
+            entityStream.write(t.toString().getBytes(MessageUtils.getCharset(mediaType)));
+        }
+
+        @Override
+        public Object readFrom(final Class type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType, final MultivaluedMap httpHeaders, final InputStream entityStream)
+                throws IOException, WebApplicationException {
+            return new ClipboardData(readStringFromStream(entityStream, MessageUtils.getCharset(mediaType)));
+        }
+    }
+
+    @Provider
+    @Consumes("application/json")
+    @Produces("application/json")
+    public static class ApplicationJson extends ClipboardDataProvider {
+
+        private static final String JsonOpenning = "{\"content\":\"";
+        private static final String JsonClosing = "\"}";
+
+        @Override
+        public void writeTo(final Object t, final Class type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType, final MultivaluedMap httpHeaders, final OutputStream entityStream)
+                throws IOException, WebApplicationException {
+            entityStream.write(String.format("%s%s%s", JsonOpenning, t.toString(), JsonClosing)
+                    .getBytes(MessageUtils.getCharset(mediaType)));
+        }
+
+        @Override
+        public Object readFrom(final Class type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType, final MultivaluedMap httpHeaders, final InputStream entityStream)
+                throws IOException, WebApplicationException {
+            final String jsonExpression = readStringFromStream(entityStream, MessageUtils.getCharset(mediaType));
+            return new ClipboardData(jsonExpression.replace(JsonOpenning, "").replace(JsonClosing, ""));
+        }
+    }
+
+    @Override
+    public boolean isWriteable(final Class type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return isKnownType(type, genericType);
+    }
+
+    private boolean isKnownType(final Class<?> type, final Type genericType) {
+        return type.isAssignableFrom(ClipboardData.class)
+                || (Collection.class.isAssignableFrom(type)
+                    && (((ParameterizedType) genericType).getActualTypeArguments()[0]).equals(String.class));
+    }
+
+    @Override
+    public long getSize(final Object t, final Class type, final Type genericType, final Annotation[] annotations,
+                        final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public boolean isReadable(final Class type, final Type genericType, final Annotation[] annotations,
+                              final MediaType mediaType) {
+        return isKnownType(type, genericType);
+    }
+
+    private static String readStringFromStream(final InputStream entityStream, Charset charset) throws IOException {
+        final StringBuilder result = new StringBuilder();
+        final byte[] buf = new byte[2048];
+        int i;
+        while ((i = entityStream.read(buf)) != -1) {
+            result.append(new String(buf, 0, i, charset));
+        }
+        return result.toString();
+    }
+}
diff --git a/examples/clipboard/src/main/java/org/glassfish/jersey/examples/clipboard/ClipboardResource.java b/examples/clipboard/src/main/java/org/glassfish/jersey/examples/clipboard/ClipboardResource.java
new file mode 100644
index 0000000..f167d5c
--- /dev/null
+++ b/examples/clipboard/src/main/java/org/glassfish/jersey/examples/clipboard/ClipboardResource.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.clipboard;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Variant;
+
+/**
+ * Very basic resource example showcases CRUD functionality
+ * implemented via HTTP POST, GET, PUT and DELETE methods.
+ * A simple clipboard is simulated which is capable of handling
+ * text data only.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("clipboard")
+public class ClipboardResource {
+
+    static final List<Variant> supportedVariants =
+            Variant.mediaTypes(
+                    MediaType.APPLICATION_JSON_TYPE,
+                    MediaType.TEXT_PLAIN_TYPE).add().build();
+
+    private static final List<String> history = new LinkedList<String>();
+
+    private static ClipboardData content = new ClipboardData("");
+
+    @Context
+    Request request;
+
+    @GET
+    public Response content() {
+
+        if (content.isEmpty()) {
+            return Response.noContent().build();
+        }
+
+        final Variant variant = request.selectVariant(supportedVariants);
+
+        if (variant == null) {
+            return Response.notAcceptable(supportedVariants).build();
+        } else {
+            return Response.ok(content, variant.getMediaType()).build();
+        }
+    }
+
+    @PUT
+    @Consumes({"text/plain", "application/json"})
+    public void setContent(ClipboardData newContent) {
+        saveHistory();
+        updateContent(newContent);
+    }
+
+    private static void updateContent(ClipboardData newContent) {
+        content = newContent;
+    }
+
+    @POST
+    @Consumes({"text/plain", "application/json"})
+    @Produces({"text/plain", "application/json"})
+    public ClipboardData append(ClipboardData appendix) {
+        saveHistory();
+        return content.append(appendix);
+    }
+
+    @DELETE
+    public void clear() {
+        saveHistory();
+        content.clear();
+    }
+
+    @GET
+    @Path("history")
+    @Produces({"text/plain", "application/json"})
+    public List<String> getHistory() {
+        return history;
+    }
+
+    @DELETE
+    @Path("history")
+    public void clearHistory() {
+        history.clear();
+    }
+
+    private void saveHistory() {
+        String currentContent = content.toString();
+        if (!currentContent.isEmpty()) {
+            history.add(currentContent);
+        }
+    }
+}
diff --git a/examples/clipboard/src/test/java/org/glassfish/jersey/examples/clipboard/ClipboardTest.java b/examples/clipboard/src/test/java/org/glassfish/jersey/examples/clipboard/ClipboardTest.java
new file mode 100644
index 0000000..4fdb3b3
--- /dev/null
+++ b/examples/clipboard/src/test/java/org/glassfish/jersey/examples/clipboard/ClipboardTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.clipboard;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+public class ClipboardTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return App.createApp();
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        client().target(getBaseUri()).path(App.ROOT_PATH).request().delete();
+        client().target(getBaseUri()).path(App.ROOT_PATH).path("history").request().delete();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(ClipboardDataProvider.ApplicationJson.class).register(ClipboardDataProvider.TextPlain.class);
+    }
+
+    @Test
+    public void testDeclarativeClipboardTextPlain() throws Exception {
+        testDeclarativeClipboard(MediaType.TEXT_PLAIN_TYPE);
+    }
+
+    @Test
+    public void testDeclarativeClipboardAppJson() throws Exception {
+        testDeclarativeClipboard(MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    public void testDeclarativeClipboard(MediaType mediaType) throws Exception {
+        final WebTarget clipboard = client().target(getBaseUri()).path(App.ROOT_PATH);
+
+        Response response;
+
+        response = clipboard.request(mediaType).get();
+        assertEquals(204, response.getStatus());
+
+        response = clipboard.request(mediaType).put(Entity.entity(new ClipboardData("Hello"), mediaType));
+        assertEquals(204, response.getStatus());
+
+        assertEquals("Hello", clipboard.request(mediaType).get(ClipboardData.class).toString());
+
+        response = clipboard.request(mediaType).post(Entity.entity(new ClipboardData(" World!"), mediaType));
+        assertEquals(200, response.getStatus());
+
+        assertEquals("Hello World!", clipboard.request(mediaType).get(ClipboardData.class).toString());
+
+        response = clipboard.request(mediaType).delete();
+        assertEquals(204, response.getStatus());
+
+        assertEquals(204, clipboard.request(mediaType).get().getStatus());
+    }
+
+    @Test
+    public void testProgrammaticEchoTextPlain() throws Exception {
+        testProgrammaticEcho(MediaType.TEXT_PLAIN_TYPE);
+    }
+
+    @Test
+    public void testProgrammaticEchoAppJson() throws Exception {
+        testProgrammaticEcho(MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    @Test
+    public void testHistoryInJson() throws Exception {
+        callGetHistory(MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    @Test
+    public void testHistoryInTextPlain() throws Exception {
+        callGetHistory(MediaType.TEXT_PLAIN_TYPE);
+    }
+
+    private void callGetHistory(final MediaType mediaType) {
+        final WebTarget clipboard = client().target(getBaseUri()).path(App.ROOT_PATH);
+        clipboard.request(mediaType).post(Entity.entity(new ClipboardData("Task 1 "), mediaType));
+        clipboard.request(mediaType).post(Entity.entity(new ClipboardData("Task 2 "), mediaType));
+        clipboard.request(mediaType).post(Entity.entity(new ClipboardData("Task 3 "), mediaType));
+
+        ClipboardData response = clipboard.path("history").request(mediaType).get(ClipboardData.class);
+        assertEquals(new ClipboardData("[Task 1 , Task 1 Task 2 ]"), response);
+    }
+
+    public void testProgrammaticEcho(MediaType mediaType) throws Exception {
+        final WebTarget echo = client().target(getBaseUri()).path("echo");
+
+        Response response = echo.request(mediaType).post(Entity.entity(new ClipboardData("Hello"), mediaType));
+        assertEquals("Hello", response.readEntity(ClipboardData.class).toString());
+    }
+}
diff --git a/examples/declarative-linking/README.MD b/examples/declarative-linking/README.MD
new file mode 100644
index 0000000..8e84dcf
--- /dev/null
+++ b/examples/declarative-linking/README.MD
@@ -0,0 +1,80 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Declarative Hyperlinking Example
+================================
+
+This example demonstrates how to use the declarative hyperlink
+annotations.
+
+Contents
+--------
+
+The example consists of one resource class and one representation class
+along with a couple of model classes:
+
+`org.glassfish.jersey.examples.linking.resources.ItemResource`
+
+A resource class that produces a XML response to an HTTP GET.
+
+`com.sun.jersey.samples.linking.representation.ItemRepresentation`
+
+A JAXB representation class used to produce XML. This class also
+contains @Link and @Ref annotations to produce hyperlinks in the
+XML document.
+
+The mapping of the URI path space is presented in the following table:
+
+URI path               | Resource class   | HTTP methods
+---------------------- | ---------------- | --------------
+**_/items/{index}_**   | ItemResource     | GET
+
+Sample Response
+---------------
+
+You can access resources of this application also using curl:
+
+>     curl -v http://localhost:8080/items/1
+
+In this example you should see an XML snippet with a next and a previous
+link.
+
+```xml
+<item>
+    <name>Item 1</name>
+    <link href="http://localhost:8080/items/1" rel="self"/>
+    <links>
+        <link href="http://localhost:8080/items/2" rel="next"/>
+        <link href="http://localhost:8080/items/0" rel="prev"/>
+    </links>
+</item>
+```
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys current example using Grizzly. You can access the
+application at:
+
+-   <http://localhost:8080/items/0>\
+-   <http://localhost:8080/items/1>\
+-   <http://localhost:8080/items/2>\
+-   <http://localhost:8080/items/3>\
+-   <http://localhost:8080/items/4>\
+
+Notice how the first has a self link, a next link but no previous link,
+the second, third and fourth have all three and the last one only has a
+self link and prev link.
+
+Observe the HTTP headers included in the responses (e.g. via `curl -v`).
+Notice that there is a HTTP Link header for next and previous when
+appropriate.
\ No newline at end of file
diff --git a/examples/declarative-linking/pom.xml b/examples/declarative-linking/pom.xml
new file mode 100644
index 0000000..023669d
--- /dev/null
+++ b/examples/declarative-linking/pom.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>declarative-linking</artifactId>
+
+    <packaging>jar</packaging>
+    <name>jersey-examples-declarative-linking</name>
+
+    <description>Declarative Hyperlinking - Jersey Sample</description>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-declarative-linking</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.el</groupId>
+            <artifactId>javax.el-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.web</groupId>
+            <artifactId>javax.el</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.eclipse.persistence</groupId>
+            <artifactId>org.eclipse.persistence.moxy</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.linking.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/App.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/App.java
new file mode 100644
index 0000000..5fac917
--- /dev/null
+++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/App.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.linking;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.examples.linking.resources.ItemsResource;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.linking.DeclarativeLinkingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Show link injection in action
+ *
+ * @author Mark Hadley
+ * @author Gerard Davison (gerard.davison at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/");
+    public static final String ROOT_PATH = "items";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Declarative Linking\" Jersey Example App");
+
+            final ResourceConfig resourceConfig = new ResourceConfig(ItemsResource.class);
+            resourceConfig.register(DeclarativeLinkingFeature.class);
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.\nTry out curl -L %s%s\nStop the application using CTRL+C",
+                    BASE_URI, ROOT_PATH));
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+}
diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/model/ItemModel.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/model/ItemModel.java
new file mode 100644
index 0000000..2dc21c6
--- /dev/null
+++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/model/ItemModel.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.linking.model;
+
+/**
+ * @author Mark Hadley
+ * @author Gerard Davison (gerard.davison at oracle.com)
+ */
+public class ItemModel {
+    String name;
+
+    public ItemModel(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+}
diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/model/ItemsModel.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/model/ItemsModel.java
new file mode 100644
index 0000000..50795a3
--- /dev/null
+++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/model/ItemsModel.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.linking.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Models a three entry list of items and provides a simple means of navigating
+ * the list.
+ *
+ * @author Mark Hadley
+ * @author Gerard Davison (gerard.davison at oracle.com)
+ */
+public class ItemsModel {
+
+    private List<ItemModel> items;
+    private static ItemsModel instance;
+
+    public static synchronized ItemsModel getInstance() {
+        if (instance == null) {
+            instance = new ItemsModel();
+        }
+        return instance;
+    }
+
+    private ItemsModel() {
+        items = new ArrayList<>();
+        for (int i = 0; i < 100; i++) {
+            items.add(new ItemModel("Item " + i));
+        }
+    }
+
+    public boolean hasNext(String currentId) {
+        return getIndex(currentId) < items.size() - 1;
+    }
+
+    public boolean hasPrev(String currentId) {
+        return getIndex(currentId) > 0;
+    }
+
+    public ItemModel getItem(String id) {
+        return items.get(getIndex(id));
+    }
+
+    public String getNextId(String id) {
+        return Integer.toString(getIndex(id) + 1);
+    }
+
+    public String getPrevId(String id) {
+        return Integer.toString(getIndex(id) - 1);
+    }
+
+    private int getIndex(String id) {
+        return Integer.parseInt(id);
+    }
+
+    public int getSize() {
+        return items.size();
+    }
+}
diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemRepresentation.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemRepresentation.java
new file mode 100644
index 0000000..532527a
--- /dev/null
+++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemRepresentation.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.linking.representation;
+
+import java.util.List;
+
+import javax.ws.rs.core.Link;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import org.glassfish.jersey.examples.linking.model.ItemsModel;
+import org.glassfish.jersey.examples.linking.resources.ItemResource;
+import org.glassfish.jersey.linking.Binding;
+import org.glassfish.jersey.linking.InjectLink;
+import org.glassfish.jersey.linking.InjectLink.Style;
+import org.glassfish.jersey.linking.InjectLinks;
+
+/**
+ * JAXB representation of an item
+ *
+ *
+ * @author Mark Hadley
+ * @author Gerard Davison (gerard.davison at oracle.com)
+ */
+@XmlAccessorType(XmlAccessType.NONE)
+@XmlRootElement(name = "item")
+@InjectLinks({
+        @InjectLink(
+                resource = ItemResource.class,
+                style = Style.ABSOLUTE,
+                condition = "${instance.next}",
+                bindings = @Binding(name = "id", value = "${instance.nextId}"),
+                rel = "next"
+        ),
+        @InjectLink(
+                resource = ItemResource.class,
+                style = Style.ABSOLUTE,
+                condition = "${instance.prev}",
+                bindings = @Binding(name = "id", value = "${instance.prevId}"),
+                rel = "prev"
+        )
+})
+public class ItemRepresentation {
+
+    @XmlElement
+    private String name;
+
+    @XmlTransient
+    private String id;
+    @XmlTransient
+    private ItemsModel itemsModel;
+
+    @InjectLink(
+            resource = ItemResource.class,
+            style = Style.ABSOLUTE,
+            bindings = @Binding(name = "id", value = "${instance.id}"),
+            rel = "self"
+    )
+    @XmlJavaTypeAdapter(Link.JaxbAdapter.class)
+    @XmlElement(name = "link")
+    Link self;
+
+    @InjectLinks({
+            @InjectLink(
+                    resource = ItemResource.class,
+                    style = Style.ABSOLUTE,
+                    condition = "${instance.next}",
+                    bindings = @Binding(name = "id", value = "${instance.nextId}"),
+                    rel = "next"
+            ),
+            @InjectLink(
+                    resource = ItemResource.class,
+                    style = Style.ABSOLUTE,
+                    condition = "${instance.prev}",
+                    bindings = @Binding(name = "id", value = "${instance.prevId}"),
+                    rel = "prev"
+            )})
+    @XmlElement(name = "link")
+    @XmlElementWrapper(name = "links")
+    @XmlJavaTypeAdapter(Link.JaxbAdapter.class)
+    List<Link> links;
+
+    public ItemRepresentation() {
+
+    }
+
+    public ItemRepresentation(ItemsModel itemsModel, String id, String name) {
+        this.itemsModel = itemsModel;
+        this.name = name;
+        this.id = id;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public boolean isNext() {
+        return itemsModel.hasNext(id);
+    }
+
+    public boolean isPrev() {
+        return itemsModel.hasPrev(id);
+    }
+
+    public String getNextId() {
+        return itemsModel.getNextId(id);
+    }
+
+    public String getPrevId() {
+        return itemsModel.getPrevId(id);
+    }
+
+}
diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemsRepresentation.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemsRepresentation.java
new file mode 100644
index 0000000..ae2023c
--- /dev/null
+++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/representation/ItemsRepresentation.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.linking.representation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.core.Link;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import org.glassfish.jersey.examples.linking.model.ItemsModel;
+import org.glassfish.jersey.examples.linking.resources.ItemsResource;
+import org.glassfish.jersey.linking.Binding;
+import org.glassfish.jersey.linking.InjectLink;
+import org.glassfish.jersey.linking.InjectLink.Style;
+import org.glassfish.jersey.linking.InjectLinks;
+
+/**
+ * JAXB representation of a sublist of items
+ *
+ * @author Mark Hadley
+ * @author Gerard Davison (gerard.davison at oracle.com)
+ */
+@XmlAccessorType(XmlAccessType.NONE)
+@XmlRootElement(name = "items")
+@InjectLinks({
+        @InjectLink(
+                resource = ItemsResource.class,
+                style = Style.ABSOLUTE,
+                method = "query",
+                condition = "${instance.offset + instance.limit < instance.modelLimit}",
+                bindings = {
+                        @Binding(name = "offset", value = "${instance.offset + instance.limit}"),
+                        @Binding(name = "limit", value = "${instance.limit}")
+                },
+                rel = "next"
+        ),
+        @InjectLink(
+                resource = ItemsResource.class,
+                style = Style.ABSOLUTE,
+                method = "query",
+                condition = "${instance.offset - instance.limit >= 0}",
+                bindings = {
+                        @Binding(name = "offset", value = "${instance.offset - instance.limit}"),
+                        @Binding(name = "limit", value = "${instance.limit}")
+                },
+                rel = "prev"
+        )})
+
+public class ItemsRepresentation {
+
+    @XmlElement(name = "items")
+    private List<ItemRepresentation> items;
+
+    @XmlTransient
+    private int offset, limit;
+
+    @XmlTransient
+    private ItemsModel itemsModel;
+
+    @InjectLink(
+            resource = ItemsResource.class,
+            method = "query",
+            style = Style.ABSOLUTE,
+            bindings = {@Binding(name = "offset", value = "${instance.offset}"),
+                    @Binding(name = "limit", value = "${instance.limit}")
+            },
+            rel = "self"
+    )
+    @XmlJavaTypeAdapter(Link.JaxbAdapter.class)
+    @XmlElement(name = "link")
+    Link self;
+
+    @InjectLinks({
+            @InjectLink(
+                    resource = ItemsResource.class,
+                    style = Style.ABSOLUTE,
+                    method = "query",
+                    condition = "${instance.offset + instance.limit < instance.modelLimit}",
+                    bindings = {
+                            @Binding(name = "offset", value = "${instance.offset + instance.limit}"),
+                            @Binding(name = "limit", value = "${instance.limit}")
+                    },
+                    rel = "next"
+            ),
+            @InjectLink(
+                    resource = ItemsResource.class,
+                    style = Style.ABSOLUTE,
+                    method = "query",
+                    condition = "${instance.offset - instance.limit >= 0}",
+                    bindings = {
+                            @Binding(name = "offset", value = "${instance.offset - instance.limit}"),
+                            @Binding(name = "limit", value = "${instance.limit}")
+                    },
+                    rel = "prev"
+            )})
+    @XmlElement(name = "link")
+    @XmlElementWrapper(name = "links")
+    @XmlJavaTypeAdapter(Link.JaxbAdapter.class)
+    List<Link> links;
+
+    public ItemsRepresentation() {
+        offset = 0;
+        limit = 10;
+    }
+
+    public ItemsRepresentation(ItemsModel itemsModel, int offset, int limit) {
+
+        this.offset = offset;
+        this.limit = limit;
+        this.itemsModel = itemsModel;
+
+        items = new ArrayList<>();
+        for (int i = offset; i < (offset + limit) && i < itemsModel.getSize(); i++) {
+            items.add(new ItemRepresentation(
+                    itemsModel,
+                    Integer.toString(i),
+                    itemsModel.getItem(Integer.toString(i)).getName()));
+        }
+
+    }
+
+    public int getOffset() {
+        return offset;
+    }
+
+    public int getLimit() {
+        return limit;
+    }
+
+    public int getModelLimit() {
+        return itemsModel.getSize();
+    }
+}
diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemResource.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemResource.java
new file mode 100644
index 0000000..db93267
--- /dev/null
+++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemResource.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.linking.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.examples.linking.model.ItemModel;
+import org.glassfish.jersey.examples.linking.model.ItemsModel;
+import org.glassfish.jersey.examples.linking.representation.ItemRepresentation;
+
+/**
+ * Resource that provides access to one item from a set of items managed
+ * by ItemsModel
+ *
+ * @author Mark Hadley
+ * @author Gerard Davison (gerard.davison at oracle.com)
+ */
+@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+public class ItemResource {
+
+    private ItemsModel itemsModel;
+    private ItemModel itemModel;
+    private String id;
+
+    public ItemResource() {
+        throw new IllegalStateException("Only for JAX-B dressing");
+    }
+
+    public ItemResource(ItemsModel itemsModel, String id) {
+        this.id = id;
+        this.itemsModel = itemsModel;
+        try {
+            itemModel = itemsModel.getItem(id);
+        } catch (IndexOutOfBoundsException ex) {
+            throw new NotFoundException();
+        }
+    }
+
+    @GET
+    public ItemRepresentation get() {
+        return new ItemRepresentation(itemsModel, id, itemModel.getName());
+    }
+
+    public String getId() {
+        return id;
+    }
+}
diff --git a/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemsResource.java b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemsResource.java
new file mode 100644
index 0000000..f342665
--- /dev/null
+++ b/examples/declarative-linking/src/main/java/org/glassfish/jersey/examples/linking/resources/ItemsResource.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.linking.resources;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.examples.linking.model.ItemsModel;
+import org.glassfish.jersey.examples.linking.representation.ItemsRepresentation;
+
+/**
+ * Resource that provides access to the entire list of items
+ *
+ * @author Mark Hadley
+ * @author Gerard Davison (gerard.davison at oracle.com)
+ */
+@Path("items")
+@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+public class ItemsResource {
+
+    private ItemsModel itemsModel;
+
+    public ItemsResource() {
+        itemsModel = ItemsModel.getInstance();
+    }
+
+    @GET
+    public ItemsRepresentation query(
+            @Context javax.ws.rs.core.UriInfo info,
+            @QueryParam("offset") @DefaultValue("-1") int offset, @DefaultValue("-1") @QueryParam("limit") int limit) {
+
+        if (offset == -1 || limit == -1) {
+            offset = offset == -1 ? 0 : offset;
+            limit = limit == -1 ? 10 : limit;
+
+            throw new WebApplicationException(
+                    Response.seeOther(info.getRequestUriBuilder().queryParam("offset", offset)
+                            .queryParam("limit", limit).build())
+                            .build()
+            );
+        }
+
+        return new ItemsRepresentation(itemsModel, offset, limit);
+    }
+
+    @Path("{id}")
+    public ItemResource get(@PathParam("id") String id) {
+        return new ItemResource(itemsModel, id);
+    }
+
+}
diff --git a/examples/declarative-linking/src/test/java/org/glassfish/jersey/examples/linking/LinkWebAppTest.java b/examples/declarative-linking/src/test/java/org/glassfish/jersey/examples/linking/LinkWebAppTest.java
new file mode 100644
index 0000000..f5fb724
--- /dev/null
+++ b/examples/declarative-linking/src/test/java/org/glassfish/jersey/examples/linking/LinkWebAppTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.linking;
+
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.examples.linking.resources.ItemsResource;
+import org.glassfish.jersey.linking.DeclarativeLinkingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Naresh (Srinivas.Bhimisetty@Sun.Com)
+ * @author Gerard Davison (gerard.davison at oracle.com)
+ */
+public class LinkWebAppTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        final ResourceConfig rc = new ResourceConfig(ItemsResource.class);
+        rc.register(DeclarativeLinkingFeature.class);
+        return rc;
+    }
+
+    /**
+     * Test that the expected response is sent back.
+     */
+    @Test
+    public void testLinks() throws Exception {
+        final Response response = target().path("items")
+                .queryParam("offset", 10)
+                .queryParam("limit", "10")
+                .request(MediaType.APPLICATION_XML_TYPE)
+                .get(Response.class);
+
+        final Response.StatusType statusInfo = response.getStatusInfo();
+        assertEquals("Should have succeeded", 200, statusInfo.getStatusCode());
+
+        final String content = response.readEntity(String.class);
+        final List<Object> linkHeaders = response.getHeaders().get("Link");
+
+        assertEquals("Should have two link headers", 2, linkHeaders.size());
+        assertThat("Content should contain next link",
+                content,
+                containsString("http://localhost:" + getPort() + "/items?offset=20&amp;limit=10"));
+    }
+}
diff --git a/examples/entity-filtering-security/README.MD b/examples/entity-filtering-security/README.MD
new file mode 100644
index 0000000..fcdbafb
--- /dev/null
+++ b/examples/entity-filtering-security/README.MD
@@ -0,0 +1,80 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Entity Data Filtering
+=====================
+
+### *Role-based Entity Filtering using security annotations*
+
+This example demonstrates how to use entity filtering feature together
+with security annotations (from `javax.annotation.security` package) and
+how to apply them on domain classes as well as on JAX-RS resource
+classes or JAX-RS resource methods.
+
+In addition to domain classes and JAX-RS resources (with security
+annotations applied) there is also one (pre-matching) container request
+filter, `SecurityRequestFilter`. The filter sets security context for
+each incoming request as if the request was invoked by a user in role
+"manager".
+
+The full description how Entity Data Filtering can be found in Jersey
+User Guide, chapter [Entity Data
+Filtering](https://jersey.java.net/documentation/latest/entity-filtering.html).
+Sections relevant to this example (describing this exact example) are:
+
+-   [Enabling and configuring Entity Filtering in your
+    application](https://jersey.java.net/documentation/latest/entity-filtering.html#d0e13911)
+-   [Role-based Entity Filtering using (javax.annotation.security)
+    annotations](https://jersey.java.net/documentation/latest/entity-filtering.html#ef.security.annotations)
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+
+URI path                                  | Resource class         | HTTP methods   | Notes
+----------------------------------------- | ---------------------- | -------------- | ----------------------------------------------------------------------------------------------
+**_/restricted-resource/denyAll_**        | RestrictedResource     | GET            | `@DenyAll` - returns HTTP 403, Forbidden response
+**_/restricted-resource/permitAll_**      | RestrictedResource     | GET            | `@PermitAll` - Role-based view on RestrictedEntity class - permitAll, simpleField
+**_/restricted-resource/rolesAllowed_**   | RestrictedResource     | GET            | `@RolesAllowed({"manager"})` - Role-based view on RestrictedEntity class - permitAll, simpleField, mixedField.managerField
+**_/unrestricted-resource_**              | UnrestrictedResource   | GET            | No security annotation used, user in role "manager" - Role-based view on RestrictedEntity class - permitAll, simpleField, mixedField.managerField
+
+Application is based on Grizzly container (see `App`). Everything needed
+(resources/providers) is registered in
+`SecurityEntityFilteringApplication`.
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean package exec:java
+
+This deploys current example using Grizzly. You can access the
+application at:
+
+-   <http://localhost:8080/unrestricted-resource>
+-   <http://localhost:8080/restricted-resource/denyAll>
+-   <http://localhost:8080/restricted-resource/permitAll>
+-   <http://localhost:8080/restricted-resource/rolesAllowed>
+-   <http://localhost:8080/restricted-resource/runtimeRolesAllowed?roles=manager,user>
+
+Using Jackson instead of MOXy
+-----------------------------
+
+This examples uses by default Entity Data Filtering feature together
+with MOXy. To switch MOXy JSON provider to Jackson (2.x) JSON provider
+simply
+
+-   comment registration of MOXy ContextResolver, and\
+     `register(new MoxyJsonConfig().setFormattedOutput(true).resolver())`
+-   uncomment registration of JacksonFeature\
+     `register(JacksonFeature.class)`
+
+in `SecurityEntityFilteringApplication` class.
diff --git a/examples/entity-filtering-security/pom.xml b/examples/entity-filtering-security/pom.xml
new file mode 100644
index 0000000..4eca15c
--- /dev/null
+++ b/examples/entity-filtering-security/pom.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>entity-filtering-security</artifactId>
+    <name>jersey-examples-entity-filtering-security</name>
+
+    <description>Jersey Entity Data Filtering Security Example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-entity-filtering</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Run the application using "mvn exec:java" -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.entityfiltering.security.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/App.java b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/App.java
new file mode 100644
index 0000000..e8c4cdf
--- /dev/null
+++ b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/App.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.security;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Java application class starting Grizzly2 server with Entity Data Filtering with security annotations.
+ *
+ * @author Michal Gajdos
+ */
+public final class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("Jersey Entity Data Filtering Example.");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI,
+                    new SecurityEntityFilteringApplication(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println("Application started.\nTry out one of these URIs:");
+            for (final String path : new String[] {"unrestricted-resource", "restricted-resource/denyAll",
+                    "restricted-resource/permitAll", "restricted-resource/rolesAllowed",
+                    "restricted-resource/runtimeRolesAllowed?roles=manager,user"}) {
+                System.out.println(BASE_URI + path);
+            }
+            System.out.println("Stop the application using CTRL+C");
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName())
+                    .log(Level.SEVERE, "I/O error occurred during reading from an system input stream.", ex);
+        }
+    }
+}
diff --git a/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/SecurityEntityFilteringApplication.java b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/SecurityEntityFilteringApplication.java
new file mode 100644
index 0000000..d2c741b
--- /dev/null
+++ b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/SecurityEntityFilteringApplication.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.security;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.SecurityEntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Entity Data Filtering application using security annotations.
+ *
+ * @author Michal Gajdos
+ */
+@ApplicationPath("/")
+public class SecurityEntityFilteringApplication extends ResourceConfig {
+
+    public SecurityEntityFilteringApplication() {
+        // Register all resources present under the package.
+        packages("org.glassfish.jersey.examples.entityfiltering.security");
+
+        // Register entity-filtering security feature.
+        register(SecurityEntityFilteringFeature.class);
+
+        // Configure MOXy Json provider. Comment this line to use Jackson. Uncomment to use MOXy.
+        register(new MoxyJsonConfig().setFormattedOutput(true).resolver());
+
+        // Configure Jackson Json provider. Comment this line to use MOXy. Uncomment to use Jackson.
+        // register(JacksonFeature.class);
+    }
+}
diff --git a/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/domain/RestrictedEntity.java b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/domain/RestrictedEntity.java
new file mode 100644
index 0000000..324ab22
--- /dev/null
+++ b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/domain/RestrictedEntity.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.security.domain;
+
+import javax.annotation.security.DenyAll;
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Restricted entity to demonstrate various security annotations.
+ *
+ * @author Michal Gajdos
+ */
+@XmlRootElement
+public class RestrictedEntity {
+
+    private String simpleField;
+
+    private String denyAll;
+
+    private String permitAll;
+
+    private RestrictedSubEntity mixedField;
+
+    public String getSimpleField() {
+        return simpleField;
+    }
+
+    @DenyAll
+    public String getDenyAll() {
+        return denyAll;
+    }
+
+    @PermitAll
+    public String getPermitAll() {
+        return permitAll;
+    }
+
+    @RolesAllowed({"manager", "user"})
+    public RestrictedSubEntity getMixedField() {
+        return mixedField;
+    }
+
+    public void setSimpleField(final String simpleField) {
+        this.simpleField = simpleField;
+    }
+
+    public void setDenyAll(final String denyAll) {
+        this.denyAll = denyAll;
+    }
+
+    public void setPermitAll(final String permitAll) {
+        this.permitAll = permitAll;
+    }
+
+    public void setMixedField(final RestrictedSubEntity mixedField) {
+        this.mixedField = mixedField;
+    }
+
+    /**
+     * Get an instance of RestrictedEntity. This method creates always a new instance of RestrictedEntity.
+     *
+     * @return an instance of RestrictedEntity.
+     */
+    public static RestrictedEntity instance() {
+        final RestrictedEntity entity = new RestrictedEntity();
+
+        entity.setSimpleField("Simple Field.");
+        entity.setDenyAll("Deny All.");
+        entity.setPermitAll("Permit All.");
+
+        final RestrictedSubEntity mixedField = new RestrictedSubEntity();
+        mixedField.setManagerField("Manager's Field.");
+        mixedField.setUserField("User's Field.");
+
+        entity.setMixedField(mixedField);
+
+        return entity;
+    }
+}
diff --git a/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/domain/RestrictedSubEntity.java b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/domain/RestrictedSubEntity.java
new file mode 100644
index 0000000..aabc845
--- /dev/null
+++ b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/domain/RestrictedSubEntity.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.security.domain;
+
+import javax.annotation.security.RolesAllowed;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Restricted sub-entity to demonstrate that security entity-filtering is transitive.
+ *
+ * @author Michal Gajdos
+ */
+@XmlRootElement
+public class RestrictedSubEntity {
+
+    private String managerField;
+
+    private String userField;
+
+    @RolesAllowed("manager")
+    public String getManagerField() {
+        return managerField;
+    }
+
+    @RolesAllowed("user")
+    public String getUserField() {
+        return userField;
+    }
+
+    public void setManagerField(final String managerField) {
+        this.managerField = managerField;
+    }
+
+    public void setUserField(final String userField) {
+        this.userField = userField;
+    }
+}
diff --git a/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/provider/SecurityRequestFilter.java b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/provider/SecurityRequestFilter.java
new file mode 100644
index 0000000..6bf5a29
--- /dev/null
+++ b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/provider/SecurityRequestFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.security.provider;
+
+import java.io.IOException;
+import java.security.Principal;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+@PreMatching
+public class SecurityRequestFilter implements ContainerRequestFilter {
+
+    @Override
+    public void filter(final ContainerRequestContext requestContext) throws IOException {
+        requestContext.setSecurityContext(new SecurityContext() {
+            @Override
+            public Principal getUserPrincipal() {
+                return new Principal() {
+                    @Override
+                    public String getName() {
+                        return "Jersey";
+                    }
+                };
+            }
+
+            @Override
+            public boolean isUserInRole(final String role) {
+                return "manager".equals(role);
+            }
+
+            @Override
+            public boolean isSecure() {
+                return false;
+            }
+
+            @Override
+            public String getAuthenticationScheme() {
+                return null;
+            }
+        });
+    }
+}
diff --git a/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/resource/RestrictedResource.java b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/resource/RestrictedResource.java
new file mode 100644
index 0000000..40517d3
--- /dev/null
+++ b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/resource/RestrictedResource.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.security.resource;
+
+import java.lang.annotation.Annotation;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+
+import javax.annotation.security.DenyAll;
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
+
+import org.glassfish.jersey.examples.entityfiltering.security.domain.RestrictedEntity;
+import org.glassfish.jersey.internal.util.Tokenizer;
+import org.glassfish.jersey.message.filtering.SecurityAnnotations;
+
+/**
+ * Resource restricted with security annotations. Security restrictions are defined by resource methods and
+ * {@link RestrictedEntity}.
+ *
+ * @author Michal Gajdos
+ */
+@Path("restricted-resource")
+@Produces("application/json")
+public class RestrictedResource {
+
+    @GET
+    @Path("denyAll")
+    @DenyAll
+    public RestrictedEntity denyAll() {
+        return RestrictedEntity.instance();
+    }
+
+    @GET
+    @Path("permitAll")
+    @PermitAll
+    public RestrictedEntity permitAll() {
+        return RestrictedEntity.instance();
+    }
+
+    @GET
+    @Path("rolesAllowed")
+    @RolesAllowed({"manager"})
+    public RestrictedEntity rolesAllowed() {
+        return RestrictedEntity.instance();
+    }
+
+    @GET
+    @Path("runtimeRolesAllowed")
+    public Response runtimeRolesAllowed(@QueryParam("roles") @DefaultValue("") final String roles) {
+        return Response
+                .ok()
+                .entity(RestrictedEntity.instance(),
+                        new Annotation[] {SecurityAnnotations.rolesAllowed(Tokenizer.tokenize(roles))})
+                .build();
+    }
+}
diff --git a/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/resource/UnrestrictedResource.java b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/resource/UnrestrictedResource.java
new file mode 100644
index 0000000..76e2094
--- /dev/null
+++ b/examples/entity-filtering-security/src/main/java/org/glassfish/jersey/examples/entityfiltering/security/resource/UnrestrictedResource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.security.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.glassfish.jersey.examples.entityfiltering.security.domain.RestrictedEntity;
+
+/**
+ * Resource not restricted with security annotations leaving security restrictions solely to {@link RestrictedEntity} and
+ * {@link javax.ws.rs.core.SecurityContext}.
+ *
+ * @author Michal Gajdos
+ */
+@Path("unrestricted-resource")
+@Produces("application/json")
+public class UnrestrictedResource {
+
+    @GET
+    public RestrictedEntity getRestrictedEntity() {
+        return RestrictedEntity.instance();
+    }
+}
diff --git a/examples/entity-filtering-security/src/test/java/org/glassfish/jersey/examples/entityfiltering/security/RestrictedResourceTest.java b/examples/entity-filtering-security/src/test/java/org/glassfish/jersey/examples/entityfiltering/security/RestrictedResourceTest.java
new file mode 100644
index 0000000..1a530b7
--- /dev/null
+++ b/examples/entity-filtering-security/src/test/java/org/glassfish/jersey/examples/entityfiltering/security/RestrictedResourceTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.security;
+
+import java.util.Arrays;
+
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.examples.entityfiltering.security.domain.RestrictedEntity;
+import org.glassfish.jersey.examples.entityfiltering.security.domain.RestrictedSubEntity;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.SecurityEntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * {@link org.glassfish.jersey.examples.entityfiltering.security.resource.RestrictedResource} unit tests.
+ *
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class RestrictedResourceTest extends JerseyTest {
+
+    @Parameterized.Parameters(name = "Provider: {0}")
+    public static Iterable<Class[]> providers() {
+        return Arrays.asList(new Class[][]{{MoxyJsonFeature.class}, {JacksonFeature.class}});
+    }
+
+    public RestrictedResourceTest(final Class<Feature> filteringProvider) {
+        super(new ResourceConfig(SecurityEntityFilteringFeature.class)
+                .packages("org.glassfish.jersey.examples.entityfiltering.security")
+                .register(filteringProvider));
+
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Test
+    public void testDenyAll() throws Exception {
+        assertThat(target("restricted-resource").path("denyAll").request().get().getStatus(),
+                equalTo(Response.Status.FORBIDDEN.getStatusCode()));
+    }
+
+    @Test
+    public void testPermitAll() throws Exception {
+        final RestrictedEntity entity = target("restricted-resource").path("permitAll").request().get(RestrictedEntity.class);
+        final RestrictedSubEntity mixedField = entity.getMixedField();
+
+        // Not null values.
+        assertThat(entity.getSimpleField(), notNullValue());
+        assertThat(entity.getPermitAll(), notNullValue());
+
+        // Null values.
+        assertThat(entity.getDenyAll(), nullValue());
+        assertThat(mixedField, nullValue());
+    }
+
+    @Test
+    public void testRolesAllowed() throws Exception {
+        final RestrictedEntity entity = target("restricted-resource").path("rolesAllowed").request().get(RestrictedEntity.class);
+        final RestrictedSubEntity mixedField = entity.getMixedField();
+
+        // Not null values.
+        assertThat(entity.getSimpleField(), notNullValue());
+        assertThat(entity.getPermitAll(), notNullValue());
+        assertThat(mixedField, notNullValue());
+        assertThat(mixedField.getManagerField(), notNullValue());
+
+        // Null values.
+        assertThat(entity.getDenyAll(), nullValue());
+        assertThat(mixedField.getUserField(), nullValue());
+    }
+
+    @Test
+    public void testRuntimeRolesAllowedUser() throws Exception {
+        final RestrictedEntity entity = target("restricted-resource")
+                .path("runtimeRolesAllowed")
+                .queryParam("roles", "user")
+                .request().get(RestrictedEntity.class);
+        final RestrictedSubEntity mixedField = entity.getMixedField();
+
+        // Not null values.
+        assertThat(entity.getSimpleField(), notNullValue());
+        assertThat(entity.getPermitAll(), notNullValue());
+        assertThat(mixedField, notNullValue());
+        assertThat(mixedField.getUserField(), notNullValue());
+
+        // Null values.
+        assertThat(entity.getDenyAll(), nullValue());
+        assertThat(mixedField.getManagerField(), nullValue());
+    }
+
+    @Test
+    public void testRuntimeRolesAllowedManagerUser() throws Exception {
+        final RestrictedEntity entity = target("restricted-resource")
+                .path("runtimeRolesAllowed")
+                .queryParam("roles", "user,manager")
+                .request().get(RestrictedEntity.class);
+        final RestrictedSubEntity mixedField = entity.getMixedField();
+
+        // Not null values.
+        assertThat(entity.getSimpleField(), notNullValue());
+        assertThat(entity.getPermitAll(), notNullValue());
+        assertThat(mixedField, notNullValue());
+        assertThat(mixedField.getUserField(), notNullValue());
+        assertThat(mixedField.getManagerField(), notNullValue());
+
+        // Null values.
+        assertThat(entity.getDenyAll(), nullValue());
+    }
+
+    @Test
+    public void testRuntimeRolesAllowedInvalid() throws Exception {
+        final RestrictedEntity entity = target("restricted-resource")
+                .path("runtimeRolesAllowed")
+                .queryParam("roles", "invalid")
+                .request().get(RestrictedEntity.class);
+        final RestrictedSubEntity mixedField = entity.getMixedField();
+
+        // Not null values.
+        assertThat(entity.getSimpleField(), notNullValue());
+        assertThat(entity.getPermitAll(), notNullValue());
+
+        // Null values.
+        assertThat(entity.getDenyAll(), nullValue());
+        assertThat(mixedField, nullValue());
+    }
+}
diff --git a/examples/entity-filtering-security/src/test/java/org/glassfish/jersey/examples/entityfiltering/security/UnrestrictedResourceTest.java b/examples/entity-filtering-security/src/test/java/org/glassfish/jersey/examples/entityfiltering/security/UnrestrictedResourceTest.java
new file mode 100644
index 0000000..5b6a838
--- /dev/null
+++ b/examples/entity-filtering-security/src/test/java/org/glassfish/jersey/examples/entityfiltering/security/UnrestrictedResourceTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.security;
+
+import java.util.Arrays;
+
+import javax.ws.rs.core.Feature;
+
+import org.glassfish.jersey.examples.entityfiltering.security.domain.RestrictedEntity;
+import org.glassfish.jersey.examples.entityfiltering.security.domain.RestrictedSubEntity;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.SecurityEntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * {@link org.glassfish.jersey.examples.entityfiltering.security.resource.UnrestrictedResource} unit tests.
+ *
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class UnrestrictedResourceTest extends JerseyTest {
+
+    @Parameterized.Parameters(name = "Provider: {0}")
+    public static Iterable<Class[]> providers() {
+        return Arrays.asList(new Class[][]{{MoxyJsonFeature.class}, {JacksonFeature.class}});
+    }
+
+    public UnrestrictedResourceTest(final Class<Feature> filteringProvider) {
+        super(new ResourceConfig(SecurityEntityFilteringFeature.class)
+                .packages("org.glassfish.jersey.examples.entityfiltering.security")
+                .register(filteringProvider));
+
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Test
+    public void testRestrictedEntity() throws Exception {
+        final RestrictedEntity entity = target("unrestricted-resource").request().get(RestrictedEntity.class);
+        final RestrictedSubEntity mixedField = entity.getMixedField();
+
+        // Not null values.
+        assertThat(entity.getSimpleField(), notNullValue());
+        assertThat(entity.getPermitAll(), notNullValue());
+        assertThat(mixedField, notNullValue());
+        assertThat(mixedField.getManagerField(), notNullValue());
+
+        // Null values.
+        assertThat(entity.getDenyAll(), nullValue());
+        assertThat(mixedField.getUserField(), nullValue());
+    }
+}
diff --git a/examples/entity-filtering-selectable/README.MD b/examples/entity-filtering-selectable/README.MD
new file mode 100644
index 0000000..7241635
--- /dev/null
+++ b/examples/entity-filtering-selectable/README.MD
@@ -0,0 +1,99 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Entity Data Filtering Dynamic and Configurable Query Parameters
+===============================================================
+
+This example demonstrates how to use entity filtering feature
+dynamically with defining what should be sent to the other side based on
+query parameters and how to apply the rules on domain classes as well as
+on JAX-RS resource classes or JAX-RS resource methods.
+
+The full description how Entity Data Filtering can be found in Jersey
+User Guide, chapter [Entity Data
+Filtering](https://jersey.java.net/documentation/latest/entity-filtering.html).
+Sections relevant to this example (describing this exact example) are:
+
+-   [Enabling and configuring Entity Filtering in your
+    application](https://jersey.java.net/documentation/latest/entity-filtering.html#d0e13911)
+-   [Entity Filtering based on dynamic and configurable query
+    parameters](https://jersey.java.net/documentation/latest/entity-filtering.html#ef.selectable.annotations)
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                                     | Resource class   | HTTP methods   | Allowed values                                                               | Notes
+------------------------------------------   | ---------------- | -------------- | ---------------------------------------------------------------------------- | ----------------------------------------------
+**_/people/{id}?select={select}_**           |  PersonResource  |  GET           |  id = any value, select = field names from Person/Address/PhoneNumber class  |  Returns fields of Person/Address/PhoneNumber
+**_/people/{id}/addresses?select={select}_** |  PersonResource  |  GET           |  id = any value, select = field names from Address/PhoneNumber class         |  Returns fields of Address/PhoneNumber
+
+Application is based on Grizzly container (see `App`). Everything needed
+(resources/providers) is registered in
+`SelectableEntityFilteringApplication`.
+
+Sample Response
+---------------
+
+Even though the same instance of, e.g. Person class, is used to create
+response the given values of `select` query parameter are used to select
+the fields that would be transferred over the wire. For
+`people/1234?select=familyName,givenName` it looks like:
+
+```javascript
+{
+   "familyName": "Dowd",
+   "givenName": "Andrew"
+}
+```
+
+And for `people/1234?select=familyName,givenName,addresses.phoneNumber.number` it looks like:
+
+```javascript
+{
+   "addresses":[
+      {
+         "phoneNumber":{
+            "number": "867-5309"
+         }
+      }
+   ],
+   "familyName": "Dowd",
+   "givenName": "Andrew"
+}
+```
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean package exec:java
+
+This deploys current example using Grizzly. You can access the
+application at:
+
+-   <http://localhost:8080/people/1234>
+-   <http://localhost:8080/people/1234?select=familyName,givenName>
+-   <http://localhost:8080/people/1234?select=region,addresses.region>
+-   <http://localhost:8080/people/1234?select=familyName,givenName,addresses.phoneNumber.number>
+
+Using Jackson instead of MOXy
+-----------------------------
+
+This examples uses by default Entity Data Filtering feature together
+with MOXy. To switch MOXy JSON provider to Jackson (2.x) JSON provider
+simply
+
+-   comment registration of MOXy ContextResolver, and\
+     `register(new MoxyJsonConfig().setFormattedOutput(true).resolver())`
+-   uncomment registration of JacksonFeature\
+     `register(JacksonFeature.class)`
+
+in `SelectableEntityFilteringApplication` class.
diff --git a/examples/entity-filtering-selectable/pom.xml b/examples/entity-filtering-selectable/pom.xml
new file mode 100644
index 0000000..fa1acc5
--- /dev/null
+++ b/examples/entity-filtering-selectable/pom.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>entity-filtering-selectable</artifactId>
+    <name>jersey-examples-entity-filtering-selectable</name>
+
+    <description>Jersey Entity Data Filtering Selectable Example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-entity-filtering</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Run the application using "mvn exec:java" -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.entityfiltering.selectable.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/App.java b/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/App.java
new file mode 100644
index 0000000..67c0082
--- /dev/null
+++ b/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/App.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.selectable;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+
+/**
+ * Java application class starting Grizzly2 server with Entity Data Filtering with query parameters.
+ *
+ * @author Andy Pemberton (pembertona at oracle.com)
+ */
+public final class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/");
+
+    public static void main(final String[] args) {
+        try {
+            System.out.println("Jersey Entity Data Filtering Example.");
+
+            final HttpServer server = GrizzlyHttpServerFactory
+                    .createHttpServer(BASE_URI, new SelectableEntityFilteringApplication(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println("Application started.\nTry out one of these URIs:");
+            for (final String path : new String[]{"people/1234", "people/1234?select=familyName,givenName",
+                    "people/1234?select=region,addresses.region",
+                    "people/1234?select=familyName,givenName,addresses.phoneNumber.number"}) {
+                System.out.println(BASE_URI + path);
+            }
+            System.out.println("Stop the application using CTRL+C");
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    /**
+     * Prevent instantiation.
+     */
+    private App() {
+    }
+}
diff --git a/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/SelectableEntityFilteringApplication.java b/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/SelectableEntityFilteringApplication.java
new file mode 100644
index 0000000..4e94e3a
--- /dev/null
+++ b/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/SelectableEntityFilteringApplication.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.selectable;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.SelectableEntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Entity Data Filtering application using request parameters.
+ *
+ * @author Andy Pemberton (pembertona at gmail.com)
+ * @author Michal Gajdos
+ */
+@ApplicationPath("/")
+public class SelectableEntityFilteringApplication extends ResourceConfig {
+
+    public SelectableEntityFilteringApplication() {
+        // Register all resources present under the package.
+        packages("org.glassfish.jersey.examples.entityfiltering.selectable");
+
+        // Register entity-filtering selectable feature.
+        register(SelectableEntityFilteringFeature.class);
+        property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "select");
+
+        // Configure MOXy Json provider. Comment this line to use Jackson. Uncomment to use MOXy.
+        register(new MoxyJsonConfig().setFormattedOutput(true).resolver());
+
+        // Configure Jackson Json provider. Comment this line to use MOXy. Uncomment to use Jackson.
+        // register(JacksonFeature.class);
+    }
+}
diff --git a/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/domain/Address.java b/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/domain/Address.java
new file mode 100644
index 0000000..cc4edbb
--- /dev/null
+++ b/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/domain/Address.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.selectable.domain;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class Address {
+
+    private String streetAddress;
+
+    private String region;
+
+    private PhoneNumber phoneNumber;
+
+    public String getStreetAddress() {
+        return streetAddress;
+    }
+
+    public void setStreetAddress(String streetAddress) {
+        this.streetAddress = streetAddress;
+    }
+
+    public String getRegion() {
+        return region;
+    }
+
+    public void setRegion(String region) {
+        this.region = region;
+    }
+
+    public PhoneNumber getPhoneNumber() {
+        return phoneNumber;
+    }
+
+    public void setPhoneNumber(PhoneNumber phoneNumber) {
+        this.phoneNumber = phoneNumber;
+    }
+
+}
diff --git a/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/domain/Person.java b/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/domain/Person.java
new file mode 100644
index 0000000..8d4b9b5
--- /dev/null
+++ b/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/domain/Person.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.selectable.domain;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class Person {
+    private String givenName;
+
+    private String familyName;
+
+    private String honorificSuffix;
+
+    private String honorificPrefix;
+
+    // same name as Address.region
+    private String region;
+
+    private List<Address> addresses;
+
+    private Map<String, PhoneNumber> phoneNumbers;
+
+    public String getGivenName() {
+        return givenName;
+    }
+
+    public void setGivenName(String givenName) {
+        this.givenName = givenName;
+    }
+
+    public String getFamilyName() {
+        return familyName;
+    }
+
+    public void setFamilyName(String familyName) {
+        this.familyName = familyName;
+    }
+
+    public String getHonorificSuffix() {
+        return honorificSuffix;
+    }
+
+    public void setHonorificSuffix(String honorificSuffix) {
+        this.honorificSuffix = honorificSuffix;
+    }
+
+    public String getHonorificPrefix() {
+        return honorificPrefix;
+    }
+
+    public void setHonorificPrefix(String honorificPrefix) {
+        this.honorificPrefix = honorificPrefix;
+    }
+
+    public List<Address> getAddresses() {
+        return addresses;
+    }
+
+    public void setAddresses(List<Address> addresses) {
+        this.addresses = addresses;
+    }
+
+    public String getRegion() {
+        return region;
+    }
+
+    public void setRegion(String region) {
+        this.region = region;
+    }
+
+    public Map<String, PhoneNumber> getPhoneNumbers() {
+        return phoneNumbers;
+    }
+
+    public void setPhoneNumbers(Map<String, PhoneNumber> phoneNumbers) {
+        this.phoneNumbers = phoneNumbers;
+    }
+}
diff --git a/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/domain/PhoneNumber.java b/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/domain/PhoneNumber.java
new file mode 100644
index 0000000..de8e89e
--- /dev/null
+++ b/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/domain/PhoneNumber.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.selectable.domain;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class PhoneNumber {
+
+    private String areaCode;
+
+    private String number;
+
+    public String getNumber() {
+        return number;
+    }
+
+    public void setNumber(String number) {
+        this.number = number;
+    }
+
+    public String getAreaCode() {
+        return areaCode;
+    }
+
+    public void setAreaCode(String areaCode) {
+        this.areaCode = areaCode;
+    }
+
+}
diff --git a/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/resource/PersonResource.java b/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/resource/PersonResource.java
new file mode 100644
index 0000000..45bb8b6
--- /dev/null
+++ b/examples/entity-filtering-selectable/src/main/java/org/glassfish/jersey/examples/entityfiltering/selectable/resource/PersonResource.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.selectable.resource;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.glassfish.jersey.examples.entityfiltering.selectable.domain.Address;
+import org.glassfish.jersey.examples.entityfiltering.selectable.domain.Person;
+import org.glassfish.jersey.examples.entityfiltering.selectable.domain.PhoneNumber;
+
+/**
+ * Resource to support query parameter driven entity filtering.
+ *
+ * @author Andy Pemberton (pembertona at gmail.com)
+ */
+@Path("people")
+@Produces("application/json")
+public class PersonResource {
+
+    @GET
+    @Path("{id}")
+    public Person getPerson() {
+        final Person person = new Person();
+        person.setGivenName("Andrew");
+        person.setFamilyName("Dowd");
+        person.setHonorificPrefix("Mr.");
+        person.setHonorificSuffix("PhD");
+        person.setRegion("1st Level Region");
+
+        final ArrayList<Address> addresses = new ArrayList<>();
+        person.setAddresses(addresses);
+
+        final Address address = new Address();
+        addresses.add(address);
+        address.setRegion("2nd Level Region");
+        address.setStreetAddress("1234 fake st.");
+        address.setPhoneNumber(new PhoneNumber());
+        address.getPhoneNumber().setNumber("867-5309");
+        address.getPhoneNumber().setAreaCode("540");
+
+        person.setPhoneNumbers(new HashMap<String, PhoneNumber>());
+        final PhoneNumber number = new PhoneNumber();
+        number.setAreaCode("804");
+        number.setNumber("867-5309");
+        person.getPhoneNumbers().put("HOME", number);
+
+        return person;
+    }
+
+    @GET
+    @Path("{id}/addresses")
+    public Address getAddress() {
+        return this.getPerson().getAddresses().get(0);
+    }
+}
diff --git a/examples/entity-filtering-selectable/src/test/java/org/glassfish/jersey/examples/entityfiltering/selectable/PersonResourceTest.java b/examples/entity-filtering-selectable/src/test/java/org/glassfish/jersey/examples/entityfiltering/selectable/PersonResourceTest.java
new file mode 100644
index 0000000..dc0a275
--- /dev/null
+++ b/examples/entity-filtering-selectable/src/test/java/org/glassfish/jersey/examples/entityfiltering/selectable/PersonResourceTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.selectable;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.Feature;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.examples.entityfiltering.selectable.domain.Address;
+import org.glassfish.jersey.examples.entityfiltering.selectable.domain.Person;
+import org.glassfish.jersey.examples.entityfiltering.selectable.domain.PhoneNumber;
+import org.glassfish.jersey.examples.entityfiltering.selectable.resource.PersonResource;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.SelectableEntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * {@link PersonResource} unit tests.
+ *
+ * @author Andy Pemberton (pembertona at gmail.com)
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class PersonResourceTest extends JerseyTest {
+
+    @Parameterized.Parameters(name = "Provider: {0}")
+    public static Iterable<Class[]> providers() {
+        return Arrays.asList(new Class[][]{{MoxyJsonFeature.class}, {JacksonFeature.class}});
+    }
+
+    private final Class<Feature> filteringProvider;
+
+    public PersonResourceTest(final Class<Feature> filteringProvider) {
+        super(new ResourceConfig(SelectableEntityFilteringFeature.class)
+                .packages("org.glassfish.jersey.examples.entityfiltering.selectable")
+                .property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "select")
+                .register(filteringProvider));
+
+        this.filteringProvider = filteringProvider;
+
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(filteringProvider);
+    }
+
+    @Test
+    public void testNoFilter() throws Exception {
+        final Person entity = target("people").path("1234").request().get(Person.class);
+
+        // Not null values.
+        assertThat(entity.getFamilyName(), notNullValue());
+        assertThat(entity.getGivenName(), notNullValue());
+        assertThat(entity.getHonorificPrefix(), notNullValue());
+        assertThat(entity.getHonorificSuffix(), notNullValue());
+        assertThat(entity.getRegion(), notNullValue());
+
+        final List<Address> addresses = entity.getAddresses();
+        assertThat(addresses, notNullValue());
+        final Address address = addresses.get(0);
+        assertThat(address, notNullValue());
+        assertThat(address.getRegion(), notNullValue());
+        assertThat(address.getStreetAddress(), notNullValue());
+        PhoneNumber phoneNumber = address.getPhoneNumber();
+        assertThat(phoneNumber, notNullValue());
+        assertThat(phoneNumber.getAreaCode(), notNullValue());
+        assertThat(phoneNumber.getNumber(), notNullValue());
+
+        final Map<String, PhoneNumber> phoneNumbers = entity.getPhoneNumbers();
+        assertThat(phoneNumbers, notNullValue());
+
+        // TODO: enable for MOXy as well when JERSEY-2751 gets fixed.
+        if (JacksonFeature.class.isAssignableFrom(filteringProvider)) {
+            phoneNumber = phoneNumbers.get("HOME");
+            assertThat(phoneNumber, notNullValue());
+            assertThat(phoneNumber.getAreaCode(), notNullValue());
+            assertThat(phoneNumber.getNumber(), notNullValue());
+        }
+    }
+
+    @Test
+    public void testInvalidFilter() throws Exception {
+        final Person entity = target("people").path("1234")
+                .queryParam("select", "invalid").request().get(Person.class);
+
+        // All null values.
+        assertThat(entity.getFamilyName(), nullValue());
+        assertThat(entity.getGivenName(), nullValue());
+        assertThat(entity.getHonorificPrefix(), nullValue());
+        assertThat(entity.getHonorificSuffix(), nullValue());
+        assertThat(entity.getRegion(), nullValue());
+        assertThat(entity.getAddresses(), nullValue());
+        assertThat(entity.getPhoneNumbers(), nullValue());
+    }
+
+    /**
+     * Test first level filters.
+     */
+    @Test
+    public void testFilters() throws Exception {
+        final Person entity = target("people").path("1234")
+                .queryParam("select", "familyName,givenName").request()
+                .get(Person.class);
+
+        // Not null values.
+        assertThat(entity.getFamilyName(), notNullValue());
+        assertThat(entity.getGivenName(), notNullValue());
+
+        // Null values.
+        assertThat(entity.getAddresses(), nullValue());
+        assertThat(entity.getPhoneNumbers(), nullValue());
+        assertThat(entity.getRegion(), nullValue());
+    }
+
+    /**
+     * Test 2nd and 3rd level filters.
+     */
+    @Test
+    public void testSubFilters() throws Exception {
+        final Person entity = target("people")
+                .path("1234")
+                .queryParam("select",
+                        "familyName,givenName,addresses.streetAddress,addresses.phoneNumber.areaCode")
+                .request().get(Person.class);
+
+        // Not null values.
+        assertThat(entity.getFamilyName(), notNullValue());
+        assertThat(entity.getGivenName(), notNullValue());
+        assertThat(entity.getAddresses().get(0).getStreetAddress(), notNullValue());
+        assertThat(entity.getAddresses().get(0).getPhoneNumber().getAreaCode(), notNullValue());
+
+        // Null values.
+        assertThat(entity.getRegion(), nullValue());
+        assertThat(entity.getAddresses().get(0).getPhoneNumber().getNumber(), nullValue());
+    }
+
+    /**
+     * Test that 1st and 2nd level filters with the same name act as expected.
+     */
+    @Test
+    public void testFiltersSameName() throws Exception {
+        final Person firstLevel = target("people").path("1234")
+                .queryParam("select", "familyName,region").request()
+                .get(Person.class);
+        final Person secondLevel = target("people").path("1234")
+                .queryParam("select", "familyName,addresses.region").request()
+                .get(Person.class);
+
+        // Not null values.
+        assertThat(firstLevel.getRegion(), notNullValue());
+        assertThat(secondLevel.getAddresses().get(0).getRegion(), notNullValue());
+
+        // Null values.
+        assertThat(firstLevel.getAddresses(), nullValue()); //confirms 2nd level region on addresses is null
+        assertThat(secondLevel.getRegion(), nullValue());
+    }
+
+}
diff --git a/examples/entity-filtering/README.MD b/examples/entity-filtering/README.MD
new file mode 100644
index 0000000..96c2539
--- /dev/null
+++ b/examples/entity-filtering/README.MD
@@ -0,0 +1,114 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Entity Data Filtering
+=====================
+
+### *Entity Data Filtering Using custom annotations to filter entities*
+
+This example demonstrates how to define custom entity filtering
+annotations (views) and how to apply them on domain classes as well as
+on JAX-RS resource classes or JAX-RS resource methods.
+
+The full description how Entity Data Filtering can be found in Jersey User Guide, chapter
+[Entity Data Filtering](https://jersey.java.net/documentation/latest/entity-filtering.html).
+Sections relevant to this example (describing this exact example) are:
+
+-   [Enabling and configuring Entity Filtering in your application](https://jersey.java.net/documentation/latest/entity-filtering.html#d0e13911)
+-   [Components used to describe Entity Filtering concepts](https://jersey.java.net/documentation/latest/entity-filtering.html#d0e14024)
+-   [Using custom annotations to filter entities](https://jersey.java.net/documentation/latest/entity-filtering.html#ef.annotations)
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                                   | Resource class     | HTTP methods   | Allowed values
+-----------------------------------------  | ------------------ | -------------- | ----------------
+**_/projects/{id}_**                       | ProjectsResource   | GET            | int
+**_/projects_**                            | ProjectsResource   | GET            | N/A
+**_/projects/detailed/{id}_**              | ProjectsResource   | GET            | int
+**_/projects/detailed_**                   | ProjectsResource   | GET            | N/A
+**_/tasks/{id}?detailed={true\|false}_**   | TasksResource      | GET            | int, boolean
+**_/tasks_**                               | TasksResource      | GET            | N/A
+**_/tasks/detailed_**                      | TasksResource      | GET            | N/A
+**_/users/{id}?detailed={true\|false}_**   | UsersResource      | GET            | int, boolean
+**_/tasks?detailed={true\|false}_**        | UsersResource      | GET            | N/A
+
+Application is based on Grizzly container (see `App`). Everything needed
+(resources/providers) is registered in `EntityFilteringApplication`.
+
+Sample Response
+---------------
+
+Even though the same instance of, e.g. Project class, is used to create
+response for both basic and detailed view the actual data sent over the
+wire differ for each of these two views. For basic view it looks like:
+
+```javascript
+{
+   "description" : "Jersey is the open source (under dual EPL+GPL license) JAX-RS 2.1 (JSR 370) production quality Reference Implementation for building RESTful Web services.",
+   "id" : 1,
+   "name" : "Jersey"
+}
+```
+
+And for detailed view it looks like:
+
+```javascript
+{
+   "description" : "Jersey is the open source (under dual EPL+GPL license) JAX-RS 2.1 (JSR 370) production quality Reference Implementation for building RESTful Web services.",
+   "id" : 1,
+   "name" : "Jersey",
+   "tasks" : [ {
+      "description" : "Entity Data Filtering",
+      "id" : 1,
+      "name" : "ENT_FLT"
+   }, {
+      "description" : "OAuth 1 + 2",
+      "id" : 2,
+      "name" : "OAUTH"
+   } ],
+   "users" : [ {
+      "email" : "very@secret.com",
+      "id" : 1,
+      "name" : "Jersey Robot"
+   } ]
+}
+```
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean package exec:java
+
+This deploys current example using Grizzly. You can access the
+application at:
+
+-   <http://localhost:8080/projects>
+-   <http://localhost:8080/projects/detailed>
+-   <http://localhost:8080/users>
+-   <http://localhost:8080/users?detailed=true>
+-   <http://localhost:8080/tasks>
+-   <http://localhost:8080/tasks/detailed>
+
+Using Jackson instead of MOXy
+-----------------------------
+
+This examples uses by default Entity Data Filtering feature together
+with MOXy. To switch MOXy JSON provider to Jackson (2.x) JSON provider
+simply
+
+-   comment registration of MOXy ContextResolver, and\
+     `register(new MoxyJsonConfig().setFormattedOutput(true).resolver())`
+-   uncomment registration of JacksonFeature\
+     `register(JacksonFeature.class)`
+
+in `EntityFilteringApplication` class.
diff --git a/examples/entity-filtering/pom.xml b/examples/entity-filtering/pom.xml
new file mode 100644
index 0000000..0d4f615
--- /dev/null
+++ b/examples/entity-filtering/pom.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>entity-filtering</artifactId>
+    <name>jersey-examples-entity-filtering</name>
+
+    <description>Jersey Entity Data Filtering Example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-entity-filtering</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Run the application using "mvn exec:java" -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.entityfiltering.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/App.java b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/App.java
new file mode 100644
index 0000000..bcdb95f
--- /dev/null
+++ b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/App.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Java application class starting Grizzly2 server with Entity Data Filtering app deployed.
+ *
+ * @author Michal Gajdos
+ */
+public final class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("Jersey Entity Data Filtering Example.");
+
+            final HttpServer server = GrizzlyHttpServerFactory
+                    .createHttpServer(BASE_URI, new EntityFilteringApplication(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println("Application started.\nTry out one of these URIs:");
+            for (final String path : new String[]{"projects", "projects/detailed", "users", "users?detailed=true", "tasks",
+                    "tasks/detailed"}) {
+                System.out.println(BASE_URI + path);
+            }
+            System.out.println("Stop the application using CTRL+C");
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName())
+                    .log(Level.SEVERE, "I/O error occurred during reading from an system input stream.", ex);
+        }
+    }
+}
diff --git a/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/EntityFilteringApplication.java b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/EntityFilteringApplication.java
new file mode 100644
index 0000000..a98c5d4
--- /dev/null
+++ b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/EntityFilteringApplication.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Entity Data Filtering application.
+ *
+ * @author Michal Gajdos
+ */
+@ApplicationPath("/")
+public class EntityFilteringApplication extends ResourceConfig {
+
+    public EntityFilteringApplication() {
+        // Register all resources present under the package.
+        packages("org.glassfish.jersey.examples.entityfiltering");
+
+        // Entity Data Filtering feature.
+        register(EntityFilteringFeature.class);
+
+        // Configure MOXy Json provider. Comment this line to use Jackson. Uncomment to use MOXy.
+        register(new MoxyJsonConfig().setFormattedOutput(true).resolver());
+
+        // Configure Jackson Json provider. Comment this line to use MOXy. Uncomment to use Jackson.
+        // register(JacksonFeature.class);
+    }
+}
diff --git a/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/domain/EntityStore.java b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/domain/EntityStore.java
new file mode 100644
index 0000000..0d99eda
--- /dev/null
+++ b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/domain/EntityStore.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.domain;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Entity-store utility class. Class creates a sample instance of each entity.
+ *
+ * @author Michal Gajdos
+ */
+@SuppressWarnings({"JavaDoc", "UnusedDeclaration"})
+public final class EntityStore {
+
+    private static final Map<Long, Project> PROJECTS = new LinkedHashMap<>();
+    private static final Map<Long, User> USERS = new LinkedHashMap<>();
+    private static final Map<Long, Task> TASKS = new LinkedHashMap<>();
+
+    static {
+        // Projects.
+        final Project project = createProject("Jersey", "Jersey is the open source (under dual EPL+GPL license) JAX-RS 2.1 "
+                + "(JSR 370) production quality Reference Implementation for building RESTful Web services.");
+
+        // Users.
+        final User robot = createUser("Jersey Robot", "very@secret.com");
+
+        // Tasks.
+        final Task filtering = createTask("ENT_FLT", "Entity Data Filtering");
+        final Task oauth = createTask("OAUTH", "OAuth 1 + 2");
+
+        // Project -> Users, Tasks.
+        add(project, robot);
+        filtering.setProject(project);
+        oauth.setProject(project);
+
+        // Users -> Projects, Tasks.
+        add(robot, project);
+        filtering.setUser(robot);
+        oauth.setUser(robot);
+
+        // Tasks -> Projects, Users.
+        add(filtering, project);
+        add(oauth, project);
+        add(filtering, robot);
+        add(oauth, robot);
+    }
+
+    public static void add(final Project project, final User user) {
+        user.getProjects().add(project);
+    }
+
+    public static void add(final User user, final Project project) {
+        project.getUsers().add(user);
+    }
+
+    public static void add(final Task task, final User user) {
+        user.getTasks().add(task);
+    }
+
+    public static void add(final Task task, final Project project) {
+        project.getTasks().add(task);
+    }
+
+    public static Project createProject(final String name, final String description) {
+        return createProject(name, description, null, null);
+    }
+
+    public static Project createProject(final String name, final String description, final List<User> users,
+                                        final List<Task> tasks) {
+        final Project project = new Project(PROJECTS.size() + 1L, name, description);
+
+        project.setTasks(tasks == null ? new ArrayList<Task>() : tasks);
+        project.setUsers(users == null ? new ArrayList<User>() : users);
+        PROJECTS.put(project.getId(), project);
+
+        return project;
+    }
+
+    public static User createUser(final String name, final String email) {
+        return createUser(name, email, null, null);
+    }
+
+    public static User createUser(final String name, final String email, final List<Project> projects, final List<Task> tasks) {
+        final User user = new User(USERS.size() + 1L, name, email);
+
+        user.setProjects(projects == null ? new ArrayList<Project>() : projects);
+        user.setTasks(tasks == null ? new ArrayList<Task>() : tasks);
+        USERS.put(user.getId(), user);
+
+        return user;
+    }
+
+    public static Task createTask(final String name, final String description) {
+        return createTask(name, description, null, null);
+    }
+
+    public static Task createTask(final String name, final String description, final Project project, final User user) {
+        final Task task = new Task(TASKS.size() + 1L, name, description);
+
+        task.setProject(project);
+        task.setUser(user);
+        TASKS.put(task.getId(), task);
+
+        return task;
+    }
+
+    public static Project getProject(final Long id) {
+        return PROJECTS.get(id);
+    }
+
+    public static User getUser(final Long id) {
+        return USERS.get(id);
+    }
+
+    public static Task getTask(final Long id) {
+        return TASKS.get(id);
+    }
+
+    public static List<Project> getProjects() {
+        return new ArrayList<>(PROJECTS.values());
+    }
+
+    public static List<User> getUsers() {
+        return new ArrayList<>(USERS.values());
+    }
+
+    public static List<Task> getTasks() {
+        return new ArrayList<>(TASKS.values());
+    }
+
+    /**
+     * Prevent instantiation.
+     */
+    private EntityStore() {
+    }
+}
diff --git a/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/domain/Project.java b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/domain/Project.java
new file mode 100644
index 0000000..8533ea9
--- /dev/null
+++ b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/domain/Project.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.domain;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.examples.entityfiltering.filtering.ProjectDetailedView;
+
+/**
+ * Project entity class. Fields {@code tasks} and {@code users} are available only in detailed view (defined via
+ * {@link ProjectDetailedView}).
+ *
+ * @author Michal Gajdos
+ */
+@SuppressWarnings({"JavaDoc", "UnusedDeclaration"})
+@XmlRootElement
+public class Project {
+
+    private Long id;
+
+    private String name;
+
+    private String description;
+
+    @ProjectDetailedView
+    private List<Task> tasks;
+
+    @ProjectDetailedView
+    private List<User> users;
+
+    public Project() {
+    }
+
+    public Project(final Long id, final String name, final String description) {
+        this.id = id;
+        this.name = name;
+        this.description = description;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(final String description) {
+        this.description = description;
+    }
+
+    public List<Task> getTasks() {
+        return tasks;
+    }
+
+    public void setTasks(final List<Task> tasks) {
+        this.tasks = tasks;
+    }
+
+    public List<User> getUsers() {
+        return users;
+    }
+
+    public void setUsers(final List<User> users) {
+        this.users = users;
+    }
+}
diff --git a/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/domain/Task.java b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/domain/Task.java
new file mode 100644
index 0000000..dd7d889
--- /dev/null
+++ b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/domain/Task.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.domain;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.examples.entityfiltering.filtering.TaskDetailedView;
+
+/**
+ * Task entity class. Fields {@code project} and {@code user} are available only in detailed view (defined via
+ * {@link TaskDetailedView}).
+ *
+ * @author Michal Gajdos
+ */
+@SuppressWarnings({"UnusedDeclaration", "JavaDoc"})
+@XmlRootElement
+public class Task {
+
+    private Long id;
+
+    private String name;
+
+    private String description;
+
+    @TaskDetailedView
+    private Project project;
+
+    @TaskDetailedView
+    private User user;
+
+    public Task() {
+    }
+
+    public Task(final Long id, final String name, final String description) {
+        this.id = id;
+        this.name = name;
+        this.description = description;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(final String description) {
+        this.description = description;
+    }
+
+    public Project getProject() {
+        return project;
+    }
+
+    public void setProject(final Project project) {
+        this.project = project;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(final User user) {
+        this.user = user;
+    }
+}
diff --git a/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/domain/User.java b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/domain/User.java
new file mode 100644
index 0000000..5a2673f
--- /dev/null
+++ b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/domain/User.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.domain;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.examples.entityfiltering.filtering.UserDetailedView;
+
+/**
+ * User entity class. Fields {@code projects} and {@code tasks} are available only in detailed view (defined via
+ * {@link UserDetailedView} on getters).
+ *
+ * @author Michal Gajdos
+ */
+@SuppressWarnings({"UnusedDeclaration", "JavaDoc"})
+@XmlRootElement
+public class User {
+
+    private Long id;
+
+    private String name;
+
+    private String email;
+
+    private List<Project> projects;
+
+    private List<Task> tasks;
+
+    public User() {
+    }
+
+    public User(final Long id, final String name, final String email) {
+        this.id = id;
+        this.name = name;
+        this.email = email;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(final String email) {
+        this.email = email;
+    }
+
+    @UserDetailedView
+    public List<Project> getProjects() {
+        return projects;
+    }
+
+    public void setProjects(final List<Project> projects) {
+        this.projects = projects;
+    }
+
+    @UserDetailedView
+    public List<Task> getTasks() {
+        return tasks;
+    }
+
+    public void setTasks(final List<Task> tasks) {
+        this.tasks = tasks;
+    }
+}
diff --git a/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/filtering/ProjectDetailedView.java b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/filtering/ProjectDetailedView.java
new file mode 100644
index 0000000..0264e9d
--- /dev/null
+++ b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/filtering/ProjectDetailedView.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.filtering;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.message.filtering.EntityFiltering;
+
+/**
+ * Entity-filtering annotation used to define detailed view on returned
+ * {@link org.glassfish.jersey.examples.entityfiltering.domain.Project} entities.
+ *
+ * @author Michal Gajdos
+ */
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@EntityFiltering
+public @interface ProjectDetailedView {
+
+    /**
+     * Factory class for creating instances of {@code ProjectDetailedView} annotation.
+     */
+    public static class Factory extends AnnotationLiteral<ProjectDetailedView> implements ProjectDetailedView {
+
+        private Factory() {
+        }
+
+        public static ProjectDetailedView get() {
+            return new Factory();
+        }
+    }
+}
diff --git a/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/filtering/TaskDetailedView.java b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/filtering/TaskDetailedView.java
new file mode 100644
index 0000000..293e809
--- /dev/null
+++ b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/filtering/TaskDetailedView.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.filtering;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.message.filtering.EntityFiltering;
+
+/**
+ * Entity-filtering annotation used to define detailed view on returned
+ * {@link org.glassfish.jersey.examples.entityfiltering.domain.Task} entities.
+ *
+ * @author Michal Gajdos
+ */
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@EntityFiltering
+public @interface TaskDetailedView {
+
+    /**
+     * Factory class for creating instances of {@code TaskDetailedView} annotation.
+     */
+    public static class Factory extends AnnotationLiteral<TaskDetailedView> implements TaskDetailedView {
+
+        private Factory() {
+        }
+
+        public static TaskDetailedView get() {
+            return new Factory();
+        }
+    }
+}
diff --git a/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/filtering/UserDetailedView.java b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/filtering/UserDetailedView.java
new file mode 100644
index 0000000..4af93cd
--- /dev/null
+++ b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/filtering/UserDetailedView.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.filtering;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.message.filtering.EntityFiltering;
+
+/**
+ * Entity-filtering annotation used to define detailed view on returned
+ * {@link org.glassfish.jersey.examples.entityfiltering.domain.User} entities.
+ *
+ * @author Michal Gajdos
+ */
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@EntityFiltering
+public @interface UserDetailedView {
+
+    /**
+     * Factory class for creating instances of {@code UserDetailedView} annotation.
+     */
+    public static class Factory extends AnnotationLiteral<UserDetailedView> implements UserDetailedView {
+
+        private Factory() {
+        }
+
+        public static UserDetailedView get() {
+            return new Factory();
+        }
+    }
+}
diff --git a/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/resource/ProjectsResource.java b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/resource/ProjectsResource.java
new file mode 100644
index 0000000..fe0ece6
--- /dev/null
+++ b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/resource/ProjectsResource.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.resource;
+
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+import org.glassfish.jersey.examples.entityfiltering.domain.EntityStore;
+import org.glassfish.jersey.examples.entityfiltering.domain.Project;
+import org.glassfish.jersey.examples.entityfiltering.filtering.ProjectDetailedView;
+
+/**
+ * Resource class for {@link Project projects}. Provides methods to retrieve projects in "default" view ({@link #getProjects()}
+ * and {@link #getProject(Long)}) and in "detailed" view ({@link #getDetailedProjects()} and {@link #getDetailedProject(Long)}).
+ * <p/>
+ * To reduce the number of methods while keeping the functionality of resource unchanged (support for both "default" and
+ * "detailed" view) see {@link UsersResource}.
+ *
+ * @author Michal Gajdos
+ * @see UsersResource
+ */
+@Path("projects")
+@Produces("application/json")
+public class ProjectsResource {
+
+    @GET
+    @Path("{id}")
+    public Project getProject(@PathParam("id") final Long id) {
+        return getDetailedProject(id);
+    }
+
+    @GET
+    public List<Project> getProjects() {
+        return getDetailedProjects();
+    }
+
+    @GET
+    @Path("detailed/{id}")
+    @ProjectDetailedView
+    public Project getDetailedProject(@PathParam("id") final Long id) {
+        return EntityStore.getProject(id);
+    }
+
+    @GET
+    @Path("detailed")
+    @ProjectDetailedView
+    public List<Project> getDetailedProjects() {
+        return EntityStore.getProjects();
+    }
+}
diff --git a/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/resource/TasksResource.java b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/resource/TasksResource.java
new file mode 100644
index 0000000..1b1f177
--- /dev/null
+++ b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/resource/TasksResource.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.resource;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.examples.entityfiltering.domain.EntityStore;
+import org.glassfish.jersey.examples.entityfiltering.domain.Task;
+import org.glassfish.jersey.examples.entityfiltering.filtering.TaskDetailedView;
+
+/**
+ * Resource class for {@link Task tasks}.
+ *
+ * @author Michal Gajdos
+ */
+@Path("tasks")
+@Produces("application/json")
+public class TasksResource {
+
+    @GET
+    public List<Task> getTasks() {
+        return getDetailedTasks();
+    }
+
+    @GET
+    @Path("detailed")
+    @TaskDetailedView
+    public List<Task> getDetailedTasks() {
+        return EntityStore.getTasks();
+    }
+
+    @GET
+    @Path("{id}")
+    public Response getTask(@PathParam("id") final Long id, @QueryParam("detailed") final boolean isDetailed) {
+        return Response
+                .ok()
+                .entity(EntityStore.getTask(id),
+                        isDetailed ? new Annotation[] {TaskDetailedView.Factory.get()} : new Annotation[0])
+                .build();
+    }
+}
diff --git a/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/resource/UsersResource.java b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/resource/UsersResource.java
new file mode 100644
index 0000000..71d7646
--- /dev/null
+++ b/examples/entity-filtering/src/main/java/org/glassfish/jersey/examples/entityfiltering/resource/UsersResource.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering.resource;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.examples.entityfiltering.domain.EntityStore;
+import org.glassfish.jersey.examples.entityfiltering.domain.User;
+import org.glassfish.jersey.examples.entityfiltering.filtering.UserDetailedView;
+
+/**
+ * Resource class for {@link User users}. Provides combined methods to retrieve users in "default" view and "detailed" view.
+ * Detailed view can be triggered by appending {@code detailed} query param to the URI.
+ * <p/>
+ * To see the resource methods expanded refer to the {@link ProjectsResource}.
+ *
+ * @author Michal Gajdos
+ * @see ProjectsResource
+ */
+@Path("users")
+@Produces("application/json")
+public class UsersResource {
+
+    @GET
+    public Response getUsers(@QueryParam("detailed") final boolean isDetailed) {
+        return Response
+                .ok()
+                .entity(new GenericEntity<List<User>>(EntityStore.getUsers()) {},
+                        isDetailed ? new Annotation[]{UserDetailedView.Factory.get()} : new Annotation[0])
+                .build();
+    }
+
+    @GET
+    @Path("{id}")
+    public Response getUser(@PathParam("id") final Long id, @QueryParam("detailed") final boolean isDetailed) {
+        return Response
+                .ok()
+                .entity(EntityStore.getUser(id),
+                        isDetailed ? new Annotation[]{UserDetailedView.Factory.get()} : new Annotation[0])
+                .build();
+    }
+}
diff --git a/examples/entity-filtering/src/test/java/org/glassfish/jersey/examples/entityfiltering/ProjectsResourceTest.java b/examples/entity-filtering/src/test/java/org/glassfish/jersey/examples/entityfiltering/ProjectsResourceTest.java
new file mode 100644
index 0000000..5d51237
--- /dev/null
+++ b/examples/entity-filtering/src/test/java/org/glassfish/jersey/examples/entityfiltering/ProjectsResourceTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.GenericType;
+
+import org.glassfish.jersey.examples.entityfiltering.domain.Project;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * {@link org.glassfish.jersey.examples.entityfiltering.resource.UsersResource} unit tests.
+ *
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class ProjectsResourceTest extends JerseyTest {
+
+    @Parameterized.Parameters(name = "Provider: {0}")
+    public static Iterable<Class[]> providers() {
+        return Arrays.asList(new Class[][]{{MoxyJsonFeature.class}, {JacksonFeature.class}});
+    }
+
+    public ProjectsResourceTest(final Class<Feature> filteringProvider) {
+        super(new ResourceConfig(EntityFilteringFeature.class)
+                .packages("org.glassfish.jersey.examples.entityfiltering.resource")
+                .register(filteringProvider));
+
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Test
+    public void testProjects() throws Exception {
+        for (final Project project : target("projects").request().get(new GenericType<List<Project>>() {})) {
+            testProject(project, false);
+        }
+    }
+
+    @Test
+    public void testProject() throws Exception {
+        testProject(target("projects").path("1").request().get(Project.class), false);
+    }
+
+    @Test
+    public void testDetailedProjects() throws Exception {
+        for (final Project project : target("projects/detailed").request().get(new GenericType<List<Project>>() {})) {
+            testProject(project, true);
+        }
+    }
+
+    @Test
+    public void testDetailedProject() throws Exception {
+        testProject(target("projects/detailed").path("1").request().get(Project.class), true);
+    }
+
+    private void testProject(final Project project, final boolean isDetailed) {
+        // Following properties should be in every returned project.
+        assertThat(project.getId(), notNullValue());
+        assertThat(project.getName(), notNullValue());
+        assertThat(project.getDescription(), notNullValue());
+
+        // Tasks and users should be only in "detailed" view.
+        if (!isDetailed) {
+            assertThat("Users present in non-detailed project view", project.getUsers(), nullValue());
+            assertThat("Tasks present in non-detailed project view", project.getTasks(), nullValue());
+        } else {
+            assertThat("Users not present in detailed project view", project.getUsers(), notNullValue());
+            assertThat("Tasks not present in detailed project view", project.getTasks(), notNullValue());
+        }
+    }
+}
diff --git a/examples/entity-filtering/src/test/java/org/glassfish/jersey/examples/entityfiltering/TaskResourceTest.java b/examples/entity-filtering/src/test/java/org/glassfish/jersey/examples/entityfiltering/TaskResourceTest.java
new file mode 100644
index 0000000..c7171ac
--- /dev/null
+++ b/examples/entity-filtering/src/test/java/org/glassfish/jersey/examples/entityfiltering/TaskResourceTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.GenericType;
+
+import org.glassfish.jersey.examples.entityfiltering.domain.Task;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * {@link org.glassfish.jersey.examples.entityfiltering.resource.TasksResource} unit tests.
+ *
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class TaskResourceTest extends JerseyTest {
+
+    @Parameterized.Parameters(name = "Provider: {0}")
+    public static Iterable<Class[]> providers() {
+        return Arrays.asList(new Class[][]{{MoxyJsonFeature.class}, {JacksonFeature.class}});
+    }
+
+    public TaskResourceTest(final Class<Feature> filteringProvider) {
+        super(new ResourceConfig(EntityFilteringFeature.class)
+                .packages("org.glassfish.jersey.examples.entityfiltering.resource")
+                .register(filteringProvider));
+
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Test
+    public void testTasks() throws Exception {
+        for (final Task task : target("tasks").request().get(new GenericType<List<Task>>() {})) {
+            testTask(task, false);
+        }
+    }
+
+    @Test
+    public void testTask() throws Exception {
+        testTask(target("tasks").path("1").request().get(Task.class), false);
+    }
+
+    @Test
+    public void testDetailedTasks() throws Exception {
+        for (final Task task : target("tasks").path("detailed").request().get(new GenericType<List<Task>>() {})) {
+            testTask(task, true);
+        }
+    }
+
+    @Test
+    public void testDetailedTask() throws Exception {
+        testTask(target("tasks").path("1").queryParam("detailed", true).request().get(Task.class), true);
+    }
+
+    private void testTask(final Task task, final boolean isDetailed) {
+        // Following properties should be in every returned task.
+        assertThat(task.getId(), notNullValue());
+        assertThat(task.getName(), notNullValue());
+        assertThat(task.getDescription(), notNullValue());
+
+        // Tasks and tasks should be in "detailed" view.
+        if (!isDetailed) {
+            assertThat(task.getProject(), nullValue());
+            assertThat(task.getUser(), nullValue());
+        } else {
+            assertThat(task.getProject(), notNullValue());
+            assertThat(task.getUser(), notNullValue());
+        }
+    }
+}
diff --git a/examples/entity-filtering/src/test/java/org/glassfish/jersey/examples/entityfiltering/UsersResourceTest.java b/examples/entity-filtering/src/test/java/org/glassfish/jersey/examples/entityfiltering/UsersResourceTest.java
new file mode 100644
index 0000000..e85f779
--- /dev/null
+++ b/examples/entity-filtering/src/test/java/org/glassfish/jersey/examples/entityfiltering/UsersResourceTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.entityfiltering;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.GenericType;
+
+import org.glassfish.jersey.examples.entityfiltering.domain.User;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * {@link org.glassfish.jersey.examples.entityfiltering.resource.UsersResource} unit tests.
+ *
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class UsersResourceTest extends JerseyTest {
+
+    @Parameterized.Parameters(name = "Provider: {0}")
+    public static Iterable<Class[]> providers() {
+        return Arrays.asList(new Class[][]{{MoxyJsonFeature.class}, {JacksonFeature.class}});
+    }
+
+    public UsersResourceTest(final Class<Feature> filteringProvider) {
+        super(new ResourceConfig(EntityFilteringFeature.class)
+                .packages("org.glassfish.jersey.examples.entityfiltering.resource")
+                .register(filteringProvider));
+
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Test
+    public void testUsers() throws Exception {
+        for (final User user : target("users").request().get(new GenericType<List<User>>() {})) {
+            testUser(user, false);
+        }
+    }
+
+    @Test
+    public void testUser() throws Exception {
+        testUser(target("users").path("1").request().get(User.class), false);
+    }
+
+    @Test
+    public void testDetailedUsers() throws Exception {
+        for (final User user : target("users").queryParam("detailed", true).request().get(new GenericType<List<User>>() {})) {
+            testUser(user, true);
+        }
+    }
+
+    @Test
+    public void testDetailedUser() throws Exception {
+        testUser(target("users").path("1").queryParam("detailed", true).request().get(User.class), true);
+    }
+
+    private void testUser(final User user, final boolean isDetailed) {
+        // Following properties should be in every returned user.
+        assertThat(user.getId(), notNullValue());
+        assertThat(user.getName(), notNullValue());
+        assertThat(user.getEmail(), notNullValue());
+
+        // Tasks and users should be in "detailed" view.
+        if (!isDetailed) {
+            assertThat(user.getProjects(), nullValue());
+            assertThat(user.getTasks(), nullValue());
+        } else {
+            assertThat(user.getProjects(), notNullValue());
+            assertThat(user.getTasks(), notNullValue());
+        }
+    }
+}
diff --git a/examples/etc/gf-project-src-pom.xsl b/examples/etc/gf-project-src-pom.xsl
new file mode 100644
index 0000000..50df08f
--- /dev/null
+++ b/examples/etc/gf-project-src-pom.xsl
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:pom="http://maven.apache.org/POM/4.0.0"
+                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+                version="1.0">
+
+    <xsl:output method="xml" indent="yes" />
+
+    <xsl:template match="/">
+        <xsl:apply-templates />
+    </xsl:template>
+
+    <xsl:template
+            match="pom:dependencies/pom:dependency[pom:groupId='org.glassfish.jersey.core'
+            or pom:groupId='org.glassfish.jersey.containers'
+            or pom:groupId='org.glassfish.jersey.ext.cdi'
+            or pom:groupId='org.glassfish.jersey.media'
+            or pom:artifactId='jersey-wadl-doclet'
+            or pom:artifactId='jersey-mvc-jsp'
+            or pom:artifactId='jersey-bean-validation'
+            or pom:groupId='com.sun.xml.bind'
+            or pom:groupId='org.codehaus.jettison'
+            or pom:groupId='javax.annotation'
+            or pom:groupId='javax.enterprise'
+            or pom:groupId='javax.servlet'
+            or pom:groupId='javax.ws.rs']/pom:scope[text()!=test]">
+        <scope>provided</scope>
+    </xsl:template>
+
+    <xsl:template
+            match="pom:dependencies/pom:dependency[pom:groupId='org.glassfish.jersey.core'
+            or pom:groupId='org.glassfish.jersey.containers'
+            or pom:groupId='org.glassfish.jersey.ext.cdi'
+            or pom:groupId='org.glassfish.jersey.media'
+            or pom:artifactId='jersey-wadl-doclet'
+            or pom:artifactId='jersey-mvc-jsp'
+            or pom:artifactId='jersey-bean-validation'
+            or pom:groupId='com.sun.xml.bind'
+            or pom:groupId='javax.validation'
+            or pom:groupId='org.codehaus.jettison'
+            or pom:groupId='javax.annotation'
+            or pom:groupId='javax.enterprise'
+            or pom:groupId='javax.servlet'
+            or pom:groupId='javax.ws.rs']">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <xsl:if test="count(pom:scope)=0">
+                <scope>provided</scope>
+            </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <xsl:template
+            match="pom:dependencies/pom:dependency[pom:artifactId='jersey-mvc-freemarker']">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <exclusions>
+              <exclusion>
+                <groupId>org.glassfish.jersey.ext</groupId>
+                <artifactId>jersey-mvc</artifactId>
+              </exclusion>
+              <exclusion>
+                <groupId>javax.ws.rs</groupId>
+                <artifactId>javax.ws.rs-api</artifactId>
+              </exclusion>
+            </exclusions>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- There is problem to run Spring example on GF - https://java.net/jira/browse/JERSEY-2032
+    <xsl:template
+            match="pom:dependencies/pom:dependency[pom:artifactId='jersey-spring3']">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.ws.rs</groupId>
+                    <artifactId>javax.ws.rs-api</artifactId>
+                </exclusion>
+
+
+                <exclusion>
+                    <groupId>org.glassfish.jersey.core</groupId>
+                    <artifactId>jersey-server</artifactId>
+                </exclusion>
+
+                <exclusion>
+                    <groupId>org.glassfish.jersey.containers</groupId>
+                    <artifactId>jersey-container-servlet-core</artifactId>
+                </exclusion>
+
+                <exclusion>
+                    <groupId>org.glassfish.hk2</groupId>
+                    <artifactId>hk2</artifactId>
+                </exclusion>
+
+                <exclusion>
+                    <groupId>javax.servlet</groupId>
+                    <artifactId>javax.servlet-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </xsl:copy>
+    </xsl:template>
+    -->
+
+    <xsl:template match="pom:dependencies">
+      <xsl:copy>
+        <xsl:apply-templates />
+        <xsl:if test="count(pom:dependency[pom:artifactId='jersey-container-servlet-core'])=0">
+          <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+            <scope>provided</scope>
+          </dependency>
+        </xsl:if>
+        <xsl:if test="count(pom:dependency[pom:artifactId='jersey-mvc-freemarker'])=1">
+          <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc</artifactId>
+            <scope>provided</scope>
+          </dependency>
+        </xsl:if>
+       </xsl:copy>
+    </xsl:template>
+
+    <xsl:template match="pom:project">
+       <xsl:copy>
+        <xsl:apply-templates />
+        <xsl:if test="count(pom:dependencies)=0">
+          <dependencies>
+            <dependency>
+              <groupId>org.glassfish.jersey.containers</groupId>
+              <artifactId>jersey-container-servlet-core</artifactId>
+              <scope>provided</scope>
+            </dependency>
+          </dependencies>
+        </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- remove <packagingExcludes>WEB-INF/glassfish-web.xml</packagingExcludes>
+         as this file is required in Glassfish bundle since <class-loader>
+         is defined in it -->
+    <xsl:template match="pom:plugin[pom:artifactId='maven-war-plugin']/pom:configuration[pom:packagingExcludes]">
+    </xsl:template>
+
+    <!--build war even if web.xml is missing as it's not required,
+        <packagingIncludes> defaults to 'all' so it includes
+        <packagingIncludes>WEB-INF/glassfish-web.xml</packagingIncludes>
+        to pick up <class-loader> -->
+    <xsl:template match="pom:plugin[pom:artifactId='maven-war-plugin']">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <xsl:if test="count(pom:configuration)=1">
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                </configuration>
+            </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- remove examples-source-zip profile -->
+    <xsl:template match="pom:profile/pom:plugins/pom:plugin[pom:id='examples-source-zip']">
+    </xsl:template>
+
+    <!--&lt;!&ndash; remove xslt-maven-plugin &ndash;&gt;-->
+    <!--<xsl:template match="pom:plugin[pom:artifactId='xml-maven-plugin']">-->
+    <!--</xsl:template>-->
+
+    <!--&lt;!&ndash; remove maven-assembly-plugin &ndash;&gt;-->
+    <!--<xsl:template match="pom:plugin[pom:artifactId='maven-assembly-plugin']">-->
+    <!--</xsl:template>-->
+
+    <!-- remove maven-jetty-plugin -->
+    <xsl:template match="pom:plugin[pom:artifactId='maven-jetty-plugin']">
+    </xsl:template>
+
+    <!-- remove jetty-maven-plugin -->
+    <xsl:template match="pom:plugin[pom:artifactId='jetty-maven-plugin']">
+    </xsl:template>
+
+    <!-- remove failsafe plugin (integration testing not possible without jetty/other container) -->
+    <xsl:template match="pom:plugin[pom:artifactId='maven-failsafe-plugin']">
+    </xsl:template>
+
+    <xsl:template match="comment()">
+        <xsl:comment>
+            <xsl:value-of select="." />
+        </xsl:comment>
+    </xsl:template>
+
+    <xsl:template match="*">
+        <xsl:copy>
+            <xsl:apply-templates />
+        </xsl:copy>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/examples/etc/wls-project-src-pom.xsl b/examples/etc/wls-project-src-pom.xsl
new file mode 100644
index 0000000..6116b5b
--- /dev/null
+++ b/examples/etc/wls-project-src-pom.xsl
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+        xmlns:pom="http://maven.apache.org/POM/4.0.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+        version="1.0">
+
+    <xsl:output method="xml" indent="yes" />
+
+    <xsl:template match="/">
+        <xsl:apply-templates />
+    </xsl:template>
+
+    <xsl:template
+            match="pom:dependencies/pom:dependency[pom:groupId='org.glassfish.jersey.core'
+            or pom:groupId='org.glassfish.jersey.containers'
+            or pom:artifactId='jersey-bean-validation'
+            or pom:artifactId='jersey-media-json-jackson'
+            or pom:artifactId='jersey-media-json-jettison'
+            or pom:artifactId='jersey-media-moxy'
+            or pom:artifactId='jersey-media-multipart'
+            or pom:artifactId='jersey-media-sse'
+            or pom:groupId='com.sun.xml.bind'
+            or pom:groupId='javax.servlet']/pom:scope[text()!=test]">
+        <scope>provided</scope>
+    </xsl:template>
+
+    <xsl:template
+            match="pom:dependencies/pom:dependency[pom:groupId='org.glassfish.jersey.core'
+            or pom:groupId='org.glassfish.jersey.containers'
+            or pom:artifactId='jersey-bean-validation'
+            or pom:artifactId='jersey-media-json-jackson'
+            or pom:artifactId='jersey-media-json-jettison'
+            or pom:artifactId='jersey-media-moxy'
+            or pom:artifactId='jersey-media-multipart'
+            or pom:artifactId='jersey-media-sse'
+            or pom:groupId='com.sun.xml.bind'
+            or pom:groupId='javax.servlet']">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <xsl:if test="count(pom:scope)=0">
+                <scope>provided</scope>
+            </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <xsl:template
+            match="pom:dependencies/pom:dependency[pom:artifactId='jersey-mvc-jsp'
+            or pom:artifactId='jersey-mvc-freemarker'
+            or pom:artifactId='jersey-media-json-processing']/pom:scope[text()!=test]">
+        <exclusions>
+            <exclusion>
+                <groupId>org.glassfish.jersey.core</groupId>
+                <artifactId>jersey-common</artifactId>
+            </exclusion>
+            <exclusion>
+                <groupId>org.glassfish.jersey.core</groupId>
+                <artifactId>jersey-server</artifactId>
+            </exclusion>
+            <exclusion>
+                <groupId>org.glassfish.hk2.external</groupId>
+                <artifactId>javax.inject</artifactId>
+            </exclusion>
+            <exclusion>
+                <groupId>javax.ws.rs</groupId>
+                <artifactId>javax.ws.rs-api</artifactId>
+            </exclusion>
+        </exclusions>
+    </xsl:template>
+
+    <xsl:template
+            match="pom:dependencies/pom:dependency[pom:artifactId='jersey-mvc-jsp'
+            or pom:artifactId='jersey-mvc-freemarker'
+            or pom:artifactId='jersey-media-json-processing']">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <xsl:if test="count(pom:scope)=0">
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.glassfish.jersey.core</groupId>
+                        <artifactId>jersey-common</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>org.glassfish.jersey.core</groupId>
+                        <artifactId>jersey-server</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>org.glassfish.hk2.external</groupId>
+                        <artifactId>javax.inject</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>javax.ws.rs</groupId>
+                        <artifactId>javax.ws.rs-api</artifactId>
+                    </exclusion>
+                </exclusions>
+            </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <xsl:template match="pom:dependencies">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <xsl:if test="count(pom:dependency[pom:artifactId='jersey-container-servlet-core'])=0">
+                <dependency>
+                    <groupId>org.glassfish.jersey.containers</groupId>
+                    <artifactId>jersey-container-servlet-core</artifactId>
+                    <scope>provided</scope>
+                </dependency>
+            </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <xsl:template match="pom:project">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <xsl:if test="count(pom:dependencies)=0">
+                <dependencies>
+                    <dependency>
+                        <groupId>org.glassfish.jersey.containers</groupId>
+                        <artifactId>jersey-container-servlet-core</artifactId>
+                        <scope>provided</scope>
+                    </dependency>
+                </dependencies>
+            </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- remove <packagingExcludes>WEB-INF/glassfish-web.xml</packagingExcludes>
+         as this file is required in Glassfish bundle since <class-loader>
+         is defined in it -->
+    <xsl:template match="pom:plugin[pom:artifactId='maven-war-plugin']/pom:configuration[pom:packagingExcludes]">
+    </xsl:template>
+
+    <!--build war even if web.xml is missing as it's not required,
+        <packagingIncludes> defaults to 'all' so it includes
+        <packagingIncludes>WEB-INF/glassfish-web.xml</packagingIncludes>
+        to pick up <class-loader> -->
+    <xsl:template match="pom:plugin[pom:artifactId='maven-war-plugin']">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <xsl:if test="count(pom:configuration)=1">
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                </configuration>
+            </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- remove examples-source-zip profile -->
+    <xsl:template match="pom:profile/pom:plugins/pom:plugin[pom:id='examples-source-zip']">
+    </xsl:template>
+
+    <!--&lt;!&ndash; remove xslt-maven-plugin &ndash;&gt;-->
+    <!--<xsl:template match="pom:plugin[pom:artifactId='xml-maven-plugin']">-->
+    <!--</xsl:template>-->
+
+    <!--&lt;!&ndash; remove maven-assembly-plugin &ndash;&gt;-->
+    <!--<xsl:template match="pom:plugin[pom:artifactId='maven-assembly-plugin']">-->
+    <!--</xsl:template>-->
+
+    <!-- remove maven-jetty-plugin -->
+    <xsl:template match="pom:plugin[pom:artifactId='maven-jetty-plugin']">
+    </xsl:template>
+
+    <!-- remove jetty-maven-plugin -->
+    <xsl:template match="pom:plugin[pom:artifactId='jetty-maven-plugin']">
+    </xsl:template>
+
+    <xsl:template match="comment()">
+        <xsl:comment>
+            <xsl:value-of select="." />
+        </xsl:comment>
+    </xsl:template>
+
+    <xsl:template match="*">
+        <xsl:copy>
+            <xsl:apply-templates />
+        </xsl:copy>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/examples/etc/wls-project-src-web.xsl b/examples/etc/wls-project-src-web.xsl
new file mode 100644
index 0000000..46009cf
--- /dev/null
+++ b/examples/etc/wls-project-src-web.xsl
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+        xmlns:web="http://java.sun.com/xml/ns/javaee"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+        version="1.0">
+
+    <xsl:template match="node()|@*">
+        <xsl:copy>
+            <xsl:apply-templates select="node()|@*"/>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- fixes the hardcoded port in managed-client-webapp example's web.xml -->
+    <xsl:template match="//web:web-app/web:servlet/web:init-param[web:param-name='org.glassfish.jersey.examples.managedclient.ClientA.baseUri']/web:param-value">
+        <xsl:copy>
+            <xsl:text>http://localhost:7001/managed-client-webapp/internal</xsl:text>
+        </xsl:copy>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/examples/etc/wls1213-project-src-pom.xsl b/examples/etc/wls1213-project-src-pom.xsl
new file mode 100644
index 0000000..40b63b5
--- /dev/null
+++ b/examples/etc/wls1213-project-src-pom.xsl
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+        xmlns:pom="http://maven.apache.org/POM/4.0.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+        version="1.0">
+
+    <xsl:output method="xml" indent="yes" />
+
+    <xsl:template match="/">
+        <xsl:apply-templates />
+    </xsl:template>
+
+    <xsl:template
+            match="pom:dependencies/pom:dependency[pom:groupId='org.glassfish.jersey.core'
+            or pom:groupId='org.glassfish.jersey.containers'
+            or pom:artifactId='jersey-media-json-jackson'
+            or pom:artifactId='jersey-media-json-jettison'
+            or pom:artifactId='jersey-media-moxy'
+            or pom:artifactId='jersey-media-multipart'
+            or pom:artifactId='jersey-media-sse'
+            or pom:groupId='com.sun.xml.bind'
+            or pom:groupId='javax.servlet']/pom:scope[text()!=test]">
+        <scope>provided</scope>
+    </xsl:template>
+
+    <xsl:template
+            match="pom:dependencies/pom:dependency[pom:groupId='org.glassfish.jersey.core'
+            or pom:groupId='org.glassfish.jersey.containers'
+            or pom:artifactId='jersey-media-json-jackson'
+            or pom:artifactId='jersey-media-json-jettison'
+            or pom:artifactId='jersey-media-moxy'
+            or pom:artifactId='jersey-media-multipart'
+            or pom:artifactId='jersey-media-sse'
+            or pom:groupId='com.sun.xml.bind'
+            or pom:groupId='javax.servlet']">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <xsl:if test="count(pom:scope)=0">
+                <scope>provided</scope>
+            </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <xsl:template
+            match="pom:dependencies/pom:dependency[pom:artifactId='jersey-bean-validation'
+            or pom:artifactId='jersey-mvc-jsp'
+            or pom:artifactId='jersey-mvc-freemarker'
+            or pom:artifactId='jersey-media-json-processing']/pom:scope[text()!=test]">
+        <exclusions>
+            <exclusion>
+                <groupId>org.glassfish.jersey.core</groupId>
+                <artifactId>jersey-common</artifactId>
+            </exclusion>
+            <exclusion>
+                <groupId>org.glassfish.jersey.core</groupId>
+                <artifactId>jersey-server</artifactId>
+            </exclusion>
+            <exclusion>
+                <groupId>org.glassfish.hk2.external</groupId>
+                <artifactId>javax.inject</artifactId>
+            </exclusion>
+            <exclusion>
+                <groupId>javax.ws.rs</groupId>
+                <artifactId>javax.ws.rs-api</artifactId>
+            </exclusion>
+        </exclusions>
+    </xsl:template>
+
+    <xsl:template
+            match="pom:dependencies/pom:dependency[pom:artifactId='jersey-bean-validation'
+            or pom:artifactId='jersey-mvc-jsp'
+            or pom:artifactId='jersey-mvc-freemarker'
+            or pom:artifactId='jersey-media-json-processing']">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <xsl:if test="count(pom:scope)=0">
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.glassfish.jersey.core</groupId>
+                        <artifactId>jersey-common</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>org.glassfish.jersey.core</groupId>
+                        <artifactId>jersey-server</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>org.glassfish.hk2.external</groupId>
+                        <artifactId>javax.inject</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>javax.ws.rs</groupId>
+                        <artifactId>javax.ws.rs-api</artifactId>
+                    </exclusion>
+                </exclusions>
+            </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <xsl:template match="pom:dependencies">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <xsl:if test="count(pom:dependency[pom:artifactId='jersey-container-servlet-core'])=0">
+                <dependency>
+                    <groupId>org.glassfish.jersey.containers</groupId>
+                    <artifactId>jersey-container-servlet-core</artifactId>
+                    <scope>provided</scope>
+                </dependency>
+            </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <xsl:template match="pom:project">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <xsl:if test="count(pom:dependencies)=0">
+                <dependencies>
+                    <dependency>
+                        <groupId>org.glassfish.jersey.containers</groupId>
+                        <artifactId>jersey-container-servlet-core</artifactId>
+                        <scope>provided</scope>
+                    </dependency>
+                </dependencies>
+            </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- remove <packagingExcludes>WEB-INF/glassfish-web.xml</packagingExcludes>
+         as this file is required in Glassfish bundle since <class-loader>
+         is defined in it -->
+    <xsl:template match="pom:plugin[pom:artifactId='maven-war-plugin']/pom:configuration[pom:packagingExcludes]">
+    </xsl:template>
+
+    <!--build war even if web.xml is missing as it's not required,
+        <packagingIncludes> defaults to 'all' so it includes
+        <packagingIncludes>WEB-INF/glassfish-web.xml</packagingIncludes>
+        to pick up <class-loader> -->
+    <xsl:template match="pom:plugin[pom:artifactId='maven-war-plugin']">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <xsl:if test="count(pom:configuration)=1">
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                </configuration>
+            </xsl:if>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- remove examples-source-zip profile -->
+    <xsl:template match="pom:profile/pom:plugins/pom:plugin[pom:id='examples-source-zip']">
+    </xsl:template>
+
+    <!--&lt;!&ndash; remove xslt-maven-plugin &ndash;&gt;-->
+    <!--<xsl:template match="pom:plugin[pom:artifactId='xml-maven-plugin']">-->
+    <!--</xsl:template>-->
+
+    <!--&lt;!&ndash; remove maven-assembly-plugin &ndash;&gt;-->
+    <!--<xsl:template match="pom:plugin[pom:artifactId='maven-assembly-plugin']">-->
+    <!--</xsl:template>-->
+
+    <!-- remove maven-jetty-plugin -->
+    <xsl:template match="pom:plugin[pom:artifactId='maven-jetty-plugin']">
+    </xsl:template>
+
+    <!-- remove jetty-maven-plugin -->
+    <xsl:template match="pom:plugin[pom:artifactId='jetty-maven-plugin']">
+    </xsl:template>
+
+    <xsl:template match="comment()">
+        <xsl:comment>
+            <xsl:value-of select="." />
+        </xsl:comment>
+    </xsl:template>
+
+    <xsl:template match="*">
+        <xsl:copy>
+            <xsl:apply-templates />
+        </xsl:copy>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/examples/etc/wls1213-project-src-web.xsl b/examples/etc/wls1213-project-src-web.xsl
new file mode 100644
index 0000000..c241e7b
--- /dev/null
+++ b/examples/etc/wls1213-project-src-web.xsl
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+        xmlns:web="http://java.sun.com/xml/ns/javaee"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+        version="1.0">
+
+    <xsl:template match="node()|@*">
+        <xsl:copy>
+            <xsl:apply-templates select="node()|@*"/>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- fixes the hardcoded port in managed-client-webapp example's web.xml -->
+    <xsl:template match="//web:web-app/web:servlet/web:init-param[web:param-name='org.glassfish.jersey.examples.managedclient.ClientA.baseUri']/web:param-value">
+        <xsl:copy>
+            <xsl:text>http://localhost:7001/managed-client-webapp/internal</xsl:text>
+        </xsl:copy>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/examples/etc/wls1213/README-WLS.html b/examples/etc/wls1213/README-WLS.html
new file mode 100644
index 0000000..be6b9b5
--- /dev/null
+++ b/examples/etc/wls1213/README-WLS.html
@@ -0,0 +1,78 @@
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE html>
+    <head>
+        <meta charset="utf-8">
+        <meta http-equiv="X-UA-Compatible" content="IE=edge">
+        <title>WLS 12.1.3 Shared Library installation steps.</title>
+
+        <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css">
+    </head>
+
+    <body>
+        <div class="container">
+            <div class="row">
+                <h1>Weblogic 12.1.3 Shared Library Installation Steps</h1>
+
+                <p>
+                    Before deploying an JAX-RS 2.0 application into WLS 12.1.3 you need to install (deploy) the JAX-RS 2.0 shared
+                    library that provides necessary dependencies and configuration. This library is located at
+                    <code>MW_HOME\wlserver\common\deployable-libraries\jax-rs-2.0.war</code>.
+                </p>
+                <p>
+                    There are two ways, how you can deploy this library:
+                    <ul>
+                        <li>Using WLS console</li>
+                        <li>Using WLST</li>
+                    </ul>
+                </p>
+
+                <h2>Deploying shared library using WLS console</h2>
+
+                <ol>
+                    <li>Log into WLS console at <code>http://&lt;host&gt;:&lt;port&gt;/console</code> (e.g. <code>http://localhost:7001/console</code>)</li>
+                    <li>Click on <em>Deployments</em></li>
+                    <li>Find Shared Library WAR (located at <code>MW_HOME\wlserver\common\deployable-libraries\jax-rs-2.0.war</code>)</li>
+                    <li>Click on <em>Next</em></li>
+                    <li>Choose <em>Deploy as Library</em></li>
+                    <li>Click on <em>Next</em></li>
+                    <li>Click on <em>Finish</em></li>
+                </ol>
+
+                <h2>Deploying shared library using WLST</h2>
+
+                <p>Lets assume that WLS is running and your domain is located in <code>MW_HOME</code> (<code>MW_HOME\domain</code>).</p>
+
+                <p>Setup your shell and enter the WLST console:</p>
+
+<pre>
+> # bash prompt
+> cd $MW_HOME\domain
+> . ./bin/setDomainEnv.sh
+> java weblogic.WLST
+</pre>
+
+                <p>Deploy the library:</p>
+
+<pre>
+> # WLST console
+> connect('user', 'pass', 'host')
+> deploy('jax-rs', '../wlserver/common/deployable-libraries/jax-rs-2.0.war', libraryModule='true')
+> exit()
+</pre>
+
+
+            </div>
+        </div>
+    </body>
+</html>
diff --git a/examples/etc/wls1213/src/main/webapp/WEB-INF/weblogic.xml b/examples/etc/wls1213/src/main/webapp/WEB-INF/weblogic.xml
new file mode 100644
index 0000000..61c11c0
--- /dev/null
+++ b/examples/etc/wls1213/src/main/webapp/WEB-INF/weblogic.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<wls:weblogic-web-app xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd">
+
+    <wls:library-ref>
+        <wls:library-name>jax-rs</wls:library-name>
+        <wls:specification-version>2.0</wls:specification-version>
+        <wls:exact-match>true</wls:exact-match>
+    </wls:library-ref>
+</wls:weblogic-web-app>
diff --git a/examples/exception-mapping/README.MD b/examples/exception-mapping/README.MD
new file mode 100644
index 0000000..613624d
--- /dev/null
+++ b/examples/exception-mapping/README.MD
@@ -0,0 +1,49 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Exception Mapping Example
+=========================
+
+This example demonstrates how to create a custom exception mapping. There are described several common use-cases such as
+`WebApplicationException`, custom exceptions and an exception mapper inheritance.
+
+The full description how to handle exception and map them to Response object can be found in Jersey User Guide, chapter
+[WebApplicationException and Mapping Exceptions to Responses](https://jersey.java.net/documentation/latest/representations.html#d0e6567).
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                                   | HTTP methods  | Allowed Values                              | Description
+-----------------------------------------  | ------------- |-------------------------------------------- | ----------------
+**_/exception_**                           | GET           | ---                                         | returns "ping!"
+**_/exception/webapplication_entity_**     | POST          | two numbers delimited by colons e.g. 1:201  | not handled by `WebApplicationExceptionMapper` (already has an entity in the exception)
+**_/exception/webapplication_noentity_**   | POST          | e.g. 1:201                                  | handled by `WebApplicationExceptionMapper`
+**_/exception/my_**                        | POST          | e.g. 1:201                                  | handled by `MyExceptionMapper`
+**_/exception/mysub_**                     | POST          | e.g. 1:201                                  | handled by `MySubExceptionMapper`
+**_/exception/mysubsub_**                  | POST          | e.g. 1:201                                  | handled by `MySubExceptionMapper`
+**_/exception/request_exception_**         | POST          | String "Request Exception"                  | not reached a resource method, processed by `ContainerRequestFilter`
+**_/exception/response_exception_**        | GET           | ---                                         | response handled and changed by `ContainerResponseFilter`
+
+
+Application is based on Grizzly container (see `App`). Everything needed
+(resources/providers) is registered in `ExceptionResource` and custom exceptions
+along with mappers can be found in `Exceptions` class.
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
+
+-   <http://localhost:8080/base/exception>
+-   <http://localhost:8080/base/exception/response_exception>
diff --git a/examples/exception-mapping/pom.xml b/examples/exception-mapping/pom.xml
new file mode 100644
index 0000000..68f418d
--- /dev/null
+++ b/examples/exception-mapping/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>exception-mapping</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-exception-mapping</name>
+
+    <description>Jersey example showing exception mappers in action.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.exception.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/exception-mapping/src/main/java/org/glassfish/jersey/examples/exception/App.java b/examples/exception-mapping/src/main/java/org/glassfish/jersey/examples/exception/App.java
new file mode 100644
index 0000000..b520eda
--- /dev/null
+++ b/examples/exception-mapping/src/main/java/org/glassfish/jersey/examples/exception/App.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.exception;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Hello world!
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/base/");
+    public static final String ROOT_PATH = "exception";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Exception Mapping\" Jersey Example App");
+
+            final ResourceConfig resourceConfig = new ResourceConfig(
+                    ExceptionResource.class,
+                    ExceptionResource.MyResponseFilter.class,
+                    ExceptionResource.WebApplicationExceptionFilter.class,
+                    Exceptions.MyExceptionMapper.class,
+                    Exceptions.MySubExceptionMapper.class,
+                    Exceptions.WebApplicationExceptionMapper.class);
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format(
+                    "Application started.%n"
+                    + "Try out %s%s%n"
+                    + "Stop the application using CTRL+C",
+                    BASE_URI, ROOT_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+}
diff --git a/examples/exception-mapping/src/main/java/org/glassfish/jersey/examples/exception/ExceptionResource.java b/examples/exception-mapping/src/main/java/org/glassfish/jersey/examples/exception/ExceptionResource.java
new file mode 100644
index 0000000..e4cfe74
--- /dev/null
+++ b/examples/exception-mapping/src/main/java/org/glassfish/jersey/examples/exception/ExceptionResource.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.exception;
+
+import java.io.IOException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.examples.exception.Exceptions.MyException;
+import org.glassfish.jersey.examples.exception.Exceptions.MySubException;
+import org.glassfish.jersey.examples.exception.Exceptions.MySubSubException;
+import org.glassfish.jersey.server.ContainerRequest;
+
+/**
+ * ExceptionResource class.
+ *
+ * @author Santiago.PericasGeertsen at oracle.com
+ */
+@Path("exception")
+@Consumes("text/plain")
+@Produces("text/plain")
+public class ExceptionResource {
+
+    @Provider
+    static class MyResponseFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+            System.out.println("MyResponseFilter.postFilter() enter");
+            responseContext.setEntity(
+                    responseContext.getEntity() + ":" + getClass().getSimpleName(), null, MediaType.TEXT_PLAIN_TYPE);
+            System.out.println("MyResponseFilter.postFilter() exit");
+        }
+    }
+
+    @Provider
+    static class WebApplicationExceptionFilter implements ContainerRequestFilter, ContainerResponseFilter {
+
+        @Override
+        public void filter(ContainerRequestContext context) throws IOException {
+            System.out.println("WebApplicationExceptionFilter.preFilter() enter");
+
+            String path = ((ContainerRequest) context).getRequestUri().getPath();
+            if (path.endsWith("request_exception") && context.hasEntity() && ((ContainerRequest) context)
+                    .readEntity(String.class).equals("Request Exception")) {
+                throw new WebApplicationException(Response.Status.OK);
+            }
+            System.out.println("WebApplicationExceptionFilter.preFilter() exit");
+        }
+
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+            System.out.println("WebApplicationExceptionFilter.postFilter() enter");
+            if (responseContext.hasEntity() && responseContext.getEntity().equals("Response Exception")) {
+                throw new WebApplicationException(Response.Status.OK);
+            }
+            System.out.println("WebApplicationExceptionFilter.postFilter() exit");
+        }
+    }
+
+    @GET
+    public String pingMe() {
+        return "ping!";
+    }
+
+    @POST
+    @Path("webapplication_entity")
+    public String testWebApplicationExceptionEntity(String s) {
+        String[] tokens = s.split(":");
+        assert tokens.length == 2;
+        int statusCode = Integer.valueOf(tokens[1]);
+        Response r = Response.status(statusCode).entity(s).build();
+        throw new WebApplicationException(r);
+    }
+
+    @POST
+    @Path("webapplication_noentity")
+    public String testWebApplicationExceptionNoEntity(String s) {
+        String[] tokens = s.split(":");
+        assert tokens.length == 2;
+        int statusCode = Integer.valueOf(tokens[1]);
+        Response r = Response.status(statusCode).build();
+        throw new WebApplicationException(r);
+    }
+
+    @POST
+    @Path("my")
+    public String testMyException(String s) {
+        String[] tokens = s.split(":");
+        assert tokens.length == 2;
+        int statusCode = Integer.valueOf(tokens[1]);
+        Response r = Response.status(statusCode).build();
+        throw new MyException(r);
+    }
+
+    @POST
+    @Path("mysub")
+    public String testMySubException(String s) {
+        String[] tokens = s.split(":");
+        assert tokens.length == 2;
+        int statusCode = Integer.valueOf(tokens[1]);
+        Response r = Response.status(statusCode).build();
+        throw new MySubException(r);
+    }
+
+    @POST
+    @Path("mysubsub")
+    public String testMySubSubException(String s) {
+        String[] tokens = s.split(":");
+        assert tokens.length == 2;
+        int statusCode = Integer.valueOf(tokens[1]);
+        Response r = Response.status(statusCode).build();
+        throw new MySubSubException(r);
+    }
+
+    @POST
+    @Path("request_exception")
+    public String exceptionInRequestFilter() {
+        throw new InternalServerErrorException();        // should not reach here
+    }
+
+    @GET
+    @Path("response_exception")
+    public String exceptionInResponseFilter() {
+        return "Response Exception";
+    }
+}
diff --git a/examples/exception-mapping/src/main/java/org/glassfish/jersey/examples/exception/Exceptions.java b/examples/exception-mapping/src/main/java/org/glassfish/jersey/examples/exception/Exceptions.java
new file mode 100644
index 0000000..670bde8
--- /dev/null
+++ b/examples/exception-mapping/src/main/java/org/glassfish/jersey/examples/exception/Exceptions.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.exception;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Exceptions class.
+ *
+ * @author Santiago.Pericas-Geertsen at oracle.com
+ */
+public class Exceptions {
+
+    // -- Exceptions
+    public static class MyException extends RuntimeException {
+
+        private Response response;
+
+        public MyException(Response response) {
+            this.response = response;
+        }
+
+        public Response getResponse() {
+            return response;
+        }
+    }
+
+    public static class MySubException extends MyException {
+
+        public MySubException(Response response) {
+            super(response);
+        }
+    }
+
+    public static class MySubSubException extends MySubException {
+
+        public MySubSubException(Response response) {
+            super(response);
+        }
+    }
+
+    // -- Exception Mappers
+    @Provider
+    public static class MyExceptionMapper implements ExceptionMapper<MyException> {
+
+        @Override
+        public Response toResponse(MyException exception) {
+            Response r = exception.getResponse();
+            return Response.status(r.getStatus()).entity(
+                    "Code:" + r.getStatus() + ":" + getClass().getSimpleName()).build();
+        }
+    }
+
+    @Provider
+    public static class MySubExceptionMapper implements ExceptionMapper<MySubException> {
+
+        @Override
+        public Response toResponse(MySubException exception) {
+            Response r = exception.getResponse();
+            return Response.status(r.getStatus()).entity(
+                    "Code:" + r.getStatus() + ":" + getClass().getSimpleName()).build();
+        }
+    }
+
+    @Provider
+    public static class WebApplicationExceptionMapper implements ExceptionMapper<WebApplicationException> {
+
+        @Override
+        public Response toResponse(WebApplicationException exception) {
+            Response r = exception.getResponse();
+            return Response.status(r.getStatus()).entity("Code:" + r.getStatus() + ":"
+                    + getClass().getSimpleName()).build();
+        }
+    }
+}
diff --git a/examples/exception-mapping/src/test/java/org/glassfish/jersey/examples/exception/ExceptionMappingFilterTest.java b/examples/exception-mapping/src/test/java/org/glassfish/jersey/examples/exception/ExceptionMappingFilterTest.java
new file mode 100644
index 0000000..d509934
--- /dev/null
+++ b/examples/exception-mapping/src/test/java/org/glassfish/jersey/examples/exception/ExceptionMappingFilterTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.exception;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.glassfish.jersey.examples.exception.ExceptionResource.MyResponseFilter;
+import static org.glassfish.jersey.examples.exception.Exceptions.MyExceptionMapper;
+import static org.glassfish.jersey.examples.exception.Exceptions.MySubExceptionMapper;
+import static org.glassfish.jersey.examples.exception.Exceptions.WebApplicationExceptionMapper;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * ExceptionMappingFilterTest class.
+ *
+ * @author Santiago.PericasGeertsen at oracle.com
+ */
+public class ExceptionMappingFilterTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        // mvn test -Djersey.test.containerFactory=org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory
+        // mvn test -Djersey.test.containerFactory=org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory
+
+        final ResourceConfig resourceConfig = new ResourceConfig(
+                ExceptionResource.class,
+                MyResponseFilter.class,
+                ExceptionResource.WebApplicationExceptionFilter.class,
+                MyExceptionMapper.class,
+                MySubExceptionMapper.class,
+                WebApplicationExceptionMapper.class);
+
+        return resourceConfig;
+    }
+
+    /**
+     * Instructs request filter to throw a WebApplicationException which must be mapped
+     * to a response and processed through response pipeline.
+     */
+    @Test
+    public void testWebApplicationExceptionInRequestFilter() {
+        WebTarget t = client().target(UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).path("request_exception").build());
+        Response r = t.request("text/plain").post(Entity.text("Request Exception"));
+        assertEquals(200, r.getStatus());
+        final String entity = r.readEntity(String.class);
+        System.out.println("entity = " + entity);
+        assertTrue(entity.contains(MyResponseFilter.class.getSimpleName()));
+    }
+
+    @Test
+    public void testWebApplicationExceptionInResponseFilter() {
+        WebTarget t = client().target(UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).path("response_exception").build());
+        Response r = t.request("text/plain").get();
+        assertEquals(200, r.getStatus());
+        final String entity = r.readEntity(String.class);
+        System.out.println("entity = " + entity);
+        assertTrue(entity.contains(MyResponseFilter.class.getSimpleName()));
+    }
+}
diff --git a/examples/exception-mapping/src/test/java/org/glassfish/jersey/examples/exception/ExceptionMappingTest.java b/examples/exception-mapping/src/test/java/org/glassfish/jersey/examples/exception/ExceptionMappingTest.java
new file mode 100644
index 0000000..3166312
--- /dev/null
+++ b/examples/exception-mapping/src/test/java/org/glassfish/jersey/examples/exception/ExceptionMappingTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.exception;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.glassfish.jersey.examples.exception.ExceptionResource.MyResponseFilter;
+import static org.glassfish.jersey.examples.exception.Exceptions.MyExceptionMapper;
+import static org.glassfish.jersey.examples.exception.Exceptions.MySubExceptionMapper;
+import static org.glassfish.jersey.examples.exception.Exceptions.MySubSubException;
+import static org.glassfish.jersey.examples.exception.Exceptions.WebApplicationExceptionMapper;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * ExceptionMappingTest class.
+ *
+ * @author Santiago.PericasGeertsen at oracle.com
+ */
+public class ExceptionMappingTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        // mvn test -Djersey.test.containerFactory=org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory
+        // mvn test -Djersey.test.containerFactory=org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory
+        return new ResourceConfig(
+                ExceptionResource.class,
+                MyResponseFilter.class,
+                MyExceptionMapper.class,
+                MySubExceptionMapper.class,
+                MySubSubException.class,
+                WebApplicationExceptionMapper.class);
+    }
+
+    /**
+     * Ensure we can access resource with response filter installed.
+     */
+    @Test
+    public void testPingAndFilter() {
+        WebTarget t = client().target(UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).build());
+        Response r = t.request("text/plain").get();
+        assertEquals(200, r.getStatus());
+        assertTrue(r.readEntity(String.class).contains(MyResponseFilter.class.getSimpleName()));
+    }
+
+    /**
+     * No mapper should be used if WebApplicationException already contains a
+     * Response with a non-empty entity.
+     */
+    @Test
+    public void testWebApplicationExceptionWithEntity() {
+        WebTarget t = client().target(UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).path("webapplication_entity").build());
+        Response r = t.request("text/plain").post(Entity.text("Code:200"));
+        assertEquals(200, r.getStatus());
+        final String entity = r.readEntity(String.class);
+        assertTrue(entity.contains("Code:200"));
+        assertTrue(entity.contains(MyResponseFilter.class.getSimpleName()));
+    }
+
+    /**
+     * No mapper should be used if WebApplicationException already contains a
+     * Response with a non-empty entity. Same as last test but using 400 code.
+     */
+    @Test
+    public void testWebApplicationExceptionWithEntity400() {
+        WebTarget t = client().target(UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).path("webapplication_entity").build());
+        Response r = t.request("text/plain").post(Entity.text("Code:400"));
+        assertEquals(400, r.getStatus());
+        final String entity = r.readEntity(String.class);
+        assertTrue(entity.contains("Code:400"));
+        assertTrue(entity.contains(MyResponseFilter.class.getSimpleName()));
+    }
+
+    /**
+     * WebApplicationExceptionMapper should be used if WebApplicationException contains
+     * empty entity.
+     */
+    @Test
+    public void testWebApplicationExceptionUsingMapper() {
+        WebTarget t = client()
+                .target(UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).path("webapplication_noentity").build());
+        Response r = t.request("text/plain").post(Entity.text("Code:200"));
+        assertEquals(200, r.getStatus());
+        String entity = r.readEntity(String.class);
+        assertTrue(entity.contains("Code:200"));
+        assertTrue(entity.contains(WebApplicationExceptionMapper.class.getSimpleName()));
+        assertTrue(entity.contains(MyResponseFilter.class.getSimpleName()));
+    }
+
+    /**
+     * MyExceptionMapper should be used if MyException is thrown.
+     */
+    @Test
+    public void testMyException() {
+        WebTarget t = client().target(UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).path("my").build());
+        Response r = t.request("text/plain").post(Entity.text("Code:200"));
+        assertEquals(200, r.getStatus());
+        String entity = r.readEntity(String.class);
+        assertTrue(entity.contains("Code:200"));
+        assertTrue(entity.contains(MyExceptionMapper.class.getSimpleName()));
+        assertTrue(entity.contains(MyResponseFilter.class.getSimpleName()));
+    }
+
+    /**
+     * MySubExceptionMapper should be used if MySubException is thrown.
+     */
+    @Test
+    public void testMySubException() {
+        WebTarget t = client().target(UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).path("mysub").build());
+        Response r = t.request("text/plain").post(Entity.text("Code:200"));
+        assertEquals(200, r.getStatus());
+        String entity = r.readEntity(String.class);
+        assertTrue(entity.contains("Code:200"));
+        assertTrue(entity.contains(MySubExceptionMapper.class.getSimpleName()));
+        assertTrue(entity.contains(MyResponseFilter.class.getSimpleName()));
+    }
+
+    /**
+     * MySubExceptionMapper should be used if MySubSubException is thrown, given that
+     * there is no mapper for MySubSubException and MySubException is the nearest
+     * super type.
+     */
+    @Test
+    public void testMySubSubException() {
+        WebTarget t = client().target(UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).path("mysub").build());
+        Response r = t.request("text/plain").post(Entity.text("Code:200"));
+        assertEquals(200, r.getStatus());
+        String entity = r.readEntity(String.class);
+        assertTrue(entity.contains("Code:200"));
+        assertTrue(entity.contains(MySubExceptionMapper.class.getSimpleName()));
+        assertTrue(entity.contains(MyResponseFilter.class.getSimpleName()));
+    }
+}
diff --git a/examples/extended-wadl-webapp/README.MD b/examples/extended-wadl-webapp/README.MD
new file mode 100644
index 0000000..d7a59d1
--- /dev/null
+++ b/examples/extended-wadl-webapp/README.MD
@@ -0,0 +1,63 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Extended WADL WebApp Example
+============================
+
+This example demonstrates how to get an extended WADL generated by
+jersey.
+
+Contents
+--------
+
+The description of what's done here you'll find in the [jersey 1
+wiki](https://wikis.oracle.com/display/Jersey/HowToConfigureExtendedWADL).
+
+The difference in configuration against jersey 1.x is in property
+configuring the custom WadlGeneratorConfig. Instead of property key
+'com.sun.jersey.config.property.WadlGeneratorConfig' use the property
+key 'jersey.config.server.wadl.generatorConfig'. Also check pom.xml of
+this sample to see other configuration differences.
+
+Just as a pointer at which files you might have a look:
+
+-   pom.xml
+-   src/main/webapp/WEB-INF/web.xml
+-   src/main/resources/\*
+-   src/main/xsd/schema.xsd
+-   org.glassfish.jersey.examples.extendedwadl.SampleWadlGeneratorConfig
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                        | Resource class   | HTTP methods   | Description
+------------------------------- | ---------------- | -------------- | ---------------------------------------------------------------------------------------------------------------
+**_/items_**                    | ItemsResource    | POST           | Creates a new item and allocates a new unique URI for it, the new URI is returned back in the Location header
+**_/items/{id}_**               | ItemResource     | GET            | Returns an item with the id={id}
+**_/items/{id}/value/{val}_**   | ItemResource     | PUT            | Updates the value of the item with the id={id} with the new value {val}
+
+Running the Example
+-------------------
+
+If you are working with Jersey GlassFish update center module installed
+into your existing GlassFish instance, you will need to follow
+instructions at [the module README file](../../README.html) in order to
+deploy the example.
+
+Otherwise, you can run the example using embedded GlassFish as follows:
+
+You can run the example using Grizzly as follows:
+
+>     mvn clean package exec:java
+
+Get the generated wadl via curl:
+
+>     curl http://localhost:8080/extended-wadl-webapp/application.wadl
+
+Or from a web browser, visit:
+
+>     http://localhost:8080/extended-wadl-webapp/application.wadl
diff --git a/examples/extended-wadl-webapp/pom.xml b/examples/extended-wadl-webapp/pom.xml
new file mode 100644
index 0000000..4c14f2e
--- /dev/null
+++ b/examples/extended-wadl-webapp/pom.xml
@@ -0,0 +1,282 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>extended-wadl-webapp</artifactId>
+    <name>jersey-examples-extended-wadl-webapp</name>
+    <packaging>war</packaging>
+
+    <description>Extended WADL example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+        </dependency>
+        <dependency>
+            <!-- Extended Wadl Config: Dependency to jersey-wadl-doclet is needed here to keep the correct build order. -->
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-wadl-doclet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-servlet</artifactId>
+        </dependency>
+
+        <!-- Pax-Exam and JUnit dependencies -->
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-junit4</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-container-forked</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-junit-extender-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.tinybundles</groupId>
+            <artifactId>tinybundles</artifactId>
+            <scope>test</scope>
+            <version>2.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-link-mvn</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- OSGi runtime - Felix -->
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+
+        <!-- logging -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>1.6.4</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.codehaus.jettison</groupId>
+            <artifactId>jettison</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.hk2.external</groupId>
+            <artifactId>aopalliance-repackaged</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Extended Wadl Config: this plugin generates the jaxb beans from xsd schemas -->
+            <plugin>
+                <groupId>org.jvnet.jaxb2.maven2</groupId>
+                <artifactId>maven-jaxb2-plugin</artifactId>
+                <version>0.8.0</version>
+                <executions>
+                    <execution>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <generatePackage>org.glassfish.jersey.examples.extendedwadl</generatePackage>
+                    <schemaDirectory>src/main/xsd</schemaDirectory>
+                    <!--<includeSchemas>-->
+                    <!--<includeSchema>**/*.xsd</includeSchema>-->
+                    <!--</includeSchemas>-->
+                    <extension>true</extension>
+                    <strict>false</strict>
+                    <verbose>true</verbose>
+                </configuration>
+            </plugin>
+
+            <!-- Extended Wadl Config: javadoc plugin generates resourcedoc.xml during the compilation using doclet.
+            Generated file is then used during runtime to attach javadoc information to wadl. -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>javadoc</goal>
+                        </goals>
+                        <phase>process-classes</phase>
+                    </execution>
+                </executions>
+                <configuration>
+                    <encoding>UTF-8</encoding>
+                    <verbose>false</verbose>
+                    <show>public</show>
+                    <subpackages>org.glassfish.jersey.examples.extendedwadl</subpackages>
+                    <doclet>org.glassfish.jersey.wadl.doclet.ResourceDoclet</doclet>
+                    <docletPath>${path.separator}${project.build.outputDirectory}</docletPath>
+                    <docletArtifacts>
+                        <!-- jersey doclet generator -->
+                        <docletArtifact>
+                            <groupId>org.glassfish.jersey.ext</groupId>
+                            <artifactId>jersey-wadl-doclet</artifactId>
+                            <version>${project.version}</version>
+                        </docletArtifact>
+                        <!--
+                            Also specify jersey and xerces as doclet artifacts as the ResourceDoclet
+                            uses classes provided by them to generate the resourcedoc.
+                         -->
+                        <docletArtifact>
+                            <groupId>org.glassfish.jersey.core</groupId>
+                            <artifactId>jersey-server</artifactId>
+                            <version>${project.version}</version>
+                        </docletArtifact>
+                        <docletArtifact>
+                            <groupId>xerces</groupId>
+                            <artifactId>xercesImpl</artifactId>
+                            <version>${xerces.version}</version>
+                        </docletArtifact>
+                    </docletArtifacts>
+                    <!-- the following option is required as a work around for
+                         version 2.5 of the javadoc plugin which will be used
+                         by a maven version > 2.0.9 -->
+                    <useStandardDocletOptions>false</useStandardDocletOptions>
+                    <additionalparam>-output ${project.build.outputDirectory}/resourcedoc.xml</additionalparam>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkMode>always</forkMode>
+                    <enableAssertions>false</enableAssertions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.ops4j.pax.exam</groupId>
+                <artifactId>maven-paxexam-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.extendedwadl.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>transform-gf-pom</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>transform</goal>
+                                </goals>
+                                <configuration>
+                                    <transformationSets>
+                                        <transformationSet>
+                                            <dir>${project.build.directory}/gf-pom-file</dir>
+                                            <includes>
+                                                <include>pom.xml</include>
+                                            </includes>
+                                            <stylesheet>src/main/xslt/gf.xsl</stylesheet>
+                                            <outputDir>${project.build.directory}/gf-pom-file</outputDir>
+                                        </transformationSet>
+                                    </transformationSets>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/App.java b/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/App.java
new file mode 100644
index 0000000..b552d3a
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/App.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.extendedwadl;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.examples.extendedwadl.resources.MyApplication;
+import org.glassfish.jersey.grizzly2.servlet.GrizzlyWebContainerFactory;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.servlet.ServletContainer;
+import org.glassfish.jersey.servlet.ServletProperties;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+
+/**
+ * Runner for the Jersey extended-wadl-webapp sample.
+ *
+ * @author Miroslav Fuksa
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/extended-wadl-webapp");
+    public static final String ROOT_PATH = "/application.wadl";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("Extended WADL web application example");
+
+            Map<String, String> initParams = new HashMap<>();
+
+            initParams.put(
+                    ServletProperties.JAXRS_APPLICATION_CLASS,
+                    MyApplication.class.getName());
+
+            initParams.put(ServerProperties.WADL_GENERATOR_CONFIG, "org.glassfish.jersey.examples.extendedwadl"
+                    + ".SampleWadlGeneratorConfig");
+
+            final HttpServer server = GrizzlyWebContainerFactory.create(BASE_URI, ServletContainer.class, initParams);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+
+            System.out.println(String.format("Application started.%nTry out %s%s%nStop the application using CTRL+C",
+                    BASE_URI, ROOT_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+}
diff --git a/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/SampleWadlGeneratorConfig.java b/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/SampleWadlGeneratorConfig.java
new file mode 100644
index 0000000..dd70bde
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/SampleWadlGeneratorConfig.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.extendedwadl;
+
+import java.util.List;
+
+import org.glassfish.jersey.server.wadl.config.WadlGeneratorConfig;
+import org.glassfish.jersey.server.wadl.config.WadlGeneratorDescription;
+import org.glassfish.jersey.server.wadl.internal.generators.WadlGeneratorApplicationDoc;
+import org.glassfish.jersey.server.wadl.internal.generators.WadlGeneratorGrammarsSupport;
+import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.WadlGeneratorResourceDocSupport;
+
+/**
+ * This subclass of {@link WadlGeneratorConfig} defines/configures {@link org.glassfish.jersey.server.wadl.WadlGenerator}s
+ * to be used for generating WADL.
+ *
+ * @author Martin Grotzke (martin.grotzke@freiheit.com)
+ */
+public class SampleWadlGeneratorConfig extends WadlGeneratorConfig {
+
+    @Override
+    public List<WadlGeneratorDescription> configure() {
+        return generator(WadlGeneratorApplicationDoc.class)
+                .prop("applicationDocsStream", "application-doc.xml")
+                .generator(WadlGeneratorGrammarsSupport.class)
+                .prop("grammarsStream", "application-grammars.xml")
+                .prop("overrideGrammars", true)
+                .generator(WadlGeneratorResourceDocSupport.class)
+                .prop("resourceDocStream", "resourcedoc.xml")
+                .descriptions();
+    }
+
+}
diff --git a/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/resources/ItemResource.java b/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/resources/ItemResource.java
new file mode 100644
index 0000000..97ae2ee
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/resources/ItemResource.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.extendedwadl.resources;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.glassfish.jersey.examples.extendedwadl.Item;
+
+import org.codehaus.jettison.json.JSONArray;
+
+/**
+ * This resource is used to manage a single item.
+ *
+ * @author Martin Grotzke (martin.grotzke@freiheit.com)
+ */
+public class ItemResource {
+
+    private final Item _item;
+
+    public ItemResource(Item item) {
+        _item = item;
+    }
+
+    /**
+     * Typically returns the item if it exists. Please be aware that this method is extremely
+     * expensive, so we can't guarantee that getting items is all the time possible.
+     *
+     * @response.representation.200.qname {http://www.example.com}item
+     * @response.representation.200.mediaType application/xml
+     * @response.representation.200.doc This is the representation returned by default
+     *                                  (if we have an even number of millis since 1970...:)
+     * @response.representation.200.example {@link org.glassfish.jersey.examples.extendedwadl.util.Examples#SAMPLE_ITEM}
+     *
+     * @response.representation.503.mediaType text/plain
+     * @response.representation.503.example You'll get some explanation why this service is not available
+     *
+     * @return the requested item if this service is available, otherwise a 503.
+     */
+    @GET
+    @Produces({"application/xml", "text/plain"})
+    public Response getItem() {
+        if (System.currentTimeMillis() % 2 == 0) {
+            return Response.status(Status.SERVICE_UNAVAILABLE)
+                    .entity("Sorry, but right now we can't process this request,"
+                            + " try again an odd number of milliseconds later, please :)")
+                    .type(MediaType.TEXT_PLAIN).build();
+        }
+        return Response.ok(_item).build();
+    }
+
+    /**
+     * Tries hard to return the item if it exists. If "Try-Hard" header is set to "true", the method is guaranteed to always
+     * complete successfully if the item exists.
+     *
+     * @request.param {@name Try-Hard}
+     *                  {@style header}
+     *                  {@type {http://www.w3.org/2001/XMLSchema}string}
+     *                  {@doc If set to "true", the call will always succeed provided the item exists.}
+     *
+     * @response.representation.200.qname {http://www.example.com}item
+     * @response.representation.200.mediaType application/xml
+     * @response.representation.200.doc This is the representation returned by default
+     *                                  (if we have an even number of millis since 1970...:)
+     * @response.representation.200.example {@link org.glassfish.jersey.examples.extendedwadl.util.Examples#SAMPLE_ITEM}
+     *
+     * @response.representation.503.mediaType text/plain
+     * @response.representation.503.example You'll get some explanation why this service is not available.
+     *
+     * @return the requested item if it exists and the "Try-Hard" header is set to "true", otherwise a 503.
+     */
+    // Method added to reproduce OWLS-24243 issue.
+    @GET
+    @Produces({"application/xml", "text/plain"})
+    @Path("try-hard")
+    public Response getItem(@HeaderParam("Try-Hard") boolean tryHard) {
+        if (!tryHard) {
+            return Response.status(Status.SERVICE_UNAVAILABLE)
+                    .entity("Sorry, but right now we can't process this request,"
+                            + " try again and set the \"Try-Hard\" header to \"true\"")
+                    .type(MediaType.TEXT_PLAIN)
+                    .build();
+        }
+        return Response.ok(_item).build();
+    }
+
+
+    /**
+     * Returns the item if existing.
+     *
+     * @response.representation.200.mediaType application/json
+     * @response.representation.200.example ["myValue"]
+     *
+     * @return the requested item.
+     */
+    @GET
+    @Produces({"application/json"})
+    public JSONArray getItemAsJSON() {
+        final JSONArray result = new JSONArray();
+        result.put(_item.getValue());
+        return result;
+    }
+
+    /**
+     * Update the value property of the current item.
+     *
+     * @param value the new value to set
+     */
+    @Path("value/{value}")
+    @PUT
+    @Consumes({"application/xml"})
+    public void updateItemValue(@PathParam("value") String value) {
+        _item.setValue(value);
+    }
+
+}
diff --git a/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/resources/ItemsResource.java b/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/resources/ItemsResource.java
new file mode 100644
index 0000000..b1b7f51
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/resources/ItemsResource.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.extendedwadl.resources;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.examples.extendedwadl.Item;
+
+/**
+ * This is the root resource for managing items.
+ *
+ * @author Martin Grotzke (martin.grotzke@freiheit.com)
+ */
+@Singleton
+@Path("items")
+public class ItemsResource {
+
+    @Context
+    UriInfo _uriInfo;
+    private final AtomicInteger _sequence;
+    private final Map<Integer, Item> _repository;
+
+    public ItemsResource() {
+        _sequence = new AtomicInteger();
+        _repository = new HashMap<>();
+    }
+
+    /**
+     * Get an item resource for an item from the list of managed items based on the assigned id extracted from the path parameter.
+     *
+     * @param id The ID of the item to retrieve.
+     * @return respective items resource.
+     *
+     */
+    @Path("{id}")
+    public ItemResource getItem(@PathParam("id") final Integer id) {
+        final Item item = _repository.get(id);
+        if (item == null) {
+            throw new NotFoundException(Response.status(Response.Status.NOT_FOUND).entity("Item with id " + id + " does not "
+                    + "exist!").build());
+        }
+
+        return new ItemResource(item);
+    }
+
+    /**
+     * Add a new item to the list of managed items. The item will get assigned an id,
+     * the resource where the item is available will be returned in the location header.
+     *
+     * @param item The item to create.
+     *
+     * @request.representation.qname {http://www.example.com}item
+     * @request.representation.mediaType application/xml
+     * @request.representation.example {@link org.glassfish.jersey.examples.extendedwadl.util.Examples#SAMPLE_ITEM}
+     *
+     * @response.param {@name Location}
+     *                  {@style header}
+     *                  {@type {http://www.w3.org/2001/XMLSchema}anyURI}
+     *                  {@doc The URI where the created item is accessable.}
+     *
+     * @return The response with the status code and the location header.
+     *
+     */
+    @POST
+    @Consumes({"application/xml"})
+    public Response createItem(Item item) {
+        final Integer id = _sequence.incrementAndGet();
+        _repository.put(id, item);
+        return Response.created(
+                _uriInfo.getAbsolutePathBuilder().clone().path(id.toString()).build())
+                .build();
+    }
+
+}
diff --git a/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/resources/MyApplication.java b/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/resources/MyApplication.java
new file mode 100644
index 0000000..93a7289
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/resources/MyApplication.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.extendedwadl.resources;
+
+import org.glassfish.jersey.examples.extendedwadl.SampleWadlGeneratorConfig;
+import org.glassfish.jersey.examples.extendedwadl.util.Examples;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * @author Jonathan Benoit
+ */
+public class MyApplication extends ResourceConfig {
+    public MyApplication() {
+        super(ItemResource.class, ItemsResource.class, Examples.class, SampleWadlGeneratorConfig.class);
+    }
+}
diff --git a/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/util/Examples.java b/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/util/Examples.java
new file mode 100644
index 0000000..702ee0b
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/main/java/org/glassfish/jersey/examples/extendedwadl/util/Examples.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.extendedwadl.util;
+
+import org.glassfish.jersey.examples.extendedwadl.Item;
+
+/**
+ * This class provides example representations that can be used for wadl docs.<br>
+ * Created on: Jul 20, 2008<br>
+ *
+ * @author Martin Grotzke (martin.grotzke@freiheit.com)
+ */
+public class Examples {
+
+    public static final Item SAMPLE_ITEM = new Item();
+
+    static {
+        SAMPLE_ITEM.setValue("foo");
+    }
+
+}
diff --git a/examples/extended-wadl-webapp/src/main/resources/application-doc.xml b/examples/extended-wadl-webapp/src/main/resources/application-doc.xml
new file mode 100644
index 0000000..9b1ecc8
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/main/resources/application-doc.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<applicationDocs targetNamespace="http://wadl.dev.java.net/2009/02">
+
+    <doc xml:lang="en" title="The doc for your API">
+        This is a paragraph that is added to the start of the generated application.wadl
+    </doc>
+
+    <doc xml:lang="de" title="Die Dokumentation fuer Ihre API">
+        Dies in ein Abschnitt, der dem Beginn der generierte application.wadl hinzugefügt wird - in deutscher Sprache.
+    </doc>
+</applicationDocs>
diff --git a/examples/extended-wadl-webapp/src/main/resources/application-grammars.xml b/examples/extended-wadl-webapp/src/main/resources/application-grammars.xml
new file mode 100644
index 0000000..6827aa0
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/main/resources/application-grammars.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<grammars xmlns="http://wadl.dev.java.net/2009/02"
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+    xmlns:xi="http://www.w3.org/1999/XML/xinclude">
+    <include href="schema.xsd" />
+</grammars>
diff --git a/examples/extended-wadl-webapp/src/main/webapp/WEB-INF/glassfish-web.xml b/examples/extended-wadl-webapp/src/main/webapp/WEB-INF/glassfish-web.xml
new file mode 100644
index 0000000..8ddb1e4
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/main/webapp/WEB-INF/glassfish-web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
+<glassfish-web-app error-url="">
+    <context-root>/extended-wadl-webapp</context-root>
+    <class-loader delegate="true" />
+    <jsp-config>
+        <property name="classdebuginfo" value="true">
+            <description>Enable debug info compilation in the generated servlet class</description>
+        </property>
+        <property name="keepgenerated" value="true">
+            <description>Keep a copy of the generated servlet class' java code.</description>
+        </property>
+        <property name="mappedfile" value="true">
+            <description>Maintain a one-to-one correspondence between static content and the generated servlet class' java code</description>
+        </property>
+    </jsp-config>
+</glassfish-web-app>
diff --git a/examples/extended-wadl-webapp/src/main/webapp/WEB-INF/web.xml b/examples/extended-wadl-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..ccb2ede
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.examples.extendedwadl.resources.MyApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.examples.extendedwadl.resources.MyApplication</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.server.wadl.generatorConfig</param-name>
+            <param-value>org.glassfish.jersey.examples.extendedwadl.SampleWadlGeneratorConfig</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.examples.extendedwadl.resources.MyApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/examples/extended-wadl-webapp/src/main/xsd/schema.xsd b/examples/extended-wadl-webapp/src/main/xsd/schema.xsd
new file mode 100644
index 0000000..b08d2bc
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/main/xsd/schema.xsd
@@ -0,0 +1,35 @@
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<schema
+        xmlns="http://www.w3.org/2001/XMLSchema"
+        xmlns:example="http://www.example.com"
+        targetNamespace="http://www.example.com"
+        >
+
+    <element name="items" type="example:items"/>
+
+    <complexType name="items">
+        <sequence>
+            <element name="item" type="string" minOccurs="0" maxOccurs="unbounded"/>
+        </sequence>
+    </complexType>
+
+    <element name="item">
+        <complexType>
+            <simpleContent>
+                <extension base="string"></extension>
+            </simpleContent>
+        </complexType>
+    </element>
+
+</schema>
diff --git a/examples/extended-wadl-webapp/src/main/xslt/gf.xsl b/examples/extended-wadl-webapp/src/main/xslt/gf.xsl
new file mode 100644
index 0000000..39ad615
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/main/xslt/gf.xsl
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:pom="http://maven.apache.org/POM/4.0.0"
+                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+                version="1.0">
+
+    <xsl:output method="xml" indent="yes" />
+
+    <xsl:template match="/">
+        <xsl:apply-templates />
+    </xsl:template>
+
+    <xsl:template
+            match="pom:plugins/pom:plugin[pom:artifactId='maven-surefire-plugin']/pom:configuration">
+        <xsl:copy>
+            <xsl:apply-templates />
+            <excludes>
+                <exclude>**/ExtendedWadlWebappOsgiTest.java</exclude>
+            </excludes>
+        </xsl:copy>
+    </xsl:template>
+
+    <xsl:template match="comment()">
+        <xsl:comment>
+            <xsl:value-of select="." />
+        </xsl:comment>
+    </xsl:template>
+
+    <xsl:template match="*">
+        <xsl:copy>
+            <xsl:apply-templates />
+        </xsl:copy>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/examples/extended-wadl-webapp/src/test/java/org/glassfish/jersey/examples/extendedwadl/ExtendedWadlWebappOsgiTest.java b/examples/extended-wadl-webapp/src/test/java/org/glassfish/jersey/examples/extendedwadl/ExtendedWadlWebappOsgiTest.java
new file mode 100644
index 0000000..2853bf8
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/test/java/org/glassfish/jersey/examples/extendedwadl/ExtendedWadlWebappOsgiTest.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.extendedwadl;
+
+import java.io.ByteArrayInputStream;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.security.AccessController;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import javax.inject.Inject;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+
+import org.glassfish.jersey.examples.extendedwadl.resources.ItemResource;
+import org.glassfish.jersey.examples.extendedwadl.resources.ItemsResource;
+import org.glassfish.jersey.examples.extendedwadl.resources.MyApplication;
+import org.glassfish.jersey.examples.extendedwadl.util.Examples;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.internal.util.SimpleNamespaceResolver;
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.wadl.internal.WadlUtils;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.w3c.dom.Document;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.provision;
+import static org.ops4j.pax.exam.CoreOptions.systemPackage;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.bundle;
+
+/**
+ * @author Naresh
+ * @author Miroslav Fuksa
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@RunWith(PaxExam.class)
+public class ExtendedWadlWebappOsgiTest {
+
+    @Inject
+    BundleContext bundleContext;
+
+    private static final Logger LOGGER = Logger.getLogger(ExtendedWadlWebappOsgiTest.class.getName());
+
+    // we want to re-use the port number as set for Jersey test container to avoid CT port number clashes
+    private static final String testContainerPort = System.getProperty(TestProperties.CONTAINER_PORT);
+    private static final int testPort = testContainerPort == null
+            ? TestProperties.DEFAULT_CONTAINER_PORT : Integer.parseInt(testContainerPort);
+
+    private static final URI baseUri = UriBuilder
+            .fromUri("http://localhost")
+            .port(testPort)
+            .path("extended-wadl-webapp").build();
+
+    @Configuration
+    public static Option[] configuration() {
+        List<Option> options = Arrays.asList(options(
+                // systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("FINEST"),
+                systemProperty("org.osgi.framework.system.packages.extra").value("javax.annotation"),
+
+                // javax.annotation must go first!
+                mavenBundle().groupId("javax.annotation").artifactId("javax.annotation-api").versionAsInProject(),
+
+                junitBundles(),
+
+                mavenBundle("org.ops4j.pax.url", "pax-url-mvn"),
+
+                // HK2
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("hk2-api").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("osgi-resource-locator").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("hk2-locator").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("hk2-utils").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2.external").artifactId("javax.inject").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2.external").artifactId("aopalliance-repackaged").versionAsInProject(),
+                mavenBundle().groupId("org.javassist").artifactId("javassist").versionAsInProject(),
+
+                // JAX-RS API
+                mavenBundle().groupId("javax.ws.rs").artifactId("javax.ws.rs-api").versionAsInProject(),
+
+                // Jersey bundles
+                mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-common").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-jaxb").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-server").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-client").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.inject").artifactId("jersey-hk2").versionAsInProject(),
+
+                // jettison
+                mavenBundle().groupId("org.codehaus.jettison").artifactId("jettison").versionAsInProject(),
+
+                // validation
+                mavenBundle().groupId("javax.validation").artifactId("validation-api").versionAsInProject(),
+
+                // Grizzly
+                systemPackage("sun.misc"),       // required by grizzly-framework
+                mavenBundle().groupId("org.glassfish.grizzly").artifactId("grizzly-framework").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.grizzly").artifactId("grizzly-http").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.grizzly").artifactId("grizzly-http-server").versionAsInProject(),
+
+                // Jersey Grizzly
+                mavenBundle().groupId("org.glassfish.jersey.containers").artifactId("jersey-container-grizzly2-http")
+                        .versionAsInProject(),
+
+                // tinybundles + required dependencies
+                mavenBundle().groupId("org.ops4j.pax.tinybundles").artifactId("tinybundles").versionAsInProject(),
+                mavenBundle().groupId("biz.aQute.bnd").artifactId("bndlib").versionAsInProject(),
+
+                // create ad-hoc bundle
+                provision(
+                        bundle()
+                                .add(MyApplication.class)
+                                .add(ItemResource.class)
+                                .add(ItemsResource.class)
+                                .add(Examples.class)
+                                .add(SampleWadlGeneratorConfig.class)
+                                .add(Item.class)
+                                .add(Items.class)
+                                .add(ObjectFactory.class)
+                                .add("application-doc.xml", ClassLoader.getSystemResourceAsStream("application-doc.xml"))
+                                .add("application-grammars.xml",
+                                        ClassLoader.getSystemResourceAsStream("application-grammars.xml"))
+                                .add("resourcedoc.xml", ClassLoader.getSystemResourceAsStream("resourcedoc.xml"))
+                                .set("Export-Package",
+                                        MyApplication.class.getPackage().getName() + "," + SampleWadlGeneratorConfig.class
+                                                .getPackage().getName())
+                                .set("DynamicImport-Package", "*")
+                                .set("Bundle-SymbolicName", "webapp").build())
+        ));
+        final String localRepository = AccessController.doPrivileged(PropertiesHelper.getSystemProperty("localRepository"));
+        if (localRepository != null) {
+            options = new ArrayList<>(options);
+            options.add(systemProperty("org.ops4j.pax.url.mvn.localRepository").value(localRepository));
+        }
+        return options.toArray(new Option[options.size()]);
+    }
+
+    private ResourceConfig createResourceConfig() {
+        final ResourceConfig resourceConfig = new ResourceConfig(new MyApplication().getClasses());
+        resourceConfig.property(ServerProperties.WADL_GENERATOR_CONFIG, SampleWadlGeneratorConfig.class.getName());
+
+        return resourceConfig;
+    }
+
+    /**
+     * Test checks that the WADL generated using the WadlGenerator api doesn't
+     * contain the expected text.
+     *
+     * @throws java.lang.Exception in case of a test error.
+     */
+    @Test
+    public void testExtendedWadl() throws Exception {
+
+        // TODO - temporary workaround
+        // This is a workaround related to issue JERSEY-2093; grizzly (1.9.5) needs to have the correct context
+        // class loader set
+        ClassLoader myClassLoader = this.getClass().getClassLoader();
+
+        for (Bundle bundle : bundleContext.getBundles()) {
+            if ("webapp".equals(bundle.getSymbolicName())) {
+                myClassLoader = bundle.loadClass("org.glassfish.jersey.examples.extendedwadl.resources.MyApplication")
+                        .getClassLoader();
+                break;
+            }
+        }
+
+        Thread.currentThread().setContextClassLoader(myClassLoader);
+        // END of workaround - the entire block can be deleted after grizzly is updated to recent version
+
+        // List all the OSGi bundles
+        StringBuilder sb = new StringBuilder();
+        sb.append("-- Bundle list -- \n");
+        for (Bundle b : bundleContext.getBundles()) {
+            sb.append(String.format("%1$5s", "[" + b.getBundleId() + "]")).append(" ")
+                    .append(String.format("%1$-70s", b.getSymbolicName())).append(" | ")
+                    .append(String.format("%1$-20s", b.getVersion())).append(" |");
+            try {
+                b.start();
+                sb.append(" STARTED  | ");
+            } catch (BundleException e) {
+                sb.append(" *FAILED* | ").append(e.getMessage());
+            }
+            sb.append(b.getLocation()).append("\n");
+        }
+        sb.append("-- \n\n");
+        LOGGER.fine(sb.toString());
+
+        final ResourceConfig resourceConfig = createResourceConfig();
+        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+        final Client client = ClientBuilder.newClient();
+        final Response response = client.target(baseUri).path("application.wadl").request(MediaTypes.WADL_TYPE).buildGet()
+                .invoke();
+
+        String wadl = response.readEntity(String.class);
+        LOGGER.info("RESULT = " + wadl);
+
+        assertTrue("Generated wadl is of null length", !wadl.isEmpty());
+        assertTrue("Generated wadl doesn't contain the expected text",
+                wadl.contains("This is a paragraph"));
+
+        assertFalse(wadl.contains("application.wadl/xsd0.xsd"));
+
+        server.shutdownNow();
+    }
+
+    @Test
+    public void testWadlOptionsMethod() throws Exception {
+        // TODO - temporary workaround
+        // This is a workaround related to issue JERSEY-2093; grizzly (1.9.5) needs to have the correct context
+        // class loader set
+        ClassLoader myClassLoader = this.getClass().getClassLoader();
+        for (Bundle bundle : bundleContext.getBundles()) {
+            if ("webapp".equals(bundle.getSymbolicName())) {
+                myClassLoader = bundle.loadClass("org.glassfish.jersey.examples.extendedwadl.resources.MyApplication")
+                        .getClassLoader();
+                break;
+            }
+        }
+
+        Thread.currentThread().setContextClassLoader(myClassLoader);
+        // END of workaround; The entire block can be removed after grizzly is migrated to more recent version
+
+        final ResourceConfig resourceConfig = createResourceConfig();
+        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+        final Client client = ClientBuilder.newClient();
+
+        String wadl = client.target(baseUri).path("items").queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true")
+                .request(MediaTypes.WADL_TYPE).options(String.class);
+
+        assertTrue("Generated wadl is of null length", !wadl.isEmpty());
+        assertTrue("Generated wadl doesn't contain the expected text",
+                wadl.contains("This is a paragraph"));
+
+        checkWadl(wadl, baseUri);
+
+        server.shutdownNow();
+    }
+
+    private void checkWadl(String wadl, URI baseUri) throws Exception {
+        DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+        bf.setNamespaceAware(true);
+        bf.setValidating(false);
+        DocumentBuilder b = bf.newDocumentBuilder();
+        Document document = b.parse(new ByteArrayInputStream(wadl.getBytes(Charset.forName("UTF-8"))));
+        XPath xp = XPathFactory.newInstance().newXPath();
+        xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+        String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", document, XPathConstants.STRING);
+        assertEquals(baseUri.toString(), val.endsWith("/") ? val.substring(0, val.length() - 1) : val);
+        val = (String) xp.evaluate("count(//wadl:resource)", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of resource elements.", val, "4");
+        val = (String) xp.evaluate("count(//wadl:resource[@path='items'])", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of resource elements with 'items' path.", "1", val);
+        val = (String) xp.evaluate("count(//wadl:resource[@path='{id}'])", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of resource elements with '{id}' path.", "1", val);
+        val = (String) xp.evaluate("count(//wadl:resource[@path='try-hard'])", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of resource elements with 'try-hard' path.", "1", val);
+        val = (String) xp.evaluate("count(//wadl:resource[@path='value/{value}'])", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of resource elements with 'value/{value}' path.", "1", val);
+
+        val = (String) xp.evaluate("count(//wadl:resource[@path='{id}']/wadl:method)", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of methods in resource element with '{id}' path.", "2", val);
+        val = (String) xp.evaluate("count(//wadl:resource[@path='{id}']/wadl:method[@id='getItem']"
+                        + "/wadl:doc[contains(., 'Typically returns the item if it exists.')])",
+                document, XPathConstants.STRING);
+        assertEquals("Unexpected documentation of getItem resource method at '{id}' path", "1", val);
+
+        val = (String) xp.evaluate("count(//wadl:resource[@path='try-hard']/wadl:method)", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of methods in resource element with 'try-hard' path.", "1", val);
+        val = (String) xp.evaluate("count(//wadl:resource[@path='try-hard']/wadl:method[@id='getItem']"
+                        + "/wadl:doc[contains(., 'Tries hard to return the item if it exists.')])",
+                document, XPathConstants.STRING);
+        assertEquals("Unexpected documentation of getItem resource method at 'try-hard' path", "1", val);
+
+        val = (String) xp.evaluate("count(//wadl:resource[@path='items']/wadl:method)", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of methods in resource element with 'items' path.", "4", val);
+        val = (String) xp.evaluate("count(//wadl:resource[@path='value/{value}']/wadl:method)", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of methods in resource element with 'value/{value}' path.", "1", val);
+
+        val = (String) xp.evaluate("count(//wadl:application/wadl:doc)", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of doc elements in application element.", "3", val);
+    }
+}
diff --git a/examples/extended-wadl-webapp/src/test/java/org/glassfish/jersey/examples/extendedwadl/ExtendedWadlWebappTest.java b/examples/extended-wadl-webapp/src/test/java/org/glassfish/jersey/examples/extendedwadl/ExtendedWadlWebappTest.java
new file mode 100644
index 0000000..e2050b1
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/test/java/org/glassfish/jersey/examples/extendedwadl/ExtendedWadlWebappTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.extendedwadl;
+
+import java.io.ByteArrayInputStream;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.util.logging.Logger;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Response;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+
+import org.glassfish.jersey.examples.extendedwadl.resources.MyApplication;
+import org.glassfish.jersey.internal.util.SimpleNamespaceResolver;
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.server.wadl.internal.WadlUtils;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import org.w3c.dom.Document;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * WADL extension example tests.
+ *
+ * @author Naresh
+ * @author Miroslav Fuksa
+ */
+public class ExtendedWadlWebappTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(ExtendedWadlWebappTest.class.getName());
+
+    @Override
+    protected DeploymentContext configureDeployment() {
+        final ResourceConfig resourceConfig = new ResourceConfig(new MyApplication().getClasses());
+        resourceConfig.property(ServerProperties.WADL_GENERATOR_CONFIG, "org.glassfish.jersey.examples.extendedwadl"
+                + ".SampleWadlGeneratorConfig");
+
+        final Resource.Builder resourceBuilder = Resource.builder();
+        resourceBuilder.name("resource-programmatic").path("programmatic").addMethod("GET")
+
+                .handledBy(new ProgrammaticResource());
+        resourceConfig.registerResources(resourceBuilder.build());
+        return DeploymentContext.builder(resourceConfig).contextPath("extended-wadl-webapp").build();
+    }
+
+    /**
+     * Test checks that the WADL generated using the WadlGenerator api doesn't
+     * contain the expected text.
+     *
+     * @throws java.lang.Exception in case of test error.
+     */
+    @Test
+    public void testExtendedWadl() throws Exception {
+        String wadl = target().path("application.wadl")
+                .queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true").request(MediaTypes.WADL_TYPE).get(String.class);
+
+        LOGGER.fine(wadl);
+        assertTrue("Generated wadl is of null length", !wadl.isEmpty());
+        assertTrue("Generated wadl doesn't contain the expected text",
+                wadl.contains("This is a paragraph"));
+
+        assertFalse(wadl.contains("application.wadl/xsd0.xsd"));
+    }
+
+    @Test
+    public void testWadlOptionsMethod() throws Exception {
+        String wadl = target().path("items")
+                .queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true").request(MediaTypes.WADL_TYPE).options(String.class);
+
+        LOGGER.fine(wadl);
+        assertTrue("Generated wadl is of null length", !wadl.isEmpty());
+        assertTrue("Generated wadl doesn't contain the expected text",
+                wadl.contains("This is a paragraph"));
+        checkWadl(wadl, getBaseUri());
+    }
+
+    /**
+     * Programmatic resource class javadoc.
+     */
+    private static class ProgrammaticResource implements Inflector<ContainerRequestContext, Response> {
+        @Override
+        public Response apply(ContainerRequestContext data) {
+            return Response.ok("programmatic").build();
+        }
+    }
+
+    private void checkWadl(String wadl, URI baseUri) throws Exception {
+        DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+        bf.setNamespaceAware(true);
+        bf.setValidating(false);
+//        if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) {
+//            bf.setXIncludeAware(false);
+//        }
+        DocumentBuilder b = bf.newDocumentBuilder();
+        Document document = b.parse(new ByteArrayInputStream(wadl.getBytes(Charset.forName("UTF-8"))));
+        XPath xp = XPathFactory.newInstance().newXPath();
+        xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+        String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", document, XPathConstants.STRING);
+        assertEquals(baseUri.toString(), val.endsWith("/") ? val.substring(0, val.length() - 1) : val);
+        val = (String) xp.evaluate("count(//wadl:resource)", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of resource elements.", val, "4");
+        val = (String) xp.evaluate("count(//wadl:resource[@path='items'])", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of resource elements with 'items' path.", "1", val);
+        val = (String) xp.evaluate("count(//wadl:resource[@path='{id}'])", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of resource elements with '{id}' path.", "1", val);
+        val = (String) xp.evaluate("count(//wadl:resource[@path='try-hard'])", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of resource elements with 'try-hard' path.", "1", val);
+        val = (String) xp.evaluate("count(//wadl:resource[@path='value/{value}'])", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of resource elements with 'value/{value}' path.", "1", val);
+
+        val = (String) xp.evaluate("count(//wadl:resource[@path='{id}']/wadl:method)", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of methods in resource element with '{id}' path.", "2", val);
+        val = (String) xp.evaluate("count(//wadl:resource[@path='{id}']/wadl:method[@id='getItem']"
+                        + "/wadl:doc[contains(., 'Typically returns the item if it exists.')])",
+                document, XPathConstants.STRING);
+        assertEquals("Unexpected documentation of getItem resource method at '{id}' path", "1", val);
+
+        val = (String) xp.evaluate("count(//wadl:resource[@path='try-hard']/wadl:method)", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of methods in resource element with 'try-hard' path.", "1", val);
+        val = (String) xp.evaluate("count(//wadl:resource[@path='try-hard']/wadl:method[@id='getItem']"
+                        + "/wadl:doc[contains(., 'Tries hard to return the item if it exists.')])",
+                document, XPathConstants.STRING);
+        assertEquals("Unexpected documentation of getItem resource method at 'try-hard' path", "1", val);
+
+        val = (String) xp.evaluate("count(//wadl:resource[@path='items']/wadl:method)", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of methods in resource element with 'items' path.", "4", val);
+        val = (String) xp.evaluate("count(//wadl:resource[@path='value/{value}']/wadl:method)", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of methods in resource element with 'value/{value}' path.", "1", val);
+
+        val = (String) xp.evaluate("count(//wadl:application/wadl:doc)", document, XPathConstants.STRING);
+        assertEquals("Unexpected number of doc elements in application element.", "3", val);
+    }
+}
diff --git a/examples/extended-wadl-webapp/src/test/resources/log4j.properties b/examples/extended-wadl-webapp/src/test/resources/log4j.properties
new file mode 100644
index 0000000..01fd9e5
--- /dev/null
+++ b/examples/extended-wadl-webapp/src/test/resources/log4j.properties
@@ -0,0 +1,14 @@
+#
+# Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Distribution License v. 1.0, which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+log4j.rootCategory=DEBUG, stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout.ConversionPattern=[%30.30c{1}] - %m%n
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
diff --git a/examples/freemarker-webapp/README.MD b/examples/freemarker-webapp/README.MD
new file mode 100644
index 0000000..11bfa2b
--- /dev/null
+++ b/examples/freemarker-webapp/README.MD
@@ -0,0 +1,52 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Freemaker example
+=================
+
+This example demonstrates how to use Freemarker templating engine to
+produce web pages via Jersey viewable support.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                    | Resource class       | HTTP methods
+--------------------------- | -------------------- | --------------
+**_/hello_**                | FreemarkerResource   | GET
+**_/autoTemplate_**         | FreemarkerResource   | GET
+**_/helloWithoutSuffix_**   | FreemarkerResource   | GET
+
+See FreemarkerResource for details on how to use jersey-freemaker-webapp
+module for generating web pages.
+
+If you want to use freemarker in your project, don't forget to set
+**"jersey.config.freemarker.TemplateBasePath"** init param, which will
+be pointing to base directory where your templates are stored
+(FreemarkerTest class does that).
+
+Running the Example
+-------------------
+
+Run the tests of the example as follows:
+
+>     mvn clean test
+
+This deploys this example using [Grizzly](http://grizzly.java.net/) and
+executes the Jersey Test Framework using the unit test framework.
+
+You can see test classes at `src/main/test` subdirectory for detailed
+information how to consume the service using Client API.
+
+Run the example as follows:
+
+>     mvn clean package jetty:run
+
+This deploys current example using Jetty. You can access the application
+at <http://localhost:8080/freemarker-webapp/hello>
\ No newline at end of file
diff --git a/examples/freemarker-webapp/pom.xml b/examples/freemarker-webapp/pom.xml
new file mode 100644
index 0000000..97c6b2b
--- /dev/null
+++ b/examples/freemarker-webapp/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>freemarker-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-freemarker-webapp</name>
+
+    <description>Jersey Freemarker example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-freemarker</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Run the application using "mvn jetty:run" -->
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>maven-jetty-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/freemarker-webapp/src/main/java/org/glassfish/jersey/examples/freemarker/MyApplication.java b/examples/freemarker-webapp/src/main/java/org/glassfish/jersey/examples/freemarker/MyApplication.java
new file mode 100644
index 0000000..ae318cc
--- /dev/null
+++ b/examples/freemarker-webapp/src/main/java/org/glassfish/jersey/examples/freemarker/MyApplication.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.freemarker;
+
+import org.glassfish.jersey.examples.freemarker.resources.FreemarkerResource;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.freemarker.FreemarkerMvcFeature;
+
+/**
+ * Freemarker application configuration.
+ *
+ * @author Michal Gajdos
+ */
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        super(FreemarkerResource.class);
+
+        register(LoggingFeature.class);
+        register(FreemarkerMvcFeature.class);
+    }
+}
diff --git a/examples/freemarker-webapp/src/main/java/org/glassfish/jersey/examples/freemarker/resources/FreemarkerResource.java b/examples/freemarker-webapp/src/main/java/org/glassfish/jersey/examples/freemarker/resources/FreemarkerResource.java
new file mode 100644
index 0000000..61b71e3
--- /dev/null
+++ b/examples/freemarker-webapp/src/main/java/org/glassfish/jersey/examples/freemarker/resources/FreemarkerResource.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.freemarker.resources;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.server.mvc.Viewable;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("/")
+public class FreemarkerResource {
+
+    @GET
+    @Path("hello")
+    @Produces(MediaType.TEXT_HTML)
+    public Viewable getHello() {
+        final Map<String, Object> map = new HashMap<String, Object>();
+        map.put("user", "Pavel");
+        final List<String> list = new ArrayList<String>();
+        list.add("item1");
+        list.add("item2");
+        list.add("item3");
+        map.put("items", list);
+
+        return new Viewable("/hello.ftl", map);
+    }
+
+    @GET
+    @Path("hello-default-model")
+    @Produces(MediaType.TEXT_HTML)
+    public Viewable getHelloWithDefaultModel() {
+        return new Viewable("/hello-default-model.ftl", "Pavel");
+    }
+
+    @GET
+    @Path("autoTemplate")
+    @Produces(MediaType.TEXT_HTML)
+    public Viewable getAutoTemplate() {
+        final Map<String, String> map = new HashMap<String, String>();
+        map.put("user", "Pavel");
+
+        // template name is derived from resource class name
+        return new Viewable("/org/glassfish/jersey/examples/freemarker/resources/FreemarkerResource.ftl",
+                map);
+    }
+
+    @GET
+    @Path("helloWithoutSuffix")
+    @Produces(MediaType.TEXT_HTML)
+    public Viewable getHelloWithoutSuffix() {
+        final Map<String, Object> map = new HashMap<String, Object>();
+        map.put("user", "Pavel");
+        final List<String> list = new ArrayList<String>();
+        list.add("item1");
+        list.add("item2");
+        list.add("item3");
+        map.put("items", list);
+
+        return new Viewable("/hello", map);
+    }
+}
diff --git a/examples/freemarker-webapp/src/main/resources/freemarker/hello-default-model.ftl b/examples/freemarker-webapp/src/main/resources/freemarker/hello-default-model.ftl
new file mode 100644
index 0000000..8e3c203
--- /dev/null
+++ b/examples/freemarker-webapp/src/main/resources/freemarker/hello-default-model.ftl
@@ -0,0 +1,20 @@
+<#--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+<html>
+    <head>
+      <title>Welcome!</title>
+    </head>
+
+    <body>
+      <h1>Welcome ${model}!</h1>
+    </body>
+</html>
diff --git a/examples/freemarker-webapp/src/main/resources/freemarker/hello.ftl b/examples/freemarker-webapp/src/main/resources/freemarker/hello.ftl
new file mode 100644
index 0000000..137bfd7
--- /dev/null
+++ b/examples/freemarker-webapp/src/main/resources/freemarker/hello.ftl
@@ -0,0 +1,25 @@
+<#--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+<html>
+    <head>
+      <title>Welcome!</title>
+    </head>
+
+    <body>
+      <h1>Welcome ${user}!</h1>
+      <p>items:<br />
+            <#list items as item>
+                ${item}<br />
+            </#list>
+      </p>
+    </body>
+</html>
diff --git a/examples/freemarker-webapp/src/main/resources/freemarker/org/glassfish/jersey/examples/freemarker/resources/FreemarkerResource.ftl b/examples/freemarker-webapp/src/main/resources/freemarker/org/glassfish/jersey/examples/freemarker/resources/FreemarkerResource.ftl
new file mode 100644
index 0000000..e71ad7b
--- /dev/null
+++ b/examples/freemarker-webapp/src/main/resources/freemarker/org/glassfish/jersey/examples/freemarker/resources/FreemarkerResource.ftl
@@ -0,0 +1,20 @@
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+<html>
+    <head>
+      <title>Welcome - automatically chosen template</title>
+    </head>
+
+    <body>
+      <h1>Welcome ${user}!</h1>
+    </body>
+</html>
diff --git a/examples/freemarker-webapp/src/main/webapp/WEB-INF/glassfish-web.xml b/examples/freemarker-webapp/src/main/webapp/WEB-INF/glassfish-web.xml
new file mode 100644
index 0000000..3965fcb
--- /dev/null
+++ b/examples/freemarker-webapp/src/main/webapp/WEB-INF/glassfish-web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
+<glassfish-web-app error-url="">
+    <context-root>/freemarker-webapp</context-root>
+    <class-loader delegate="true" />
+    <jsp-config>
+        <property name="classdebuginfo" value="true">
+            <description>Enable debug info compilation in the generated servlet class</description>
+        </property>
+        <property name="keepgenerated" value="true">
+            <description>Keep a copy of the generated servlet class' java code.</description>
+        </property>
+        <property name="mappedfile" value="true">
+            <description>Maintain a one-to-one correspondence between static content and the generated servlet class' java code</description>
+        </property>
+    </jsp-config>
+</glassfish-web-app>
diff --git a/examples/freemarker-webapp/src/main/webapp/WEB-INF/web.xml b/examples/freemarker-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..940b11a
--- /dev/null
+++ b/examples/freemarker-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="2.5"
+        xmlns="http://java.sun.com/xml/ns/javaee"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <servlet>
+        <servlet-name>org.glassfish.jersey.examples.freemarker.MyApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.examples.freemarker.MyApplication</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.server.mvc.templateBasePath.freemarker</param-name>
+            <param-value>freemarker</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.examples.freemarker.MyApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
diff --git a/examples/freemarker-webapp/src/test/java/org/glassfish/jersey/examples/freemarker/FreemarkerTest.java b/examples/freemarker-webapp/src/test/java/org/glassfish/jersey/examples/freemarker/FreemarkerTest.java
new file mode 100644
index 0000000..0701654
--- /dev/null
+++ b/examples/freemarker-webapp/src/test/java/org/glassfish/jersey/examples/freemarker/FreemarkerTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.freemarker;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.server.mvc.freemarker.FreemarkerMvcFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class FreemarkerTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        return new MyApplication().property(FreemarkerMvcFeature.TEMPLATE_BASE_PATH, "freemarker");
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("freemarker-webapp").build();
+    }
+
+    @Test
+    public void testHello() {
+        assertTrue(target().path("hello").request().get(String.class).contains("Pavel"));
+    }
+
+    @Test
+    public void testHellowithDefaultModel() {
+        assertTrue(target().path("hello-default-model").request().get(String.class).contains("Pavel"));
+    }
+
+    @Test
+    public void testAutoTemplate() {
+        assertTrue(target().path("autoTemplate").request().get(String.class).contains("Pavel"));
+    }
+
+    @Test
+    public void testAutoTemplateWithoutSuffix() {
+        assertTrue(target().path("helloWithoutSuffix").request().get(String.class).contains("Pavel"));
+    }
+
+}
diff --git a/examples/groovy/README.MD b/examples/groovy/README.MD
new file mode 100644
index 0000000..68e5c16
--- /dev/null
+++ b/examples/groovy/README.MD
@@ -0,0 +1,41 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Groovy Example
+==============
+
+This example demonstrates how to develop RESTful hello world web service
+using Groovy and the embedded Grizzly server.
+
+Contents
+--------
+
+The example consists of just one Jersey resource:
+
+`org.glassfish.jersey.examples.groovy.GroovyResource`
+
+hat produces a textual response to an HTTP GET.
+
+The mapping of the URI path space is presented in the following table:
+
+URI path      | Resource class   | HTTP methods
+-----------   | ---------------- | --------------
+**_/groovy_** | GroovyResource  | GET
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
+
+From a web browser, visit:
+
+-   <http://localhost:9998/groovy>
diff --git a/examples/groovy/pom.xml b/examples/groovy/pom.xml
new file mode 100644
index 0000000..2ce8088
--- /dev/null
+++ b/examples/groovy/pom.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+    <artifactId>groovy</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-groovy</name>
+    <description>Groovy Jersey</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy-all</artifactId>
+            <version>2.4.3</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>groovy.ui.GroovyMain</mainClass>
+                    <arguments>
+                        <argument>${pom.basedir}/src/main/script/NewGroovyScript.groovy</argument>
+                    </arguments>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.gmavenplus</groupId>
+                <artifactId>gmavenplus-plugin</artifactId>
+                <version>1.6</version>
+                <executions>
+                    <execution>
+                        <id>1</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>addSources</goal>
+                            <goal>addTestSources</goal>
+                            <goal>generateStubs</goal>
+                            <goal>compile</goal>
+                            <goal>generateTestStubs</goal>
+                            <goal>compileTests</goal>
+                            <goal>removeStubs</goal>
+                            <goal>removeTestStubs</goal>
+                            <goal>groovydoc</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>2</id>
+                        <phase>generate-resources</phase>
+                        <configuration>
+                            <target>
+                                <jar destfile="${project.build.directory}/${project.artifactId}-javadoc.jar">
+                                    <zipfileset dir="${project.build.directory}/gapidocs" />
+                                </jar>
+                            </target>
+                        </configuration>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>3.0.0</version>
+                <executions>
+                    <execution>
+                        <id>3</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>attach-artifact</goal>
+                        </goals>
+                        <configuration>
+                            <artifacts>
+                                <artifact>
+                                    <file>${project.build.directory}/${project.artifactId}-javadoc.jar</file>
+                                    <type>jar</type>
+                                    <classifier>javadoc</classifier>
+                                </artifact>
+                            </artifacts>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/examples/groovy/src/main/groovy/org/glassfish/jersey/examples/groovy/GroovyResource.groovy b/examples/groovy/src/main/groovy/org/glassfish/jersey/examples/groovy/GroovyResource.groovy
new file mode 100644
index 0000000..8f67e15
--- /dev/null
+++ b/examples/groovy/src/main/groovy/org/glassfish/jersey/examples/groovy/GroovyResource.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.groovy
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Example Groovy JAX-RS resource.
+ */
+@Path("groovy")
+class GroovyResource {
+    @Produces(["text/plain"]) @GET def show() {
+        "groovy"
+    }
+}
diff --git a/examples/groovy/src/main/script/NewGroovyScript.groovy b/examples/groovy/src/main/script/NewGroovyScript.groovy
new file mode 100644
index 0000000..c3aae52
--- /dev/null
+++ b/examples/groovy/src/main/script/NewGroovyScript.groovy
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.groovy
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory
+import org.glassfish.jersey.server.ResourceConfig
+
+import javax.ws.rs.core.UriBuilder
+
+/*
+ * Groovy script to start the example app
+ */
+baseUri = UriBuilder.fromUri("http://localhost/").port(9998).build()
+server = GrizzlyHttpServerFactory.createHttpServer(baseUri, new ResourceConfig(GroovyResource.class))
+
+System.out.println(String.format("Jersey app started with WADL available at " + "%sapplication.wadl\n"
+        + "Try out %sgroovy\nHit  enter to stop it...", baseUri, baseUri));
+
+System.in.read()
+server.shutdown()
diff --git a/examples/groovy/src/test/groovy/org/glassfish/jersey/examples/groovy/GroovyResourceTest.groovy b/examples/groovy/src/test/groovy/org/glassfish/jersey/examples/groovy/GroovyResourceTest.groovy
new file mode 100644
index 0000000..11db4f6
--- /dev/null
+++ b/examples/groovy/src/test/groovy/org/glassfish/jersey/examples/groovy/GroovyResourceTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.groovy
+
+import org.glassfish.jersey.server.ResourceConfig
+import org.glassfish.jersey.test.JerseyTest
+import org.glassfish.jersey.test.TestProperties
+import org.junit.Test
+
+import javax.ws.rs.core.Response
+import static org.junit.Assert.assertEquals
+
+/**
+ * Test the availability of the {@link GroovyResource}.
+ */
+class GroovyResourceTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(GroovyResource.class)
+    }
+
+    @Test
+    public void testGroovyResource() {
+        final Response response = target("groovy").request().get();
+        assertEquals("groovy", response.readEntity(String.class));
+    }
+}
diff --git a/examples/helloworld-benchmark/README.MD b/examples/helloworld-benchmark/README.MD
new file mode 100644
index 0000000..7aa30ca
--- /dev/null
+++ b/examples/helloworld-benchmark/README.MD
@@ -0,0 +1,85 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Hello World JMH Example
+=======================
+
+### *How to write JAX-RS Micro-Benchmarks*
+
+This example demonstrates how to write simple micro-benchmarks, based on
+JMH, for JAX-RS application. This particular example shows how to obtain
+throughput of a simple 'Hello World' application.
+
+The application consists of one resource, `HelloWorldResource` with
+three resource methods (one method per HTTP GET, POST and PUT methods).
+To make it more interesting the resource also acts like sub-resource
+locator which allows us to measure the difference between processing of
+normal resource methods and sub-resource locators.
+
+This application is solely focused on showing how to write benchmarks
+and it doesn't start any server that would allow one to send request to
+the `HelloWorldResource`.
+
+Main class that illustrates writing benchmarks for JAX-RS applications
+is `HelloWorldBenchmark`. Measurements are not based on any container
+such as Servlet or Grizzly container. Requests are directly invoked on
+`ApplicationHandler` class from Jersey Server module to eliminate any
+network influences. Still, all JAX-RS features, like filters and
+interceptors, are taken into consideration when the benchmark is
+executed.
+
+It's also possible to run the benchmark on older versions of Jersey,
+e.g. 2.15, which had known performance issues with sub-resource
+locators. The recommended approach is to run the benchmarks without any
+changes at first and then modify the `pom.xml` to switch version of
+Jersey to 2.15 and run the benchmarks again to see the (big) difference.
+Look for `jersey-bom` artifact in `pom.xml` to see how the switch should
+be done.
+
+Sample Results
+--------------
+
+The execution takes around 2 minutes and you can see partial results
+(for each combination of parameters) of every iteration. At the end JMH
+gives you whole summary. For example, Jersey 2.16 gives the following
+results:
+
+    # Run complete. Total time: 00:01:41
+
+    Benchmark                    (method)              (path)   Mode  Cnt      Score      Error  Units
+    HelloWorldBenchmark.measure       GET          helloworld  thrpt    8  74343.864 ± 4814.979  ops/s
+    HelloWorldBenchmark.measure       GET  helloworld/locator  thrpt    8  54137.102 ± 8996.766  ops/s
+    HelloWorldBenchmark.measure      POST          helloworld  thrpt    8  45173.853 ± 5349.363  ops/s
+    HelloWorldBenchmark.measure      POST  helloworld/locator  thrpt    8  37144.797 ± 4699.782  ops/s
+    HelloWorldBenchmark.measure       PUT          helloworld  thrpt    8  45945.974 ± 4116.752  ops/s
+    HelloWorldBenchmark.measure       PUT  helloworld/locator  thrpt    8  36345.667 ± 5929.480  ops/s
+
+Running the Example
+-------------------
+
+There are two ways how to run the micro-benchmark. Either via Maven
+`exec:exec`
+
+>     mvn clean install exec:exec
+
+or you can build the benchmark JAR at first
+
+>     mvn clean install
+
+and then invoking it using `java` command
+
+>     java -jar target/benchmark.jar
+
+Resources
+---------
+
+JMH has excellent overview of it's features presented as list of
+different samples. You can find them on [JMH
+Samples](http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/).
+The general overview of JMH is available at their [home
+page](http://openjdk.java.net/projects/code-tools/jmh/).
diff --git a/examples/helloworld-benchmark/pom.xml b/examples/helloworld-benchmark/pom.xml
new file mode 100644
index 0000000..6ce7c4c
--- /dev/null
+++ b/examples/helloworld-benchmark/pom.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>helloworld-benchmark</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-helloworld-benchmark</name>
+
+    <description>Jersey "Hello World" benchmark example.</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.glassfish.jersey</groupId>
+                <artifactId>jersey-bom</artifactId>
+                <type>pom</type>
+                <scope>import</scope>
+
+                <!-- Comment this line to use Jersey 2.15. Un-comment to use the current version. -->
+                <version>${project.version}</version>
+                <!-- Comment this line to use the current version. Un-comment to use Jersey 2.15. -->
+                <!--<version>2.15</version>-->
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-generator-annprocess</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <executable>java</executable>
+                    <arguments>
+                        <argument>-classpath</argument>
+                        <classpath />
+                        <argument>org.openjdk.jmh.Main</argument>
+                    </arguments>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <configuration>
+                    <minimizeJar>false</minimizeJar>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>shade-archive</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <finalName>${uberjar.name}</finalName>
+                            <transformers>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>org.openjdk.jmh.Main</mainClass>
+                                </transformer>
+                            </transformers>
+                            <filters>
+                                <filter>
+                                    <!--
+                                        Shading signed JARs will fail without this.
+                                        http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
+                                    -->
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>META-INF/*.SF</exclude>
+                                        <exclude>META-INF/*.DSA</exclude>
+                                        <exclude>META-INF/*.RSA</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <uberjar.name>benchmark</uberjar.name>
+    </properties>
+</project>
diff --git a/examples/helloworld-benchmark/src/main/java/org/glassfish/jersey/examples/helloworld/Application.java b/examples/helloworld-benchmark/src/main/java/org/glassfish/jersey/examples/helloworld/Application.java
new file mode 100644
index 0000000..a52b33d
--- /dev/null
+++ b/examples/helloworld-benchmark/src/main/java/org/glassfish/jersey/examples/helloworld/Application.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+/**
+ * @author Michal Gajdos
+ */
+public class Application extends ResourceConfig {
+
+    public Application() {
+        register(HelloWorldResource.class);
+
+        // Turn off Monitoring to not affect benchmarks.
+        property(ServerProperties.MONITORING_ENABLED, false);
+        property(ServerProperties.MONITORING_STATISTICS_ENABLED, false);
+        property(ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED, false);
+    }
+}
diff --git a/examples/helloworld-benchmark/src/main/java/org/glassfish/jersey/examples/helloworld/HelloWorldBenchmark.java b/examples/helloworld-benchmark/src/main/java/org/glassfish/jersey/examples/helloworld/HelloWorldBenchmark.java
new file mode 100644
index 0000000..89c6365
--- /dev/null
+++ b/examples/helloworld-benchmark/src/main/java/org/glassfish/jersey/examples/helloworld/HelloWorldBenchmark.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.test.util.server.ContainerRequestBuilder;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+/**
+ * "Hello world!" Jersey {@link org.glassfish.jersey.server.ApplicationHandler} benchmark.
+ *
+ * @author Michal Gajdos
+ */
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@Warmup(iterations = 8, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 8, time = 1, timeUnit = TimeUnit.SECONDS)
+@Fork(1)
+@State(Scope.Benchmark)
+public class HelloWorldBenchmark {
+
+    @Param(value = {"helloworld", "helloworld/locator"})
+    private String path;
+
+    @Param(value = {"GET", "POST", "PUT"})
+    private String method;
+
+    private volatile ApplicationHandler handler;
+    private volatile ContainerRequest request;
+
+    @Setup
+    public void start() throws Exception {
+        handler = new ApplicationHandler(new Application());
+    }
+
+    @Setup(Level.Iteration)
+    public void request() {
+        request = ContainerRequestBuilder
+                .from(path, method)
+                .entity("GET".equals(method) ? null : HelloWorldResource.CLICHED_MESSAGE, handler)
+                .build();
+    }
+
+    @TearDown
+    public void shutdown() {
+    }
+
+    @Benchmark
+    public Future<ContainerResponse> measure() throws Exception {
+        return handler.apply(request);
+    }
+
+    public static void main(final String[] args) throws Exception {
+        final Options opt = new OptionsBuilder()
+                // Register our benchmarks.
+                .include(HelloWorldBenchmark.class.getSimpleName())
+                .build();
+
+        new Runner(opt).run();
+    }
+}
diff --git a/examples/helloworld-benchmark/src/main/java/org/glassfish/jersey/examples/helloworld/HelloWorldResource.java b/examples/helloworld-benchmark/src/main/java/org/glassfish/jersey/examples/helloworld/HelloWorldResource.java
new file mode 100644
index 0000000..a57a53d
--- /dev/null
+++ b/examples/helloworld-benchmark/src/main/java/org/glassfish/jersey/examples/helloworld/HelloWorldResource.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Simple "Hello World" resource with three resource methods and a sub-resource locator (that points again to this class itself).
+ *
+ * @author Michal Gajdos
+ */
+@Path("helloworld")
+@Produces("text/plain")
+public class HelloWorldResource {
+
+    public static final String CLICHED_MESSAGE = "Hello World!";
+
+    @GET
+    public String get() {
+        return CLICHED_MESSAGE;
+    }
+
+    @POST
+    public String post(final String entity) {
+        return entity;
+    }
+
+    @PUT
+    public void put(final String entity) {
+        // NOOP
+    }
+
+    @Path("locator")
+    public Class<?> sub() {
+        return HelloWorldResource.class;
+    }
+}
diff --git a/examples/helloworld-benchmark/src/test/java/org/glassfish/jersey/examples/helloworld/HelloWorldTest.java b/examples/helloworld-benchmark/src/test/java/org/glassfish/jersey/examples/helloworld/HelloWorldTest.java
new file mode 100644
index 0000000..6e7c046
--- /dev/null
+++ b/examples/helloworld-benchmark/src/test/java/org/glassfish/jersey/examples/helloworld/HelloWorldTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * {@link org.glassfish.jersey.examples.helloworld.HelloWorldResource} tests.
+ *
+ * @author Michal Gajdos
+ */
+public class HelloWorldTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Test
+    public void testGet() {
+        final Response response = target().path("helloworld").request("text/plain").get();
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(200));
+        assertThat("Unexpected response entity.", response.readEntity(String.class), is(HelloWorldResource.CLICHED_MESSAGE));
+    }
+
+    @Test
+    public void testGetLocator() {
+        final Response response = target().path("helloworld").path("locator").request("text/plain").get();
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(200));
+        assertThat("Unexpected response entity.", response.readEntity(String.class), is(HelloWorldResource.CLICHED_MESSAGE));
+    }
+
+    @Test
+    public void testPost() {
+        final Response response = target().path("helloworld")
+                .request("text/plain")
+                .post(Entity.text(HelloWorldResource.CLICHED_MESSAGE));
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(200));
+        assertThat("Unexpected response entity.", response.readEntity(String.class), is(HelloWorldResource.CLICHED_MESSAGE));
+    }
+
+    @Test
+    public void testPostLocator() {
+        final Response response = target().path("helloworld").path("locator")
+                .request("text/plain")
+                .post(Entity.text(HelloWorldResource.CLICHED_MESSAGE));
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(200));
+        assertThat("Unexpected response entity.", response.readEntity(String.class), is(HelloWorldResource.CLICHED_MESSAGE));
+    }
+
+    @Test
+    public void testPut() {
+        final Response response = target().path("helloworld")
+                .request("text/plain")
+                .put(Entity.text(HelloWorldResource.CLICHED_MESSAGE));
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(204));
+    }
+
+    @Test
+    public void testPutLocator() {
+        final Response response = target().path("helloworld").path("locator")
+                .request("text/plain")
+                .put(Entity.text(HelloWorldResource.CLICHED_MESSAGE));
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(204));
+    }
+}
diff --git a/examples/helloworld-cdi2-se/README.MD b/examples/helloworld-cdi2-se/README.MD
new file mode 100644
index 0000000..6e4026f
--- /dev/null
+++ b/examples/helloworld-cdi2-se/README.MD
@@ -0,0 +1,34 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Hello World and Counter Example
+===============================
+
+This example demonstrates Hello World and Counter examples with CDI 2 SE implemented by Weld.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                    | Resource class      | HTTP methods | Notes
+--------------------------- | ------------------- | ------------ | --------------------------------------------------------
+**_/helloworld/{name}_**    | HelloWorldResource  |  GET         |  Returns `Hello {name}`
+**_/counter/request_**      | CounterResource     |  GET         |  Returns always `1` (injected always a new instance of counter)
+**_/counter/application_**  | CounterResource     |  GET         |  Returns an incremented number (injected always the same counter).
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
+
+-   <http://localhost:8080/helloworld>
diff --git a/examples/helloworld-cdi2-se/pom.xml b/examples/helloworld-cdi2-se/pom.xml
new file mode 100644
index 0000000..ba3cfaa
--- /dev/null
+++ b/examples/helloworld-cdi2-se/pom.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>helloworld-cdi2-se</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-helloworld-cdi2-se</name>
+
+    <description>Jersey "Hello world" example with CDI 2 SE.</description>
+
+    <properties>
+        <cdi.api.version>2.0</cdi.api.version>
+        <weld.version>${weld3.version}</weld.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-cdi2-se</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>java.org.glassfish.jersey.examples.helloworld.cdi2se.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/App.java b/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/App.java
new file mode 100644
index 0000000..d35233f
--- /dev/null
+++ b/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/App.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.cdi2se;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Hello world!
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/base/");
+    public static final String ROOT_HELLO_PATH = "helloworld";
+    public static final String ROOT_COUNTER_PATH = "counter";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Hello World\" Jersey Example App");
+
+            ResourceConfig resourceConfig = new ResourceConfig(HelloWorldResource.class, CounterResource.class);
+            HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, false);
+            Runtime.getRuntime().addShutdownHook(new Thread(server::shutdownNow));
+
+            server.start();
+
+            System.out.println("Application started.\nTry out");
+            System.out.println(String.format("%s%s", BASE_URI, ROOT_HELLO_PATH));
+            System.out.println(String.format("%s%s%s", BASE_URI, ROOT_COUNTER_PATH, "/request"));
+            System.out.println(String.format("%s%s%s", BASE_URI, ROOT_COUNTER_PATH, "/application"));
+            System.out.println("Stop the application using CTRL+C");
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+}
diff --git a/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/ApplicationScopedCounter.java b/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/ApplicationScopedCounter.java
new file mode 100644
index 0000000..119aa08
--- /dev/null
+++ b/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/ApplicationScopedCounter.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.cdi2se;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.enterprise.context.ApplicationScoped;
+
+/**
+ * Application-scoped counter.
+ */
+@ApplicationScoped
+public class ApplicationScopedCounter {
+
+    private final AtomicInteger counter = new AtomicInteger();
+
+    public int getNumber() {
+        return counter.incrementAndGet();
+    }
+}
diff --git a/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/CounterResource.java b/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/CounterResource.java
new file mode 100644
index 0000000..519da94
--- /dev/null
+++ b/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/CounterResource.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.cdi2se;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import javax.inject.Inject;
+
+/**
+ * Request-scoped resource counter.
+ *
+ * @author Petr Bouda
+ */
+@Path("counter")
+public class CounterResource {
+
+    @Inject
+    private RequestScopedCounter requestScoped;
+
+    @Inject
+    private ApplicationScopedCounter applicationScoped;
+
+    @GET
+    @Path("application")
+    @Produces("text/plain")
+    public int getAppCounter() {
+        return applicationScoped.getNumber();
+    }
+
+    @GET
+    @Path("request")
+    @Produces("text/plain")
+    public int getReqCounter() {
+        return requestScoped.getNumber();
+    }
+}
diff --git a/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/HelloBean.java b/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/HelloBean.java
new file mode 100644
index 0000000..7a06759
--- /dev/null
+++ b/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/HelloBean.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.cdi2se;
+
+import javax.enterprise.context.ApplicationScoped;
+
+/**
+ * Application-scoped service returning "hello" sentence.
+ *
+ * @author Petr Bouda
+ */
+@ApplicationScoped
+public class HelloBean {
+
+    public String hello(String name) {
+        return "Hello " + name;
+    }
+}
diff --git a/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/HelloWorldResource.java b/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/HelloWorldResource.java
new file mode 100644
index 0000000..7799da3
--- /dev/null
+++ b/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/HelloWorldResource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.cdi2se;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Singleton-scoped resource.
+ *
+ * @author Petr Bouda
+ */
+@Singleton
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @Inject
+    private HelloBean helloBean;
+
+    @GET
+    @Path("{name}")
+    @Produces("text/plain")
+    public String getHello(@PathParam("name") String name) {
+        return helloBean.hello(name);
+    }
+}
diff --git a/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/RequestScopedCounter.java b/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/RequestScopedCounter.java
new file mode 100644
index 0000000..d457878
--- /dev/null
+++ b/examples/helloworld-cdi2-se/src/main/java/org/glassfish/jersey/examples/helloworld/cdi2se/RequestScopedCounter.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.cdi2se;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.enterprise.context.RequestScoped;
+
+/**
+ * Request-scoped counter.
+ */
+@RequestScoped
+public class RequestScopedCounter {
+
+    private final AtomicInteger counter = new AtomicInteger();
+
+    public int getNumber() {
+        return counter.incrementAndGet();
+    }
+}
diff --git a/examples/helloworld-cdi2-se/src/main/resources/META-INF/beans.xml b/examples/helloworld-cdi2-se/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..fc210a0
--- /dev/null
+++ b/examples/helloworld-cdi2-se/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<beans/>
diff --git a/examples/helloworld-cdi2-se/src/test/java/org/glassfish/jersey/examples/helloworld/cdi2se/CounterTest.java b/examples/helloworld-cdi2-se/src/test/java/org/glassfish/jersey/examples/helloworld/cdi2se/CounterTest.java
new file mode 100644
index 0000000..9017419
--- /dev/null
+++ b/examples/helloworld-cdi2-se/src/test/java/org/glassfish/jersey/examples/helloworld/cdi2se/CounterTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.cdi2se;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests CDI counter resource.
+ */
+public class CounterTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(CounterResource.class);
+    }
+
+    @Test
+    public void testRequestCounter() throws InterruptedException {
+        Integer response1 = target().path(App.ROOT_COUNTER_PATH).path("request").request().get(Integer.class);
+        Integer response2 = target().path(App.ROOT_COUNTER_PATH).path("request").request().get(Integer.class);
+        assertEquals((Integer) 1, response1);
+        assertEquals((Integer) 1, response2);
+    }
+
+    @Test
+    public void testApplicationCounter() throws InterruptedException {
+        Integer response1 = target().path(App.ROOT_COUNTER_PATH).path("application").request().get(Integer.class);
+        Integer response2 = target().path(App.ROOT_COUNTER_PATH).path("application").request().get(Integer.class);
+        assertEquals((Integer) 1, response1);
+        assertEquals((Integer) 2, response2);
+    }
+}
diff --git a/examples/helloworld-cdi2-se/src/test/java/org/glassfish/jersey/examples/helloworld/cdi2se/HelloWorldTest.java b/examples/helloworld-cdi2-se/src/test/java/org/glassfish/jersey/examples/helloworld/cdi2se/HelloWorldTest.java
new file mode 100644
index 0000000..0bba28a
--- /dev/null
+++ b/examples/helloworld-cdi2-se/src/test/java/org/glassfish/jersey/examples/helloworld/cdi2se/HelloWorldTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.cdi2se;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests CDI helloworld resource.
+ */
+public class HelloWorldTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Test
+    public void testHello() throws InterruptedException {
+        String response = target().path(App.ROOT_HELLO_PATH).path("James").request().get(String.class);
+        assertEquals("Hello James", response);
+    }
+}
diff --git a/examples/helloworld-netty/README.MD b/examples/helloworld-netty/README.MD
new file mode 100644
index 0000000..dbf5e9c
--- /dev/null
+++ b/examples/helloworld-netty/README.MD
@@ -0,0 +1,33 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Hello World Example
+===================
+
+This example demonstrates Hello World example running on top of Netty container and using Netty connector as a client
+implementation for testing. JAX-RS resource returns the usual text `Hello World!`.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path             | Resource class      | HTTP methods | Notes
+-------------------- | ------------------- | ------------ | --------------------------------------------------------
+**_/helloworld_**    | HelloWorldResource  |  GET         |  Returns `Hello World!`
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Netty](http://netty.io/) container.
+
+-   <http://localhost:8080/helloworld>
diff --git a/examples/helloworld-netty/pom.xml b/examples/helloworld-netty/pom.xml
new file mode 100644
index 0000000..e3ec898
--- /dev/null
+++ b/examples/helloworld-netty/pom.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>helloworld-netty</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-helloworld-netty</name>
+
+    <description>Jersey "Hello world" example on Netty container.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-netty-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.helloworld.netty.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/helloworld-netty/src/main/java/org/glassfish/jersey/examples/helloworld/netty/App.java b/examples/helloworld-netty/src/main/java/org/glassfish/jersey/examples/helloworld/netty/App.java
new file mode 100644
index 0000000..f118831
--- /dev/null
+++ b/examples/helloworld-netty/src/main/java/org/glassfish/jersey/examples/helloworld/netty/App.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.netty;
+
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import io.netty.channel.Channel;
+import org.glassfish.jersey.netty.httpserver.NettyHttpContainerProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Hello world!
+ */
+public class App {
+
+    static final String ROOT_PATH = "helloworld";
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Hello World\" Jersey Example App on Netty container.");
+
+            ResourceConfig resourceConfig = new ResourceConfig(HelloWorldResource.class);
+            final Channel server = NettyHttpContainerProvider.createHttp2Server(BASE_URI, resourceConfig, null);
+
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.close();
+                }
+            }));
+
+            System.out.println(String.format("Application started. (HTTP/2 enabled!)\nTry out %s%s\nStop the application using "
+                                                     + "CTRL+C.", BASE_URI, ROOT_PATH));
+            Thread.currentThread().join();
+        } catch (InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+}
diff --git a/examples/helloworld-netty/src/main/java/org/glassfish/jersey/examples/helloworld/netty/HelloWorldResource.java b/examples/helloworld-netty/src/main/java/org/glassfish/jersey/examples/helloworld/netty/HelloWorldResource.java
new file mode 100644
index 0000000..baf1be7
--- /dev/null
+++ b/examples/helloworld-netty/src/main/java/org/glassfish/jersey/examples/helloworld/netty/HelloWorldResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.netty;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+    public static final String CLICHED_MESSAGE = "Hello World!";
+
+    @GET
+    @Produces("text/plain")
+    public String getHello() {
+        return CLICHED_MESSAGE;
+    }
+
+}
diff --git a/examples/helloworld-netty/src/test/java/org/glassfish/jersey/examples/helloworld/netty/CustomLoggingFilter.java b/examples/helloworld-netty/src/test/java/org/glassfish/jersey/examples/helloworld/netty/CustomLoggingFilter.java
new file mode 100644
index 0000000..396abdd
--- /dev/null
+++ b/examples/helloworld-netty/src/test/java/org/glassfish/jersey/examples/helloworld/netty/CustomLoggingFilter.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.netty;
+
+import java.io.IOException;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Custom logging filter.
+ *
+ * @author Santiago Pericas-Geertsen (santiago.pericasgeertsen at oracle.com)
+ */
+public class CustomLoggingFilter implements ContainerRequestFilter, ContainerResponseFilter,
+        ClientRequestFilter, ClientResponseFilter {
+
+    private static final Logger LOGGER = Logger.getLogger(CustomLoggingFilter.class.getName());
+
+    static int preFilterCalled = 0;
+    static int postFilterCalled = 0;
+
+    @Override
+    public void filter(ClientRequestContext context) throws IOException {
+        LOGGER.info("CustomLoggingFilter.preFilter called");
+        assertEquals(context.getConfiguration().getProperty("foo"), "bar");
+        preFilterCalled++;
+    }
+
+    @Override
+    public void filter(ClientRequestContext context, ClientResponseContext clientResponseContext) throws IOException {
+        LOGGER.info("CustomLoggingFilter.postFilter called");
+        assertEquals(context.getConfiguration().getProperty("foo"), "bar");
+        postFilterCalled++;
+    }
+
+    @Override
+    public void filter(ContainerRequestContext context) throws IOException {
+        LOGGER.info("CustomLoggingFilter.preFilter called");
+        assertEquals(context.getProperty("foo"), "bar");
+        preFilterCalled++;
+    }
+
+    @Override
+    public void filter(ContainerRequestContext context, ContainerResponseContext containerResponseContext) throws IOException {
+        LOGGER.info("CustomLoggingFilter.postFilter called");
+        assertEquals(context.getProperty("foo"), "bar");
+        postFilterCalled++;
+    }
+}
+
diff --git a/examples/helloworld-netty/src/test/java/org/glassfish/jersey/examples/helloworld/netty/HelloWorldTest.java b/examples/helloworld-netty/src/test/java/org/glassfish/jersey/examples/helloworld/netty/HelloWorldTest.java
new file mode 100644
index 0000000..cdb82b5
--- /dev/null
+++ b/examples/helloworld-netty/src/test/java/org/glassfish/jersey/examples/helloworld/netty/HelloWorldTest.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.netty;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.netty.NettyTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+import org.glassfish.jersey.test.util.runner.RunSeparately;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(ConcurrentRunner.class)
+public class HelloWorldTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        // enable(TestProperties.DUMP_ENTITY);
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig clientConfig) {
+        clientConfig.connectorProvider(new NettyConnectorProvider());
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new NettyTestContainerFactory();
+    }
+
+    @Test
+    @Ignore("not compatible with test framework (doesn't use client())")
+    public void testHelloWorld() throws Exception {
+        URL getUrl = UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).build().toURL();
+        HttpURLConnection connection = (HttpURLConnection) getUrl.openConnection();
+        try {
+            connection.setDoOutput(true);
+            connection.setInstanceFollowRedirects(false);
+            connection.setRequestMethod("GET");
+            connection.setRequestProperty("Content-Type", "text/plain");
+            assertEquals(HttpURLConnection.HTTP_OK, connection.getResponseCode());
+        } finally {
+            connection.disconnect();
+        }
+    }
+
+    @Test
+    public void testConnection() {
+        Response response = target().path(App.ROOT_PATH).request("text/plain").get();
+        assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testClientStringResponse() {
+        String s = target().path(App.ROOT_PATH).request().get(String.class);
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+    }
+
+    @Test
+    public void testAsyncClientRequests() throws InterruptedException {
+        final int REQUESTS = 10;
+        final CountDownLatch latch = new CountDownLatch(REQUESTS);
+        final long tic = System.currentTimeMillis();
+        for (int i = 0; i < REQUESTS; i++) {
+            final int id = i;
+            target().path(App.ROOT_PATH).request().async().get(new InvocationCallback<Response>() {
+                @Override
+                public void completed(Response response) {
+                    try {
+                        final String result = response.readEntity(String.class);
+                        assertEquals(HelloWorldResource.CLICHED_MESSAGE, result);
+                    } finally {
+                        latch.countDown();
+                    }
+                }
+
+                @Override
+                public void failed(Throwable error) {
+                    error.printStackTrace();
+                    latch.countDown();
+                }
+            });
+        }
+        latch.await(10 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+        final long toc = System.currentTimeMillis();
+        Logger.getLogger(HelloWorldTest.class.getName()).info("Executed in: " + (toc - tic));
+    }
+
+    @Test
+    public void testHead() {
+        Response response = target().path(App.ROOT_PATH).request().head();
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+    }
+
+    @Test
+    public void testFooBarOptions() {
+        Response response = target().path(App.ROOT_PATH).request().header("Accept", "foo/bar").options();
+        assertEquals(200, response.getStatus());
+        final String allowHeader = response.getHeaderString("Allow");
+        _checkAllowContent(allowHeader);
+        assertEquals("foo/bar", response.getMediaType().toString());
+        assertEquals(0, response.getLength());
+    }
+
+    @Test
+    public void testTextPlainOptions() {
+        Response response = target().path(App.ROOT_PATH).request().header("Accept", MediaType.TEXT_PLAIN).options();
+        assertEquals(200, response.getStatus());
+        final String allowHeader = response.getHeaderString("Allow");
+        _checkAllowContent(allowHeader);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+        final String responseBody = response.readEntity(String.class);
+        _checkAllowContent(responseBody);
+    }
+
+    private void _checkAllowContent(final String content) {
+        assertTrue(content.contains("GET"));
+        assertTrue(content.contains("HEAD"));
+        assertTrue(content.contains("OPTIONS"));
+    }
+
+    @Test
+    public void testMissingResourceNotFound() {
+        Response response;
+
+        response = target().path(App.ROOT_PATH + "arbitrary").request().get();
+        assertEquals(404, response.getStatus());
+
+        response = target().path(App.ROOT_PATH).path("arbitrary").request().get();
+        assertEquals(404, response.getStatus());
+    }
+
+    @Test
+    @RunSeparately
+    public void testLoggingFilterClientClass() {
+        Client client = client();
+        client.register(CustomLoggingFilter.class).property("foo", "bar");
+        CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+        String s = target().path(App.ROOT_PATH).request().get(String.class);
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+        assertEquals(1, CustomLoggingFilter.preFilterCalled);
+        assertEquals(1, CustomLoggingFilter.postFilterCalled);
+    }
+
+    @Test
+    @RunSeparately
+    public void testLoggingFilterClientInstance() {
+        Client client = client();
+        client.register(new CustomLoggingFilter()).property("foo", "bar");
+        CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+        String s = target().path(App.ROOT_PATH).request().get(String.class);
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+        assertEquals(1, CustomLoggingFilter.preFilterCalled);
+        assertEquals(1, CustomLoggingFilter.postFilterCalled);
+    }
+
+    @Test
+    @RunSeparately
+    public void testLoggingFilterTargetClass() {
+        WebTarget target = target().path(App.ROOT_PATH);
+        target.register(CustomLoggingFilter.class).property("foo", "bar");
+        CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+        String s = target.request().get(String.class);
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+        assertEquals(1, CustomLoggingFilter.preFilterCalled);
+        assertEquals(1, CustomLoggingFilter.postFilterCalled);
+    }
+
+    @Test
+    @RunSeparately
+    public void testLoggingFilterTargetInstance() {
+        WebTarget target = target().path(App.ROOT_PATH);
+        target.register(new CustomLoggingFilter()).property("foo", "bar");
+        CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+        String s = target.request().get(String.class);
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+        assertEquals(1, CustomLoggingFilter.preFilterCalled);
+        assertEquals(1, CustomLoggingFilter.postFilterCalled);
+    }
+
+    @Test
+    @RunSeparately
+    public void testConfigurationUpdate() {
+        Client client1 = client();
+        client1.register(CustomLoggingFilter.class).property("foo", "bar");
+
+        Client client = ClientBuilder.newClient(client1.getConfiguration());
+        CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+        String s = target().path(App.ROOT_PATH).request().get(String.class);
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+        assertEquals(1, CustomLoggingFilter.preFilterCalled);
+        assertEquals(1, CustomLoggingFilter.postFilterCalled);
+    }
+}
diff --git a/examples/helloworld-programmatic/README.MD b/examples/helloworld-programmatic/README.MD
new file mode 100644
index 0000000..4fd210d
--- /dev/null
+++ b/examples/helloworld-programmatic/README.MD
@@ -0,0 +1,35 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Hello World Example
+===================
+
+This example demonstrates how to create programmatically Hello World example.
+JAX-RS resource returns the usual text `Hello World!`.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path             | Resource class      | HTTP methods | Notes
+-------------------- | ------------------- | ------------ | --------------------------------------------------------
+**_/helloworld_**    | HelloWorldResource  | GET          | Returns `Hello World!`
+**_/helloworld_**    | HelloWorldResource  | OPTIONS      | Returns HTTP Status 204
+**_/helloworld_**    | HelloWorldResource  | HEAD         | Returns HTTP Status 204
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container. You can access the application at:
+
+-   <http://localhost:8080/base/helloworld>
diff --git a/examples/helloworld-programmatic/pom.xml b/examples/helloworld-programmatic/pom.xml
new file mode 100644
index 0000000..5356b50
--- /dev/null
+++ b/examples/helloworld-programmatic/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>helloworld-programmatic</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-helloworld-programmatic</name>
+
+    <description>Jersey programmatic resource API "Hello world" example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.helloworld.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/helloworld-programmatic/src/main/java/org/glassfish/jersey/examples/helloworld/App.java b/examples/helloworld-programmatic/src/main/java/org/glassfish/jersey/examples/helloworld/App.java
new file mode 100644
index 0000000..5969cc5
--- /dev/null
+++ b/examples/helloworld-programmatic/src/main/java/org/glassfish/jersey/examples/helloworld/App.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * This is the example entry point, where Jersey application for the example
+ * gets populated and published using the Grizzly 2 HTTP container.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/base/");
+    /**
+     * "Hello World" root resource path.
+     */
+    public static final String ROOT_PATH = "helloworld";
+
+    /**
+     * Main application entry point.
+     *
+     * @param args application arguments.
+     */
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Hello World\" Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, create(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(
+                    String.format("Application started.%n"
+                    + "Try out %s%s%n"
+                    + "Stop the application using CTRL+C",
+                    BASE_URI, ROOT_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+
+    /**
+     * Test assertion indicator that a GET method handler has been called.
+     */
+    public static volatile boolean getMethodCalled = false;
+    /**
+     * Test assertion indicator that a HEAD method handler has been called.
+     */
+    public static volatile boolean headMethodCalled = false;
+
+    /**
+     * Create example application resource configuration.
+     *
+     * @return initialized resource configuration of the example application.
+     */
+    public static ResourceConfig create() {
+        final Resource.Builder resourceBuilder = Resource.builder(ROOT_PATH);
+
+        resourceBuilder.addMethod("GET").handledBy(new Inflector<ContainerRequestContext, Response>() {
+
+                    @Override
+                    public Response apply(ContainerRequestContext data) {
+                        getMethodCalled = true;
+                        return Response.ok("Hello World!").build();
+                    }
+                });
+
+        Inflector<ContainerRequestContext, Response> noContentResponder = new Inflector<ContainerRequestContext, Response>() {
+
+            @Override
+            public Response apply(ContainerRequestContext data) {
+                headMethodCalled = true;
+                return Response.noContent().build();
+            }
+        };
+        resourceBuilder.addMethod("HEAD").handledBy(noContentResponder);
+        resourceBuilder.addMethod("OPTIONS").handledBy(noContentResponder);
+
+        return new ResourceConfig().registerResources(resourceBuilder.build());
+    }
+}
diff --git a/examples/helloworld-programmatic/src/test/java/org/glassfish/jersey/examples/helloworld/HelloWorldTest.java b/examples/helloworld-programmatic/src/test/java/org/glassfish/jersey/examples/helloworld/HelloWorldTest.java
new file mode 100644
index 0000000..f9596ef
--- /dev/null
+++ b/examples/helloworld-programmatic/src/test/java/org/glassfish/jersey/examples/helloworld/HelloWorldTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class HelloWorldTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return App.create();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        Client client = ClientBuilder.newClient();
+
+        assertFalse(App.getMethodCalled);
+        Response response = client.target(UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).build().toString())
+                .request("text/plain").get();
+        assertTrue(App.getMethodCalled);
+        assertEquals(200, response.getStatus());
+        assertTrue(response.hasEntity());
+        assertTrue("Hello World!".equals(response.readEntity(String.class)));
+
+        String s = client.target(getBaseUri()).path("helloworld").request().get(String.class);
+        assertTrue(s.equals("Hello World!"));
+    }
+
+    @Test
+    public void testHelloWorldOtherMethods() throws Exception {
+        Client client = ClientBuilder.newClient();
+        assertFalse(App.headMethodCalled);
+        Response response = client.target(UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).build().toString())
+                .request("text/plain").head();
+        assertTrue(App.headMethodCalled);
+        assertEquals(204, response.getStatus());
+
+        response = client.target(UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).build().toString()).request("text/plain")
+                .options();
+        assertEquals(204, response.getStatus());
+    }
+}
diff --git a/examples/helloworld-pure-jax-rs/README.MD b/examples/helloworld-pure-jax-rs/README.MD
new file mode 100644
index 0000000..8129168
--- /dev/null
+++ b/examples/helloworld-pure-jax-rs/README.MD
@@ -0,0 +1,32 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Hello World Example
+===================
+
+This example demonstrates Hello World example. JAX-RS resource returns the usual text `Hello World!`.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path             | Resource class      | HTTP methods | Notes
+-------------------- | ------------------- | ------------ | --------------------------------------------------------
+**_/helloworld_**    | HelloWorldResource  |  GET         |  Returns `Hello World!`
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container. You can access the application at:
+
+-   <http://localhost:8080/helloworld>
diff --git a/examples/helloworld-pure-jax-rs/pom.xml b/examples/helloworld-pure-jax-rs/pom.xml
new file mode 100644
index 0000000..7e34ec4
--- /dev/null
+++ b/examples/helloworld-pure-jax-rs/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>helloworld-pure-jax-rs</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-helloworld-pure-jax-rs</name>
+
+    <description>Example using only the standard JAX-RS API's and the lightweight HTTP server bundled in JDK.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-jdk-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.helloworld.jaxrs.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/helloworld-pure-jax-rs/src/main/java/org/glassfish/jersey/examples/helloworld/jaxrs/App.java b/examples/helloworld-pure-jax-rs/src/main/java/org/glassfish/jersey/examples/helloworld/jaxrs/App.java
new file mode 100644
index 0000000..3bb193c
--- /dev/null
+++ b/examples/helloworld-pure-jax-rs/src/main/java/org/glassfish/jersey/examples/helloworld/jaxrs/App.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.jaxrs;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.URI;
+
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+
+/**
+ * Hello world application using only the standard JAX-RS API and lightweight HTTP server bundled in JDK.
+ *
+ * @author Martin Matula
+ */
+public class App {
+
+    /**
+     * Starts the lightweight HTTP server serving the JAX-RS application.
+     *
+     * @return new instance of the lightweight HTTP server
+     * @throws IOException
+     */
+    static HttpServer startServer() throws IOException {
+        // create a new server listening at port 8080
+        final HttpServer server = HttpServer.create(new InetSocketAddress(getBaseURI().getPort()), 0);
+        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+            @Override
+            public void run() {
+                server.stop(0);
+            }
+        }));
+
+        // create a handler wrapping the JAX-RS application
+        HttpHandler handler = RuntimeDelegate.getInstance().createEndpoint(new JaxRsApplication(), HttpHandler.class);
+
+        // map JAX-RS handler to the server root
+        server.createContext(getBaseURI().getPath(), handler);
+
+        // start the server
+        server.start();
+
+        return server;
+    }
+
+    public static void main(String[] args) throws IOException, InterruptedException {
+        System.out.println("\"Hello World\" Jersey Example Application");
+
+        startServer();
+
+        System.out.println("Application started.\n"
+                + "Try accessing " + getBaseURI() + "helloworld in the browser.\n"
+                + "Hit enter to stop the application...");
+
+        Thread.currentThread().join();
+    }
+
+    private static int getPort(int defaultPort) {
+        final String port = System.getProperty("jersey.config.test.container.port");
+        if (null != port) {
+            try {
+                return Integer.parseInt(port);
+            } catch (NumberFormatException e) {
+                System.out.println("Value of jersey.config.test.container.port property"
+                        + " is not a valid positive integer [" + port + "]."
+                        + " Reverting to default [" + defaultPort + "].");
+            }
+        }
+        return defaultPort;
+    }
+
+    /**
+     * Gets base {@link URI}.
+     *
+     * @return base {@link URI}.
+     */
+    public static URI getBaseURI() {
+        return UriBuilder.fromUri("http://localhost/").port(getPort(8080)).build();
+    }
+}
diff --git a/examples/helloworld-pure-jax-rs/src/main/java/org/glassfish/jersey/examples/helloworld/jaxrs/HelloWorldResource.java b/examples/helloworld-pure-jax-rs/src/main/java/org/glassfish/jersey/examples/helloworld/jaxrs/HelloWorldResource.java
new file mode 100644
index 0000000..856f233
--- /dev/null
+++ b/examples/helloworld-pure-jax-rs/src/main/java/org/glassfish/jersey/examples/helloworld/jaxrs/HelloWorldResource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.jaxrs;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+    public static final String CLICHED_MESSAGE = "Hello World!";
+
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String getHello() {
+        return CLICHED_MESSAGE;
+    }
+
+}
diff --git a/examples/helloworld-pure-jax-rs/src/main/java/org/glassfish/jersey/examples/helloworld/jaxrs/JaxRsApplication.java b/examples/helloworld-pure-jax-rs/src/main/java/org/glassfish/jersey/examples/helloworld/jaxrs/JaxRsApplication.java
new file mode 100644
index 0000000..240ea1c
--- /dev/null
+++ b/examples/helloworld-pure-jax-rs/src/main/java/org/glassfish/jersey/examples/helloworld/jaxrs/JaxRsApplication.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.jaxrs;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS Application class for this example.
+ *
+ * @author Martin Matula
+ */
+public class JaxRsApplication extends Application {
+    private final Set<Class<?>> classes;
+
+    public JaxRsApplication() {
+        HashSet<Class<?>> c = new HashSet<Class<?>>();
+        c.add(HelloWorldResource.class);
+        classes = Collections.unmodifiableSet(c);
+    }
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return classes;
+    }
+}
diff --git a/examples/helloworld-pure-jax-rs/src/test/java/org/glassfish/jersey/examples/helloworld/jaxrs/HelloWorldTest.java b/examples/helloworld-pure-jax-rs/src/test/java/org/glassfish/jersey/examples/helloworld/jaxrs/HelloWorldTest.java
new file mode 100644
index 0000000..2562a4f
--- /dev/null
+++ b/examples/helloworld-pure-jax-rs/src/test/java/org/glassfish/jersey/examples/helloworld/jaxrs/HelloWorldTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.jaxrs;
+
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+import com.sun.net.httpserver.HttpServer;
+
+/**
+ * Simple test to confirm the server is running and serving our resource.
+ *
+ * @author Martin Matula
+ */
+public class HelloWorldTest {
+    @Test
+    public void testHelloWorld() throws Exception {
+        HttpServer server = App.startServer();
+
+        WebTarget target = ClientBuilder.newClient().target(App.getBaseURI() + "helloworld");
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, target.request(MediaType.TEXT_PLAIN).get(String.class));
+
+        server.stop(0);
+    }
+}
diff --git a/examples/helloworld-spring-annotations/README.MD b/examples/helloworld-spring-annotations/README.MD
new file mode 100644
index 0000000..359a595
--- /dev/null
+++ b/examples/helloworld-spring-annotations/README.MD
@@ -0,0 +1,37 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Jersey Spring Integration Spring Helloworld WebApp Example
+==========================================================
+
+This example demonstrates how to create simple Jersey application that
+utilises Spring 3 DI features based on Spring annotations, packaged into WAR file.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                                   | Resource class            | HTTP methods
+------------------------------------------ | ------------------------- | --------------
+**_/spring-resource_**                     | SpringRequestResource     | GET
+**_/spring-resource/goodbye_**             | SpringRequestResource     | GET
+**_/spring-resource/norwegian-goodbye_**   | SpringRequestResource     | GET
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
+
+-   <http://localhost:8080/base/spring-resource>
+-   <http://localhost:8080/base/spring-resource/goodbye>
+-   <http://localhost:8080/base/spring-resource/norwegian-goodbye>
\ No newline at end of file
diff --git a/examples/helloworld-spring-annotations/pom.xml b/examples/helloworld-spring-annotations/pom.xml
new file mode 100644
index 0000000..555e33e
--- /dev/null
+++ b/examples/helloworld-spring-annotations/pom.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>helloworld-spring-annotations</artifactId>
+    <description>Spring 4 Integration Jersey Example</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-spring4</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <version>${spring4.version}</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <version>1.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-inmemory</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.hello.spring.annotations.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>delayed-strategy-skip-test</id>
+            <activation>
+                <property>
+                    <name>org.glassfish.jersey.injection.manager.strategy</name>
+                    <value>delayed</value>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <skipTests>true</skipTests>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/App.java b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/App.java
new file mode 100644
index 0000000..3556034
--- /dev/null
+++ b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/App.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.hello.spring.annotations;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/**
+ * @author Petr Bouda
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/base/");
+
+    public static final String ROOT_PATH = "base";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Hello World\" Jersey-Spring Example App");
+
+            final JerseyConfig resourceConfig = new JerseyConfig();
+            resourceConfig.property("contextConfig", new AnnotationConfigApplicationContext(SpringAnnotationConfig.class));
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.\nTry out %s%s\nStop the application using CTRL+C",
+                    BASE_URI, ROOT_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+}
diff --git a/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/EnglishGoodbyeService.java b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/EnglishGoodbyeService.java
new file mode 100644
index 0000000..117174a
--- /dev/null
+++ b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/EnglishGoodbyeService.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.hello.spring.annotations;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public class EnglishGoodbyeService implements GoodbyeService {
+
+    @Override
+    public String goodbye(final String who) {
+        return String.format("goodbye, %s!", who);
+    }
+}
diff --git a/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/GoodbyeService.java b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/GoodbyeService.java
new file mode 100644
index 0000000..9657d8a
--- /dev/null
+++ b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/GoodbyeService.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.hello.spring.annotations;
+
+public interface GoodbyeService {
+
+    String goodbye(String who);
+}
diff --git a/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/GreetingService.java b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/GreetingService.java
new file mode 100644
index 0000000..213f107
--- /dev/null
+++ b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/GreetingService.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.hello.spring.annotations;
+
+import org.springframework.stereotype.Service;
+
+/**
+ * Simple greeting service
+ *
+ * @author Geoffroy Warin (http://geowarin.github.io)
+ */
+@Service
+public class GreetingService {
+    public String greet(String who) {
+        return String.format("hello, %s!", who);
+    }
+}
diff --git a/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/JerseyConfig.java b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/JerseyConfig.java
new file mode 100644
index 0000000..372ca86
--- /dev/null
+++ b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/JerseyConfig.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.hello.spring.annotations;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spring.scope.RequestContextFilter;
+
+/**
+ * A simple package based configuration
+ *
+ * @author Geoffroy Warin (http://geowarin.github.io)
+ */
+public class JerseyConfig extends ResourceConfig {
+
+    public JerseyConfig() {
+        register(RequestContextFilter.class);
+        packages("org.glassfish.jersey.examples.hello.spring.annotations");
+        register(LoggingFeature.class);
+    }
+}
diff --git a/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/NorwegianGoodbyeService.java b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/NorwegianGoodbyeService.java
new file mode 100644
index 0000000..ac5fe19
--- /dev/null
+++ b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/NorwegianGoodbyeService.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.hello.spring.annotations;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public class NorwegianGoodbyeService implements GoodbyeService {
+
+    @Override
+    public String goodbye(final String who) {
+        return String.format("hadet, %s!", who);
+    }
+}
diff --git a/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/SpringAnnotationConfig.java b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/SpringAnnotationConfig.java
new file mode 100644
index 0000000..b0e63f0
--- /dev/null
+++ b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/SpringAnnotationConfig.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.hello.spring.annotations;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Spring configuration to include our services
+ *
+ * @author Geoffroy Warin (http://geowarin.github.io)
+ */
+@Configuration
+@ComponentScan(basePackageClasses = {GreetingService.class})
+public class SpringAnnotationConfig {
+}
diff --git a/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/SpringRequestResource.java b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/SpringRequestResource.java
new file mode 100644
index 0000000..c0dbbbe
--- /dev/null
+++ b/examples/helloworld-spring-annotations/src/main/java/org/glassfish/jersey/examples/hello/spring/annotations/SpringRequestResource.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.hello.spring.annotations;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.inject.Singleton;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * Integration of jersey and spring.
+ * This rest controller is a singleton spring bean with autowired dependencies
+ * from spring
+ *
+ * @author Geoffroy Warin (http://geowarin.github.io)
+ */
+@Singleton
+@Path("spring-resource")
+@Service
+public class SpringRequestResource {
+
+    AtomicInteger counter = new AtomicInteger();
+
+    @Autowired
+    private GreetingService greetingService;
+
+    @Autowired
+    private List<GoodbyeService> goodbyeServicesList;
+    @Autowired
+    private Set<GoodbyeService> goodbyeServicesSet;
+
+    @Autowired
+    private List<GoodbyeService> goodbyeServicesIterable;
+
+
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String getHello() {
+        return greetingService.greet("world " + counter.incrementAndGet());
+    }
+
+    private void checkIntegrity() {
+        final Iterator<GoodbyeService> it = goodbyeServicesIterable.iterator();
+        int i = 0;
+        while (it.hasNext()) {
+
+            final GoodbyeService s1 = it.next();
+            final GoodbyeService s2 = goodbyeServicesList.get(i);
+            if (s1 != s2) {
+                throw new ProcessingException("Instance of service s1 (" + s1.getClass()
+                        + ") is not equal to service s2(" + s2.getClass() + ")");
+            }
+            i++;
+        }
+
+        if (goodbyeServicesList.size() != goodbyeServicesSet.size()) {
+            throw new ProcessingException("Size of set and size of the list differs. list=" + goodbyeServicesList.size()
+                    + "; set=" + goodbyeServicesSet.size());
+        }
+    }
+
+    private GoodbyeService getService(Class<?> serviceClass) {
+        for (GoodbyeService service : goodbyeServicesList) {
+            if (serviceClass.isAssignableFrom(service.getClass())) {
+                return service;
+            }
+        }
+        return null;
+    }
+
+    @Path("goodbye")
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String getGoodbye() {
+        checkIntegrity();
+
+        final GoodbyeService goodbyeService = getService(EnglishGoodbyeService.class);
+        return goodbyeService.goodbye("cruel world");
+    }
+
+    @Path("norwegian-goodbye")
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String getNorwegianGoodbye() {
+        checkIntegrity();
+        return getService(NorwegianGoodbyeService.class).goodbye("på badet");
+    }
+}
diff --git a/examples/helloworld-spring-annotations/src/test/java/org/glassfish/jersey/examples/hello/spring/annotations/SpringRequestResourceTest.java b/examples/helloworld-spring-annotations/src/test/java/org/glassfish/jersey/examples/hello/spring/annotations/SpringRequestResourceTest.java
new file mode 100644
index 0000000..0f6fd4a
--- /dev/null
+++ b/examples/helloworld-spring-annotations/src/test/java/org/glassfish/jersey/examples/hello/spring/annotations/SpringRequestResourceTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.hello.spring.annotations;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/**
+ * Testing our service with our annotation context being passed directly to jersey-spring
+ *
+ * @author Geoffroy Warin (http://geowarin.github.io)
+ */
+public class SpringRequestResourceTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        ApplicationContext context = new AnnotationConfigApplicationContext(SpringAnnotationConfig.class);
+        return new JerseyConfig().property("contextConfig", context);
+    }
+
+    @Test
+    public void testGreet() throws Exception {
+        final String greeting = target("spring-resource").request().get(String.class);
+        Assert.assertEquals("hello, world 1!", greeting);
+        final String greeting2 = target("spring-resource").request().get(String.class);
+        Assert.assertEquals("hello, world 2!", greeting2);
+    }
+
+    @Test
+    public void testGoodbye() {
+        final String goodbye = target("spring-resource").path("goodbye").request().get(String.class);
+        Assert.assertEquals("goodbye, cruel world!", goodbye);
+        final String norwegianGoodbye = target("spring-resource").path("norwegian-goodbye").request().get(String.class);
+        Assert.assertEquals("hadet, på badet!", norwegianGoodbye);
+    }
+}
diff --git a/examples/helloworld-spring-webapp/README.MD b/examples/helloworld-spring-webapp/README.MD
new file mode 100644
index 0000000..a5f07f7
--- /dev/null
+++ b/examples/helloworld-spring-webapp/README.MD
@@ -0,0 +1,43 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Jersey Spring Integration Spring Helloworld WebApp Example
+==========================================================
+
+This example demonstrates how to create simple Jersey application that
+utilises Spring 3 DI features, packaged into WAR file.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                        | Resource class            | HTTP methods
+------------------------------- | ------------------------- | --------------
+**_/jersey-hello_**             | JerseyResource            | GET
+**_/spring-hello_**             | SpringRequestResource     | GET
+**_/spring-singleton-hello_**   | SpringSingletonResource   | GET
+
+Application is configured by using web.xml, which registers
+[javax.ws.rs.core.Application](https://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Application.html)
+descendant to get classes and singletons from it (see class
+MyApplication).
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean package jetty:run
+
+This deploys current example using Jetty web server. Once deployed, the
+application is ready to serve the following resources:
+
+-   <http://localhost:8080/helloworld-spring-webapp/jersey-hello>
+-   <http://localhost:8080/helloworld-spring-webapp/spring-hello>
+-   <http://localhost:8080/helloworld-spring-webapp/spring-singleton-hello>
\ No newline at end of file
diff --git a/examples/helloworld-spring-webapp/pom.xml b/examples/helloworld-spring-webapp/pom.xml
new file mode 100644
index 0000000..1ef882f
--- /dev/null
+++ b/examples/helloworld-spring-webapp/pom.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <!-- There is problem to run Spring example on GF - https://java.net/jira/browse/JERSEY-2032
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.2-SNAPSHOT</version>
+    </parent>
+    -->
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>helloworld-spring-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-helloworld-spring-webapp</name>
+
+    <description>Spring 4 Integration Jersey Example</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <version>${spring4.version}</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <version>1.2</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.servlet</groupId>
+                    <artifactId>servlet-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-spring4</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>servlet-api-2.5</artifactId>
+            <version>${jetty.servlet.api.25.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Run the application using "mvn jetty:run" to deploy to Jetty-->
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>maven-jetty-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/CustomExceptionMapper.java b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/CustomExceptionMapper.java
new file mode 100644
index 0000000..ec1e6b4
--- /dev/null
+++ b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/CustomExceptionMapper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.spring;
+
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Exception mapper to convert {@link IllegalArgumentException} into a textual response.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+@Provider
+public class CustomExceptionMapper implements ExceptionMapper<IllegalArgumentException> {
+
+    @Override
+    public Response toResponse(IllegalArgumentException exception) {
+        return Response.ok("Illegal Argument Exception Caught").build();
+    }
+}
diff --git a/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/DateTimeService.java b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/DateTimeService.java
new file mode 100644
index 0000000..2bc3a42
--- /dev/null
+++ b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/DateTimeService.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.spring;
+
+import java.util.Date;
+
+/**
+ * Simple date service that provides actual time and date info.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+public class DateTimeService {
+
+    /**
+     * Get current date and time.
+     *
+     * @return current date.
+     */
+    public Date getDateTime() {
+        return new Date();
+    }
+}
diff --git a/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/GreetingService.java b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/GreetingService.java
new file mode 100644
index 0000000..ca9f719
--- /dev/null
+++ b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/GreetingService.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.spring;
+
+/**
+ * Simple greeting service.
+ *
+ * @author Marko Asplund (marko.asplund at oracle.com)
+ */
+public interface GreetingService {
+
+    /**
+     * Workout a greeting.
+     *
+     * @param who to greet.
+     * @return greeting.
+     */
+    String greet(String who);
+}
diff --git a/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/GreetingServiceImpl.java b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/GreetingServiceImpl.java
new file mode 100644
index 0000000..af54d2a
--- /dev/null
+++ b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/GreetingServiceImpl.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.spring;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Simple {@link GreetingService} implementation to just say hello.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class GreetingServiceImpl implements GreetingService {
+
+    @Autowired
+    private HttpServletRequest servletRequest;
+
+    @Override
+    public String greet(String who) {
+        final String serverName = servletRequest.getServerName();
+        return String.format("hello, %s! Greetings from server %s!", who, serverName);
+    }
+
+}
diff --git a/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/JerseyResource.java b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/JerseyResource.java
new file mode 100644
index 0000000..6c0439c
--- /dev/null
+++ b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/JerseyResource.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.spring;
+
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Jersey Spring integration example.
+ * Demonstrate how to inject a Spring bean into a Jersey managed JAX-RS resource class.
+ *
+ * @author Marko Asplund (marko.asplund at gmail.com)
+ */
+@Path("jersey-hello")
+public class JerseyResource {
+    private static final Logger LOGGER = Logger.getLogger(JerseyResource.class.getName());
+
+    @Autowired
+    private GreetingService greetingService;
+
+    @Inject
+    private DateTimeService timeService;
+
+    public JerseyResource() {
+        LOGGER.fine("HelloWorldResource()");
+    }
+
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String getHello() {
+        return String.format("%s: %s", timeService.getDateTime(), greetingService.greet("world"));
+    }
+
+}
diff --git a/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/MyApplication.java b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/MyApplication.java
new file mode 100644
index 0000000..f07b225
--- /dev/null
+++ b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/MyApplication.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.spring;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spring.scope.RequestContextFilter;
+
+/**
+ * Spring HelloWorld Web Application configuration.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class MyApplication extends ResourceConfig {
+
+    /**
+     * Register JAX-RS application components.
+     */
+    public MyApplication() {
+        register(RequestContextFilter.class);
+        register(JerseyResource.class);
+        register(SpringSingletonResource.class);
+        register(SpringRequestResource.class);
+        register(CustomExceptionMapper.class);
+    }
+}
diff --git a/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/SpringRequestResource.java b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/SpringRequestResource.java
new file mode 100644
index 0000000..8987a85
--- /dev/null
+++ b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/SpringRequestResource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.spring;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * Jersey Spring integration example.
+ * Demonstrate how to use Spring managed JAX-RS resource class with request scope (+ Spring bean DI).
+ *
+ * @author Marko Asplund (marko.asplund at gmail.com)
+ */
+@Path("spring-hello")
+@Component
+public class SpringRequestResource {
+
+    @Autowired
+    private GreetingService greetingService;
+
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String getHello() {
+        return greetingService.greet("world");
+    }
+}
diff --git a/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/SpringSingletonResource.java b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/SpringSingletonResource.java
new file mode 100644
index 0000000..de91d63
--- /dev/null
+++ b/examples/helloworld-spring-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/spring/SpringSingletonResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.spring;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+
+import javax.inject.Singleton;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * Jersey Spring integration example.
+ * Demonstrate how to use Spring managed JAX-RS resource class with singleton scope (+ Spring bean DI).
+ *
+ * @author Marko Asplund (marko.asplund at gmail.com)
+ */
+@Path("spring-singleton-hello")
+@Component
+@Singleton
+public class SpringSingletonResource {
+
+    private final AtomicInteger counter = new AtomicInteger();
+
+    @Autowired
+    private GreetingService greetingService;
+
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String getHello(@Context HttpHeaders headers, @QueryParam("p1") String p1) {
+        if ("foobar".equals(p1)) {
+            throw new IllegalArgumentException("foobar is illegal");
+        }
+        return String.format("%d: %s", counter.incrementAndGet(), greetingService.greet("world"));
+    }
+}
diff --git a/examples/helloworld-spring-webapp/src/main/resources/applicationContext.xml b/examples/helloworld-spring-webapp/src/main/resources/applicationContext.xml
new file mode 100644
index 0000000..0a3ffd1
--- /dev/null
+++ b/examples/helloworld-spring-webapp/src/main/resources/applicationContext.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+       http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+    <bean id="greetingService" class="org.glassfish.jersey.examples.helloworld.spring.GreetingServiceImpl"/>
+
+    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
+
+    <bean class="org.glassfish.jersey.examples.helloworld.spring.DateTimeService" scope="request"/>
+
+    <bean class="org.glassfish.jersey.examples.helloworld.spring.SpringSingletonResource"/>
+
+    <bean class="org.glassfish.jersey.examples.helloworld.spring.SpringRequestResource" scope="request"/>
+
+    <bean class="org.glassfish.jersey.examples.helloworld.spring.CustomExceptionMapper"/>
+
+</beans>
diff --git a/examples/helloworld-spring-webapp/src/main/webapp/WEB-INF/web.xml b/examples/helloworld-spring-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..c3bb6a5
--- /dev/null
+++ b/examples/helloworld-spring-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
+
+    <module-name>helloworld-spring</module-name>
+
+    <listener>
+        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+    </listener>
+
+    <context-param>
+        <param-name>contextConfigLocation</param-name>
+        <param-value>classpath:applicationContext.xml</param-value>
+    </context-param>
+
+    <servlet>
+        <servlet-name>SpringApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.examples.helloworld.spring.MyApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>SpringApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
diff --git a/examples/helloworld-webapp/README.MD b/examples/helloworld-webapp/README.MD
new file mode 100644
index 0000000..e449fce
--- /dev/null
+++ b/examples/helloworld-webapp/README.MD
@@ -0,0 +1,38 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Helloworld webapp Example
+=========================
+
+This example demonstrates how to create simple Jersey application,
+package it into WAR file and how to run it using [Grizzly
+container](http://grizzly.java.net).
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path            | Resource class       | HTTP methods
+------------------- | -------------------- | --------------
+**_/helloworld_**   | HelloWorldResource   | GET
+
+Application is configured by using web.xml, which registers
+[javax.ws.rs.core.Application](https://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Application.html)
+descendant to get classes and singletons from it (see class MyApplication).
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container. You can access the application at:
+
+- <http://localhost:8080/helloworld-webapp/helloworld>
diff --git a/examples/helloworld-webapp/pom.xml b/examples/helloworld-webapp/pom.xml
new file mode 100644
index 0000000..ad575de
--- /dev/null
+++ b/examples/helloworld-webapp/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>helloworld-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-helloworld-webapp</name>
+
+    <description>Jersey annotated resource class "Hello world" example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet4.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Run the application using "mvn jetty:run" to deploy to Jetty-->
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>maven-jetty-plugin</artifactId>
+            </plugin>
+            <!-- Run the application using "mvn exec:java" to deploy to Grizzly Servlet-->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.helloworld.webapp.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/helloworld-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/webapp/App.java b/examples/helloworld-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/webapp/App.java
new file mode 100644
index 0000000..85618ce
--- /dev/null
+++ b/examples/helloworld-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/webapp/App.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.webapp;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.servlet.GrizzlyWebContainerFactory;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.servlet.ServletContainer;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/helloworld-webapp/");
+    public static final String ROOT_PATH = "helloworld";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Hello World\" Jersey Example App");
+
+            Map<String, String> initParams = new HashMap<>();
+            initParams.put(
+                    ServerProperties.PROVIDER_PACKAGES,
+                    HelloWorldResource.class.getPackage().getName());
+            final HttpServer server = GrizzlyWebContainerFactory.create(BASE_URI, ServletContainer.class, initParams);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+
+            System.out.println(String.format("Application started.%nTry out %s%s%nStop the application using CTRL+C",
+                    BASE_URI, ROOT_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+}
diff --git a/examples/helloworld-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/webapp/HelloWorldResource.java b/examples/helloworld-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/webapp/HelloWorldResource.java
new file mode 100644
index 0000000..3669b69
--- /dev/null
+++ b/examples/helloworld-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/webapp/HelloWorldResource.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.webapp;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String getHello() {
+        return "Hello World!";
+    }
+
+}
diff --git a/examples/helloworld-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/webapp/MyApplication.java b/examples/helloworld-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/webapp/MyApplication.java
new file mode 100644
index 0000000..5e929e5
--- /dev/null
+++ b/examples/helloworld-webapp/src/main/java/org/glassfish/jersey/examples/helloworld/webapp/MyApplication.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.webapp;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@ApplicationPath("/")
+public class MyApplication extends Application {
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> classes = new HashSet<Class<?>>();
+        // register root resource
+        classes.add(HelloWorldResource.class);
+        return classes;
+    }
+}
diff --git a/examples/helloworld-webapp/src/main/webapp/WEB-INF/glassfish-web.xml b/examples/helloworld-webapp/src/main/webapp/WEB-INF/glassfish-web.xml
new file mode 100644
index 0000000..683dff4
--- /dev/null
+++ b/examples/helloworld-webapp/src/main/webapp/WEB-INF/glassfish-web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
+<glassfish-web-app error-url="">
+    <context-root>/helloworld-webapp</context-root>
+    <class-loader delegate="true" />
+    <jsp-config>
+        <property name="classdebuginfo" value="true">
+            <description>Enable debug info compilation in the generated servlet class</description>
+        </property>
+        <property name="keepgenerated" value="true">
+            <description>Keep a copy of the generated servlet class' java code.</description>
+        </property>
+        <property name="mappedfile" value="true">
+            <description>Maintain a one-to-one correspondence between static content and the generated servlet class' java code</description>
+        </property>
+    </jsp-config>
+</glassfish-web-app>
diff --git a/examples/helloworld-webapp/src/main/webapp/WEB-INF/web.xml b/examples/helloworld-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..1e484b9
--- /dev/null
+++ b/examples/helloworld-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.examples.helloworld_servlet.MyApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.examples.helloworld.webapp.MyApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.examples.helloworld_servlet.MyApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/examples/helloworld-webapp/src/test/java/org/glassfish/jersey/examples/helloworld/webapp/HelloWorldTest.java b/examples/helloworld-webapp/src/test/java/org/glassfish/jersey/examples/helloworld/webapp/HelloWorldTest.java
new file mode 100644
index 0000000..4da4a9f
--- /dev/null
+++ b/examples/helloworld-webapp/src/test/java/org/glassfish/jersey/examples/helloworld/webapp/HelloWorldTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.webapp;
+
+import java.net.URI;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Simple test to check "Hello World!" is being returned from the helloworld resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class HelloWorldTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("helloworld-webapp").build();
+    }
+
+    @Test
+    public void testClientStringResponse() {
+        String s = target().path(App.ROOT_PATH).request().get(String.class);
+        assertEquals("Hello World!", s);
+    }
+}
+
diff --git a/examples/helloworld-weld/README.MD b/examples/helloworld-weld/README.MD
new file mode 100644
index 0000000..e139e21
--- /dev/null
+++ b/examples/helloworld-weld/README.MD
@@ -0,0 +1,40 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Jersey Weld Integration Jersey Weld on Grizzly Example
+======================================================
+
+This example demonstrates how to develop RESTful web service with CDI
+managed beans and Grizzly HTTP container.
+
+We suggest you inspect the example source code to get better
+understanding on various CDI features utilized there. Following table
+contains some of the published resources that you might want to check.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URL                                            | Description                                                          | Expected Results
+---------------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------
+**_/helloworld?name=Bill_**                    | A hello-world managed bean                                           | Hello Bill
+**_/app/count_**                               | Application scoped resource                                          | request count (incremented with every other request)
+**_/req/app/counter_**                         | Request scoped resource                                              | links to above request counter
+**_/req/parameterized-async?q=echo%20this_**   | Asynchronous echo resource method that is being JAX-RS intercepted   | echo this
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container. You can access the application at:
+
+- <http://localhost:8080/weld>
\ No newline at end of file
diff --git a/examples/helloworld-weld/pom.xml b/examples/helloworld-weld/pom.xml
new file mode 100644
index 0000000..44adf89
--- /dev/null
+++ b/examples/helloworld-weld/pom.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>helloworld-weld</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-helloworld-weld</name>
+
+    <description>Jersey annotated resource class "Hello world" example with Weld support.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-weld2-se</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.helloworld.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>delayed-strategy-skip-test</id>
+            <activation>
+                <property>
+                    <name>org.glassfish.jersey.injection.manager.strategy</name>
+                    <value>delayed</value>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <skipTests>true</skipTests>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/App.java b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/App.java
new file mode 100644
index 0000000..d9ed128
--- /dev/null
+++ b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/App.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.annotation.ManagedBean;
+import javax.enterprise.context.ApplicationScoped;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.jboss.weld.environment.se.Weld;
+
+/**
+ * Main Java application. Used to bootstrap Weld container and start Grizzly HTTP container.
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/weld/");
+    public static final String ROOT_PATH = "application.wadl";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Hello World\" Jersey Example Weld App");
+
+            final Weld weld = new Weld();
+            weld.initialize();
+
+            final ResourceConfig resourceConfig = createJaxRsApp();
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                    weld.shutdown();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.\nTry out %s%s\nStop the application using CTRL+C",
+                    BASE_URI, ROOT_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+
+    /**
+     * JAX-RS application defined as a CDI bean.
+     */
+    @ManagedBean
+    public static class JaxRsApplication extends Application {
+
+        @Context
+        UriInfo uInfo;
+
+        static final Set<Class<?>> appClasses = new HashSet<>();
+
+        static {
+            appClasses.add(HelloWorldResource.class);
+            appClasses.add(AppScopedResource.class);
+            appClasses.add(RequestScopedResource.class);
+            appClasses.add(CustomInterceptor.class);
+        }
+
+        @Override
+        public Set<Class<?>> getClasses() {
+            return appClasses;
+        }
+    }
+
+    /**
+     * Create JAX-RS application. The same one is used also in the tests.
+     *
+     * @return Jersey's resource configuration of the Weld application.
+     */
+    public static ResourceConfig createJaxRsApp() {
+        return ResourceConfig.forApplicationClass(JaxRsApplication.class);
+    }
+}
diff --git a/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/AppScopedResource.java b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/AppScopedResource.java
new file mode 100644
index 0000000..fa231fc
--- /dev/null
+++ b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/AppScopedResource.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Application scoped CDI based resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("app")
+@ApplicationScoped
+public class AppScopedResource {
+
+    AtomicInteger counter = new AtomicInteger();
+
+    @GET
+    @Path("count")
+    @Produces("text/plain")
+    public int getCount() {
+        return counter.incrementAndGet();
+    }
+}
diff --git a/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/CustomInterceptor.java b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/CustomInterceptor.java
new file mode 100644
index 0000000..342a7c7
--- /dev/null
+++ b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/CustomInterceptor.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import java.io.IOException;
+
+import javax.enterprise.context.ApplicationScoped;
+
+import javax.inject.Inject;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * CDI based JAX-RS interceptor that re-writes the original output
+ * with request ID obtained from another CDI bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ResponseBodyFromCdiBean
+@ApplicationScoped
+public class CustomInterceptor implements WriterInterceptor {
+
+    // CDI injected proxy
+    @Inject RequestScopedBean bean;
+
+    @Override
+    public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+        final String requestId = bean.getRequestId();
+        context.getOutputStream().write(requestId.getBytes());
+    }
+}
diff --git a/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/HelloWorldResource.java b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/HelloWorldResource.java
new file mode 100644
index 0000000..aa76f50
--- /dev/null
+++ b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/HelloWorldResource.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import javax.enterprise.context.RequestScoped;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+
+/**
+ * Hello-world JAX-RS resource implemented as CDI bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("helloworld")
+@RequestScoped
+public class HelloWorldResource {
+
+    @QueryParam("name")
+    String name;
+
+    @GET
+    @Produces("text/plain")
+    public String getHello() {
+        return String.format("Hello %s", name);
+    }
+}
diff --git a/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/RequestScopedBean.java b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/RequestScopedBean.java
new file mode 100644
index 0000000..8cac041
--- /dev/null
+++ b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/RequestScopedBean.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import javax.enterprise.context.RequestScoped;
+
+/**
+ * Request scoped CDI bean. Serves as a storage
+ * for request scoped data to demonstrate that CDI based
+ * JAX-RS interceptor, {@link CustomInterceptor}, could
+ * use CDI means to obtain JAX-RS request data.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+public class RequestScopedBean {
+
+    private String requestId;
+
+    /**
+     * Request ID setter.
+     *
+     * @param requestId
+     */
+    public void setRequestId(String requestId) {
+        this.requestId = requestId;
+    }
+
+    /**
+     * Get me current request ID.
+     *
+     * @return request id.
+     */
+    public String getRequestId() {
+        return requestId;
+    }
+}
diff --git a/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/RequestScopedResource.java b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/RequestScopedResource.java
new file mode 100644
index 0000000..135b110
--- /dev/null
+++ b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/RequestScopedResource.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * JAX-RS resource class backed by a request scoped CDI bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Path("req")
+public class RequestScopedResource {
+
+    @Inject
+    AppScopedResource appResource;
+
+    @Inject
+    RequestScopedBean bean;
+
+    @GET
+    @Path("app/counter")
+    public int getCounter() {
+        return appResource.getCount();
+    }
+
+    @GET
+    @Path("myself")
+    public String getMyself() {
+        return this.toString();
+    }
+
+    @GET
+    @Path("parameterized")
+    @ResponseBodyFromCdiBean
+    public String interceptedParameterized(@QueryParam("q") String q) {
+        bean.setRequestId(q);
+        return "does not matter";
+    }
+
+    @GET
+    @Path("straight")
+    public String parameterizedStraight(@QueryParam("q") String q) {
+        return "straight: " + q;
+    }
+
+    private static final Executor executor = Executors.newCachedThreadPool();
+
+    @GET
+    @Path("parameterized-async")
+    @ResponseBodyFromCdiBean
+    public void interceptedParameterizedAsync(@QueryParam("q") final String q, @Suspended final AsyncResponse response) {
+        bean.setRequestId(q);
+        executor.execute(new Runnable() {
+
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException ex) {
+                    Logger.getLogger(RequestScopedResource.class.getName()).log(Level.SEVERE, null, ex);
+                }
+                response.resume("this will never make it to the client");
+            }
+        });
+    }
+
+    @Context
+    UriInfo uriInfo;
+
+    @Inject App.JaxRsApplication jaxRsApplication;
+
+    @GET
+    @Path("ui/jax-rs-field/{d}")
+    public String getJaxRsInjectedUIUri() {
+
+        if (uriInfo == jaxRsApplication.uInfo) {
+            throw new IllegalStateException("UriInfo injected into req scoped cdi bean should not get proxied.");
+        }
+
+        return uriInfo.getRequestUri().toString();
+    }
+
+    @GET
+    @Path("ui/jax-rs-app-field/{d}")
+    public String getCdiInjectedJaxRsAppUri() {
+        return jaxRsApplication.uInfo.getRequestUri().toString();
+    }
+}
diff --git a/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/ResponseBodyFromCdiBean.java b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/ResponseBodyFromCdiBean.java
new file mode 100644
index 0000000..f403648
--- /dev/null
+++ b/examples/helloworld-weld/src/main/java/org/glassfish/jersey/examples/helloworld/ResponseBodyFromCdiBean.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import javax.ws.rs.NameBinding;
+
+/**
+ * Binds {@link org.glassfish.jersey.examples.helloworld.CustomInterceptor} with resource methods that should return modified
+ * entity than the one returned from the method.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Retention(RUNTIME)
+@Target({METHOD, TYPE})
+@NameBinding
+public @interface ResponseBodyFromCdiBean {
+}
diff --git a/examples/helloworld-weld/src/main/resources/META-INF/beans.xml b/examples/helloworld-weld/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..5d361e9
--- /dev/null
+++ b/examples/helloworld-weld/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<beans/>
diff --git a/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/AppScopedResourceTest.java b/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/AppScopedResourceTest.java
new file mode 100644
index 0000000..33dc579
--- /dev/null
+++ b/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/AppScopedResourceTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.jboss.weld.environment.se.Weld;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test for the {@link AppScopedResource} JAX-RS resource class.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class AppScopedResourceTest extends JerseyTest {
+
+    Weld weld;
+
+    @Override
+    public void setUp() throws Exception {
+        weld = new Weld();
+        weld.initialize();
+        super.setUp();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        weld.shutdown();
+        super.tearDown();
+    }
+
+    @Override
+    protected ResourceConfig configure() {
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.simple.SimpleTestContainerFactory
+        enable(TestProperties.LOG_TRAFFIC);
+        // enable(TestProperties.DUMP_ENTITY);
+        return App.createJaxRsApp();
+    }
+
+    @Test
+    public void testCounter() {
+        final Invocation.Builder counter = target().path("app/count").request("text/plain");
+
+        Response response = counter.get();
+
+        assertEquals(200, response.getStatus());
+        int c1 = response.readEntity(Integer.class);
+        int c2 = counter.get(Integer.class);
+        int c3 = counter.get(Integer.class);
+        assertTrue(c1 < c2);
+        assertTrue(c2 < c3);
+    }
+}
diff --git a/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/HelloWorldTest.java b/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/HelloWorldTest.java
new file mode 100644
index 0000000..df6a311
--- /dev/null
+++ b/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/HelloWorldTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.jboss.weld.environment.se.Weld;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for the {@link HelloWorldResource} JAX-RS resource class.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class HelloWorldTest extends JerseyTest {
+
+    private Weld weld;
+
+    @Override
+    public void setUp() throws Exception {
+        weld = new Weld();
+        weld.initialize();
+        super.setUp();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        weld.shutdown();
+        super.tearDown();
+    }
+
+    @Override
+    protected ResourceConfig configure() {
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.simple.SimpleTestContainerFactory
+        enable(TestProperties.LOG_TRAFFIC);
+        // enable(TestProperties.DUMP_ENTITY);
+        return App.createJaxRsApp();
+    }
+
+    @Test
+    public void testHelloWorld() {
+        Response response = target().path("helloworld").queryParam("name", "Josef").request("text/plain").get();
+        assertEquals(200, response.getStatus());
+        assertEquals(String.format("Hello %s", "Josef"), response.readEntity(String.class));
+    }
+}
diff --git a/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/RequestScopeAlignmentTest.java b/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/RequestScopeAlignmentTest.java
new file mode 100644
index 0000000..b6be123
--- /dev/null
+++ b/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/RequestScopeAlignmentTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.jboss.weld.environment.se.Weld;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for the request scoped managed bean resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class RequestScopeAlignmentTest extends JerseyTest {
+
+    static Weld weld;
+
+    @BeforeClass
+    public static void before() throws Exception {
+        weld = new Weld();
+        weld.initialize();
+    }
+
+    @AfterClass
+    public static void after() throws Exception {
+        weld.shutdown();
+    }
+
+    @Override
+    protected ResourceConfig configure() {
+        return App.createJaxRsApp();
+    }
+
+    @Test
+    public void testUriInfoPropagatesToApp() {
+
+        for (String d : new String[]{"one", "two", "three"}) {
+
+            final WebTarget fieldTarget = target().path("req/ui/jax-rs-field").path(d);
+            final WebTarget appFieldTarget = target().path("req/ui/jax-rs-app-field").path(d);
+
+            String f = fieldTarget.request().get(String.class);
+            assertThat(f, containsString(fieldTarget.getUri().toString()));
+            String af = appFieldTarget.request().get(String.class);
+            assertThat(af, containsString(appFieldTarget.getUri().toString()));
+        }
+    }
+}
diff --git a/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/RequestScopedResourceTest.java b/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/RequestScopedResourceTest.java
new file mode 100644
index 0000000..f6a4fa9
--- /dev/null
+++ b/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/RequestScopedResourceTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import java.util.Iterator;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.util.runner.ConcurrentParameterizedRunner;
+
+import org.jboss.weld.environment.se.Weld;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Test request scoped resource. Number of various requests will be made in parallel
+ * against a single Grizzly instance. This is to ensure server side external request scope
+ * binding does not mix different request data.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(ConcurrentParameterizedRunner.class)
+public class RequestScopedResourceTest extends JerseyTest {
+
+    // Total number of requests to make
+    static final int REQUEST_COUNT = 1000;
+    // basis for test data sequence
+    static final AtomicInteger dataFeed = new AtomicInteger();
+
+    // to help us randomily select resource method to test
+    static final Random RANDOMIZER = new Random();
+
+    // our Weld container instance
+    static Weld weld;
+
+    /**
+     * Take test data sequence from here
+     *
+     * @return iterable test input data
+     */
+    @Parameterized.Parameters
+    public static Iterable<Object[]> data() {
+        return new Iterable<Object[]>() {
+
+            @Override
+            public Iterator<Object[]> iterator() {
+                return new Iterator<Object[]>() {
+
+                    @Override
+                    public boolean hasNext() {
+                        return dataFeed.get() < REQUEST_COUNT;
+                    }
+
+                    @Override
+                    public Object[] next() {
+                        Object[] result = new Object[1];
+                        int nextValue = dataFeed.getAndIncrement();
+                        result[0] = String.format("%02d", nextValue);
+                        return result;
+                    }
+
+                    @Override
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+        };
+    }
+
+    @BeforeClass
+    public static void before() throws Exception {
+        weld = new Weld();
+        weld.initialize();
+    }
+
+    @AfterClass
+    public static void after() throws Exception {
+        weld.shutdown();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        System.out.printf("SYNC: %d, ASYNC: %d, STRAIGHT: %d%n",
+                parameterizedCounter.intValue(), parameterizedAsyncCounter.intValue(), straightCounter.intValue());
+    }
+
+    @Override
+    protected ResourceConfig configure() {
+        //        enable(TestProperties.LOG_TRAFFIC);
+        return App.createJaxRsApp();
+    }
+
+    // we want to keep some statistics
+    final AtomicInteger parameterizedCounter = new AtomicInteger(0);
+    final AtomicInteger parameterizedAsyncCounter = new AtomicInteger(0);
+    final AtomicInteger straightCounter = new AtomicInteger(0);
+
+    @Test
+    public void testRequestScopedResource(final String param) {
+
+        String path;
+        String expected = param;
+
+        // select one of the three resource methods available
+        switch (RANDOMIZER.nextInt(3)) {
+            case 0:
+                path = "req/parameterized";
+                parameterizedCounter.incrementAndGet();
+                break;
+            case 1:
+                path = "req/parameterized-async";
+                parameterizedAsyncCounter.incrementAndGet();
+                break;
+            default:
+                path = "req/straight";
+                expected = String.format("straight: %s", param);
+                straightCounter.incrementAndGet();
+                break;
+        }
+
+        final Response response = target().path(path).queryParam("q", param).request("text/plain").get();
+
+        assertNotNull(String.format("Request failed for %s", path), response);
+        assertEquals(200, response.getStatus());
+        assertEquals(expected, response.readEntity(String.class));
+    }
+}
diff --git a/examples/helloworld/README.MD b/examples/helloworld/README.MD
new file mode 100644
index 0000000..350d991
--- /dev/null
+++ b/examples/helloworld/README.MD
@@ -0,0 +1,32 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Hello World Example
+===================
+
+This example demonstrates Hello World example. JAX-RS resource returns the usual text
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path             | Resource class      | HTTP methods | Notes
+-------------------- | ------------------- | ------------ | --------------------------------------------------------
+**_/helloworld_**    | HelloWorldResource  |  GET         |  Returns `Hello World!`
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
+
+-   <http://localhost:8080/base/helloworld>
diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml
new file mode 100644
index 0000000..e716587
--- /dev/null
+++ b/examples/helloworld/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>helloworld</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-helloworld</name>
+
+    <description>Jersey annotated resource class "Hello world" example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.helloworld.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/helloworld/src/main/java/org/glassfish/jersey/examples/helloworld/App.java b/examples/helloworld/src/main/java/org/glassfish/jersey/examples/helloworld/App.java
new file mode 100644
index 0000000..ae3ec2b
--- /dev/null
+++ b/examples/helloworld/src/main/java/org/glassfish/jersey/examples/helloworld/App.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Hello world!
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/base/");
+    public static final String ROOT_PATH = "helloworld";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Hello World\" Jersey Example App");
+
+            final ResourceConfig resourceConfig = new ResourceConfig(HelloWorldResource.class);
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.\nTry out %s%s\nStop the application using CTRL+C",
+                    BASE_URI, ROOT_PATH));
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+}
diff --git a/examples/helloworld/src/main/java/org/glassfish/jersey/examples/helloworld/HelloWorldResource.java b/examples/helloworld/src/main/java/org/glassfish/jersey/examples/helloworld/HelloWorldResource.java
new file mode 100644
index 0000000..f1326e5
--- /dev/null
+++ b/examples/helloworld/src/main/java/org/glassfish/jersey/examples/helloworld/HelloWorldResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+    public static final String CLICHED_MESSAGE = "Hello World!";
+
+    @GET
+    @Produces("text/plain")
+    public String getHello() {
+        return CLICHED_MESSAGE;
+    }
+
+}
diff --git a/examples/helloworld/src/test/java/org/glassfish/jersey/examples/helloworld/CustomLoggingFilter.java b/examples/helloworld/src/test/java/org/glassfish/jersey/examples/helloworld/CustomLoggingFilter.java
new file mode 100644
index 0000000..4e04d46
--- /dev/null
+++ b/examples/helloworld/src/test/java/org/glassfish/jersey/examples/helloworld/CustomLoggingFilter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import java.io.IOException;
+
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Custom logging filter.
+ *
+ * @author Santiago Pericas-Geertsen (santiago.pericasgeertsen at oracle.com)
+ */
+public class CustomLoggingFilter implements ContainerRequestFilter, ContainerResponseFilter,
+        ClientRequestFilter, ClientResponseFilter {
+
+    static int preFilterCalled = 0;
+    static int postFilterCalled = 0;
+
+    @Override
+    public void filter(ClientRequestContext context) throws IOException {
+        System.out.println("CustomLoggingFilter.preFilter called");
+        assertEquals(context.getConfiguration().getProperty("foo"), "bar");
+        preFilterCalled++;
+    }
+
+    @Override
+    public void filter(ClientRequestContext context, ClientResponseContext clientResponseContext) throws IOException {
+        System.out.println("CustomLoggingFilter.postFilter called");
+        assertEquals(context.getConfiguration().getProperty("foo"), "bar");
+        postFilterCalled++;
+    }
+
+    @Override
+    public void filter(ContainerRequestContext context) throws IOException {
+        System.out.println("CustomLoggingFilter.preFilter called");
+        assertEquals(context.getProperty("foo"), "bar");
+        preFilterCalled++;
+    }
+
+    @Override
+    public void filter(ContainerRequestContext context, ContainerResponseContext containerResponseContext) throws IOException {
+        System.out.println("CustomLoggingFilter.postFilter called");
+        assertEquals(context.getProperty("foo"), "bar");
+        postFilterCalled++;
+    }
+}
+
diff --git a/examples/helloworld/src/test/java/org/glassfish/jersey/examples/helloworld/HelloWorldTest.java b/examples/helloworld/src/test/java/org/glassfish/jersey/examples/helloworld/HelloWorldTest.java
new file mode 100644
index 0000000..538c212
--- /dev/null
+++ b/examples/helloworld/src/test/java/org/glassfish/jersey/examples/helloworld/HelloWorldTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+import org.glassfish.jersey.test.util.runner.RunSeparately;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(ConcurrentRunner.class)
+public class HelloWorldTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.simple.SimpleTestContainerFactory
+        enable(TestProperties.LOG_TRAFFIC);
+        // enable(TestProperties.DUMP_ENTITY);
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+//    Uncomment to use Grizzly async client
+//    @Override
+//    protected void configureClient(ClientConfig clientConfig) {
+//        clientConfig.connector(new GrizzlyConnector(clientConfig));
+//    }
+
+    @Test
+    @Ignore("not compatible with test framework (doesn't use client())")
+    public void testHelloWorld() throws Exception {
+        URL getUrl = UriBuilder.fromUri(getBaseUri()).path(App.ROOT_PATH).build().toURL();
+        HttpURLConnection connection = (HttpURLConnection) getUrl.openConnection();
+        try {
+            connection.setDoOutput(true);
+            connection.setInstanceFollowRedirects(false);
+            connection.setRequestMethod("GET");
+            connection.setRequestProperty("Content-Type", "text/plain");
+            assertEquals(HttpURLConnection.HTTP_OK, connection.getResponseCode());
+        } finally {
+            connection.disconnect();
+        }
+    }
+
+    @Test
+    public void testConnection() {
+        Response response = target().path(App.ROOT_PATH).request("text/plain").get();
+        assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testClientStringResponse() {
+        String s = target().path(App.ROOT_PATH).request().get(String.class);
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+    }
+
+    @Test
+    public void testAsyncClientRequests() throws InterruptedException {
+        final int REQUESTS = 10;
+        final CountDownLatch latch = new CountDownLatch(REQUESTS);
+        final long tic = System.currentTimeMillis();
+        for (int i = 0; i < REQUESTS; i++) {
+            final int id = i;
+            target().path(App.ROOT_PATH).request().async().get(new InvocationCallback<Response>() {
+                @Override
+                public void completed(Response response) {
+                    try {
+                        final String result = response.readEntity(String.class);
+                        assertEquals(HelloWorldResource.CLICHED_MESSAGE, result);
+                    } finally {
+                        latch.countDown();
+                    }
+                }
+
+                @Override
+                public void failed(Throwable error) {
+                    error.printStackTrace();
+                    latch.countDown();
+                }
+            });
+        }
+        latch.await(10 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+        final long toc = System.currentTimeMillis();
+        Logger.getLogger(HelloWorldTest.class.getName()).info("Executed in: " + (toc - tic));
+    }
+
+    @Test
+    public void testHead() {
+        Response response = target().path(App.ROOT_PATH).request().head();
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+    }
+
+    @Test
+    public void testFooBarOptions() {
+        Response response = target().path(App.ROOT_PATH).request().header("Accept", "foo/bar").options();
+        assertEquals(200, response.getStatus());
+        final String allowHeader = response.getHeaderString("Allow");
+        _checkAllowContent(allowHeader);
+        assertEquals("foo/bar", response.getMediaType().toString());
+        assertEquals(0, response.getLength());
+    }
+
+    @Test
+    public void testTextPlainOptions() {
+        Response response = target().path(App.ROOT_PATH).request().header("Accept", MediaType.TEXT_PLAIN).options();
+        assertEquals(200, response.getStatus());
+        final String allowHeader = response.getHeaderString("Allow");
+        _checkAllowContent(allowHeader);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+        final String responseBody = response.readEntity(String.class);
+        _checkAllowContent(responseBody);
+    }
+
+    private void _checkAllowContent(final String content) {
+        assertTrue(content.contains("GET"));
+        assertTrue(content.contains("HEAD"));
+        assertTrue(content.contains("OPTIONS"));
+    }
+
+    @Test
+    public void testMissingResourceNotFound() {
+        Response response;
+
+        response = target().path(App.ROOT_PATH + "arbitrary").request().get();
+        assertEquals(404, response.getStatus());
+
+        response = target().path(App.ROOT_PATH).path("arbitrary").request().get();
+        assertEquals(404, response.getStatus());
+    }
+
+    @Test
+    @RunSeparately
+    public void testLoggingFilterClientClass() {
+        Client client = client();
+        client.register(CustomLoggingFilter.class).property("foo", "bar");
+        CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+        String s = target().path(App.ROOT_PATH).request().get(String.class);
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+        assertEquals(1, CustomLoggingFilter.preFilterCalled);
+        assertEquals(1, CustomLoggingFilter.postFilterCalled);
+    }
+
+    @Test
+    @RunSeparately
+    public void testLoggingFilterClientInstance() {
+        Client client = client();
+        client.register(new CustomLoggingFilter()).property("foo", "bar");
+        CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+        String s = target().path(App.ROOT_PATH).request().get(String.class);
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+        assertEquals(1, CustomLoggingFilter.preFilterCalled);
+        assertEquals(1, CustomLoggingFilter.postFilterCalled);
+    }
+
+    @Test
+    @RunSeparately
+    public void testLoggingFilterTargetClass() {
+        WebTarget target = target().path(App.ROOT_PATH);
+        target.register(CustomLoggingFilter.class).property("foo", "bar");
+        CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+        String s = target.request().get(String.class);
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+        assertEquals(1, CustomLoggingFilter.preFilterCalled);
+        assertEquals(1, CustomLoggingFilter.postFilterCalled);
+    }
+
+    @Test
+    @RunSeparately
+    public void testLoggingFilterTargetInstance() {
+        WebTarget target = target().path(App.ROOT_PATH);
+        target.register(new CustomLoggingFilter()).property("foo", "bar");
+        CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+        String s = target.request().get(String.class);
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+        assertEquals(1, CustomLoggingFilter.preFilterCalled);
+        assertEquals(1, CustomLoggingFilter.postFilterCalled);
+    }
+
+    @Test
+    @RunSeparately
+    public void testConfigurationUpdate() {
+        Client client1 = client();
+        client1.register(CustomLoggingFilter.class).property("foo", "bar");
+
+        Client client = ClientBuilder.newClient(client1.getConfiguration());
+        CustomLoggingFilter.preFilterCalled = CustomLoggingFilter.postFilterCalled = 0;
+        String s = target().path(App.ROOT_PATH).request().get(String.class);
+        assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
+        assertEquals(1, CustomLoggingFilter.preFilterCalled);
+        assertEquals(1, CustomLoggingFilter.postFilterCalled);
+    }
+}
diff --git a/examples/http-patch/README.MD b/examples/http-patch/README.MD
new file mode 100644
index 0000000..9d6f6ad
--- /dev/null
+++ b/examples/http-patch/README.MD
@@ -0,0 +1,101 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Jersey HTTP PATCH
+=================
+
+### *Jersey HTTP PATCH Support Example*
+
+This example demonstrates how to implement generic support for
+[HTTP PATCH Method (RFC-5789)](https://tools.ietf.org/html/rfc5789)
+via JAX-RS reader interceptor. The example has been created based on
+[this Gerard Davison's blogentry] (http://kingsfleet.blogspot.co.uk/2014/02/transparent-patch-support-in-jax-rs-20.html).
+The patch format supported by this example is [JSON Patch (RFC-6902)](http://tools.ietf.org/html/rfc6902).
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                 | Resource class      | HTTP methods
+------------------------ | ------------------- | --------------
+**_/patchable-state_**   | PatchableResource   | GET, PATCH
+
+As you can see in the table, there is only a single resource deployed in
+this application, that supports `GET` and `PATCH` methods. The resource
+represents a *patchable* state, which consists of 3 properties:
+
+-   a `title` text property,
+-   a `message` text property, and
+-   a `list` property that represents a list/array of text strings.
+
+This state can be patch via HTTP GET method by sending the desired
+patch/diff in the JSON Patch format as defined in RFC-6902, for example:
+
+```javascript
+[
+  {
+    "op" : "replace",
+    "path" : "/message",
+    "value" : "patchedMessage"
+  }, {
+    "op" : "add",
+    "path" : "/list/-",
+    "value" : "one"
+  }
+]
+```
+
+This patch will instruct the resource to replace it's `message` property
+content with a new `"patchedMessage"` text and also to append a new
+`"one"` string value to the list of valued contained in the `list`
+property.
+
+(See `HttpPatchTest` for more details.)
+
+Sample Response
+---------------
+
+You can apply a patch using curl:
+
+>     curl -v -X PATCH http://localhost:8080/http-patch/patchable-state -H "Content-Type:application/json-patch+json" -d '[{
+>        "op": "replace",
+>        "path": "/message",
+>        "value": "patchedMessage"
+>     }, {
+>        "op": "add",
+>        "path": "/list/-",
+>        "value": "one"
+>     }]'
+
+The application will answer with a patched object:
+
+```javascript
+{
+   "list" : [ "one" ],
+   "message" : "patchedMessage",
+   "title" : ""
+}
+```
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
+
+A [WADL description](http://wadl.java.net/#spec) may be accessed at the URL:
+
+-   <http://localhost:8080/http-patch/application.wadl>
+
+The resource is available at
+
+-   <http://localhost:8080/http-patch/patchable-state>
diff --git a/examples/http-patch/pom.xml b/examples/http-patch/pom.xml
new file mode 100644
index 0000000..8bd5179
--- /dev/null
+++ b/examples/http-patch/pom.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>http-patch</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-http-patch</name>
+
+    <description>
+        Jersey example for implementing generic PATCH support via JAX-RS reader interceptor.
+        Taken from Gerard Davison's blog entry:
+        http://kingsfleet.blogspot.co.uk/2014/02/transparent-patch-support-in-jax-rs-20.html
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.fge</groupId>
+            <artifactId>json-patch</artifactId>
+            <version>1.9</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-processing</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.httppatch.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/App.java b/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/App.java
new file mode 100644
index 0000000..3efc9e6
--- /dev/null
+++ b/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/App.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httppatch;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.ext.ContextResolver;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * HTTP PATCH Demo Application.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public final class App {
+    /**
+     * Prevents instantiation.
+     */
+    private App() {
+    }
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/http-patch");
+    /**
+     * Root resource path.
+     */
+    static final String ROOT_PATH = "patchable-state";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("Jersey HTTP PATCH Support Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, create(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.\nTry out %s/%s\nStop the application using CTRL+C",
+                    BASE_URI,
+                    ROOT_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    /**
+     * Create JAX-RS application for the example.
+     *
+     * @return created application instance.
+     */
+    public static ResourceConfig create() {
+        final ResourceConfig app = new ResourceConfig(
+                PatchableResource.class,
+                OptionsAcceptPatchHeaderFilter.class,
+                PatchingInterceptor.class);
+
+        app.register(createMoxyJsonResolver());
+
+        // Enable on-demand tracing
+        app.property(ServerProperties.TRACING, "ON_DEMAND");
+
+        return app;
+    }
+
+    /**
+     * Create {@link javax.ws.rs.ext.ContextResolver} for {@link org.glassfish.jersey.moxy.json.MoxyJsonConfig}
+     * for this application.
+     *
+     * @return {@code MoxyJsonConfig} context resolver.
+     */
+    public static ContextResolver<MoxyJsonConfig> createMoxyJsonResolver() {
+        final MoxyJsonConfig moxyJsonConfig = new MoxyJsonConfig()
+                .setFormattedOutput(true)
+                .setNamespaceSeparator(':');
+        return moxyJsonConfig.resolver();
+    }
+}
diff --git a/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/OptionsAcceptPatchHeaderFilter.java b/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/OptionsAcceptPatchHeaderFilter.java
new file mode 100644
index 0000000..2715ce0
--- /dev/null
+++ b/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/OptionsAcceptPatchHeaderFilter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httppatch;
+
+import java.io.IOException;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+/**
+ * A JAX-RS container response filter that applies {@code Accept-Patch} header
+ * to any response to an {@code OPTIONS} request.
+ *
+ * @author Gerard Davison (gerard.davison at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class OptionsAcceptPatchHeaderFilter implements ContainerResponseFilter {
+
+    private static final String ACCEPT_PATCH_HEADER = "Accept-Patch";
+
+    @Override
+    public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
+            throws IOException {
+
+        if (HttpMethod.OPTIONS.equals(requestContext.getMethod())) {
+            final MultivaluedMap<String, Object> headers = responseContext.getHeaders();
+            if (!headers.containsKey(ACCEPT_PATCH_HEADER)) {
+                headers.putSingle(ACCEPT_PATCH_HEADER, MediaType.APPLICATION_JSON_PATCH_JSON);
+            }
+        }
+    }
+}
diff --git a/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/PatchableResource.java b/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/PatchableResource.java
new file mode 100644
index 0000000..06e6440
--- /dev/null
+++ b/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/PatchableResource.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httppatch;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PATCH;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * Patchable resource.
+ *
+ * @author Gerard Davison (gerard.davison at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path(App.ROOT_PATH)
+@Produces(MediaType.APPLICATION_JSON)
+public class PatchableResource {
+    private static volatile State state = new State();
+    private static final Logger LOGGER = Logger.getLogger(PatchableResource.class.getName());
+
+    /**
+     * Get current resource state.
+     *
+     * @return current resource state.
+     */
+    @GET
+    @Consumes(MediaType.APPLICATION_JSON)
+    public State getState() {
+        return state;
+    }
+
+    /**
+     * Set new resource state.
+     * <p>
+     * We only need to replace the resource state with the one passed into the method
+     * as the actual state patching occurred in the {@link PatchingInterceptor}.
+     * </p>
+     *
+     * @param newState new resource state.
+     * @return patched state.
+     */
+    @PATCH
+    @Consumes(MediaType.APPLICATION_JSON_PATCH_JSON)
+    public State patchState(State newState) {
+        LOGGER.info("New resource state: " + newState.toString());
+        state = newState;
+        return newState;
+    }
+}
diff --git a/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/PatchingInterceptor.java b/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/PatchingInterceptor.java
new file mode 100644
index 0000000..05451b4
--- /dev/null
+++ b/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/PatchingInterceptor.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httppatch;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+
+import org.glassfish.jersey.message.MessageBodyWorkers;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.fge.jsonpatch.JsonPatch;
+import com.github.fge.jsonpatch.JsonPatchException;
+
+/**
+ * JAX-RS reader interceptor that implements server-side PATCH support.
+ *
+ * @author Gerard Davison (gerard.davison at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class PatchingInterceptor implements ReaderInterceptor {
+
+    private final UriInfo uriInfo;
+    private final MessageBodyWorkers workers;
+
+    /**
+     * {@code PatchingInterceptor} injection constructor.
+     *
+     * @param uriInfo {@code javax.ws.rs.core.UriInfo} proxy instance.
+     * @param workers {@link org.glassfish.jersey.message.MessageBodyWorkers} message body workers.
+     */
+    public PatchingInterceptor(@Context UriInfo uriInfo, @Context MessageBodyWorkers workers) {
+        this.uriInfo = uriInfo;
+        this.workers = workers;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object aroundReadFrom(ReaderInterceptorContext readerInterceptorContext) throws IOException, WebApplicationException {
+        // Get the resource we are being called on, and find the GET method
+        Object resource = uriInfo.getMatchedResources().get(0);
+
+        Method found = null;
+        for (Method next : resource.getClass().getMethods()) {
+            if (next.getAnnotation(GET.class) != null) {
+                found = next;
+                break;
+            }
+        }
+
+        if (found == null) {
+            throw new InternalServerErrorException("No matching GET method on resource");
+        }
+
+        // Invoke the get method to get the state we are trying to patch
+        Object bean;
+        try {
+            bean = found.invoke(resource);
+        } catch (Exception e) {
+            throw new WebApplicationException(e);
+        }
+
+        // Convert this object to a an array of bytes
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        MessageBodyWriter bodyWriter =
+                workers.getMessageBodyWriter(bean.getClass(), bean.getClass(),
+                        new Annotation[0], MediaType.APPLICATION_JSON_TYPE);
+
+        bodyWriter.writeTo(bean, bean.getClass(), bean.getClass(),
+                new Annotation[0], MediaType.APPLICATION_JSON_TYPE,
+                new MultivaluedHashMap<String, Object>(), baos);
+
+
+        // Use the Jackson 2.x classes to convert both the incoming patch
+        // and the current state of the object into a JsonNode / JsonPatch
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode serverState = mapper.readValue(baos.toByteArray(), JsonNode.class);
+        JsonNode patchAsNode = mapper.readValue(readerInterceptorContext.getInputStream(), JsonNode.class);
+        JsonPatch patch = JsonPatch.fromJson(patchAsNode);
+
+        try {
+            // Apply the patch
+            JsonNode result = patch.apply(serverState);
+
+            // Stream the result & modify the stream on the readerInterceptor
+            ByteArrayOutputStream resultAsByteArray = new ByteArrayOutputStream();
+            mapper.writeValue(resultAsByteArray, result);
+            readerInterceptorContext.setInputStream(new ByteArrayInputStream(resultAsByteArray.toByteArray()));
+
+            // Pass control back to the Jersey code
+            return readerInterceptorContext.proceed();
+        } catch (JsonPatchException ex) {
+            throw new InternalServerErrorException("Error applying patch.", ex);
+        }
+    }
+}
diff --git a/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/State.java b/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/State.java
new file mode 100644
index 0000000..c00ea15
--- /dev/null
+++ b/examples/http-patch/src/main/java/org/glassfish/jersey/examples/httppatch/State.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httppatch;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A resource state modelled as Java bean (that can be patched).
+ *
+ * @author Gerard Davison (gerard.davison at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class State {
+
+    private String title;
+    private String message;
+    private List<String> list;
+
+    /**
+     * Create new bean instance without any data.
+     */
+    public State() {
+        this.title = "";
+        this.message = "";
+        this.list = new ArrayList<String>();
+    }
+
+    public void setList(List<String> list) {
+        this.list = list;
+    }
+
+    public List<String> getList() {
+        return list;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        State bean = (State) o;
+
+        if (list != null ? !list.equals(bean.list) : bean.list != null) {
+            return false;
+        }
+        if (message != null ? !message.equals(bean.message) : bean.message != null) {
+            return false;
+        }
+        if (title != null ? !title.equals(bean.title) : bean.title != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = title != null ? title.hashCode() : 0;
+        result = 31 * result + (message != null ? message.hashCode() : 0);
+        result = 31 * result + (list != null ? list.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "State{"
+                + "title='" + title + '\''
+                + ", message='" + message + '\''
+                + ", list=" + list
+                + '}';
+    }
+}
diff --git a/examples/http-patch/src/test/java/org/glassfish/jersey/examples/httppatch/HttpPatchTest.java b/examples/http-patch/src/test/java/org/glassfish/jersey/examples/httppatch/HttpPatchTest.java
new file mode 100644
index 0000000..02bbacd
--- /dev/null
+++ b/examples/http-patch/src/test/java/org/glassfish/jersey/examples/httppatch/HttpPatchTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httppatch;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * HTTP PATCH Example unit tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class HttpPatchTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        // Uncomment to enable message exchange logging
+        // enable(TestProperties.DUMP_ENTITY);
+        // enable(TestProperties.LOG_TRAFFIC);
+        return App.create();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(App.createMoxyJsonResolver())
+                .connectorProvider(new GrizzlyConnectorProvider());
+
+    }
+
+    /**
+     * This test verifies that the patching of the resource state works.
+     * <p>
+     * The patch is created using the new standard JSON Processing API for Java and
+     * is then sent to the server. {@code PATCH} response as well as the new resource
+     * state obtained via subsequent {@code GET} method is verified against the expected
+     * state.
+     * </p>
+     */
+    @Test
+    public void testPatch() {
+        final WebTarget target = target(App.ROOT_PATH);
+
+        // initial precondition check
+        final State expected = new State();
+        assertEquals(expected, target.request("application/json").get(State.class));
+
+        // apply first patch
+        expected.setMessage("patchedMessage");
+        expected.setTitle("patchedTitle");
+        expected.getList().add("one");
+        expected.getList().add("two");
+
+        JsonArray patch_1 = Json.createArrayBuilder()
+                .add(Json.createObjectBuilder()
+                        .add("op", "replace")
+                        .add("path", "/message")
+                        .add("value", expected.getMessage())
+                        .build())
+                .add(Json.createObjectBuilder()
+                        .add("op", "replace")
+                        .add("path", "/title")
+                        .add("value", expected.getTitle())
+                        .build())
+                .add(Json.createObjectBuilder()
+                        .add("op", "replace")
+                        .add("path", "/list")
+                        .add("value", Json.createArrayBuilder()
+                                .add(expected.getList().get(0))
+                                .add(expected.getList().get(1))
+                                .build())
+                        .build())
+                .build();
+
+        assertEquals(expected, target.request()
+                                     .method("PATCH",
+                                             Entity.entity(patch_1, MediaType.APPLICATION_JSON_PATCH_JSON), State.class));
+        assertEquals(expected, target.request("application/json").get(State.class));
+
+        // apply second patch
+        expected.getList().add("three");
+
+        JsonArray patch_2 = Json.createArrayBuilder()
+                .add(Json.createObjectBuilder()
+                        .add("op", "add")
+                        .add("path", "/list/-")
+                        .add("value", expected.getList().get(2))
+                        .build())
+                .build();
+        assertEquals(expected, target.request()
+                                     .method("PATCH",
+                                             Entity.entity(patch_2, MediaType.APPLICATION_JSON_PATCH_JSON), State.class));
+        assertEquals(expected, target.request("application/json").get(State.class));
+    }
+}
diff --git a/examples/http-trace/README.MD b/examples/http-trace/README.MD
new file mode 100644
index 0000000..e7769b3
--- /dev/null
+++ b/examples/http-trace/README.MD
@@ -0,0 +1,46 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Jersey HTTP PATCH
+=================
+
+### *Jersey HTTP PATCH Support Example*
+
+This example demonstrates how to implement a support for **HTTP TRACE method** via **HttpMethod** annotation in Jersey.
+
+Using HttpMethod annotation you can implement whatever name of an HTTP method you want.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                    | Resource class      | HTTP methods
+--------------------------- | ------------------- | --------------
+**_/tracing/annotated_**    | TracingResource     | TRACE
+**_/tracing/programmatic_** | ---                 | TRACE
+
+Sample Response
+---------------
+
+You can trace your annotated resource using this command:
+
+>   curl -v -X TRACE http://localhost:9998/base/tracing/annotated
+
+or programmatic resource:
+
+>   curl -v -X TRACE http://localhost:9998/base/tracing/programmatic
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
\ No newline at end of file
diff --git a/examples/http-trace/pom.xml b/examples/http-trace/pom.xml
new file mode 100644
index 0000000..e0e77ab
--- /dev/null
+++ b/examples/http-trace/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>http-trace</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-http-trace</name>
+
+    <description>Jersey HTTP TRACE support example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.httptrace.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/http-trace/src/main/java/org/glassfish/jersey/examples/httptrace/App.java b/examples/http-trace/src/main/java/org/glassfish/jersey/examples/httptrace/App.java
new file mode 100644
index 0000000..f2d05af
--- /dev/null
+++ b/examples/http-trace/src/main/java/org/glassfish/jersey/examples/httptrace/App.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httptrace;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * This is the example entry point, where Jersey application gets populated and published
+ * using the Grizzly 2 HTTP container.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:9998/base/");
+    /**
+     * Programmatic tracing root resource path.
+     */
+    public static final String ROOT_PATH_PROGRAMMATIC = "tracing/programmatic";
+    /**
+     * Annotated class-based tracing root resource path.
+     */
+    public static final String ROOT_PATH_ANNOTATED = "tracing/annotated";
+
+    /**
+     * Main application entry point.
+     *
+     * @param args application arguments.
+     */
+    public static void main(String[] args) {
+        try {
+            System.out.println("HTTP TRACE Support Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, create(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format(
+                    "Application started.\n"
+                            + "To test TRACE with a programmatic resource, send HTTP TRACE request to:%n  %s%s%n"
+                            + "To test TRACE with an annotated resource, send HTTP TRACE request to:%n  %s%s%n"
+                            + "Stop the application using CTRL+C",
+                    BASE_URI, ROOT_PATH_PROGRAMMATIC,
+                    BASE_URI, ROOT_PATH_ANNOTATED));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    /**
+     * Create example application resource configuration.
+     *
+     * @return initialized resource configuration of the example application.
+     */
+    public static ResourceConfig create() {
+        final ResourceConfig resourceConfig = new ResourceConfig(TracingResource.class);
+
+        final Resource.Builder resourceBuilder = Resource.builder(ROOT_PATH_PROGRAMMATIC);
+        resourceBuilder.addMethod(TRACE.NAME).handledBy(new Inflector<ContainerRequestContext, Response>() {
+
+            @Override
+            public Response apply(ContainerRequestContext request) {
+                if (request == null) {
+                    return Response.noContent().build();
+                } else {
+                    return Response.ok(Stringifier.stringify((ContainerRequest) request), MediaType.TEXT_PLAIN).build();
+                }
+            }
+        });
+
+        return resourceConfig.registerResources(resourceBuilder.build());
+    }
+}
diff --git a/examples/http-trace/src/main/java/org/glassfish/jersey/examples/httptrace/Stringifier.java b/examples/http-trace/src/main/java/org/glassfish/jersey/examples/httptrace/Stringifier.java
new file mode 100644
index 0000000..1839f1b
--- /dev/null
+++ b/examples/http-trace/src/main/java/org/glassfish/jersey/examples/httptrace/Stringifier.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httptrace;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Request;
+
+import org.glassfish.jersey.server.ContainerRequest;
+
+/**
+ * Request stringifier.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class Stringifier {
+
+    private Stringifier() {
+    }
+
+    public static String stringify(ContainerRequest request) {
+        StringBuilder buffer = new StringBuilder();
+
+        printRequestLine(buffer, request);
+        printPrefixedHeaders(buffer, request.getHeaders());
+
+        if (request.hasEntity()) {
+            buffer.append(request.readEntity(String.class)).append("\n");
+        }
+
+        return buffer.toString();
+    }
+
+    private static void printRequestLine(StringBuilder buffer, ContainerRequest request) {
+        buffer.append(request.getMethod()).append(" ").append(request.getUriInfo().getRequestUri().toASCIIString()).append("\n");
+    }
+
+    private static void printPrefixedHeaders(StringBuilder buffer, Map<String, List<String>> headers) {
+        for (Map.Entry<String, List<String>> e : headers.entrySet()) {
+            List<String> val = e.getValue();
+            String header = e.getKey();
+
+            if (val.size() == 1) {
+                buffer.append(header).append(": ").append(val.get(0)).append("\n");
+            } else {
+                StringBuilder sb = new StringBuilder();
+                boolean add = false;
+                for (String s : val) {
+                    if (add) {
+                        sb.append(',');
+                    }
+                    add = true;
+                    sb.append(s);
+                }
+                buffer.append(header).append(": ").append(sb.toString()).append("\n");
+            }
+        }
+    }
+}
diff --git a/examples/http-trace/src/main/java/org/glassfish/jersey/examples/httptrace/TRACE.java b/examples/http-trace/src/main/java/org/glassfish/jersey/examples/httptrace/TRACE.java
new file mode 100644
index 0000000..e37d522
--- /dev/null
+++ b/examples/http-trace/src/main/java/org/glassfish/jersey/examples/httptrace/TRACE.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httptrace;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.ws.rs.HttpMethod;
+
+/**
+ * HTTP TRACE method annotation.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@HttpMethod(TRACE.NAME)
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TRACE {
+    public static final String NAME = "TRACE";
+}
diff --git a/examples/http-trace/src/main/java/org/glassfish/jersey/examples/httptrace/TracingResource.java b/examples/http-trace/src/main/java/org/glassfish/jersey/examples/httptrace/TracingResource.java
new file mode 100644
index 0000000..d6fd371
--- /dev/null
+++ b/examples/http-trace/src/main/java/org/glassfish/jersey/examples/httptrace/TracingResource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httptrace;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Request;
+
+import org.glassfish.jersey.server.ContainerRequest;
+
+/**
+ * This very basic resource showcases support of a HTTP TRACE method,
+ * not directly supported by JAX-RS API.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path(App.ROOT_PATH_ANNOTATED)
+public class TracingResource {
+
+    @TRACE
+    @Produces("text/plain")
+    public String trace(Request request) {
+        return Stringifier.stringify((ContainerRequest) request);
+    }
+}
diff --git a/examples/http-trace/src/test/java/org/glassfish/jersey/examples/httptrace/TraceSupportTest.java b/examples/http-trace/src/test/java/org/glassfish/jersey/examples/httptrace/TraceSupportTest.java
new file mode 100644
index 0000000..4dec416
--- /dev/null
+++ b/examples/http-trace/src/test/java/org/glassfish/jersey/examples/httptrace/TraceSupportTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httptrace;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class TraceSupportTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+        return App.create();
+    }
+
+    private String[] expectedFragmentsProgrammatic = new String[]{
+            "TRACE http://localhost:" + this.getPort() + "/tracing/programmatic"
+    };
+    private String[] expectedFragmentsAnnotated = new String[]{
+            "TRACE http://localhost:" + this.getPort() + "/tracing/annotated"
+    };
+
+    private WebTarget prepareTarget(String path) {
+        final WebTarget target = target();
+        target.register(LoggingFeature.class);
+        return target.path(path);
+    }
+
+    @Test
+    public void testProgrammaticApp() throws Exception {
+        Response response = prepareTarget(App.ROOT_PATH_PROGRAMMATIC).request("text/plain").method(TRACE.NAME);
+
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+        String responseEntity = response.readEntity(String.class);
+        for (String expectedFragment : expectedFragmentsProgrammatic) {
+            assertTrue("Expected fragment '" + expectedFragment + "' not found in response:\n" + responseEntity,
+                    // toLowerCase - http header field names are case insensitive
+                    responseEntity.contains(expectedFragment));
+        }
+    }
+
+    @Test
+    public void testAnnotatedApp() throws Exception {
+        Response response = prepareTarget(App.ROOT_PATH_ANNOTATED).request("text/plain").method(TRACE.NAME);
+
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+        String responseEntity = response.readEntity(String.class);
+        for (String expectedFragment : expectedFragmentsAnnotated) {
+            assertTrue("Expected fragment '" + expectedFragment + "' not found in response:\n" + responseEntity,
+                    // toLowerCase - http header field names are case insensitive
+                    responseEntity.contains(expectedFragment));
+        }
+    }
+
+    @Test
+    public void testTraceWithEntity() throws Exception {
+        _testTraceWithEntity(false, false);
+    }
+
+    @Test
+    public void testAsyncTraceWithEntity() throws Exception {
+        _testTraceWithEntity(true, false);
+    }
+
+    @Test
+    public void testTraceWithEntityGrizzlyConnector() throws Exception {
+        _testTraceWithEntity(false, true);
+    }
+
+    @Test
+    public void testAsyncTraceWithEntityGrizzlyConnector() throws Exception {
+        _testTraceWithEntity(true, true);
+    }
+
+    private void _testTraceWithEntity(final boolean isAsync, final boolean useGrizzlyConnection) throws Exception {
+        try {
+            WebTarget target = useGrizzlyConnection ? getGrizzlyClient().target(target().getUri()) : target();
+            target = target.path(App.ROOT_PATH_ANNOTATED);
+
+            final Entity<String> entity = Entity.entity("trace", MediaType.WILDCARD_TYPE);
+
+            Response response;
+            if (!isAsync) {
+                response = target.request().method(TRACE.NAME, entity);
+            } else {
+                response = target.request().async().method(TRACE.NAME, entity).get();
+            }
+
+            fail("A TRACE request MUST NOT include an entity. (response=" + response + ")");
+        } catch (Exception e) {
+            // OK
+        }
+    }
+
+    private Client getGrizzlyClient() {
+        return ClientBuilder.newClient(new ClientConfig().connectorProvider(new GrizzlyConnectorProvider()));
+    }
+}
diff --git a/examples/https-clientserver-grizzly/README.MD b/examples/https-clientserver-grizzly/README.MD
new file mode 100644
index 0000000..7a6180d
--- /dev/null
+++ b/examples/https-clientserver-grizzly/README.MD
@@ -0,0 +1,114 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+HTTPS Client/Server Example - Grizzly
+=====================================
+
+This example demonstrates how to develop RESTful HTTPS Server using
+Grizzly and how to implement HTTPS Client using Jersey with server
+authentication.
+
+Contents
+--------
+
+### Server side
+
+This example consists of just one resource - RootResource, which is
+basically copy of HelloWorld Resource from corresponding sample with
+little improvement.
+
+Other classes are used to start Grizzly embedded server and set up its
+authentication and authorization mechanism and keystore and truststore.
+
+### Client side
+
+Client side is implemented as a test case, see class
+**org.glassfish.jersey.examples.httpsclientservergrizzly.MainTest**, and
+its method `testSSLWithAuth` (others are just tests for invalid
+authorization). First thing you have to do if you want to communicate
+with service via https is set up **SSLContext** which is basically
+providing keystore and truststore. Keystore is used for storing own keys
+and truststore is used for storing certificates to which you have
+decided to trust. For more informations see [\[1\]](#JSSERefGuide).
+
+To set **SSLContext** on Jersey client you have to set it as a property
+to the client instance:
+
+    Client client = ClientFactory.newClient();
+    client.configuration().setProperty(ClientProperties.SSL_CONTEXT, context);
+
+### Certificates setup
+
+**These steps are not required to run this example. Pre-generated
+keystore and truststore files are already present.**
+
+We needed set up few things to get this example working:
+
+-   generate client and server keys
+-   generate client and server certificates
+-   import certificates to corresponding truststores
+
+Client certificate is needed too because we're going to use server-side
+certificate authentication as well (yes, after this Http Basic
+authentication seems to be kind of redundant but there are some usecases
+where you might want to use them both).
+Generate client key and store it into keystore:\
+
+    keytool -genkey -keystore ./keystore_client -alias clientKey -dname "CN=Client, OU=Jersey, O=Oracle Corporation, L=Prague, ST=Czech Republic, C=CZ"
+
+Generate client certificate (this will generate self-signed certificate;
+if you have certification authority and want generate certificate
+request, use keytool -certreq):
+
+    keytool -export -alias clientKey -rfc -keystore ./keystore_client > ./client.cert
+
+Import client certificate to servers truststore:
+
+    keytool -import -alias clientCert -file ./client.cert -keystore ./truststore_server
+
+These steps are similar for server side:
+
+    keytool -genkey -keystore ./keystore_server -alias serverKey -dname "CN=localhost, OU=Jersey, O=Oracle Corporation, L=Prague, ST=Czech Republic, C=CZ"
+    keytool -export -alias serverKey -rfc -keystore ./keystore_server > ./server.cert
+    keytool -import -alias serverCert -file ./server.cert -keystore ./truststore_client
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+test
+
+>     mvn clean test
+
+run
+
+>     mvn compile exec:java
+
+From a web browser, visit:\
+ **This won't work! \***
+
+> `https://localhost:8463`
+
+**\[\*\]** Your web browser needs have and use generated client keys. Or
+you have to disable server side client authentication - set NeedClientAuth to false:
+**new SSLEngineConfigurator(sslContext).setClientMode(false).setNeedClientAuth(false)** in *Server.java*.
+
+Then ignore any security warning (self-signed certificates aren't trusted in general)
+and login with username "user" and password "password". Text "JERSEY
+HTTPS EXAMPLE" should appear.
+
+Mozila Firefox and Internet Explorer don't allow users to display any
+content provided on behalf of any self-signed certificate so you have to
+use some other browser which allows this (for example Safari or Opera).
+
+References
+----------
+
+\[1\]
+<http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html>
diff --git a/examples/https-clientserver-grizzly/client.cert b/examples/https-clientserver-grizzly/client.cert
new file mode 100644
index 0000000..d43d88b
--- /dev/null
+++ b/examples/https-clientserver-grizzly/client.cert
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIDJTCCAuOgAwIBAgIET5ZyYjALBgcqhkjOOAQDBQAwdjELMAkGA1UEBhMCQ1oxFzAVBgNVBAgT
+DkN6ZWNoIFJlcHVibGljMQ8wDQYDVQQHEwZQcmFndWUxGzAZBgNVBAoTEk9yYWNsZSBDb3Jwb3Jh
+dGlvbjEPMA0GA1UECxMGSmVyc2V5MQ8wDQYDVQQDEwZDbGllbnQwHhcNMTIwNDI0MDkyOTA2WhcN
+MTIwNzIzMDkyOTA2WjB2MQswCQYDVQQGEwJDWjEXMBUGA1UECBMOQ3plY2ggUmVwdWJsaWMxDzAN
+BgNVBAcTBlByYWd1ZTEbMBkGA1UEChMST3JhY2xlIENvcnBvcmF0aW9uMQ8wDQYDVQQLEwZKZXJz
+ZXkxDzANBgNVBAMTBkNsaWVudDCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu
+7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSf
+n+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgB
+xwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9
+B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6
+ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GEAAKBgBmHNACDk1aw
+vUZjsRecMSBlkkCSqr/cCrYOsNwpfleQKsM6rdOofujANUVeoUFhX8e8K45FknxEqAugmhGQ9NRn
+uMenrvV+XupC0V2uGH0OciXeAzHbfeItBCbmJcvMdPW/q+I2vFchv6+ajEiNHogBrCc3qwSMhyVQ
+ug2fXHmJMAsGByqGSM44BAMFAAMvADAsAhQYznYmH0hrcLni4EqX3Ovac+pNJgIUehnEaW1V5djn
+dhYBAYUkSycETl4=
+-----END CERTIFICATE-----
diff --git a/examples/https-clientserver-grizzly/keystore_client b/examples/https-clientserver-grizzly/keystore_client
new file mode 100644
index 0000000..d016fd2
--- /dev/null
+++ b/examples/https-clientserver-grizzly/keystore_client
Binary files differ
diff --git a/examples/https-clientserver-grizzly/keystore_server b/examples/https-clientserver-grizzly/keystore_server
new file mode 100644
index 0000000..a7c93fc
--- /dev/null
+++ b/examples/https-clientserver-grizzly/keystore_server
Binary files differ
diff --git a/examples/https-clientserver-grizzly/pom.xml b/examples/https-clientserver-grizzly/pom.xml
new file mode 100644
index 0000000..fb060b0
--- /dev/null
+++ b/examples/https-clientserver-grizzly/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>https-clientserver-grizzly</artifactId>
+    <name>jersey-examples-https-clientserver-grizzly</name>
+    <packaging>jar</packaging>
+
+    <description>Jersey HTTPS Client/Server example on Grizzly.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.httpsclientservergrizzly.Server</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/https-clientserver-grizzly/server.cert b/examples/https-clientserver-grizzly/server.cert
new file mode 100644
index 0000000..820841c
--- /dev/null
+++ b/examples/https-clientserver-grizzly/server.cert
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIDKzCCAumgAwIBAgIET5ZyzjALBgcqhkjOOAQDBQAweTELMAkGA1UEBhMCQ1oxFzAVBgNVBAgT
+DkN6ZWNoIFJlcHVibGljMQ8wDQYDVQQHEwZQcmFndWUxGzAZBgNVBAoTEk9yYWNsZSBDb3Jwb3Jh
+dGlvbjEPMA0GA1UECxMGSmVyc2V5MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMTIwNDI0MDkzMDU0
+WhcNMTIwNzIzMDkzMDU0WjB5MQswCQYDVQQGEwJDWjEXMBUGA1UECBMOQ3plY2ggUmVwdWJsaWMx
+DzANBgNVBAcTBlByYWd1ZTEbMBkGA1UEChMST3JhY2xlIENvcnBvcmF0aW9uMQ8wDQYDVQQLEwZK
+ZXJzZXkxEjAQBgNVBAMTCWxvY2FsaG9zdDCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUS
+KVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3
+a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii
+Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrq
+gvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1
+kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GEAAKBgGIs
+VTo7dODp6iOyHFL+mkOVlOloZcymyWlHUZXzKqrvAi5jISptZZM+AoJcUUlUWEO9uwVTvX0MCk+4
+viwlPwt+XhaPM0kqfFcx1IS07BAx7z9cXREfYQpoFSsFW7pUs6cdvu0rjj8Ip6BnHALxQDgaBk40
+zXM39kB9LdGBt4uDMAsGByqGSM44BAMFAAMvADAsAhQE4QoQP4xPibjnozo8x5ORJqBuCAIUTkLQ
+2udZ2DeknwPYXp/zMkYXLN4=
+-----END CERTIFICATE-----
diff --git a/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/AuthenticationException.java b/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/AuthenticationException.java
new file mode 100644
index 0000000..73eeedf
--- /dev/null
+++ b/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/AuthenticationException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httpsclientservergrizzly;
+
+/**
+ * A runtime exception representing a failure to provide correct authentication credentials.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class AuthenticationException extends RuntimeException {
+
+    public AuthenticationException(String message, String realm) {
+        super(message);
+        this.realm = realm;
+    }
+
+    private String realm = null;
+
+    public String getRealm() {
+        return this.realm;
+    }
+
+}
diff --git a/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/AuthenticationExceptionMapper.java b/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/AuthenticationExceptionMapper.java
new file mode 100644
index 0000000..78d12f7
--- /dev/null
+++ b/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/AuthenticationExceptionMapper.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httpsclientservergrizzly;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Map an authentication exception to an HTTP 401 response, optionally including the realm for a credentials challenge at the client.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Provider
+public class AuthenticationExceptionMapper implements ExceptionMapper<AuthenticationException> {
+
+    public Response toResponse(AuthenticationException e) {
+        if (e.getRealm() != null) {
+            return Response
+                    .status(Status.UNAUTHORIZED)
+                    .header("WWW-Authenticate", "Basic realm=\"" + e.getRealm() + "\"")
+                    .type("text/plain")
+                    .entity(e.getMessage())
+                    .build();
+        } else {
+            return Response
+                    .status(Status.UNAUTHORIZED)
+                    .type("text/plain")
+                    .entity(e.getMessage())
+                    .build();
+        }
+    }
+
+}
diff --git a/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/RootResource.java b/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/RootResource.java
new file mode 100644
index 0000000..1385d2f
--- /dev/null
+++ b/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/RootResource.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httpsclientservergrizzly;
+
+import java.nio.charset.Charset;
+import javax.xml.bind.DatatypeConverter;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+
+/**
+ * Simple resource demonstrating low level approach of getting user credentials.
+ *
+ * Better way would be injecting {@link javax.ws.rs.core.SecurityContext}.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("/")
+public class RootResource {
+
+    @GET
+    public String get1(@Context HttpHeaders headers) {
+        // you can get username form HttpHeaders
+        System.out.println("Service: GET / User: " + getUser(headers));
+
+        return Server.CONTENT;
+    }
+
+    private String getUser(HttpHeaders headers) {
+
+        // this is a very minimalistic and "naive" code; if you plan to use it
+        // add necessary checks (see org.glassfish.jersey.examples.httpsclientservergrizzly.authservergrizzly.SecurityFilter)
+
+        String auth = headers.getRequestHeader("authorization").get(0);
+
+        auth = auth.substring("Basic ".length());
+        String[] values = new String(DatatypeConverter.parseBase64Binary(auth), Charset.forName("ASCII")).split(":");
+
+        // String username = values[0];
+        // String password = values[1];
+
+        return values[0];
+    }
+}
diff --git a/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/SecurityFilter.java b/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/SecurityFilter.java
new file mode 100644
index 0000000..5918712
--- /dev/null
+++ b/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/SecurityFilter.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httpsclientservergrizzly;
+
+import java.io.IOException;
+import java.security.Principal;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.Provider;
+
+import javax.inject.Inject;
+
+import java.nio.charset.Charset;
+import javax.xml.bind.DatatypeConverter;
+
+/**
+ * Simple authentication filter.
+ *
+ * Returns response with http status 401 when proper authentication is not provided in incoming request.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @see ContainerRequestFilter
+ */
+@Provider
+@PreMatching
+public class SecurityFilter implements ContainerRequestFilter {
+
+    @Inject
+    javax.inject.Provider<UriInfo> uriInfo;
+    private static final String REALM = "HTTPS Example authentication";
+
+    @Override
+    public void filter(ContainerRequestContext filterContext) throws IOException {
+        User user = authenticate(filterContext);
+        filterContext.setSecurityContext(new Authorizer(user));
+    }
+
+    private User authenticate(ContainerRequestContext filterContext) {
+        // Extract authentication credentials
+        String authentication = filterContext.getHeaderString(HttpHeaders.AUTHORIZATION);
+        if (authentication == null) {
+            throw new AuthenticationException("Authentication credentials are required", REALM);
+        }
+        if (!authentication.startsWith("Basic ")) {
+            return null;
+            // additional checks should be done here
+            // "Only HTTP Basic authentication is supported"
+        }
+        authentication = authentication.substring("Basic ".length());
+        String[] values = new String(DatatypeConverter.parseBase64Binary(authentication), Charset.forName("ASCII")).split(":");
+        if (values.length < 2) {
+            throw new WebApplicationException(400);
+            // "Invalid syntax for username and password"
+        }
+        String username = values[0];
+        String password = values[1];
+        if ((username == null) || (password == null)) {
+            throw new WebApplicationException(400);
+            // "Missing username or password"
+        }
+
+        // Validate the extracted credentials
+        User user;
+
+        if (username.equals("user") && password.equals("password")) {
+            user = new User("user", "user");
+            System.out.println("USER AUTHENTICATED");
+            //        } else if (username.equals("admin") && password.equals("adminadmin")) {
+            //            user = new User("admin", "admin");
+            //            System.out.println("ADMIN AUTHENTICATED");
+        } else {
+            System.out.println("USER NOT AUTHENTICATED");
+            throw new AuthenticationException("Invalid username or password\r\n", REALM);
+        }
+        return user;
+    }
+
+    public class Authorizer implements SecurityContext {
+
+        private User user;
+        private Principal principal;
+
+        public Authorizer(final User user) {
+            this.user = user;
+            this.principal = new Principal() {
+
+                public String getName() {
+                    return user.username;
+                }
+            };
+        }
+
+        public Principal getUserPrincipal() {
+            return this.principal;
+        }
+
+        public boolean isUserInRole(String role) {
+            return (role.equals(user.role));
+        }
+
+        public boolean isSecure() {
+            return "https".equals(uriInfo.get().getRequestUri().getScheme());
+        }
+
+        public String getAuthenticationScheme() {
+            return SecurityContext.BASIC_AUTH;
+        }
+    }
+
+    public class User {
+
+        public String username;
+        public String role;
+
+        public User(String username, String role) {
+            this.username = username;
+            this.role = role;
+        }
+    }
+}
diff --git a/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/Server.java b/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/Server.java
new file mode 100644
index 0000000..b4c91e4
--- /dev/null
+++ b/examples/https-clientserver-grizzly/src/main/java/org/glassfish/jersey/examples/httpsclientservergrizzly/Server.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httpsclientservergrizzly;
+
+import java.io.IOException;
+import java.net.URI;
+import java.security.AccessController;
+import java.util.logging.Logger;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.ssl.SSLContextConfigurator;
+import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
+
+/**
+ * A simple SSL-secured HTTP server.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class Server {
+
+    private static final Logger LOGGER = Logger.getLogger(Server.class.getName());
+
+    private static final String KEYSTORE_SERVER_FILE = "./keystore_server";
+    private static final String KEYSTORE_SERVER_PWD = "asdfgh";
+    private static final String TRUSTORE_SERVER_FILE = "./truststore_server";
+    private static final String TRUSTORE_SERVER_PWD = "asdfgh";
+
+    public static final URI BASE_URI = getBaseURI();
+    public static final String CONTENT = "JERSEY HTTPS EXAMPLE\n";
+
+    private final HttpServer webServer;
+
+    private Server(final HttpServer webServer) {
+        this.webServer = webServer;
+    }
+
+    private static URI getBaseURI() {
+        return UriBuilder.fromUri("https://localhost/").port(getPort(8463)).build();
+    }
+
+    private static int getPort(int defaultPort) {
+        final String port =
+                AccessController.doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port"));
+        if (null != port) {
+            try {
+                return Integer.parseInt(port);
+            } catch (NumberFormatException e) {
+                LOGGER.warning("Value of jersey.config.test.container.port property"
+                        + " is not a valid positive integer [" + port + "]."
+                        + " Reverting to default [" + defaultPort + "].");
+            }
+        }
+        return defaultPort;
+    }
+
+    /**
+     * Start SSL-secured HTTP test server.
+     *
+     * @throws IOException in case there is an error while reading server key store or trust store.
+     * @return an instance of the started SSL-secured HTTP test server.
+     */
+    public static Server start() throws IOException {
+        // Grizzly ssl configuration
+        SSLContextConfigurator sslContext = new SSLContextConfigurator();
+
+        // set up security context
+        sslContext.setKeyStoreFile(KEYSTORE_SERVER_FILE); // contains server keypair
+        sslContext.setKeyStorePass(KEYSTORE_SERVER_PWD);
+        sslContext.setTrustStoreFile(TRUSTORE_SERVER_FILE); // contains client certificate
+        sslContext.setTrustStorePass(TRUSTORE_SERVER_PWD);
+
+        ResourceConfig rc = new ResourceConfig();
+        rc.registerClasses(RootResource.class, SecurityFilter.class, AuthenticationExceptionMapper.class);
+
+        final HttpServer grizzlyServer = GrizzlyHttpServerFactory.createHttpServer(
+                getBaseURI(),
+                rc,
+                true,
+                new SSLEngineConfigurator(sslContext).setClientMode(false).setNeedClientAuth(true)
+        );
+
+        // start Grizzly embedded server //
+        LOGGER.info("Jersey app started. Try out " + BASE_URI + "\nHit CTRL + C to stop it...");
+        grizzlyServer.start();
+
+        return new Server(grizzlyServer);
+    }
+
+    /**
+     * Stop SSL-secured HTTP test server.
+     */
+    public void stop() {
+        webServer.shutdownNow();
+    }
+
+    @SuppressWarnings("ResultOfMethodCallIgnored")
+    public static void main(String[] args) throws InterruptedException, IOException {
+        start();
+
+        System.in.read();
+    }
+}
diff --git a/examples/https-clientserver-grizzly/src/test/java/org/glassfish/jersey/examples/httpsclientservergrizzly/MainTest.java b/examples/https-clientserver-grizzly/src/test/java/org/glassfish/jersey/examples/httpsclientservergrizzly/MainTest.java
new file mode 100644
index 0000000..97fefa8
--- /dev/null
+++ b/examples/https-clientserver-grizzly/src/test/java/org/glassfish/jersey/examples/httpsclientservergrizzly/MainTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.httpsclientservergrizzly;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import javax.net.ssl.SSLContext;
+
+import org.glassfish.jersey.SslConfigurator;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.logging.LoggingFeature;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * This test class starts the grizzly server and then client performs several SSL (https)
+ * requests where different scenarios are tested (SSL Client authentication, missing truststore
+ * configuration, etc.). Server is a Grizzly server configured for SSL support and client
+ * uses both, {@link HttpUrlConnectorProvider} and {@link GrizzlyConnectorProvider}.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class MainTest {
+
+    private static final String TRUSTORE_CLIENT_FILE = "./truststore_client";
+    private static final String TRUSTSTORE_CLIENT_PWD = "asdfgh";
+    private static final String KEYSTORE_CLIENT_FILE = "./keystore_client";
+    private static final String KEYSTORE_CLIENT_PWD = "asdfgh";
+
+    private final Object serverGuard = new Object();
+    private Server server = null;
+
+    @Before
+    public void setUp() throws Exception {
+        synchronized (serverGuard) {
+            if (server != null) {
+                throw new IllegalStateException(
+                        "Test run sync issue: Another instance of the SSL-secured HTTP test server has been already started.");
+            }
+            server = Server.start();
+        }
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        synchronized (serverGuard) {
+            if (server == null) {
+                throw new IllegalStateException("Test run sync issue: There is no SSL-secured HTTP test server to stop.");
+            }
+            server.stop();
+            server = null;
+        }
+    }
+
+    @Test
+    public void testSSLWithBasicAndSSLAuthGrizzlyConnector() {
+        final ClientConfig clientConfig = getGrizzlyConfig();
+        _testSSLWithBasicAndSSLAuth(clientConfig);
+    }
+
+    private ClientConfig getGrizzlyConfig() {
+        return new ClientConfig().connectorProvider(new GrizzlyConnectorProvider());
+    }
+
+    @Test
+    public void testSSLWithBasicAndSSLAuthHttpUrlConnector() {
+        final ClientConfig clientConfig = getHttpUrlConnectorConfig();
+        _testSSLWithBasicAndSSLAuth(clientConfig);
+    }
+
+    private ClientConfig getHttpUrlConnectorConfig() {
+        return new ClientConfig().connectorProvider(new HttpUrlConnectorProvider());
+    }
+
+    /**
+     * Test to see that the correct Http status is returned.
+     */
+    private void _testSSLWithBasicAndSSLAuth(ClientConfig clientConfig) {
+        SslConfigurator sslConfig = SslConfigurator.newInstance()
+                .trustStoreFile(TRUSTORE_CLIENT_FILE)
+                .trustStorePassword(TRUSTSTORE_CLIENT_PWD)
+                .keyStoreFile(KEYSTORE_CLIENT_FILE)
+                .keyPassword(KEYSTORE_CLIENT_PWD);
+
+        final SSLContext sslContext = sslConfig.createSSLContext();
+        Client client = ClientBuilder.newBuilder().withConfig(clientConfig)
+                .sslContext(sslContext).build();
+
+        // client basic auth demonstration
+        client.register(HttpAuthenticationFeature.basic("user", "password"));
+
+        System.out.println("Client: GET " + Server.BASE_URI);
+
+        WebTarget target = client.target(Server.BASE_URI);
+
+        final Response response = target.path("/").request().get(Response.class);
+
+        assertEquals(200, response.getStatus());
+    }
+
+
+    @Test
+    public void testWithoutBasicAuthHttpUrlConnector() {
+        _testWithoutBasicAuth(getHttpUrlConnectorConfig());
+    }
+
+    @Test
+    public void testWithoutBasicAuthGrizzlyConnector() {
+        _testWithoutBasicAuth(getGrizzlyConfig());
+    }
+
+    /**
+     * Test to see that HTTP 401 is returned when client tries to GET without
+     * proper credentials.
+     */
+    private void _testWithoutBasicAuth(ClientConfig clientConfig) {
+        SslConfigurator sslConfig = SslConfigurator.newInstance()
+                .trustStoreFile(TRUSTORE_CLIENT_FILE)
+                .trustStorePassword(TRUSTSTORE_CLIENT_PWD)
+                .keyStoreFile(KEYSTORE_CLIENT_FILE)
+                .keyPassword(KEYSTORE_CLIENT_PWD);
+
+        Client client = ClientBuilder.newBuilder().withConfig(clientConfig).sslContext(sslConfig
+                .createSSLContext()).build();
+
+        System.out.println("Client: GET " + Server.BASE_URI);
+
+        WebTarget target = client.target(Server.BASE_URI);
+        target.register(LoggingFeature.class);
+
+        Response response;
+
+        try {
+            response = target.path("/").request().get(Response.class);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+
+        assertEquals(401, response.getStatus());
+    }
+
+    /**
+     * Test to see that SSLHandshakeException is thrown when client don't have
+     * trusted key.
+     */
+    private void _testWithoutSSLAuthentication(ClientConfig clientConfig) {
+        SslConfigurator sslConfig = SslConfigurator.newInstance()
+                .trustStoreFile(TRUSTORE_CLIENT_FILE)
+                .trustStorePassword(TRUSTSTORE_CLIENT_PWD);
+
+        Client client = ClientBuilder.newBuilder()
+                .withConfig(clientConfig)
+                .sslContext(sslConfig.createSSLContext()).build();
+
+        System.out.println("Client: GET " + Server.BASE_URI);
+
+        WebTarget target = client.target(Server.BASE_URI);
+        target.register(LoggingFeature.class);
+
+        boolean caught = false;
+
+        try {
+            target.path("/").request().get(String.class);
+        } catch (Exception e) {
+            caught = true;
+        }
+
+        assertTrue(caught);
+        // solaris throws java.net.SocketException instead of SSLHandshakeException
+        // assertTrue(msg.contains("SSLHandshakeException"));
+    }
+
+    @Test
+    public void testWithoutSSLAuthenticationGrizzly() {
+        _testWithoutSSLAuthentication(getGrizzlyConfig());
+    }
+
+    @Test
+    public void testWithoutSSLAuthenticationHttpUrlConnector() {
+        _testWithoutSSLAuthentication(getHttpUrlConnectorConfig());
+    }
+}
diff --git a/examples/https-clientserver-grizzly/truststore_client b/examples/https-clientserver-grizzly/truststore_client
new file mode 100644
index 0000000..74784fb
--- /dev/null
+++ b/examples/https-clientserver-grizzly/truststore_client
Binary files differ
diff --git a/examples/https-clientserver-grizzly/truststore_server b/examples/https-clientserver-grizzly/truststore_server
new file mode 100644
index 0000000..9b26ce4
--- /dev/null
+++ b/examples/https-clientserver-grizzly/truststore_server
Binary files differ
diff --git a/examples/https-server-glassfish/README.MD b/examples/https-server-glassfish/README.MD
new file mode 100644
index 0000000..4b7ad35
--- /dev/null
+++ b/examples/https-server-glassfish/README.MD
@@ -0,0 +1,101 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+HTTPS Server Example - GlassFish
+================================
+
+This example demonstrates how to develop secure RESTful "Hello world"
+web service with a Servlet 2.5 container.
+
+Contents
+--------
+
+The example consists of just one Java class:
+
+`org.glassfish.jersey.examples.https.glassfish.resources.HelloWorldResource`
+
+A resource class that produces a textual response to an HTTP GET request.
+
+The mapping of the URI path space is presented in the following table:
+
+URI path            | Resource class       | HTTP methods
+------------------- | -------------------- | --------------
+**_/helloworld_**   | HelloWorldResource   | GET
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+Create war package
+
+>     mvn clean package
+
+Setup GlassFish 4.0 (installed at `AS_HOME=<install_dir>/glassfish4`):
+
+Add new security realm called "`myRealm`"
+-   Start GlassFish using asadmin `$AS_HOME/bin/asadmin start-domain`
+-   Access Admin console, assuming the use of default admin port, at
+    <http://localhost:4848>
+-   Navigate in Admin console to:
+    `Configurations -> server-config -> Security -> Realms`, click on
+    "`New...`"
+-   Create user and set password for user `myUser` (click on "`myRealm`"
+    -&gt; "`Manage Users`" -&gt; "`New...`");
+    -   **Ensure** "`User ID:`" set to "`myUser`" **and**
+        "`Group List:`" set to "`Users`"
+    -   Enter password **`myPass`** in "`New Password:`" and
+        "`Confirm New Password:`" text fields
+-   Add one other user name `joeUser` (click on "`myRealm`" -&gt;
+    "`Manage Users`" -&gt; "`New...`");
+    -   **Ensure** "`User ID:`" set to "`joeUser`" **and**
+        "`Group List:`" set to "`Users`"
+    -   Enter password **`joePass`** in "`New Password:`" and
+        "`Confirm New Password:`" text fields
+
+Check `web.xml` and `sun-web.xml` to understand how role mappings are
+configured. [This
+blog](http://blogs.oracle.com/bobby/entry/simplified_security_role_mapping)
+is also helpful in helping to clarify role mappings.
+Deploy `https-server-glassfish.war` as a Web Application
+-   Admin console: select `Applications`, click on "`Deploy...`". Select
+    file `./target/https-server-glassfish.war`
+-   or you can use command line:
+    `$AS_HOME/bin/asadmin deploy ./target/https-server-glassfish.war`
+
+From a web browser, visit:
+
+-   <https://localhost:8181/https-server-glassfish-webapp/helloworld>
+-   Enter either Username `myUser` and Password `myPass` or, Username  `joeUser` and Password `joePass`
+-   If entered Username `myUser` and Password `myPass`, verify that
+    **expected output**: `Sending "Hello World" to user "myUser"` was displayed
+-   If entered Username `joeUser` and Password `joePass`, verify that
+    **expected output**: `Sending "Hello World" to user "joeUser"` was displayed
+
+Running the Negative Example
+----------------------------
+
+Run the example as follows:
+
+1.  Create war package
+
+    >     mvn clean package
+
+2.  setup GlassFish 4.0 (installed at
+    `AS_HOME=<install_dir>/glassfish4`):
+
+    -   Follow the steps described in the "**setup GlassFish 3.1**"
+        section from the previous chapter "**Running the Example**" but
+        instead of adding the user "`joeUser`" to the "`Group List:`" of
+        "`Users`" set "`Group List:`" for this user to "`OtherUsers`"
+
+3.  From a web browser, visit:
+
+    -   <https://localhost:8181/https-server-glassfish-webapp/helloworld>
+    -   Enter Username `joeUser` and Password `joePass`, verify that **expected output**
+        looks like: `HTTP Status 403 - Access to the requested resource has been denied`
diff --git a/examples/https-server-glassfish/pom.xml b/examples/https-server-glassfish/pom.xml
new file mode 100644
index 0000000..844ba0d
--- /dev/null
+++ b/examples/https-server-glassfish/pom.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>https-server-glassfish</artifactId>
+    <name>jersey-examples-https-server-glassfish</name>
+    <packaging>war</packaging>
+
+    <description>Jersey HTTPS server on GlassFish example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Uncomment to run the application using "mvn embedded-glassfish:run"
+                 (does not work with the security config required by example at the moment) -->
+            <!--plugin>
+                <groupId>org.glassfish.embedded</groupId>
+                <artifactId>maven-embedded-glassfish-plugin</artifactId>
+            </plugin-->
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/https-server-glassfish/src/main/java/org/glassfish/jersey/examples/https/glassfish/resources/HelloWorldResource.java b/examples/https-server-glassfish/src/main/java/org/glassfish/jersey/examples/https/glassfish/resources/HelloWorldResource.java
new file mode 100644
index 0000000..8d4dc98
--- /dev/null
+++ b/examples/https-server-glassfish/src/main/java/org/glassfish/jersey/examples/https/glassfish/resources/HelloWorldResource.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.https.glassfish.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+
+import javax.annotation.security.RolesAllowed;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * The JAX-RS resource class will be hosted at the URI path {@code "/helloworld"}.
+ *
+ * @author Paul Sandoz
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("/helloworld")
+@RolesAllowed("myRole")
+public class HelloWorldResource {
+
+    /**
+     * HTTP GET controller method.
+     *
+     * @param request HTTP servlet request.
+     * @return a simple text message.
+     */
+    @GET
+    @Produces("text/plain")
+    public Response getClichedMessage(@Context HttpServletRequest request) {
+        return Response
+                .status(Response.Status.OK)
+                .type("text/plain")
+                .entity("Sending \"Hello World\" to user \"" + request.getUserPrincipal().getName() + "\"")
+                .build();
+
+    }
+}
diff --git a/examples/https-server-glassfish/src/main/webapp/WEB-INF/glassfish-web.xml b/examples/https-server-glassfish/src/main/webapp/WEB-INF/glassfish-web.xml
new file mode 100644
index 0000000..4def0b8
--- /dev/null
+++ b/examples/https-server-glassfish/src/main/webapp/WEB-INF/glassfish-web.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
+<glassfish-web-app error-url="">
+    <context-root>/https-server-glassfish-webapp</context-root>
+
+    <security-role-mapping>
+        <!-- maps "Users" group to myRole -->
+        <role-name>myRole</role-name>
+        <group-name>Users</group-name>
+    </security-role-mapping>
+
+    <class-loader delegate="true" />
+    <jsp-config>
+        <property name="classdebuginfo" value="true">
+            <description>Enable debug info compilation in the generated servlet class</description>
+        </property>
+        <property name="keepgenerated" value="true">
+            <description>Keep a copy of the generated servlet class' java code.</description>
+        </property>
+        <property name="mappedfile" value="true">
+            <description>Maintain a one-to-one correspondence between static content and the generated servlet class' java code</description>
+        </property>
+    </jsp-config>
+</glassfish-web-app>
diff --git a/examples/https-server-glassfish/src/main/webapp/WEB-INF/web.xml b/examples/https-server-glassfish/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..9833d0f
--- /dev/null
+++ b/examples/https-server-glassfish/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>Jersey Web Application</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>org.glassfish.jersey.examples.https.glassfish.resources</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>Jersey Web Application</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Protected resource</web-resource-name>
+            <url-pattern>/*</url-pattern>
+            <http-method>GET</http-method>
+        </web-resource-collection>
+
+        <!-- added -->
+        <auth-constraint>
+            <!-- role name used in HelloWorldResource.java-->
+            <role-name>myRole</role-name>
+        </auth-constraint>
+        <!-- /added -->
+
+        <!-- https -->
+        <user-data-constraint>
+            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+        </user-data-constraint>
+    </security-constraint>
+
+    <!-- added -->
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <!-- realm name used in GlassFish -->
+        <realm-name>myRealm</realm-name>
+    </login-config>
+    <security-role>
+        <role-name>myRole</role-name>
+    </security-role>
+    <!-- /added -->
+</web-app>
diff --git a/examples/java8-webapp/README.MD b/examples/java8-webapp/README.MD
new file mode 100644
index 0000000..23d2565
--- /dev/null
+++ b/examples/java8-webapp/README.MD
@@ -0,0 +1,45 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Jersey and Java 8 Examples Features and Types
+=============================================
+
+This example demonstrates the usage of Java 8's new constructs and types.
+
+Currently demonstrated are:
+
+-   Default methods in interfaces - acting as JAX-RS Resource Methods
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                         | Resource class           | HTTP method   | description
+-------------------------------- | ------------------------ | ------------- | ------------------------------------------
+**_/j8/default-method_**         | DefaultMethodInterface   | GET           | returns `interface-root` in `text/plain`
+**_/j8/default-method/path_**    | DefaultMethodInterface   | GET           | returns `interface-path` in `text/plain`
+**_/j8/default-method/class_**   | DefaultMethodResource    | GET           | returns `class` in `text/plain`
+**_/j8/lambdas/{p}_**            | LambdaResource           | GET           | returns `{p}-lambdaized` in `text/plain`
+
+Application is Servlet 3 based, web.xml-less. Everything needed
+(resources/providers) is registered in the `Java8Application` class.
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean package jetty:run
+
+This deploys current example using Jetty. You can access the application at:
+
+-   <http://localhost:8080/j8/default-method>
+-   <http://localhost:8080/j8/default-method/path>
+-   <http://localhost:8080/j8/default-method/class>
+-   <http://localhost:8080/j8/lambdas/test>
diff --git a/examples/java8-webapp/pom.xml b/examples/java8-webapp/pom.xml
new file mode 100644
index 0000000..0b4ac28
--- /dev/null
+++ b/examples/java8-webapp/pom.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>java8-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-java8-webapp</name>
+
+    <description>Java 8 Types WebApp Example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <inherited>true</inherited>
+                <configuration>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
+                    <showWarnings>false</showWarnings>
+                    <fork>false</fork>
+                </configuration>
+            </plugin>
+            <!-- Run the application using "mvn jetty:run" -->
+            <plugin>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+                <!-- TODO: Remove the version when JERSEY-2743 is resolved. -->
+                <version>9.2.6.v20141205</version>
+                <configuration>
+                    <scanIntervalSeconds>5</scanIntervalSeconds>
+                    <stopPort>9999</stopPort>
+                    <stopKey>STOP</stopKey>
+                    <webApp>
+                        <contextPath>/</contextPath>
+                        <webInfIncludeJarPattern>.*/.*jersey-[^/]\.jar$</webInfIncludeJarPattern>
+                    </webApp>
+                    <systemProperties>
+                        <systemProperty>
+                            <name>jetty.port</name>
+                            <value>${jersey.config.test.container.port}</value>
+                        </systemProperty>
+                    </systemProperties>
+                    <war>${project.build.directory}/${project.build.finalName}.war</war>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <jersey.config.test.container.port>8080</jersey.config.test.container.port>
+    </properties>
+</project>
diff --git a/examples/java8-webapp/src/main/java/org/glassfish/jersey/examples/java8/Java8Application.java b/examples/java8-webapp/src/main/java/org/glassfish/jersey/examples/java8/Java8Application.java
new file mode 100644
index 0000000..e61c5ad
--- /dev/null
+++ b/examples/java8-webapp/src/main/java/org/glassfish/jersey/examples/java8/Java8Application.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.java8;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.examples.java8.resources.DefaultMethodResource;
+import org.glassfish.jersey.examples.java8.resources.LambdaResource;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Application for illustrating some of the features of Java 8 in JAX-RS.
+ *
+ * @author Michal Gajdos
+ */
+@ApplicationPath("j8")
+public class Java8Application extends ResourceConfig {
+
+    public Java8Application() {
+        // Resources.
+        register(DefaultMethodResource.class);
+        register(LambdaResource.class);
+    }
+}
diff --git a/examples/java8-webapp/src/main/java/org/glassfish/jersey/examples/java8/resources/DefaultMethodInterface.java b/examples/java8-webapp/src/main/java/org/glassfish/jersey/examples/java8/resources/DefaultMethodInterface.java
new file mode 100644
index 0000000..4a67e9f
--- /dev/null
+++ b/examples/java8-webapp/src/main/java/org/glassfish/jersey/examples/java8/resources/DefaultMethodInterface.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.java8.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * Example interface containing resource methods in form of Java8's default methods.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public interface DefaultMethodInterface {
+
+    @GET
+    default String root() {
+        return "interface-root";
+    }
+
+    @GET
+    @Path("path")
+    default String path() {
+        return "interface-path";
+    }
+}
+
diff --git a/examples/java8-webapp/src/main/java/org/glassfish/jersey/examples/java8/resources/DefaultMethodResource.java b/examples/java8-webapp/src/main/java/org/glassfish/jersey/examples/java8/resources/DefaultMethodResource.java
new file mode 100644
index 0000000..79e347d
--- /dev/null
+++ b/examples/java8-webapp/src/main/java/org/glassfish/jersey/examples/java8/resources/DefaultMethodResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.java8.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * JAX-RS resource inheriting some resource method implementations from the implemented interface.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("default-method")
+@Produces("text/plain")
+public class DefaultMethodResource implements DefaultMethodInterface {
+
+    @GET
+    @Path("class")
+    public String fromClass() {
+        return "class";
+    }
+}
diff --git a/examples/java8-webapp/src/main/java/org/glassfish/jersey/examples/java8/resources/LambdaResource.java b/examples/java8-webapp/src/main/java/org/glassfish/jersey/examples/java8/resources/LambdaResource.java
new file mode 100644
index 0000000..2448ea8
--- /dev/null
+++ b/examples/java8-webapp/src/main/java/org/glassfish/jersey/examples/java8/resources/LambdaResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.java8.resources;
+
+import java.util.Collections;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+
+/**
+ * JAX-RS resource using Java SE 8 lambdas.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("lambdas/{p}")
+public class LambdaResource {
+    @GET
+    public String getLambdaResult(@PathParam("p") String p) {
+        return Collections.singleton(p).stream().map(v -> v + "-lambdaized").collect(Collectors.joining());
+    }
+}
diff --git a/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/DefaultMethodResourceTest.java b/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/DefaultMethodResourceTest.java
new file mode 100644
index 0000000..bd187ef
--- /dev/null
+++ b/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/DefaultMethodResourceTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.java8;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test usage of Java8's interface default methods as resource methods.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class DefaultMethodResourceTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Java8Application();
+    }
+
+    /**
+     * Test that JDK8 default methods do work as common JAX-RS resource methods.
+     */
+    @Test
+    public void testDefaultMethods() {
+        final WebTarget defaultMethodTarget = target("default-method");
+
+        // test default method with no @Path annotation
+        String response = defaultMethodTarget.request().get(String.class);
+        assertEquals("interface-root", response);
+
+        // test default method with with @Path annotation
+        response = defaultMethodTarget.path("path").request().get(String.class);
+        assertEquals("interface-path", response);
+    }
+
+    /**
+     * Test, that resource methods defined in the class implementing the interface with default method do work normally.
+     */
+    @Test
+    public void testImplementingClass() throws Exception {
+        final String response = target("default-method").path("class").request().get(String.class);
+        assertEquals("class", response);
+    }
+}
diff --git a/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/LambdaResourceTest.java b/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/LambdaResourceTest.java
new file mode 100644
index 0000000..937b1c7
--- /dev/null
+++ b/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/LambdaResourceTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.java8;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test usage of Java SE 8 lambdas in JAX-RS resource methods.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class LambdaResourceTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Java8Application();
+    }
+
+    /**
+     * Test that JDK8 lambdas do work in common JAX-RS resource methods.
+     */
+    @Test
+    public void testLambdas() {
+        final WebTarget target = target("lambdas/{p}");
+
+        // test default method with no @Path annotation
+        String response = target.resolveTemplate("p", "test").request().get(String.class);
+        assertThat(response, equalTo("test-lambdaized"));
+    }
+
+}
diff --git a/examples/jaxb/README.MD b/examples/jaxb/README.MD
new file mode 100644
index 0000000..fcd216e
--- /dev/null
+++ b/examples/jaxb/README.MD
@@ -0,0 +1,73 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+JAXB Example
+============
+
+This example demonstrates how to use JAXB types with resource classes and the JAX-RS Client API.
+
+The following JAXB types are supported and demonstrated:
+
+-   JAXB classes annotated with @XmlRootElement
+-   JAXB classes annotated with @XmlType
+-   JAXBElement&lt;T&gt; where T is a JAXB xml type that may or may not be annotated with @XmlType
+-   List&lt;T&gt;, Collection&lt;T&gt;, and T\[\] where T is a JAXB class annotated with @XmlRootElement or @XmlType
+
+It also shows how to use @XmlHeader annotation to specify the header of the XML responses.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                                | Resource class           | HTTP methods
+--------------------------------------- | ------------------------ | --------------
+**_/jaxb/XmlRootElement_**              | JaxbResource             | GET, POST
+**_/jaxb/XmlRootElementWithHeader_**    | JaxbResource             | GET
+**_/jaxb/JAXBElement_**                 | JaxbResource             | GET, POST
+**_/jaxb/XmlType_**                     | JaxbResource             | POST
+**_/jaxb/collection/XmlRootElement_**   | JaxbCollectionResource   | GET, POST
+**_/jaxb/collection/XmlType_**          | JaxbCollectionResource   | POST
+**_/jaxb/array/XmlRootElement_**        | JaxbArrayResource        | GET, POST
+**_/jaxb/array/XmlType_**               | JaxbArrayResource        | POST
+
+JAXB classes annotated with @XmlRootElement may be produced and
+consumed. JAXB classses annotated with @XmlType can only be consumed
+hence why only the POST method is supported for the resources associated
+with XmlType. This is because the JAXB requires enough information to
+serialize the JAXB instance to a well-formed XML document. The same
+rules apply to collections of such classes.
+
+For the serialization of a Collection of JAXB type, or an array, Jersey
+will automatically derive the XML root element of the XML document to
+serialize from the XML name of the JAXB class annoated with
+@XmlRootElement.
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container. You can access the application at:
+
+-   <http://localhost:8080/jaxb/jaxb/XmlRootElement>
+-   <http://localhost:8080/jaxb/jaxb/XmlRootElementWithHeader>
+-   <http://localhost:8080/jaxb/jaxb/JAXBElement>
+-   <http://localhost:8080/jaxb/jaxb/collection/XmlRootElement>
+-   <http://localhost:8080/jaxb/jaxb/array/XmlRootElement>
+
+or you can post an xml entity:
+
+>    curl -v -X POST http://localhost:8080/jaxb/jaxb/XmlRootElement -H "Content-Type:application/xml" -d '
+>    <jaxbXmlRootElement>
+>        <value>xml root element</value>
+>    </jaxbXmlRootElement>'
+
+You can see test classes at `src/main/test` subdirectory for detailed information how to consume the service using JAX-RS Client API
\ No newline at end of file
diff --git a/examples/jaxb/pom.xml b/examples/jaxb/pom.xml
new file mode 100644
index 0000000..47f856d
--- /dev/null
+++ b/examples/jaxb/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jaxb</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-jaxb</name>
+
+    <description>Jersey JAXB example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.woodstox</groupId>
+            <artifactId>woodstox-core-asl</artifactId>
+            <version>4.1.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.jaxb.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/App.java b/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/App.java
new file mode 100644
index 0000000..c07a92b
--- /dev/null
+++ b/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/App.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jaxb;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Jersey JAXB example application.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/jaxb/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("JAXB Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, createApp(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(
+                    String.format("Application started.%nTry out %s%nStop the application using CTRL+C", BASE_URI));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    public static ResourceConfig createApp() {
+        final ResourceConfig rc = new ResourceConfig()
+                .packages(JaxbResource.class.getPackage().getName());
+
+        return rc;
+    }
+}
diff --git a/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbArrayResource.java b/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbArrayResource.java
new file mode 100644
index 0000000..9242c53
--- /dev/null
+++ b/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbArrayResource.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jaxb;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * An example resource utilizing array of JAXB beans.
+ *
+ * @author Paul Sandoz
+ */
+@Path("jaxb/array")
+@Produces("application/xml")
+@Consumes("application/xml")
+public class JaxbArrayResource {
+
+    @Path("XmlRootElement")
+    @GET
+    public JaxbXmlRootElement[] getRootElement() {
+        List<JaxbXmlRootElement> el = new ArrayList<JaxbXmlRootElement>();
+        el.add(new JaxbXmlRootElement("one root element"));
+        el.add(new JaxbXmlRootElement("two root element"));
+        el.add(new JaxbXmlRootElement("three root element"));
+        return el.toArray(new JaxbXmlRootElement[el.size()]);
+    }
+
+    @Path("XmlRootElement")
+    @POST
+    public JaxbXmlRootElement[] postRootElement(JaxbXmlRootElement[] el) {
+        return el;
+    }
+
+    @Path("XmlType")
+    @POST
+    public JaxbXmlRootElement[] postXmlType(JaxbXmlType[] tl) {
+        List<JaxbXmlRootElement> el = new ArrayList<JaxbXmlRootElement>();
+
+        for (JaxbXmlType t : tl) {
+            el.add(new JaxbXmlRootElement(t.value));
+        }
+
+        return el.toArray(new JaxbXmlRootElement[el.size()]);
+    }
+}
diff --git a/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbCollectionResource.java b/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbCollectionResource.java
new file mode 100644
index 0000000..eb0e64e
--- /dev/null
+++ b/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbCollectionResource.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jaxb;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * An example resource utilizing collections of JAXB beans.
+ *
+ * @author Paul Sandoz
+ */
+@Path("jaxb/collection")
+@Produces("application/xml")
+@Consumes("application/xml")
+public class JaxbCollectionResource {
+
+    @Path("XmlRootElement")
+    @GET
+    public List<JaxbXmlRootElement> getRootElement() {
+        List<JaxbXmlRootElement> el = new ArrayList<JaxbXmlRootElement>();
+        el.add(new JaxbXmlRootElement("one root element"));
+        el.add(new JaxbXmlRootElement("two root element"));
+        el.add(new JaxbXmlRootElement("three root element"));
+        return el;
+    }
+
+    @Path("XmlRootElement")
+    @POST
+    public Collection<JaxbXmlRootElement> postRootElement(List<JaxbXmlRootElement> el) {
+        return el;
+    }
+
+    @Path("XmlType")
+    @POST
+    public List<JaxbXmlRootElement> postXmlType(List<JaxbXmlType> tl) {
+        List<JaxbXmlRootElement> el = new ArrayList<JaxbXmlRootElement>();
+
+        for (JaxbXmlType t : tl) {
+            el.add(new JaxbXmlRootElement(t.value));
+        }
+
+        return el;
+    }
+}
diff --git a/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbResource.java b/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbResource.java
new file mode 100644
index 0000000..b4cd435
--- /dev/null
+++ b/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbResource.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jaxb;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.namespace.QName;
+
+import org.glassfish.jersey.message.XmlHeader;
+
+/**
+ * An example resource utilizing JAXB beans.
+ *
+ * @author Paul Sandoz
+ */
+@Path("jaxb")
+@Produces("application/xml")
+@Consumes("application/xml")
+public class JaxbResource {
+
+    @Path("XmlRootElement")
+    @GET
+    public JaxbXmlRootElement getRootElement() {
+        return new JaxbXmlRootElement("xml root element");
+    }
+
+    @Path("XmlRootElementWithHeader")
+    @GET
+    @XmlHeader("<?xml-stylesheet type='text/xsl' href='foobar.xsl' ?>")
+    public JaxbXmlRootElement getRootElementWithHeader() {
+        return new JaxbXmlRootElement("xml root element");
+    }
+
+    @Path("XmlRootElement")
+    @POST
+    public JaxbXmlRootElement postRootElement(JaxbXmlRootElement r) {
+        return r;
+    }
+
+    @Path("JAXBElement")
+    @GET
+    public JAXBElement<JaxbXmlType> getJAXBElement() {
+        return new JAXBElement<JaxbXmlType>(
+                new QName("jaxbXmlRootElement"),
+                JaxbXmlType.class,
+                new JaxbXmlType("xml type"));
+    }
+
+    @Path("JAXBElement")
+    @POST
+    public JAXBElement<JaxbXmlType> postJAXBElement(JAXBElement<JaxbXmlType> e) {
+        return e;
+    }
+
+    @Path("XmlType")
+    @POST
+    public JAXBElement<JaxbXmlType> postXmlType(JaxbXmlType r) {
+        return new JAXBElement<JaxbXmlType>(
+                new QName("jaxbXmlRootElement"), JaxbXmlType.class, r);
+    }
+}
diff --git a/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbXmlRootElement.java b/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbXmlRootElement.java
new file mode 100644
index 0000000..ceb6294
--- /dev/null
+++ b/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbXmlRootElement.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jaxb;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class JaxbXmlRootElement {
+
+    public String value;
+
+    public JaxbXmlRootElement() {
+    }
+
+    public JaxbXmlRootElement(String str) {
+        value = str;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof JaxbXmlRootElement)) {
+            return false;
+        }
+        return ((JaxbXmlRootElement) o).value.equals(value);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 89 * hash + (this.value != null ? this.value.hashCode() : 0);
+        return hash;
+    }
+}
diff --git a/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbXmlType.java b/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbXmlType.java
new file mode 100644
index 0000000..d09bacc
--- /dev/null
+++ b/examples/jaxb/src/main/java/org/glassfish/jersey/examples/jaxb/JaxbXmlType.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jaxb;
+
+import javax.xml.bind.annotation.XmlType;
+
+@XmlType
+public class JaxbXmlType {
+
+    public String value;
+
+    public JaxbXmlType() {
+    }
+
+    public JaxbXmlType(String str) {
+        value = str;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof JaxbXmlType)) {
+            return false;
+        }
+        return ((JaxbXmlType) o).value.equals(value);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 79 * hash + (this.value != null ? this.value.hashCode() : 0);
+        return hash;
+    }
+}
diff --git a/examples/jaxb/src/test/java/org/glassfish/jersey/examples/jaxb/JaxbTest.java b/examples/jaxb/src/test/java/org/glassfish/jersey/examples/jaxb/JaxbTest.java
new file mode 100644
index 0000000..e6f3a8c
--- /dev/null
+++ b/examples/jaxb/src/test/java/org/glassfish/jersey/examples/jaxb/JaxbTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jaxb;
+
+import java.util.Collection;
+
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.GenericType;
+import static javax.ws.rs.client.Entity.xml;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.namespace.QName;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Jersey JAXB example test.
+ *
+ * @author Paul Sandoz
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class JaxbTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        return App.createApp();
+    }
+
+    /**
+     * Test checks that the application.wadl is reachable.
+     */
+    @Test
+    public void testApplicationWadl() {
+        String applicationWadl = target().path("application.wadl").request().get(String.class);
+        assertTrue("Something wrong. Returned wadl length is not > 0",
+                applicationWadl.length() > 0);
+    }
+
+    @Test
+    public void testRootElement() {
+        JaxbXmlRootElement e1 = target().path("jaxb/XmlRootElement").request().get(JaxbXmlRootElement.class);
+
+        JaxbXmlRootElement e2 = target().path("jaxb/XmlRootElement").request("application/xml")
+                .post(xml(e1), JaxbXmlRootElement.class);
+
+        assertEquals(e1, e2);
+    }
+
+    @Test
+    public void testRootElementWithHeader() {
+        String e1 = target().path("jaxb/XmlRootElement").request().get(String.class);
+
+        String e2 = target().path("jaxb/XmlRootElementWithHeader").request().get(String.class);
+        assertTrue(e2.contains("<?xml-stylesheet type='text/xsl' href='foobar.xsl' ?>") && e2.contains(e1.substring(e1.indexOf("?>") + 2).trim()));
+    }
+
+    @Test
+    public void testJAXBElement() {
+        GenericType<JAXBElement<JaxbXmlType>> genericType = new GenericType<JAXBElement<JaxbXmlType>>() {};
+
+        JAXBElement<JaxbXmlType> e1 = target().path("jaxb/JAXBElement").request().get(genericType);
+
+        JAXBElement<JaxbXmlType> e2 = target().path("jaxb/JAXBElement").request("application/xml")
+                .post(xml(e1), genericType);
+
+        assertEquals(e1.getValue(), e2.getValue());
+    }
+
+    @Test
+    public void testXmlType() {
+        JaxbXmlType t1 = target().path("jaxb/JAXBElement").request().get(JaxbXmlType.class);
+
+        JAXBElement<JaxbXmlType> e = new JAXBElement<JaxbXmlType>(
+                new QName("jaxbXmlRootElement"),
+                JaxbXmlType.class,
+                t1);
+        JaxbXmlType t2 = target().path("jaxb/XmlType").request("application/xml")
+                .post(xml(e), JaxbXmlType.class);
+
+        assertEquals(t1, t2);
+    }
+
+
+    @Test
+    public void testRootElementCollection() {
+        GenericType<Collection<JaxbXmlRootElement>> genericType =
+                new GenericType<Collection<JaxbXmlRootElement>>() {};
+
+        Collection<JaxbXmlRootElement> ce1 = target().path("jaxb/collection/XmlRootElement").request().get(genericType);
+        Collection<JaxbXmlRootElement> ce2 = target().path("jaxb/collection/XmlRootElement").request("application/xml")
+                .post(xml(new GenericEntity<Collection<JaxbXmlRootElement>>(ce1) {}), genericType);
+
+        assertEquals(ce1, ce2);
+    }
+
+    @Test
+    public void testXmlTypeCollection() {
+        GenericType<Collection<JaxbXmlRootElement>> genericRootElement =
+                new GenericType<Collection<JaxbXmlRootElement>>() {};
+        GenericType<Collection<JaxbXmlType>> genericXmlType =
+                new GenericType<Collection<JaxbXmlType>>() {
+                };
+
+        Collection<JaxbXmlRootElement> ce1 = target().path("jaxb/collection/XmlRootElement").request()
+                .get(genericRootElement);
+
+        Collection<JaxbXmlType> ct1 = target().path("jaxb/collection/XmlType").request("application/xml")
+                .post(xml(new GenericEntity<Collection<JaxbXmlRootElement>>(ce1) {}), genericXmlType);
+
+        Collection<JaxbXmlType> ct2 = target().path("jaxb/collection/XmlRootElement").request()
+                .get(genericXmlType);
+
+        assertEquals(ct1, ct2);
+    }
+
+    @Test
+    public void testRootElementArray() {
+        JaxbXmlRootElement[] ae1 = target().path("jaxb/array/XmlRootElement").request()
+                .get(JaxbXmlRootElement[].class);
+        JaxbXmlRootElement[] ae2 = target().path("jaxb/array/XmlRootElement").request("application/xml")
+                .post(xml(ae1), JaxbXmlRootElement[].class);
+
+        assertEquals(ae1.length, ae2.length);
+        for (int i = 0; i < ae1.length; i++) {
+            assertEquals(ae1[i], ae2[i]);
+        }
+    }
+
+    @Test
+    public void testXmlTypeArray() {
+        JaxbXmlRootElement[] ae1 = target().path("jaxb/array/XmlRootElement").request()
+                .get(JaxbXmlRootElement[].class);
+
+        JaxbXmlType[] at1 = target().path("jaxb/array/XmlType").request("application/xml")
+                .post(xml(ae1), JaxbXmlType[].class);
+
+        JaxbXmlType[] at2 = target().path("jaxb/array/XmlRootElement").request()
+                .get(JaxbXmlType[].class);
+
+        assertEquals(at1.length, at2.length);
+        for (int i = 0; i < at1.length; i++) {
+            assertEquals(at1[i], at2[i]);
+        }
+    }
+}
diff --git a/examples/jaxrs-types-injection/README.MD b/examples/jaxrs-types-injection/README.MD
new file mode 100644
index 0000000..a07f51b
--- /dev/null
+++ b/examples/jaxrs-types-injection/README.MD
@@ -0,0 +1,58 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+JAX-RS Types Injection Example
+==============================
+
+This example demonstrates injection of various runtime information into Jersey/JAX-RS resources.
+
+A simple application consists of two simple resources: One is a typical
+JAX-RS `@Path` annotated resource. The second resource is created
+programmatically using the new Jeresy 2 programmatic resource binding
+API. Both resources inject the same information and produce the report
+that is returned in the response back to the client.
+
+Contents
+--------
+
+The JAX-RS `@Path` annotated web resource is implemented by the
+
+`org.glassfish.jersey.examples.jaxrstypeinjection.JaxrsInjectionReportingResource`
+
+class. The programmatic resource is implemented by the
+
+`org.glassfish.jersey.examples.jaxrstypeinjection.JaxrsInjectionReportingInflector` class.
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                                                                                 | Resource class                     | HTTP methods
+---------------------------------------------------------------------------------------- | ---------------------------------- | --------------
+**_/annotated/instance/{p1}/{p2}?q1=<int_value>&q2=<string_value>&q2=<string_value>_**   | JaxrsInjectionReportingResource    | GET
+**_/annotated/method/{p1}/{p2}?q1=<int_value>&q2=<string_value>&q2=<string_value>_**     | JaxrsInjectionReportingResource    | GET
+**_/programmatic/{p1}/{p2}?q1=<int_value>&q2=<string_value>&q2=<string_value>_**         | JaxrsInjectionReportingInflector   | GET
+
+Please note that the value of the `q1` URI query parameter is expected
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the JAX-RS Types Injection Example using [Grizzly](http://grizzly.java.net/)
+
+A [WADL description](http://wadl.java.net/#spec) may be accessed at the URL:
+
+-   <http://localhost:8080/jaxrs-type-injection/application.wadl>
+
+The resources could be accessed via
+
+-   <http://localhost:8080/jaxrs-type-injection/annotated/instance/p1/p2?q1=123&q2=abc&q2=def>
+-   <http://localhost:8080/jaxrs-type-injection/annotated/method/p1/p2?q1=123&q2=abc&q2=def>
+-   <http://localhost:8080/jaxrs-type-injection/programmatic/p1/p2?q1=123&q2=abc&q2=def>
diff --git a/examples/jaxrs-types-injection/pom.xml b/examples/jaxrs-types-injection/pom.xml
new file mode 100644
index 0000000..e026912
--- /dev/null
+++ b/examples/jaxrs-types-injection/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jaxrs-types-injection</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-jaxrs-types-injection</name>
+
+    <description>Jersey JAX-RS types injection example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.jaxrstypeinjection.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/jaxrs-types-injection/src/main/java/org/glassfish/jersey/examples/jaxrstypeinjection/App.java b/examples/jaxrs-types-injection/src/main/java/org/glassfish/jersey/examples/jaxrstypeinjection/App.java
new file mode 100644
index 0000000..d2b2ab6
--- /dev/null
+++ b/examples/jaxrs-types-injection/src/main/java/org/glassfish/jersey/examples/jaxrstypeinjection/App.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jaxrstypeinjection;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Jersey application that demonstrates injection of JAX-RS components into resources.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/jaxrs-type-injection/");
+    /**
+     * Programmatic root resource path template.
+     */
+    public static final String ROOT_PATH_PROGRAMMATIC = "programmatic/{p1}/{p2}";
+    /**
+     * Annotated class-based root resource path template demonstrating instance field injection.
+     */
+    public static final String ROOT_PATH_ANNOTATED_INSTANCE = "annotated/instance/{p1}/{p2}";
+    /**
+     * Annotated class-based root resource path template demonstrating method injection.
+     */
+    public static final String ROOT_PATH_ANNOTATED_METHOD = "annotated/method/{p1}/{p2}";
+
+    /**
+     * Main application entry point.
+     *
+     * @param args application arguments.
+     */
+    public static void main(String[] args) {
+        try {
+            System.out.println("JAX-RS Type Injection Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, create(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format(
+                    "Application started.%n"
+                            + "To test injection into a programmatic resource, try out:%n  %s%s%s%n"
+                            + "To test instance injection into an annotated resource, try out:%n  %s%s%s%n"
+                            + "To test method injection into an annotated resource, try out:%n  %s%s%s%n"
+                            + "Stop the application using CTRL+C",
+                    BASE_URI, ROOT_PATH_PROGRAMMATIC, "?q1=<value_1>&q2=<value_2>&q2=<value_3>",
+                    BASE_URI, ROOT_PATH_ANNOTATED_INSTANCE, "?q1=<value_1>&q2=<value_2>&q2=<value_3>",
+                    BASE_URI, ROOT_PATH_ANNOTATED_METHOD, "?q1=<value_1>&q2=<value_2>&q2=<value_3>"));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    /**
+     * Create example application resource configuration.
+     *
+     * @return initialized resource configuration of the example application.
+     */
+    public static ResourceConfig create() {
+        final ResourceConfig resourceConfig = new ResourceConfig(JaxrsInjectionReportingResource.class);
+        final Resource.Builder resourceBuilder = Resource.builder(ROOT_PATH_PROGRAMMATIC);
+        resourceBuilder.addMethod("GET").handledBy(JaxrsInjectionReportingInflector.class);
+
+        return resourceConfig.registerResources(resourceBuilder.build());
+    }
+}
diff --git a/examples/jaxrs-types-injection/src/main/java/org/glassfish/jersey/examples/jaxrstypeinjection/JaxrsInjectionReportingInflector.java b/examples/jaxrs-types-injection/src/main/java/org/glassfish/jersey/examples/jaxrstypeinjection/JaxrsInjectionReportingInflector.java
new file mode 100644
index 0000000..a77ab8f
--- /dev/null
+++ b/examples/jaxrs-types-injection/src/main/java/org/glassfish/jersey/examples/jaxrstypeinjection/JaxrsInjectionReportingInflector.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jaxrstypeinjection;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.PathSegment;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.process.Inflector;
+
+/**
+ * Programmatic resource.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+class JaxrsInjectionReportingInflector implements Inflector<Request, Response> {
+
+    @Inject
+    HttpHeaders httpHeaders;
+    @Inject
+    UriInfo uriInfo;
+    @PathParam(value = "p1")
+    String p1;
+    private PathSegment p2;
+
+    @PathParam(value = "p2")
+    public void setP2(PathSegment p2) {
+        this.p2 = p2;
+    }
+    @QueryParam(value = "q1")
+    private int q1;
+    @QueryParam(value = "q2")
+    private List<String> q2;
+
+    @Override
+    public Response apply(Request data) {
+        StringBuilder sb = ReportBuilder.append(
+                new StringBuilder("Injected information:\n"), uriInfo, httpHeaders);
+        sb.append("\n URI component injection:");
+        sb.append("\n   String path param p1=").append(p1);
+        sb.append("\n   PathSegment path param p2=").append(p2);
+        sb.append("\n   int query param q1=").append(q1);
+        sb.append("\n   List<String> query param q2=").append(q2);
+        return Response.ok(sb.toString(), MediaType.TEXT_PLAIN).build();
+    }
+}
diff --git a/examples/jaxrs-types-injection/src/main/java/org/glassfish/jersey/examples/jaxrstypeinjection/JaxrsInjectionReportingResource.java b/examples/jaxrs-types-injection/src/main/java/org/glassfish/jersey/examples/jaxrstypeinjection/JaxrsInjectionReportingResource.java
new file mode 100644
index 0000000..87f03d9
--- /dev/null
+++ b/examples/jaxrs-types-injection/src/main/java/org/glassfish/jersey/examples/jaxrstypeinjection/JaxrsInjectionReportingResource.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jaxrstypeinjection;
+
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.PathSegment;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * Annotated resource.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("annotated")
+public class JaxrsInjectionReportingResource {
+
+    @Context
+    HttpHeaders httpHeaders;
+    @Context
+    UriInfo uriInfo;
+    @PathParam(value = "p1")
+    String p1;
+    private PathSegment p2;
+
+    @PathParam(value = "p2")
+    public void setP2(PathSegment p2) {
+        this.p2 = p2;
+    }
+
+    @QueryParam(value = "q1")
+    private int q1;
+    @QueryParam(value = "q2")
+    private List<String> q2;
+
+    @GET
+    @Path("method/{p1}/{p2}")
+    public String doGet(
+            @Context HttpHeaders httpHeaders,
+            @Context UriInfo uriInfo,
+            @PathParam(value = "p1") String p1,
+            @PathParam(value = "p2") PathSegment p2,
+            @QueryParam(value = "q1") int q1,
+            @QueryParam(value = "q2") List<String> q2) {
+        StringBuilder sb = ReportBuilder.append(
+                new StringBuilder("Injected information:\n"), uriInfo, httpHeaders);
+        sb.append("\n URI component injection:");
+        sb.append("\n   String path param p1=").append(p1);
+        sb.append("\n   PathSegment path param p2=").append(p2);
+        sb.append("\n   int query param q1=").append(q1);
+        sb.append("\n   List<String> query param q2=").append(q2);
+        return sb.toString();
+    }
+
+    @GET
+    @Path("instance/{p1}/{p2}")
+    public String doGet() {
+        return doGet(httpHeaders, uriInfo, p1, p2, q1, q2);
+    }
+}
diff --git a/examples/jaxrs-types-injection/src/main/java/org/glassfish/jersey/examples/jaxrstypeinjection/ReportBuilder.java b/examples/jaxrs-types-injection/src/main/java/org/glassfish/jersey/examples/jaxrstypeinjection/ReportBuilder.java
new file mode 100644
index 0000000..fcafa26
--- /dev/null
+++ b/examples/jaxrs-types-injection/src/main/java/org/glassfish/jersey/examples/jaxrstypeinjection/ReportBuilder.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jaxrstypeinjection;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * Provides functionality for appending values of JAX-RS types to a string-based
+ * report.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+class ReportBuilder {
+
+    private ReportBuilder() {
+    }
+
+    public static StringBuilder append(StringBuilder sb, UriInfo uriInfo, HttpHeaders httpHeaders) {
+        sb.append("\n UriInfo:");
+        sb.append("\n   Absolute path : ").append(uriInfo.getAbsolutePath());
+        sb.append("\n   Base URI : ").append(uriInfo.getBaseUri());
+        sb.append("\n   Mathced resources : ").append(uriInfo.getMatchedResources().toString());
+        sb.append("\n   Matched URIs : ").append(uriInfo.getMatchedURIs().toString());
+        sb.append("\n   Path : ").append(uriInfo.getPath());
+        sb.append("\n   Path parameters:\n");
+        dumpMultivaluedMap(sb, uriInfo.getPathParameters());
+        sb.append("   Path segments : ").append(uriInfo.getPathSegments().toString());
+        sb.append("\n   Query parameters:\n");
+        dumpMultivaluedMap(sb, uriInfo.getQueryParameters());
+        sb.append("   Request URI : ").append(uriInfo.getRequestUri());
+        sb.append("\n\n HttpHeaders:\n");
+        dumpMultivaluedMap(sb, httpHeaders.getRequestHeaders());
+        return sb;
+    }
+
+    public static void dumpMultivaluedMap(StringBuilder sb, MultivaluedMap<String, String> map) {
+        if (map == null) {
+            sb.append("     [ null ]\n");
+            return;
+        }
+        for (Map.Entry<String, List<String>> headerEntry : map.entrySet()) {
+            sb.append("     ").append(headerEntry.getKey()).append(" : ");
+            final Iterator<String> valueIterator = headerEntry.getValue().iterator();
+            sb.append(valueIterator.next());
+            while (valueIterator.hasNext()) {
+                sb.append(", ").append(valueIterator.next());
+            }
+            sb.append('\n');
+        }
+    }
+}
diff --git a/examples/jaxrs-types-injection/src/test/java/org/glassfish/jersey/examples/jaxrstypeinjection/JaxrsTypeInjectionTest.java b/examples/jaxrs-types-injection/src/test/java/org/glassfish/jersey/examples/jaxrstypeinjection/JaxrsTypeInjectionTest.java
new file mode 100644
index 0000000..d9e87e1
--- /dev/null
+++ b/examples/jaxrs-types-injection/src/test/java/org/glassfish/jersey/examples/jaxrstypeinjection/JaxrsTypeInjectionTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jaxrstypeinjection;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+public class JaxrsTypeInjectionTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return App.create();
+    }
+
+    private String[] expectedFragmentsProgrammatic = new String[]{
+            // UriInfo
+            "Absolute path : " + this.getBaseUri() + "programmatic/v1/v2",
+            "Base URI : " + this.getBaseUri(),
+            "Path : programmatic/v1/v2",
+            "Path segments : [programmatic, v1, v2]",
+            "p1 : v1", "p2 : v2", // path params
+            "q1 : 1", "q2 : v2, v3", // query params
+            "Request URI : " + this.getBaseUri() + "programmatic/v1/v2?q1=1&q2=v2&q2=v3",
+            // RequestHeaders/HttpHeaders
+            "Accept : text/plain",
+            // Injected Parameters
+            "String path param p1=v1",
+            "PathSegment path param p2=v2",
+            "int query param q1=1",
+            "List<String> query param q2=[v2, v3]"
+    };
+    private String[] expectedFragmentsAnnotatedInstance = new String[]{
+            // UriInfo
+            "Absolute path : " + this.getBaseUri() + "annotated/instance/v1/v2",
+            "Base URI : " + this.getBaseUri(),
+            "Path : annotated/instance/v1/v2",
+            "Path segments : [annotated, instance, v1, v2]",
+            "p1 : v1", "p2 : v2", // path params
+            "q1 : 1", "q2 : v2, v3", // query params
+            "Request URI : " + this.getBaseUri() + "annotated/instance/v1/v2?q1=1&q2=v2&q2=v3",
+            // RequestHeaders/HttpHeaders
+            "Accept : text/plain",
+            // Injected Parameters
+            "String path param p1=v1",
+            "PathSegment path param p2=v2",
+            "int query param q1=1",
+            "List<String> query param q2=[v2, v3]"
+    };
+    private String[] expectedFragmentsAnnotatedMethod = new String[]{
+            // UriInfo
+            "Absolute path : " + this.getBaseUri() + "annotated/method/v1/v2",
+            "Base URI : " + this.getBaseUri(),
+            "Path : annotated/method/v1/v2",
+            "Path segments : [annotated, method, v1, v2]",
+            "p1 : v1", "p2 : v2", // path params
+            "q1 : 1", "q2 : v2, v3", // query params
+            "Request URI : " + this.getBaseUri() + "annotated/method/v1/v2?q1=1&q2=v2&q2=v3",
+            // RequestHeaders/HttpHeaders
+            "Accept : text/plain",
+            // Injected Parameters
+            "String path param p1=v1",
+            "PathSegment path param p2=v2",
+            "int query param q1=1",
+            "List<String> query param q2=[v2, v3]"
+    };
+
+    private WebTarget prepareTarget(String path) {
+        final WebTarget target = target();
+        target.register(LoggingFeature.class);
+        return target.path(path).resolveTemplate("p1", "v1").resolveTemplate("p2",
+                "v2").queryParam("q1", 1).queryParam("q2", "v2").queryParam("q2", "v3");
+    }
+
+    @Test
+    public void testProgrammaticApp() throws Exception {
+        String responseEntity = prepareTarget(App.ROOT_PATH_PROGRAMMATIC).request("text/plain").get(String.class)
+                .toLowerCase();
+
+        for (String expectedFragment : expectedFragmentsProgrammatic) {
+            assertTrue("Expected fragment '" + expectedFragment + "' not found in response:\n" + responseEntity,
+                    // http header field names are case insensitive
+                    responseEntity.contains(expectedFragment.toLowerCase()));
+        }
+    }
+
+    @Test
+    public void testAnnotatedInstanceApp() throws Exception {
+        String responseEntity = prepareTarget(App.ROOT_PATH_ANNOTATED_INSTANCE).request("text/plain").get(String.class)
+                .toLowerCase();
+
+        for (String expectedFragment : expectedFragmentsAnnotatedInstance) {
+            assertTrue("Expected fragment '" + expectedFragment + "' not found in response:\n" + responseEntity,
+                    // http header field names are case insensitive
+                    responseEntity.contains(expectedFragment.toLowerCase()));
+        }
+    }
+
+    @Test
+    public void testAnnotatedMethodApp() throws Exception {
+        String responseEntity = prepareTarget(App.ROOT_PATH_ANNOTATED_METHOD).request("text/plain").get(String.class)
+                .toLowerCase();
+
+        for (String expectedFragment : expectedFragmentsAnnotatedMethod) {
+            assertTrue("Expected fragment '" + expectedFragment + "' not found in response:\n" + responseEntity,
+                    // http header field names are case insensitive
+                    responseEntity.contains(expectedFragment.toLowerCase()));
+        }
+    }
+}
diff --git a/examples/jersey-ejb/README.MD b/examples/jersey-ejb/README.MD
new file mode 100644
index 0000000..259a9a6
--- /dev/null
+++ b/examples/jersey-ejb/README.MD
@@ -0,0 +1,44 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Jersey EJB Example
+==================
+
+This example demonstrates how to develop RESTful web service with a Servlet 3.0 and EJB 3.1 container.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                                | Resource class             | HTTP methods   | Description
+--------------------------------------- | -------------------------- | -------------- | ------------------------------------------------------------------------------------
+**_/jersey-ejb_**                       | ---                        | GET            | Simple html/ajax client
+**_/jersey-ejb/app/messages_**          | MessageBoardResourceBean   | GET            | Returns first ten messsages
+**_/jersey-ejb/app/messages/{id}_**     | MessageBoardResourceBean   | GET            | Returns message with uniqueId = {id} or returns HTTP 404 if message does not exist
+**_/jersey-ejb/app/messages/{id}_**     | MessageBoardResourceBean   | DELETE         | Deletes message with uniqueId = {id} or returns HTTP 404 if message does not exist
+**_/jersey-ejb/app/messages/_**         | MessageBoardResourceBean   | POST           | Creates new message and allocates unique id for it (with message as request entity)
+
+Running the Example
+-------------------
+
+This sample utilizes Java EE features in the GlassFish container
+
+Build the application WAR file by executing maven 2 from the project directory:
+
+>     mvn clean package
+
+To deploy on a running GlassFish container, you would then typically need to launch:
+
+>     $AS_HOME/bin/asadmin deploy target/jersey-ejb.war
+
+Another option is to deploy using [GlassFish administrator console GUI](http://localhost:4848/)
+
+You can access the application at:
+
+-   <http://localhost:8080/jersey-ejb>
diff --git a/examples/jersey-ejb/pom.xml b/examples/jersey-ejb/pom.xml
new file mode 100644
index 0000000..2a62817
--- /dev/null
+++ b/examples/jersey-ejb/pom.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-ejb</artifactId>
+    <name>jersey-examples-ejb</name>
+    <packaging>war</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ejb</groupId>
+            <artifactId>javax.ejb-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>run-external-tests</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <jersey.config.test.container.factory>${external.container.factory}</jersey.config.test.container.factory>
+                                <jersey.config.test.container.port>${external.container.port}</jersey.config.test.container.port>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <properties>
+                <!-- External test container configuration is done via properties to allow overriding via command line. -->
+                <external.container.factory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</external.container.factory>
+                <external.container.port>8080</external.container.port>
+                <maven.test.skip>false</maven.test.skip>
+            </properties>
+        </profile>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <maven.test.skip>true</maven.test.skip>
+    </properties>
+</project>
diff --git a/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/entities/Message.java b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/entities/Message.java
new file mode 100644
index 0000000..6bea954
--- /dev/null
+++ b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/entities/Message.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jersey_ejb.entities;
+
+import java.util.Date;
+
+/**
+ * Message bean representing a single message.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class Message {
+
+    private Date created;
+    private String message;
+    private int uniqueId;
+
+    public Message(Date created, String message, int uniqueId) {
+        this.created = created;
+        this.message = message;
+        this.uniqueId = uniqueId;
+    }
+
+    public int getUniqueId() {
+        return uniqueId;
+    }
+
+    @Override
+    public String toString() {
+        return "<span class='created'>CREATED: " + created + "</span> <span class='uniqueId'>ID: " + uniqueId
+                + "</span> <span class='message'>MESSAGE: " + message + "</span>";
+    }
+}
diff --git a/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/entities/MessageListWriter.java b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/entities/MessageListWriter.java
new file mode 100644
index 0000000..f4eef68
--- /dev/null
+++ b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/entities/MessageListWriter.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jersey_ejb.entities;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import javax.ejb.Stateless;
+
+import org.glassfish.jersey.message.MessageUtils;
+
+/**
+ * A simple HTML message body writer to serialize list of message beans.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Stateless
+@Provider
+public class MessageListWriter implements MessageBodyWriter<List<Message>> {
+
+    @Context
+    private javax.inject.Provider<UriInfo> ui;
+
+    @Override
+    public boolean isWriteable(final Class<?> clazz, final Type type, final Annotation[] annotation, final MediaType mediaType) {
+        return verifyGenericType(type);
+    }
+
+    private boolean verifyGenericType(final Type genericType) {
+        if (!(genericType instanceof ParameterizedType)) {
+            return false;
+        }
+
+        final ParameterizedType pt = (ParameterizedType) genericType;
+
+        if (pt.getActualTypeArguments().length > 1) {
+            return false;
+        }
+
+        if (!(pt.getActualTypeArguments()[0] instanceof Class)) {
+            return false;
+        }
+
+        final Class listClass = (Class) pt.getActualTypeArguments()[0];
+        return listClass == Message.class;
+    }
+
+    @Override
+    public long getSize(final List<Message> messages,
+                        final Class<?> clazz,
+                        final Type type,
+                        final Annotation[] annotation,
+                        final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final List<Message> messages,
+                        final Class<?> clazz,
+                        final Type type,
+                        final Annotation[] annotation,
+                        final MediaType mediaType,
+                        final MultivaluedMap<String, Object> arg5,
+                        final OutputStream ostream) throws IOException, WebApplicationException {
+        for (final Message m : messages) {
+            ostream.write(m.toString().getBytes(MessageUtils.getCharset(mediaType)));
+            final URI mUri = ui.get().getAbsolutePathBuilder().path(Integer.toString(m.getUniqueId())).build();
+            ostream.write((" <a href='" + mUri.toASCIIString() + "'>link</a><br />").getBytes());
+        }
+    }
+}
diff --git a/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/entities/MessageWriter.java b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/entities/MessageWriter.java
new file mode 100644
index 0000000..5d7d2cd
--- /dev/null
+++ b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/entities/MessageWriter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jersey_ejb.entities;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import javax.ejb.Singleton;
+import org.glassfish.jersey.message.MessageUtils;
+
+/**
+ * A simple message body writer to serialize a single message bean.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Singleton
+@Provider
+public class MessageWriter implements MessageBodyWriter<Message> {
+
+    @Override
+    public boolean isWriteable(final Class<?> clazz, final Type type, final Annotation[] annotation, final MediaType mediaType) {
+        return clazz == Message.class;
+    }
+
+    @Override
+    public long getSize(final Message message, final Class<?> clazz, final Type type, final Annotation[] annotation,
+                        final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final Message message, final Class<?> clazz, final Type type, final Annotation[] annotation,
+                        final MediaType mediaType, final MultivaluedMap<String, Object> arg5, final OutputStream ostream)
+            throws IOException, WebApplicationException {
+        ostream.write(message.toString().getBytes(MessageUtils.getCharset(mediaType)));
+    }
+}
diff --git a/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/exceptions/CustomNotFoundException.java b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/exceptions/CustomNotFoundException.java
new file mode 100644
index 0000000..966a47e
--- /dev/null
+++ b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/exceptions/CustomNotFoundException.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jersey_ejb.exceptions;
+
+/**
+ * This exceptions will get mapped to a 404 response with the application exception mapper
+ * implemented by {@link NotFoundExceptionMapper} class.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class CustomNotFoundException extends Exception {
+
+}
diff --git a/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/exceptions/NotFoundExceptionMapper.java b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/exceptions/NotFoundExceptionMapper.java
new file mode 100644
index 0000000..d36593a
--- /dev/null
+++ b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/exceptions/NotFoundExceptionMapper.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jersey_ejb.exceptions;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * An exception mapper to return 404 responses when a {@link CustomNotFoundException} is thrown.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Provider
+public class NotFoundExceptionMapper implements ExceptionMapper<CustomNotFoundException> {
+
+    @Override
+    public Response toResponse(CustomNotFoundException exception) {
+        return Response.status(404).build();
+    }
+
+}
diff --git a/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/resources/MessageBoardResourceBean.java b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/resources/MessageBoardResourceBean.java
new file mode 100644
index 0000000..a55219e
--- /dev/null
+++ b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/resources/MessageBoardResourceBean.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jersey_ejb.resources;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+
+import org.glassfish.jersey.examples.jersey_ejb.entities.Message;
+import org.glassfish.jersey.examples.jersey_ejb.exceptions.CustomNotFoundException;
+
+/**
+ * A stateless EJB bean to handle REST requests to the messages resource.
+ * Messages are stored in the injected EJB singleton instance.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Stateless
+public class MessageBoardResourceBean {
+
+    @Context
+    private UriInfo ui;
+    @EJB
+    MessageHolderSingletonBean singleton;
+
+    /**
+     * Returns a list of all messages stored in the internal message holder.
+     */
+    @GET
+    public List<Message> getMessages() {
+        return singleton.getMessages();
+    }
+
+    @POST
+    public Response addMessage(String msg) throws URISyntaxException {
+        Message m = singleton.addMessage(msg);
+
+        URI msgURI = ui.getRequestUriBuilder().path(Integer.toString(m.getUniqueId())).build();
+
+        return Response.created(msgURI).build();
+    }
+
+    @Path("{msgNum}")
+    @GET
+    public Message getMessage(@PathParam("msgNum") int msgNum) {
+        Message m = singleton.getMessage(msgNum);
+
+        if (m == null) {
+            // This exception will be passed through to the JAX-RS runtime
+            // No other runtime exception will behave this way unless the
+            // exception is annotated with javax.ejb.ApplicationException
+            throw new NotFoundException();
+        }
+
+        return m;
+
+    }
+
+    @Path("{msgNum}")
+    @DELETE
+    public void deleteMessage(@PathParam("msgNum") int msgNum) throws CustomNotFoundException {
+        boolean deleted = singleton.deleteMessage(msgNum);
+
+        if (!deleted) {
+            // This exception will be mapped to a 404 response
+            throw new CustomNotFoundException();
+        }
+    }
+}
+
+
+
+
+
diff --git a/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/resources/MessageBoardRootResource.java b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/resources/MessageBoardRootResource.java
new file mode 100644
index 0000000..6fd665a
--- /dev/null
+++ b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/resources/MessageBoardRootResource.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jersey_ejb.resources;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.ws.rs.Path;
+
+/**
+ * Message board root resource. The main message board resource
+ * gets injected as an EJB stateless bean
+ * and provided via a sub-resource locator for further processing.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Stateless
+@Path("/")
+public class MessageBoardRootResource {
+
+    @EJB MessageBoardResourceBean r;
+
+    @Path("messages")
+    public MessageBoardResourceBean getMessageBoardResourceBean() {
+        return r;
+    }
+}
+
diff --git a/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/resources/MessageHolderSingletonBean.java b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/resources/MessageHolderSingletonBean.java
new file mode 100644
index 0000000..8a700d6
--- /dev/null
+++ b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/resources/MessageHolderSingletonBean.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jersey_ejb.resources;
+
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.ejb.Singleton;
+
+import org.glassfish.jersey.examples.jersey_ejb.entities.Message;
+
+/**
+ * An EJB singleton to maintain all processed message beans.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Singleton
+public class MessageHolderSingletonBean {
+
+    private List<Message> list = new CopyOnWriteArrayList<Message>();
+    private int maxMessages = 10;
+
+    int currentId = 0;
+
+    public MessageHolderSingletonBean() {
+        // initial content
+        addMessage("msg0", new Date(0));
+        addMessage("msg1", new Date(1000));
+        addMessage("msg2", new Date(2000));
+    }
+
+    public List<Message> getMessages() {
+        List<Message> l = new LinkedList<Message>();
+
+        int index = 0;
+
+        while (index < list.size() && index < maxMessages) {
+            l.add(list.get(index));
+            index++;
+        }
+
+        return l;
+    }
+
+    private int getNewId() {
+        return currentId++;
+    }
+
+    public Message addMessage(String msg) {
+        return addMessage(msg, new Date());
+    }
+
+    private Message addMessage(String msg, Date date) {
+        Message m = new Message(date, msg, getNewId());
+
+        list.add(0, m);
+
+        return m;
+    }
+
+    public Message getMessage(int uniqueId) {
+        int index = 0;
+        Message m;
+
+        while (index < list.size()) {
+            if ((m = list.get(index)).getUniqueId() == uniqueId) {
+                return m;
+            }
+            index++;
+        }
+
+        return null;
+    }
+
+    public boolean deleteMessage(int uniqueId) {
+        int index = 0;
+
+        while (index < list.size()) {
+            if (list.get(index).getUniqueId() == uniqueId) {
+                list.remove(index);
+                return true;
+            }
+            index++;
+        }
+
+        return false;
+    }
+}
diff --git a/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/resources/MyApplication.java b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/resources/MyApplication.java
new file mode 100644
index 0000000..f21a779
--- /dev/null
+++ b/examples/jersey-ejb/src/main/java/org/glassfish/jersey/examples/jersey_ejb/resources/MyApplication.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jersey_ejb.resources;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.examples.jersey_ejb.entities.Message;
+import org.glassfish.jersey.examples.jersey_ejb.entities.MessageListWriter;
+import org.glassfish.jersey.examples.jersey_ejb.entities.MessageWriter;
+import org.glassfish.jersey.examples.jersey_ejb.exceptions.NotFoundExceptionMapper;
+
+/**
+ * Main application class.
+ *
+ * @author Jonathan Benoit
+ */
+@ApplicationPath("/app/*")
+public class MyApplication extends Application {
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> classes = new HashSet<Class<?>>();
+        // register root resources/providers
+        classes.add(MessageBoardRootResource.class);
+        classes.add(MessageBoardResourceBean.class);
+        classes.add(MessageHolderSingletonBean.class);
+        classes.add(NotFoundExceptionMapper.class);
+        classes.add(MessageWriter.class);
+        classes.add(MessageListWriter.class);
+        classes.add(Message.class);
+        return classes;
+    }
+}
diff --git a/examples/jersey-ejb/src/main/webapp/index.html b/examples/jersey-ejb/src/main/webapp/index.html
new file mode 100644
index 0000000..cdcd4d3
--- /dev/null
+++ b/examples/jersey-ejb/src/main/webapp/index.html
@@ -0,0 +1,177 @@
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+    <head>
+        <title></title>
+        <meta http-equiv="Content-Type" content="text/html; charset=MacRoman">
+
+        <style>
+            .created {color: green}
+            .uniqueId {color: red}
+            .message {color: black}
+        </style>
+    </head>
+
+    <body>
+
+        <script type="text/javascript">
+            function getXmlHttpRequest() {
+                try
+                {
+                    // Firefox, Opera 8.0+, Safari
+                    return new XMLHttpRequest();
+                }
+                catch (e)
+                {
+                    // Internet Explorer
+                    try
+                    {
+                        return new ActiveXObject("Msxml2.XMLHTTP");
+                    }
+                    catch (e)
+                    {
+                        try
+                        {
+                            return new ActiveXObject("Microsoft.XMLHTTP");
+                        }
+                        catch (e)
+                        {
+                            alert("Your browser does not support AJAX!");
+                            return null;
+                        }
+                    }
+                }
+            }
+
+            function getMessages()
+            {
+                var xmlHttp = getXmlHttpRequest();
+                xmlHttp.onreadystatechange=function()
+                {
+                    if(xmlHttp.readyState==4)
+                    {
+                        document.getElementById("query").innerHTML="GET app/messages";
+                        document.getElementById("output").innerHTML=xmlHttp.responseText;
+                    }
+                }
+                xmlHttp.open("GET","app/messages/",true);
+                xmlHttp.send(null);
+            }
+
+
+            function getMessage(index)
+            {
+                var xmlHttp = getXmlHttpRequest();
+                xmlHttp.onreadystatechange=function()
+                {
+                    if(xmlHttp.readyState==4)
+                    {
+                        document.getElementById("query").innerHTML="GET app/messages/" + index;
+                        if(xmlHttp.responseText.indexOf("<body>") != -1) {
+                            s = xmlHttp.responseText.substring(xmlHttp.responseText.indexOf("<body>") + 6, xmlHttp.responseText.indexOf("</body>"))
+                            document.getElementById("output").innerHTML=s;
+                        } else {
+                            document.getElementById("output").innerHTML=xmlHttp.responseText;
+                        }
+
+                    }
+                }
+                xmlHttp.open("GET","app/messages/" + index,true);
+                xmlHttp.send(null);
+            }
+
+            function deleteMessage(index)
+            {
+                var xmlHttp = getXmlHttpRequest();
+                xmlHttp.onreadystatechange=function()
+                {
+                    if(xmlHttp.readyState==4)
+                    {
+                        document.getElementById("query").innerHTML="DELETE app/messages/" + index;
+                        if(xmlHttp.responseText.indexOf("<body>") != -1) {
+                            s = xmlHttp.responseText.substring(xmlHttp.responseText.indexOf("<body>") + 6, xmlHttp.responseText.indexOf("</body>"))
+                            document.getElementById("output").innerHTML=s;
+                        } else {
+                            document.getElementById("output").innerHTML=xmlHttp.responseText;
+                        }
+                    }
+                }
+                xmlHttp.open("DELETE","app/messages/" + index,true);
+                xmlHttp.send(null);
+            }
+
+            function addMessage(message)
+            {
+                var xmlHttp = getXmlHttpRequest();
+                xmlHttp.onreadystatechange=function()
+                {
+                    if(xmlHttp.readyState==4)
+                    {
+                        document.getElementById("query").innerHTML="POST app/messages";
+                        document.getElementById("output").innerHTML="";
+                    }
+                }
+                xmlHttp.open("POST","app/messages/",true);
+                xmlHttp.send(message);
+            }
+
+
+        </script>
+
+
+        <h1>MessageBoard sample</h1>
+
+        <form name="form0">
+
+            <button type="button" name="GET0" onclick="getMessages()">LIST ALL MESSAGES</button>
+
+        </form>
+
+
+        <form name="form1">
+            Message id#: <input style="width: 4em" type="text" name="messageNumber" />
+
+<button type="button" name="GET1" onclick="getMessage(document.form1.messageNumber.value)">GET MESSAGE</button>
+
+</form>
+
+<form name="form2">
+    Message id#: <input style="width: 4em" type="text" name="messageNumber" />
+
+<button type="button" name="GET2" onclick="deleteMessage(document.form2.messageNumber.value)">DELETE MESSAGE</button>
+
+</form>
+
+<form name="form3">
+    Message: <input type="test" name="messageText" />
+
+    <button type="button" name="GET2" onclick="addMessage(document.form3.messageText.value)">ADD MESSAGE</button>
+
+</form>
+
+
+<h2>MessageBoard</h2>
+    <h3>query: </h3>
+<div id="query" style="font-weight: bold"></div>
+    <h3>result</h3>
+<div id="output" style="border: 1px solid black; color: black; height: 18em; width: 100%"></div>
+
+
+<script type="text/javascript">
+    getMessages();
+</script>
+
+
+</body>
+</html>
diff --git a/examples/jersey-ejb/src/test/java/org/glassfish/jersey/examples/jersey_ejb/test/MessageBoardTest.java b/examples/jersey-ejb/src/test/java/org/glassfish/jersey/examples/jersey_ejb/test/MessageBoardTest.java
new file mode 100644
index 0000000..d5503a1
--- /dev/null
+++ b/examples/jersey-ejb/src/test/java/org/glassfish/jersey/examples/jersey_ejb/test/MessageBoardTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jersey_ejb.test;
+
+import java.net.URI;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.examples.jersey_ejb.resources.MyApplication;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Basic test for adding/removing messages.
+ *
+ * Tests currently disabled in pom.xml file.
+ *
+ * To test the app, mvn clean package and asadmin deploy target/jersey-ejb
+ * and then run the tests using extenrnal test container factory:
+ * mvn -Prun-external-tests test
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class MessageBoardTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("jersey-ejb").build();
+    }
+
+    /**
+     * Regression test for JERSEY-2541.
+     */
+    @Test
+    public void testListMessages() {
+        Response response = target().path("app/messages").request(MediaType.TEXT_HTML).get();
+
+        assertEquals(
+                String.format("Response status should be 200. Current value is %d.", response.getStatus()),
+                200, response.getStatus());
+    }
+
+    @Test
+    public void testAddMessage() {
+
+        Response response = target().path("app/messages").request(MediaType.TEXT_PLAIN)
+                .post(Entity.entity("hello world!", MediaType.TEXT_PLAIN));
+
+        assertEquals("Response status should be CREATED. Current value is \"" + response.getStatus() + "\"",
+                Response.Status.CREATED.getStatusCode(), response.getStatus());
+
+        client().target(response.getLocation()).request().delete(); // remove added message
+    }
+
+    @Test
+    public void testDeleteMessage() {
+
+        URI u = null;
+
+        Response response = target().path("app/messages").request().post(Entity.entity("toDelete", MediaType.TEXT_PLAIN));
+        if (response.getStatus() == Response.Status.CREATED.getStatusCode()) {
+            u = response.getLocation();
+        } else {
+            fail();
+        }
+
+        String s = client().target(u).request().get(String.class);
+
+        assertTrue(s.contains("toDelete"));
+
+        Response firstDeleteResponse = client().target(u).request().delete();
+        final int successfulDeleteResponseStatus = firstDeleteResponse.getStatus();
+        assertTrue("First DELETE request should return with a 2xx status code",
+                (200 <= successfulDeleteResponseStatus) && (successfulDeleteResponseStatus < 300));
+
+        Response nonExistentGetResponse = client().target(u).request().get();
+        assertEquals("GET request to a non existent resource should return 404", 404, nonExistentGetResponse.getStatus());
+
+        Response nonExistentDeleteResponse = client().target(u).request().delete();
+        assertEquals("DELETE request to a non existent resource should return 404", 404, nonExistentDeleteResponse.getStatus());
+    }
+
+}
diff --git a/examples/json-binding-webapp/README.MD b/examples/json-binding-webapp/README.MD
new file mode 100644
index 0000000..e5612bf
--- /dev/null
+++ b/examples/json-binding-webapp/README.MD
@@ -0,0 +1,43 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+JSONB Example
+=============
+
+This example demonstrates how to use JSONB with resource classes.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                                | Resource class           | HTTP methods
+--------------------------------------- | ------------------------ | --------------
+**_/jsonb/cats/one_**                   | JsonbResource            | GET
+**_/jsonb/cats/all_**                   | JsonbResource            | GET
+**_/jsonb/cats/add_**                   | JsonbResource            | POST
+**_/jsonb/cats/addAll_**                | JsonbResource            | POST
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container. You can access the application at:
+
+-   <http://localhost:8080/json-binding-webapp/cats/one>
+-   <http://localhost:8080/json-binding-webapp/cats/all>
+
+or you can post an xml entity:
+
+>    curl -v -X POST http://localhost:8080/json-binding-webapp/cats/add -H "Content-Type:application/json" -d '
+>   {"color":"white","sort":"maine coon","catName":"Darwin", "domesticated":"true"}'
+
+You can also use the built war file to deploy the app to a container of your choice.
\ No newline at end of file
diff --git a/examples/json-binding-webapp/pom.xml b/examples/json-binding-webapp/pom.xml
new file mode 100644
index 0000000..5eef5aa
--- /dev/null
+++ b/examples/json-binding-webapp/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>json-binding-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-json-binding-webapp</name>
+
+    <description>Jersey JSON Binding example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-binding</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.jsonb.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/json-binding-webapp/src/main/java/org/glassfish/jersey/examples/jsonb/App.java b/examples/json-binding-webapp/src/main/java/org/glassfish/jersey/examples/jsonb/App.java
new file mode 100644
index 0000000..39d18e7
--- /dev/null
+++ b/examples/json-binding-webapp/src/main/java/org/glassfish/jersey/examples/jsonb/App.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonb;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Jersey JSON-B example standalone application.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/json-binding-webapp/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("JSON-B (json-binding) Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, new JsonbApplication(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(server::shutdownNow));
+            server.start();
+
+            System.out.println(
+                    String.format("Application started.%nTry out %s%nStop the application using CTRL+C", BASE_URI));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+}
diff --git a/examples/json-binding-webapp/src/main/java/org/glassfish/jersey/examples/jsonb/Cat.java b/examples/json-binding-webapp/src/main/java/org/glassfish/jersey/examples/jsonb/Cat.java
new file mode 100644
index 0000000..720a63e
--- /dev/null
+++ b/examples/json-binding-webapp/src/main/java/org/glassfish/jersey/examples/jsonb/Cat.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonb;
+
+import javax.json.bind.annotation.JsonbProperty;
+import javax.json.bind.annotation.JsonbPropertyOrder;
+
+/**
+ * Example cat POJO for JSONB (un)marshalling.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@JsonbPropertyOrder({"color", "sort", "name", "domesticated"})
+public class Cat {
+    @JsonbProperty("catName")
+    private String name;
+    private String sort;
+    private String color;
+    private boolean domesticated;
+
+    // json-b needs the default constructor
+    public Cat() {
+        super();
+    }
+
+    public Cat(String name, String sort, String color, boolean domesticated) {
+        this.name = name;
+        this.sort = sort;
+        this.color = color;
+        this.domesticated = domesticated;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Cat setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    @JsonbProperty("catSort")
+    public String getSort() {
+        return sort;
+    }
+
+    public Cat setSort(String sort) {
+        this.sort = sort;
+        return this;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public Cat setColor(String color) {
+        this.color = color;
+        return this;
+    }
+
+    public boolean isDomesticated() {
+        return domesticated;
+    }
+
+    public Cat setDomesticated(boolean domesticated) {
+        this.domesticated = domesticated;
+        return this;
+    }
+}
diff --git a/examples/json-binding-webapp/src/main/java/org/glassfish/jersey/examples/jsonb/JsonbApplication.java b/examples/json-binding-webapp/src/main/java/org/glassfish/jersey/examples/jsonb/JsonbApplication.java
new file mode 100644
index 0000000..c89b53d
--- /dev/null
+++ b/examples/json-binding-webapp/src/main/java/org/glassfish/jersey/examples/jsonb/JsonbApplication.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonb;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Jersey JSON-B example application.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@ApplicationPath("/")
+public class JsonbApplication extends ResourceConfig {
+    public JsonbApplication() {
+        register(JsonbResource.class);
+    }
+}
diff --git a/examples/json-binding-webapp/src/main/java/org/glassfish/jersey/examples/jsonb/JsonbResource.java b/examples/json-binding-webapp/src/main/java/org/glassfish/jersey/examples/jsonb/JsonbResource.java
new file mode 100644
index 0000000..74d6d9f
--- /dev/null
+++ b/examples/json-binding-webapp/src/main/java/org/glassfish/jersey/examples/jsonb/JsonbResource.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonb;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+
+/**
+ * An example resource utilizing JSONB.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("cats")
+@Consumes("application/json")
+public class JsonbResource {
+
+    private static List<Cat> cats = new ArrayList<>();
+
+    static {
+        cats.add(new Cat("Rosa", "semi-british", "tabby", true));
+        cats.add(new Cat("Alfred", "semi-british", "ginger", true));
+        cats.add(new Cat("Mishan", "british blue", "blue/silver", true));
+        cats.add(new Cat("Costa", "common cat", "stracciatella", true));
+    }
+
+    @Path("one")
+    @GET
+    @Produces("application/json")
+    public Cat getCat() {
+        return cats.get((int) (Math.round(Math.random() * 3)));
+    }
+
+    @Path("all")
+    @GET
+    @Produces("application/json")
+    public List<Cat> getAll() {
+        return cats;
+    }
+
+    @Path("schroedinger")
+    @GET
+    public String check() {
+        return "The cat is 9x alive!";
+    }
+
+    @Path("add")
+    @POST
+    public Response createCat(Cat cat) {
+        System.out.println("Creating cat.");
+        cats.add(cat);
+        return Response.ok().build();
+    }
+
+    @Path("addAll")
+    @POST
+    public Response createMultiple(List<Cat> addedCats) {
+        cats.addAll(addedCats);
+        return Response.ok().build();
+    }
+
+}
diff --git a/examples/json-jackson/README.MD b/examples/json-jackson/README.MD
new file mode 100644
index 0000000..6fe56aa
--- /dev/null
+++ b/examples/json-jackson/README.MD
@@ -0,0 +1,91 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Jackson JAX-RS JSON Provider Example
+====================================
+
+This example demonstrates how to produce/consume JSON representations
+from Java objects. This applies not only to JAXB beans, as shown in the
+`json-from-jaxb` example but also to ordinary, un-annotated, POJOs.
+
+This example hosts three simple read-only resources: One provides an
+example of using a Jackson JSON provider (registered by the feature
+`JacksonFeature` in the `MyApplication` class) instead of using JAXB
+(Object-&gt;JAXB-&gt;JSON) which has some limitations (e.g. empty arrays
+in JAXB beans). For this resource the JSON representation is produced by
+the Jackson JAX-RS provider, while the XML representation is generated
+by JAXB as usual. The second web resource is based on a simple
+un-annotated POJO and provides only JSON-based representations: JSON and
+JSON with padding (JSONP). The third resource depicts how Jackson and
+JAXB annotations could be mixed together within a single POJO.
+
+Contents
+--------
+
+The "empty array" web resource is implemented by the `org.glassfish.jersey.examples.jackson.EmptyArrayResource` class.
+
+The "non JAXB" web resource is implemented by the `org.glassfish.jersey.examples.jackson.NonJaxbBeanResource` class.
+
+Both resources use the default Jackson mapper configuration to serialize JSON
+data out and depicts the corner cases, where the Jersey internal, StAX
+based, JSON processing can not be utilized.
+
+The `org.glassfish.jersey.examples.jackson.CombinedAnnotationResource`
+class is used to show how JAXB and Jackson annotations could be combined
+together in one Java bean for JSON serialization. See the
+
+`org.glassfish.jersey.examples.jackson.MyObjectMapperProvider` class
+where Jackson specific options are used to set up the desired JSON
+serialization configuration.
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                     | Resource class               | HTTP method
+---------------------------- | ---------------------------- | -------------
+**_/emptyArrayResource_**    | EmptyArrayResource           | GET
+**_/nonJaxbResource_**       | NonJaxbBeanResource          | GET
+**_/combinedAnnotations_**   | CombinedAnnotationResource   | GET
+
+To use Jackson specific configuration options, one can implement a
+`ContextResolver<ObjectMapper>` provider. For an example of such an
+implementation, see the `MyObjectMapperProvider` class.
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using[Grizzly](http://grizzly.java.net/)
+
+A [WADL description](http://wadl.java.net/#spec) may be accessed at the URL:
+
+-   <http://localhost:8080/jackson/application.wadl>
+
+The three resources are available at
+
+-   <http://localhost:8080/jackson/emptyArrayResource>
+-   <http://localhost:8080/jackson/nonJaxbResource>
+-   <http://localhost:8080/jackson/combinedAnnotations>
+
+To easily obtain the different output types available,
+[cURL](http://curl.haxx.se/) can be used as follows:
+
+Obtain the JSON output of EmptyArrayResource (use
+`-HAccept:application/xml` for XML output):
+
+>     curl -HAccept:application/json http://localhost:8080/jackson/emptyArrayResource
+
+Obtain the JSON output of NonJaxbBeanResource (use `-HAccept:application/javascript` for JSONP output):
+
+>     curl -HAccept:application/json http://localhost:8080/jackson/nonJaxbResource
+
+Obtain the JSON output of CombinedAnnotationResource:
+
+>     curl -HAccept:application/json http://localhost:8080/jackson/combinedAnnotations
diff --git a/examples/json-jackson/pom.xml b/examples/json-jackson/pom.xml
new file mode 100644
index 0000000..16d0f6d
--- /dev/null
+++ b/examples/json-jackson/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>json-jackson</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-json-jackson</name>
+
+    <description>Jersey JSON with Jackson example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.jackson.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/json-jackson/src/main/assembly/src.xml b/examples/json-jackson/src/main/assembly/src.xml
new file mode 100644
index 0000000..a030433
--- /dev/null
+++ b/examples/json-jackson/src/main/assembly/src.xml
@@ -0,0 +1,28 @@
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<assembly>
+    <id>project</id>
+    <formats>
+        <format>zip</format>
+    </formats>
+    <fileSets>
+        <fileSet>
+            <directory>.</directory>
+            <outputDirectory></outputDirectory>
+            <useDefaultExcludes>true</useDefaultExcludes>
+            <excludes>
+                <exclude>**/target/**</exclude>
+            </excludes>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/App.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/App.java
new file mode 100644
index 0000000..11bb1c0
--- /dev/null
+++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/App.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Utility class which creates {@link MyApplication} instances and provides support
+ * for running this sample from command line.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/jackson/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("JSON with Jackson Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, createApp(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.%nStop the application using CTRL+C"));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+
+    public static ResourceConfig createApp() {
+        return new MyApplication();
+    }
+}
diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/CombinedAnnotationBean.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/CombinedAnnotationBean.java
new file mode 100644
index 0000000..1852773
--- /dev/null
+++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/CombinedAnnotationBean.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * TODO javadoc.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@XmlRootElement(name = "account")
+public class CombinedAnnotationBean {
+
+    @JsonProperty("value")
+    int x;
+
+    public CombinedAnnotationBean(int x) {
+        this.x = x;
+    }
+
+    public CombinedAnnotationBean() {
+        this(15);
+    }
+}
diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/CombinedAnnotationResource.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/CombinedAnnotationResource.java
new file mode 100644
index 0000000..4bdead6
--- /dev/null
+++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/CombinedAnnotationResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * TODO javadoc.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("combinedAnnotations")
+public class CombinedAnnotationResource {
+
+    @Produces(MediaType.APPLICATION_JSON)
+    @GET
+    public CombinedAnnotationBean getAccount() {
+        return new CombinedAnnotationBean(12);
+    }
+}
diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/DummyBean.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/DummyBean.java
new file mode 100644
index 0000000..e505e9d
--- /dev/null
+++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/DummyBean.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson;
+
+/**
+ * Testing bean for ExceptionMappingTestResource.
+ *
+ */
+public class DummyBean {
+    private int key;
+    private String value;
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public int getKey() {
+        return key;
+    }
+
+    public void setKey(int key) {
+        this.key = key;
+    }
+
+}
diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/EmptyArrayBean.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/EmptyArrayBean.java
new file mode 100644
index 0000000..332ae84
--- /dev/null
+++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/EmptyArrayBean.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * TODO javadoc.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@XmlRootElement
+public class EmptyArrayBean {
+
+    public String[] emtpyArray = new String[0];
+}
diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/EmptyArrayResource.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/EmptyArrayResource.java
new file mode 100644
index 0000000..0f4abf5
--- /dev/null
+++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/EmptyArrayResource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * TODO javadoc.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/emptyArrayResource")
+public class EmptyArrayResource {
+
+    // the resource JSON representation will be serialized by Jackson JAX-RS provider,
+    // while the XML will still be generated by JAXB
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    public EmptyArrayBean getIt() {
+        return new EmptyArrayBean();
+    }
+}
diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/ExceptionMappingTestResource.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/ExceptionMappingTestResource.java
new file mode 100644
index 0000000..4afef0e
--- /dev/null
+++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/ExceptionMappingTestResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * Testing bean that accepts JSON for the PUT method.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("parseExceptionTest")
+public class ExceptionMappingTestResource {
+
+    @Consumes(MediaType.APPLICATION_JSON)
+    @PUT
+    public DummyBean getAccount(DummyBean bean) {
+        return bean;
+    }
+}
diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyApplication.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyApplication.java
new file mode 100644
index 0000000..aa4c966
--- /dev/null
+++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyApplication.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * {@link javax.ws.rs.core.Application} descendant.
+ *
+ * Used to set resource and providers classes.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class MyApplication extends ResourceConfig {
+    public MyApplication() {
+        super(
+                EmptyArrayResource.class,
+                NonJaxbBeanResource.class,
+                CombinedAnnotationResource.class,
+                // register Jackson ObjectMapper resolver
+                MyObjectMapperProvider.class,
+                ExceptionMappingTestResource.class,
+                JacksonFeature.class
+        );
+    }
+}
diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyObjectMapperProvider.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyObjectMapperProvider.java
new file mode 100644
index 0000000..df9658b
--- /dev/null
+++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyObjectMapperProvider.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson;
+
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
+
+/**
+ * TODO javadoc.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {
+
+    final ObjectMapper defaultObjectMapper;
+    final ObjectMapper combinedObjectMapper;
+
+    public MyObjectMapperProvider() {
+        defaultObjectMapper = createDefaultMapper();
+        combinedObjectMapper = createCombinedObjectMapper();
+    }
+
+    @Override
+    public ObjectMapper getContext(final Class<?> type) {
+
+        if (type == CombinedAnnotationBean.class) {
+            return combinedObjectMapper;
+        } else {
+            return defaultObjectMapper;
+        }
+    }
+
+    private static ObjectMapper createCombinedObjectMapper() {
+        return new ObjectMapper()
+                .configure(SerializationFeature.WRAP_ROOT_VALUE, true)
+                .configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true)
+                .setAnnotationIntrospector(createJaxbJacksonAnnotationIntrospector());
+    }
+
+    private static ObjectMapper createDefaultMapper() {
+        final ObjectMapper result = new ObjectMapper();
+        result.enable(SerializationFeature.INDENT_OUTPUT);
+
+        return result;
+    }
+
+    private static AnnotationIntrospector createJaxbJacksonAnnotationIntrospector() {
+
+        final AnnotationIntrospector jaxbIntrospector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
+        final AnnotationIntrospector jacksonIntrospector = new JacksonAnnotationIntrospector();
+
+        return AnnotationIntrospector.pair(jacksonIntrospector, jaxbIntrospector);
+    }
+}
diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/NonJaxbBean.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/NonJaxbBean.java
new file mode 100644
index 0000000..8446f3b
--- /dev/null
+++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/NonJaxbBean.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson;
+
+/**
+ * TODO javadoc.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class NonJaxbBean {
+
+    private String name = "non-JAXB-bean";
+    private String description = "I am not a JAXB bean, just an unannotated POJO";
+    private int[] array = {1, 1, 2, 3, 5, 8, 13, 21};
+
+    public int[] getArray() {
+        return array;
+    }
+
+    public void setArray(int[] array) {
+        this.array = array;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/NonJaxbBeanResource.java b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/NonJaxbBeanResource.java
new file mode 100644
index 0000000..88e01e7
--- /dev/null
+++ b/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/NonJaxbBeanResource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.server.JSONP;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/nonJaxbResource")
+public class NonJaxbBeanResource {
+
+    @GET
+    @JSONP
+    @Produces({"application/javascript", MediaType.APPLICATION_JSON})
+    public NonJaxbBean getSimpleBeanJSONP() {
+        return new NonJaxbBean();
+    }
+}
diff --git a/examples/json-jackson/src/test/java/org/glassfish/jersey/examples/jackson/JacksonTest.java b/examples/json-jackson/src/test/java/org/glassfish/jersey/examples/jackson/JacksonTest.java
new file mode 100644
index 0000000..64e662e
--- /dev/null
+++ b/examples/json-jackson/src/test/java/org/glassfish/jersey/examples/jackson/JacksonTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.runner.RunWith;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(ConcurrentRunner.class)
+public class JacksonTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        return App.createApp();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(new JacksonFeature()).register(MyObjectMapperProvider.class);
+    }
+
+    @Test
+    public void testEmptyArrayPresent() {
+        WebTarget target = target();
+        String responseMsg = target.path("emptyArrayResource").request(MediaType.APPLICATION_JSON).get(String.class);
+        assertTrue(responseMsg.replaceAll("[ \t]*", "").contains("[]"));
+    }
+
+    @Test
+    public void testJSONPPresent() {
+        WebTarget target = target();
+        String responseMsg = target.path("nonJaxbResource").request("application/javascript").get(String.class);
+        assertTrue(responseMsg.startsWith("callback("));
+    }
+
+    @Test
+    public void testJSONDoesNotReflectJSONPWrapper() {
+        WebTarget target = target();
+        String responseMsg = target.path("nonJaxbResource").request("application/json").get(String.class);
+        assertTrue(!responseMsg.contains("jsonSource"));
+    }
+
+    @Test
+    public void testCombinedAnnotationResource() {
+        WebTarget target = target();
+        String responseMsg = target.path("combinedAnnotations").request("application/json").get(String.class);
+        assertTrue(responseMsg.contains("account") && responseMsg.contains("value"));
+    }
+
+    @Test
+    public void testEmptyArrayBean() {
+        WebTarget target = target();
+        EmptyArrayBean responseMsg = target.path("emptyArrayResource").request(MediaType.APPLICATION_JSON)
+                .get(EmptyArrayBean.class);
+        assertNotNull(responseMsg);
+    }
+
+    @Test
+    public void testCombinedAnnotationBean() {
+        WebTarget target = target();
+        CombinedAnnotationBean responseMsg = target.path("combinedAnnotations").request("application/json")
+                .get(CombinedAnnotationBean.class);
+        assertNotNull(responseMsg);
+    }
+
+    @Test
+    @Ignore
+    // TODO un-ignore once a JSON reader for "application/javascript" is supported
+    public void testJSONPBean() {
+        WebTarget target = target();
+        NonJaxbBean responseMsg = target.path("nonJaxbResource").request("application/javascript").get(NonJaxbBean.class);
+        assertNotNull(responseMsg);
+    }
+
+    /**
+     * Test if a WADL document is available at the relative path
+     * "application.wadl".
+     * <p/>
+     */
+    @Test
+    public void testApplicationWadl() {
+        WebTarget target = target();
+        String serviceWadl = target.path("application.wadl").request(MediaTypes.WADL_TYPE).get(String.class);
+
+        assertTrue(serviceWadl.length() > 0);
+    }
+
+    /**
+     * Test, that in case of malformed JSON, the jackson exception mappers will be used and the response will be
+     * 400 - bad request instead of 500 - server error
+     */
+    @Test
+    public void testExceptionMapping() {
+        enable(TestProperties.LOG_TRAFFIC);
+        // create a request with invalid json string to cause an exception in Jackson
+        Response response = target().path("parseExceptionTest").request("application/json")
+                .put(Entity.entity("Malformed json string.", MediaType.valueOf("application/json")));
+
+        assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+    }
+}
diff --git a/examples/json-jackson1/README.MD b/examples/json-jackson1/README.MD
new file mode 100644
index 0000000..feb7fa2
--- /dev/null
+++ b/examples/json-jackson1/README.MD
@@ -0,0 +1,91 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Jackson (1.x) JAX-RS JSON Provider Example
+==========================================
+
+This example demonstrates how to produce/consume JSON representations
+from Java objects. This applies not only to JAXB beans, as shown in the
+`json-from-jaxb` example but also to ordinary, un-annotated, POJOs.
+
+This example hosts three simple read-only resources: One provides an
+example of using a Jackson JSON provider (registered by the feature
+`Jackson1Feature` in the `MyApplication` class) instead of using JAXB
+(Object-&gt;JAXB-&gt;JSON) which has some limitations (e.g. empty arrays
+in JAXB beans). For this resource the JSON representation is produced by
+the Jackson JAX-RS provider, while the XML representation is generated
+by JAXB as usual. The second web resource is based on a simple
+un-annotated POJO and provides only JSON-based representations: JSON and
+JSON with padding (JSONP). The third resource depicts how Jackson and
+JAXB annotations could be mixed together within a single POJO.
+
+Contents
+--------
+
+The "empty array" web resource is implemented by the
+`org.glassfish.jersey.examples.jackson1.EmptyArrayResource` class.
+
+The "non JAXB" web resource is implemented by the
+`org.glassfish.jersey.examples.jackson1.NonJaxbBeanResource` class.
+
+Both resources use the default Jackson mapper configuration to serialize JSON
+data out and depicts the corner cases, where the Jersey internal, StAX
+based, JSON processing can not be utilized.
+
+The `org.glassfish.jersey.examples.jackson1.CombinedAnnotationResource`
+class is used to show how JAXB and Jackson annotations could be combined
+together in one Java bean for JSON serialization.
+
+See the `org.glassfish.jersey.examples.jackson1.MyObjectMapperProvider` class
+where Jackson specific options are used to set up the desired JSON
+serialization configuration.
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                     | Resource class               | HTTP method
+---------------------------- | ---------------------------- | -------------
+**_/emptyArrayResource_**    | EmptyArrayResource           | GET
+**_/nonJaxbResource_**       | NonJaxbBeanResource          | GET
+**_/combinedAnnotations_**   | CombinedAnnotationResource   | GET
+
+To use Jackson specific configuration options, one can implement a
+`ContextResolver<ObjectMapper>` provider. For an example of such an
+implementation, see the `MyObjectMapperProvider` class.
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/)
+
+A [WADL description](http://wadl.java.net/#spec) may be accessed at the URL:
+
+-   `http://localhost:8080/jackson1/application.wadl`
+
+The three resources are available at
+
+-   <http://localhost:8080/jackson1/emptyArrayResource>
+-   <http://localhost:8080/jackson1/nonJaxbResource>
+-   <http://localhost:8080/jackson1/combinedAnnotations>
+
+To easily obtain the different output types available, [cURL](http://curl.haxx.se/) can be used as follows:
+
+Obtain the JSON output of EmptyArrayResource (use `-HAccept:application/xml` for XML output):
+
+>     curl -HAccept:application/json http://localhost:8080/jackson1/emptyArrayResource
+
+Obtain the JSON output of NonJaxbBeanResource (use `-HAccept:application/javascript` for JSONP output):
+
+>     curl -HAccept:application/json http://localhost:8080/jackson1/nonJaxbResource
+
+Obtain the JSON output of CombinedAnnotationResource:
+
+>     curl -HAccept:application/json http://localhost:8080/jackson1/combinedAnnotations
diff --git a/examples/json-jackson1/pom.xml b/examples/json-jackson1/pom.xml
new file mode 100644
index 0000000..b68c645
--- /dev/null
+++ b/examples/json-jackson1/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>json-jackson1</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-json-jackson1</name>
+
+    <description>Jersey JSON with Jackson 1.x example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson1</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.jackson1.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/json-jackson1/src/main/assembly/src.xml b/examples/json-jackson1/src/main/assembly/src.xml
new file mode 100644
index 0000000..a030433
--- /dev/null
+++ b/examples/json-jackson1/src/main/assembly/src.xml
@@ -0,0 +1,28 @@
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<assembly>
+    <id>project</id>
+    <formats>
+        <format>zip</format>
+    </formats>
+    <fileSets>
+        <fileSet>
+            <directory>.</directory>
+            <outputDirectory></outputDirectory>
+            <useDefaultExcludes>true</useDefaultExcludes>
+            <excludes>
+                <exclude>**/target/**</exclude>
+            </excludes>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/App.java b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/App.java
new file mode 100644
index 0000000..0cd8dfb
--- /dev/null
+++ b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/App.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson1;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Utility class which creates {@link MyApplication} instances and provides support
+ * for running this sample from command line.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/jackson1/");
+
+    public static void main(final String[] args) {
+        try {
+            System.out.println("JSON with Jackson Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, createApp(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.%nStop the application using CTRL+C"));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+
+    public static ResourceConfig createApp() {
+        return new MyApplication();
+    }
+}
diff --git a/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/CombinedAnnotationBean.java b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/CombinedAnnotationBean.java
new file mode 100644
index 0000000..68ad3b2
--- /dev/null
+++ b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/CombinedAnnotationBean.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson1;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@XmlRootElement(name = "account")
+public class CombinedAnnotationBean {
+
+    @JsonProperty("value")
+    int x;
+
+    public CombinedAnnotationBean(final int x) {
+        this.x = x;
+    }
+
+    public CombinedAnnotationBean() {
+        this(15);
+    }
+}
diff --git a/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/CombinedAnnotationResource.java b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/CombinedAnnotationResource.java
new file mode 100644
index 0000000..841d930
--- /dev/null
+++ b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/CombinedAnnotationResource.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson1;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("combinedAnnotations")
+public class CombinedAnnotationResource {
+
+    @Produces(MediaType.APPLICATION_JSON)
+    @GET
+    public CombinedAnnotationBean getAccount() {
+        return new CombinedAnnotationBean(12);
+    }
+}
diff --git a/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/DummyBean.java b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/DummyBean.java
new file mode 100644
index 0000000..c3e66e1
--- /dev/null
+++ b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/DummyBean.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson1;
+
+/**
+ * Testing bean for ExceptionMappingTestResource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class DummyBean {
+
+    private int key;
+    private String value;
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(final String value) {
+        this.value = value;
+    }
+
+    public int getKey() {
+        return key;
+    }
+
+    public void setKey(final int key) {
+        this.key = key;
+    }
+
+}
diff --git a/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/EmptyArrayBean.java b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/EmptyArrayBean.java
new file mode 100644
index 0000000..f6c1164
--- /dev/null
+++ b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/EmptyArrayBean.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson1;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@XmlRootElement
+public class EmptyArrayBean {
+
+    public String[] emtpyArray = new String[0];
+}
diff --git a/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/EmptyArrayResource.java b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/EmptyArrayResource.java
new file mode 100644
index 0000000..53ac9f0
--- /dev/null
+++ b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/EmptyArrayResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson1;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/emptyArrayResource")
+public class EmptyArrayResource {
+
+    // the resource JSON representation will be serialized by Jackson JAX-RS provider,
+    // while the XML will still be generated by JAXB
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    public EmptyArrayBean getIt() {
+        return new EmptyArrayBean();
+    }
+}
diff --git a/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/ExceptionMappingTestResource.java b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/ExceptionMappingTestResource.java
new file mode 100644
index 0000000..542b70c
--- /dev/null
+++ b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/ExceptionMappingTestResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson1;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * Testing bean that accepts JSON for the PUT method.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("parseExceptionTest")
+public class ExceptionMappingTestResource {
+
+    @Consumes(MediaType.APPLICATION_JSON)
+    @PUT
+    public DummyBean getAccount(final DummyBean bean) {
+        return bean;
+    }
+}
diff --git a/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/MyApplication.java b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/MyApplication.java
new file mode 100644
index 0000000..1967697
--- /dev/null
+++ b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/MyApplication.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson1;
+
+import org.glassfish.jersey.jackson1.Jackson1Feature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * {@link javax.ws.rs.core.Application} descendant.
+ *
+ * Used to set resource and providers classes.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        super(
+                EmptyArrayResource.class,
+                NonJaxbBeanResource.class,
+                CombinedAnnotationResource.class,
+                // register Jackson ObjectMapper resolver
+                MyObjectMapperProvider.class,
+                ExceptionMappingTestResource.class,
+                Jackson1Feature.class
+        );
+    }
+}
diff --git a/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/MyObjectMapperProvider.java b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/MyObjectMapperProvider.java
new file mode 100644
index 0000000..7e6996f
--- /dev/null
+++ b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/MyObjectMapperProvider.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson1;
+
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import org.codehaus.jackson.map.AnnotationIntrospector;
+import org.codehaus.jackson.map.AnnotationIntrospector.Pair;
+import org.codehaus.jackson.map.DeserializationConfig;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+import org.codehaus.jackson.map.SerializationConfig.Feature;
+import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector;
+import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {
+
+    final ObjectMapper defaultObjectMapper;
+    final ObjectMapper combinedObjectMapper;
+
+    public MyObjectMapperProvider() {
+        defaultObjectMapper = createDefaultMapper();
+        combinedObjectMapper = createCombinedObjectMapper();
+    }
+
+    @Override
+    public ObjectMapper getContext(final Class<?> type) {
+
+        if (type == CombinedAnnotationBean.class) {
+            return combinedObjectMapper;
+        } else {
+            return defaultObjectMapper;
+        }
+    }
+
+    private static ObjectMapper createCombinedObjectMapper() {
+        final Pair combinedIntrospector = createJaxbJacksonAnnotationIntrospector();
+        final ObjectMapper result = new ObjectMapper();
+        result.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
+        result.configure(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE, true);
+        result.setDeserializationConfig(result.getDeserializationConfig().withAnnotationIntrospector(combinedIntrospector));
+        result.setSerializationConfig(result.getSerializationConfig().withAnnotationIntrospector(combinedIntrospector));
+
+        return result;
+    }
+
+    private static ObjectMapper createDefaultMapper() {
+
+        final ObjectMapper result = new ObjectMapper();
+        result.configure(Feature.INDENT_OUTPUT, true);
+
+        return result;
+    }
+
+    private static Pair createJaxbJacksonAnnotationIntrospector() {
+
+        final AnnotationIntrospector jaxbIntrospector = new JaxbAnnotationIntrospector();
+        final AnnotationIntrospector jacksonIntrospector = new JacksonAnnotationIntrospector();
+
+        return new AnnotationIntrospector.Pair(jacksonIntrospector, jaxbIntrospector);
+    }
+}
diff --git a/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/NonJaxbBean.java b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/NonJaxbBean.java
new file mode 100644
index 0000000..3568f39
--- /dev/null
+++ b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/NonJaxbBean.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson1;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class NonJaxbBean {
+
+    private String name = "non-JAXB-bean";
+    private String description = "I am not a JAXB bean, just an unannotated POJO";
+    private int[] array = {1, 1, 2, 3, 5, 8, 13, 21};
+
+    public int[] getArray() {
+        return array;
+    }
+
+    public void setArray(final int[] array) {
+        this.array = array;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(final String description) {
+        this.description = description;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+}
diff --git a/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/NonJaxbBeanResource.java b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/NonJaxbBeanResource.java
new file mode 100644
index 0000000..09abd34
--- /dev/null
+++ b/examples/json-jackson1/src/main/java/org/glassfish/jersey/examples/jackson1/NonJaxbBeanResource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson1;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.server.JSONP;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/nonJaxbResource")
+public class NonJaxbBeanResource {
+
+    @GET
+    @JSONP
+    @Produces({"application/javascript", MediaType.APPLICATION_JSON})
+    public NonJaxbBean getSimpleBeanJSONP() {
+        return new NonJaxbBean();
+    }
+}
diff --git a/examples/json-jackson1/src/test/java/org/glassfish/jersey/examples/jackson1/Jackson1Test.java b/examples/json-jackson1/src/test/java/org/glassfish/jersey/examples/jackson1/Jackson1Test.java
new file mode 100644
index 0000000..4d78f26
--- /dev/null
+++ b/examples/json-jackson1/src/test/java/org/glassfish/jersey/examples/jackson1/Jackson1Test.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jackson1;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson1.Jackson1Feature;
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class Jackson1Test extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        return App.createApp();
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(Jackson1Feature.class).register(MyObjectMapperProvider.class);
+    }
+
+    @Test
+    public void testEmptyArrayPresent() {
+        final String responseMsg = target("emptyArrayResource").request(MediaType.APPLICATION_JSON).get(String.class);
+        assertTrue(responseMsg.replaceAll("[ \t]*", "").contains("[]"));
+    }
+
+    @Test
+    public void testJSONPPresent() {
+        final String responseMsg = target("nonJaxbResource").request("application/javascript").get(String.class);
+        assertTrue(responseMsg.startsWith("callback("));
+    }
+
+    @Test
+    public void testJSONDoesNotReflectJSONPWrapper() {
+        final String responseMsg = target("nonJaxbResource").request("application/json").get(String.class);
+        assertTrue(!responseMsg.contains("jsonSource"));
+    }
+
+    @Test
+    public void testCombinedAnnotationResource() {
+        final String responseMsg = target("combinedAnnotations").request("application/json").get(String.class);
+        assertTrue(responseMsg.contains("account") && responseMsg.contains("value"));
+    }
+
+    @Test
+    public void testEmptyArrayBean() {
+        assertNotNull(target("emptyArrayResource").request(MediaType.APPLICATION_JSON).get(EmptyArrayBean.class));
+    }
+
+    @Test
+    public void testCombinedAnnotationBean() {
+        assertNotNull(target("combinedAnnotations").request("application/json").get(CombinedAnnotationBean.class));
+    }
+
+    @Test
+    @Ignore
+    // TODO un-ignore once a JSON reader for "application/javascript" is supported
+    public void testJSONPBean() {
+        assertNotNull(target("nonJaxbResource").request("application/javascript").get(NonJaxbBean.class));
+    }
+
+    /**
+     * Test if a WADL document is available at the relative path
+     * "application.wadl".
+     * <p/>
+     */
+    @Test
+    public void testApplicationWadl() {
+        final WebTarget target = target();
+        final String serviceWadl = target.path("application.wadl").request(MediaTypes.WADL_TYPE).get(String.class);
+
+        assertTrue(serviceWadl.length() > 0);
+    }
+
+    /**
+     * Test, that in case of malformed JSON, the jackson exception mappers will be used and the response will be
+     * 400 - bad request instead of 500 - server error
+     */
+    @Test
+    public void testExceptionMapping() {
+        enable(TestProperties.LOG_TRAFFIC);
+        // create a request with invalid json string to cause an exception in Jackson
+        final Response response = target().path("parseExceptionTest").request("application/json")
+                .put(Entity.entity("Malformed json string.", MediaType.valueOf("application/json")));
+
+        assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+    }
+}
diff --git a/examples/json-jettison/README.MD b/examples/json-jettison/README.MD
new file mode 100644
index 0000000..31e83d2
--- /dev/null
+++ b/examples/json-jettison/README.MD
@@ -0,0 +1,64 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Jettison JAX-RS JSON Provider Example
+=====================================
+
+This example demonstrates how to use JSON representation of JAXB based
+resources with Jettison JSON provider.
+
+A simple web application is presented with two resources: a flight list
+and a aircraft type list. A client can obtain the former list or update
+it using XML or JSON representation. The second list is read-only and
+shows how to provide JSON array using List&lt;JAXBBean&gt; objects.
+
+Contents
+--------
+
+The flight list web resource is implemented by
+`org.glassfish.jersey.examples.jettison.FlightList` class.
+
+The aircraft type list web resource is implemented by
+`org.glassfish.jersey.examples.jettison.AircraftTypeList` class.
+
+The mapping of the URI path space is presented in the following table:
+
+URI path           | Resource class     | HTTP method
+------------------ | ------------------ | -------------
+**_/flights_**     | FlightList         | GET, PUT
+**_/aircrafts_**   | AircraftTypeList   | GET
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the JSON from JAXB example using [Grizzly](http://grizzly.java.net/)
+
+A [WADL description](http://wadl.java.net/#spec) may be accessed at the URL:
+
+-   <http://localhost:8080/jettison/application.wadl>
+
+Following steps are using [cURL](http://curl.haxx.se/) command line tool:
+
+Get the list of flights in JSON format:
+
+>     curl -HAccept:application/json http://localhost:8080/jettison/flights
+
+Get the list of flights in XML format:
+
+>     curl -HAccept:application/xml http://localhost:8080/jettison/flights
+
+Get the list of aircraft types in JSON format:
+
+>     curl -HAccept:application/json http://localhost:8080/jettison/aircrafts
+
+You can also see test classes at `src/main/test` subdirectory for detail
+information how to consume the service using JAX-RS client API
diff --git a/examples/json-jettison/pom.xml b/examples/json-jettison/pom.xml
new file mode 100644
index 0000000..2b1226a
--- /dev/null
+++ b/examples/json-jettison/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>json-jettison</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-json-jettison</name>
+
+    <description>Jersey JSON with Jettison JAXB example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jettison</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.jettison.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/AircraftType.java b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/AircraftType.java
new file mode 100644
index 0000000..36893fa
--- /dev/null
+++ b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/AircraftType.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jettison;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * TODO javadoc.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@XmlRootElement
+public class AircraftType {
+    public String type;
+    public double length;
+    public int seatingCapacity;
+
+    public AircraftType(){}
+
+    public AircraftType(String type, double length, int seatingCapacity) {
+        this.type = type;
+        this.length = length;
+        this.seatingCapacity = seatingCapacity;
+    }
+}
diff --git a/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/AircraftTypeList.java b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/AircraftTypeList.java
new file mode 100644
index 0000000..b091f52
--- /dev/null
+++ b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/AircraftTypeList.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jettison;
+
+import java.util.LinkedList;
+import java.util.List;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * TODO javadoc.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("/aircrafts")
+public class AircraftTypeList {
+
+    static final List<AircraftType> aircraftTypes = new LinkedList<AircraftType>();
+
+    static {
+        aircraftTypes.add(new AircraftType("B737", 42.1, 204));
+        aircraftTypes.add(new AircraftType("A330", 58.8, 253));
+    }
+
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    public List<AircraftType> getAircraftTypes() {
+        return aircraftTypes;
+    }
+}
diff --git a/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/App.java b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/App.java
new file mode 100644
index 0000000..752e2e5
--- /dev/null
+++ b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/App.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jettison;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Utility class which can create {@link org.glassfish.jersey.server.ApplicationHandler} instance and provides support
+ * for running this sample from command line.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/jettison/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("JSON with JAXB Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, createApp(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.%nStop the application using CTRL+C"));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    public static ResourceConfig createApp() {
+        return new ResourceConfig()
+                .register(new JettisonFeature())
+                .packages("org.glassfish.jersey.examples.jettison");
+    }
+}
diff --git a/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/FlightList.java b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/FlightList.java
new file mode 100644
index 0000000..9c39717
--- /dev/null
+++ b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/FlightList.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jettison;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * TODO javadoc.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path(value = "/flights")
+public class FlightList {
+
+    @GET
+    @Produces({"application/json", "application/xml"})
+    public Flights getFlightList() {
+        return FlightsDataStore.getFlights();
+    }
+
+    @PUT
+    @Consumes({"application/json", "application/xml"})
+    public void putFlightList(Flights flights) {
+        FlightsDataStore.init(flights);
+    }
+
+    @POST
+    @Path("init")
+    @Produces({"application/json", "application/xml"})
+    public void initData() {
+        FlightsDataStore.init();
+    }
+
+}
diff --git a/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/FlightType.java b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/FlightType.java
new file mode 100644
index 0000000..e88161a
--- /dev/null
+++ b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/FlightType.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.0 in JDK 1.6
+// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2007.07.13 at 03:56:53 PM CEST
+//
+package org.glassfish.jersey.examples.jettison;
+
+import java.util.Formatter;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * <p>Java class for flightType complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * &lt;complexType name="flightType">
+ *   &lt;complexContent>
+ *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       &lt;sequence>
+ *         &lt;element name="company" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *         &lt;element name="number" type="{http://www.w3.org/2001/XMLSchema}long"/>
+ *         &lt;element name="aircraft" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *       &lt;/sequence>
+ *       &lt;attribute name="flightId" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *     &lt;/restriction>
+ *   &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "flightType", propOrder = {
+    "flightId",
+    "company",
+    "number",
+    "aircraft"
+})
+public class FlightType {
+
+    @XmlElement(required = true)
+    protected String company;
+    protected long number;
+    @XmlElement(required = true)
+    protected String aircraft;
+    @XmlElement(required = true)
+    protected String flightId;
+
+    /**
+     * Gets the value of the company property.
+     *
+     * @return
+     *     possible object is
+     *     {@link String }
+     *
+     */
+    public String getCompany() {
+        return company;
+    }
+
+    /**
+     * Sets the value of the company property.
+     *
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *
+     */
+    public void setCompany(String value) {
+        this.company = value;
+    }
+
+    /**
+     * Gets the value of the number property.
+     *
+     * @return flight number.
+     */
+    public long getNumber() {
+        return number;
+    }
+
+    /**
+     * Sets the value of the number property.
+     *
+     * @param value new flight number.
+     */
+    public void setNumber(long value) {
+        this.number = value;
+    }
+
+    /**
+     * Gets the value of the aircraft property.
+     *
+     * @return
+     *     possible object is
+     *     {@link String }
+     *
+     */
+    public String getAircraft() {
+        return aircraft;
+    }
+
+    /**
+     * Sets the value of the aircraft property.
+     *
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *
+     */
+    public void setAircraft(String value) {
+        this.aircraft = value;
+    }
+
+    /**
+     * Gets the value of the flightId property.
+     *
+     * @return
+     *     possible object is
+     *     {@link String }
+     *
+     */
+    public String getFlightId() {
+        return flightId;
+    }
+
+    /**
+     * Sets the value of the flightId property.
+     *
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *
+     */
+    public void setFlightId(String value) {
+        this.flightId = value;
+    }
+
+    @Override
+    public String toString() {
+        return (new Formatter()).format(
+                "{\"flightId\":\"%s\",\"company\":\"%s\",\"number\":%d,\"aircraft\":\"%s\"}",
+                this.flightId, this.company, this.number, this.aircraft).toString();
+    }
+}
diff --git a/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/Flights.java b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/Flights.java
new file mode 100644
index 0000000..325fbce
--- /dev/null
+++ b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/Flights.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.0 in JDK 1.6
+// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2007.07.13 at 03:56:53 PM CEST
+//
+package org.glassfish.jersey.examples.jettison;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * <p>Java class for anonymous complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * &lt;complexType>
+ *   &lt;complexContent>
+ *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       &lt;sequence>
+ *         &lt;element name="flight" type="{}flightType" maxOccurs="unbounded"/>
+ *       &lt;/sequence>
+ *     &lt;/restriction>
+ *   &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+    "flight"
+})
+@XmlRootElement(name = "flights")
+public class Flights {
+
+    @XmlElement(required = true)
+    protected List<FlightType> flight;
+
+    /**
+     * Gets the value of the flight property.
+     *
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the flight property.
+     *
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getFlight().add(newItem);
+     * </pre>
+     *
+     *
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link FlightType }
+     *
+     *
+     * @return list of flights.
+     */
+    public List<FlightType> getFlight() {
+        if (flight == null) {
+            flight = new ArrayList<FlightType>();
+        }
+        return this.flight;
+    }
+
+    @Override
+    public String toString() {
+        return (this.flight != null) ? "{\"flight\": " + this.flight.toString() + "}" : "{flight:[]}";
+    }
+}
diff --git a/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/FlightsDataStore.java b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/FlightsDataStore.java
new file mode 100644
index 0000000..4cbe12d
--- /dev/null
+++ b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/FlightsDataStore.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jettison;
+
+/**
+ * TODO javadoc.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class FlightsDataStore {
+
+    private static volatile Flights flightsData = initFlightsData();
+
+    public static void init() {
+        init(initFlightsData());
+    }
+
+    public static void init(final Flights flights) {
+        flightsData = flights;
+    }
+
+    public static Flights getFlights() {
+        return flightsData;
+    }
+
+    private static Flights initFlightsData() {
+        Flights flights = new Flights();
+        FlightType flight123 = new FlightType();
+        flight123.setCompany("Czech Airlines");
+        flight123.setNumber(123);
+        flight123.setFlightId("OK123");
+        flight123.setAircraft("B737");
+        FlightType flight124 = new FlightType();
+        flight124.setCompany("Czech Airlines");
+        flight124.setNumber(124);
+        flight124.setFlightId("OK124");
+        flight124.setAircraft("AB115");
+        flights.getFlight().add(flight123);
+        flights.getFlight().add(flight124);
+
+        return flights;
+    }
+}
diff --git a/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/JaxbContextResolver.java b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/JaxbContextResolver.java
new file mode 100644
index 0000000..342d61e
--- /dev/null
+++ b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/JaxbContextResolver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jettison;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import javax.xml.bind.JAXBContext;
+
+import org.glassfish.jersey.jettison.JettisonConfig;
+import org.glassfish.jersey.jettison.JettisonJaxbContext;
+
+/**
+ * {@link ContextResolver Context resolver} for {@link JAXBContext}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Provider
+public final class JaxbContextResolver implements ContextResolver<JAXBContext> {
+
+    private final JAXBContext context;
+    private final Set<Class<?>> types;
+    private final Class<?>[] cTypes = {Flights.class, FlightType.class, AircraftType.class};
+
+    public JaxbContextResolver() throws Exception {
+        this.types = new HashSet<Class<?>>(Arrays.asList(cTypes));
+        this.context = new JettisonJaxbContext(JettisonConfig.DEFAULT, cTypes);
+    }
+
+    @Override
+    public JAXBContext getContext(Class<?> objectType) {
+        return (types.contains(objectType)) ? context : null;
+    }
+}
diff --git a/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/ObjectFactory.java b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/ObjectFactory.java
new file mode 100644
index 0000000..b16560b
--- /dev/null
+++ b/examples/json-jettison/src/main/java/org/glassfish/jersey/examples/jettison/ObjectFactory.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.0 in JDK 1.6
+// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2007.07.13 at 03:56:53 PM CEST
+//
+package org.glassfish.jersey.examples.jettison;
+
+import javax.xml.bind.annotation.XmlRegistry;
+
+
+/**
+ * This object contains factory methods for each
+ * Java content interface and Java element interface
+ * generated in the com.sun.jersey.samples.jsonfromjaxb.jaxb package.
+ * <p>An ObjectFactory allows you to programatically
+ * construct new instances of the Java representation
+ * for XML content. The Java representation of XML
+ * content can consist of schema derived interfaces
+ * and classes representing the binding of schema
+ * type definitions, element declarations and model
+ * groups.  Factory methods for each of these are
+ * provided in this class.
+ *
+ */
+@XmlRegistry
+public class ObjectFactory {
+
+
+    /**
+     * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.sun.jersey.samples.jsonfromjaxb.jaxb
+     *
+     */
+    public ObjectFactory() {
+    }
+
+    /**
+     * Create an instance of {@link FlightType }
+     *
+     * @return flight type.
+     */
+    public FlightType createFlightType() {
+        return new FlightType();
+    }
+
+    /**
+     * Create an instance of {@link Flights }
+     *
+     * @return flights instance.
+     */
+    public Flights createFlights() {
+        return new Flights();
+    }
+
+}
diff --git a/examples/json-jettison/src/test/java/org/glassfish/jersey/examples/jettison/JsonJettisonTest.java b/examples/json-jettison/src/test/java/org/glassfish/jersey/examples/jettison/JsonJettisonTest.java
new file mode 100644
index 0000000..a72e521
--- /dev/null
+++ b/examples/json-jettison/src/test/java/org/glassfish/jersey/examples/jettison/JsonJettisonTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jettison;
+
+import java.util.List;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.GenericType;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class JsonJettisonTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return App.createApp();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(new JettisonFeature()).register(JaxbContextResolver.class);
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        // reset static flights list
+        target().path("flights/init").request("application/json").post(null);
+    }
+
+    /**
+     * Test checks that the application.wadl is reachable.
+     * <p/>
+     */
+    @Test
+    public void testApplicationWadl() {
+        String applicationWadl = target().path("application.wadl").request().get(String.class);
+        assertTrue("Something wrong. Returned wadl length is not > 0", applicationWadl.length() > 0);
+    }
+
+    /**
+     * Test check GET on the "flights" resource in "application/json" format.
+     */
+    @Test
+    public void testGetOnFlightsJSONFormat() {
+        // get the initial representation
+        Flights flights = target().path("flights").request("application/json").get(Flights.class);
+        // check that there are two flight entries
+        assertEquals("Expected number of initial entries not found", 2, flights.getFlight().size());
+    }
+
+    /**
+     * Test checks PUT on the "flights" resource in "application/json" format.
+     */
+    @Test
+    public void testPutOnFlightsJSONFormat() {
+        // get the initial representation
+        Flights flights = target().path("flights")
+                .request("application/json").get(Flights.class);
+        // check that there are two flight entries
+        assertEquals("Expected number of initial entries not found", 2, flights.getFlight().size());
+
+        // remove the second flight entry
+        if (flights.getFlight().size() > 1) {
+            flights.getFlight().remove(1);
+        }
+
+        // update the first entry
+        flights.getFlight().get(0).setNumber(125);
+        flights.getFlight().get(0).setFlightId("OK125");
+
+        // and send the updated list back to the server
+        target().path("flights").request().put(Entity.json(flights));
+
+        // get the updated list out from the server:
+        Flights updatedFlights = target().path("flights").request("application/json").get(Flights.class);
+        //check that there is only one flight entry
+        assertEquals("Remaining number of flight entries do not match the expected value", 1, updatedFlights.getFlight().size());
+        // check that the flight entry in retrieved list has FlightID OK!@%
+        assertEquals("Retrieved flight ID doesn't match the expected value", "OK125",
+                updatedFlights.getFlight().get(0).getFlightId());
+    }
+
+    /**
+     * Test checks GET on "flights" resource with mime-type "application/xml".
+     */
+    @Test
+    public void testGetOnFlightsXMLFormat() {
+        // get the initial representation
+        Flights flights = target().path("flights").request("application/xml").get(Flights.class);
+        // check that there are two flight entries
+        assertEquals("Expected number of initial entries not found", 2, flights.getFlight().size());
+    }
+
+    /**
+     * Test checks PUT on "flights" resource with mime-type "application/xml".
+     */
+    @Test
+    public void testPutOnFlightsXMLFormat() {
+        // get the initial representation
+        Flights flights = target().path("flights").request("application/XML").get(Flights.class);
+        // check that there are two flight entries
+        assertEquals("Expected number of initial entries not found", 2, flights.getFlight().size());
+
+        // remove the second flight entry
+        if (flights.getFlight().size() > 1) {
+            flights.getFlight().remove(1);
+        }
+
+        // update the first entry
+        flights.getFlight().get(0).setNumber(125);
+        flights.getFlight().get(0).setFlightId("OK125");
+
+        // and send the updated list back to the server
+        target().path("flights").request().put(Entity.xml(flights));
+
+        // get the updated list out from the server:
+        Flights updatedFlights = target().path("flights").request("application/XML").get(Flights.class);
+        //check that there is only one flight entry
+        assertEquals("Remaining number of flight entries do not match the expected value", 1, updatedFlights.getFlight().size());
+        // check that the flight entry in retrieved list has FlightID OK!@%
+        assertEquals("Retrieved flight ID doesn't match the expected value", "OK125",
+                updatedFlights.getFlight().get(0).getFlightId());
+    }
+
+    /**
+     * Test check GET on the "aircrafts" resource in "application/json" format.
+     */
+    @Test
+    public void testGetOnAircraftsJSONFormat() {
+        GenericType<List<AircraftType>> listOfAircrafts = new GenericType<List<AircraftType>>() {
+        };
+        // get the initial representation
+        List<AircraftType> aircraftTypes = target().path("aircrafts").request("application/json").get(listOfAircrafts);
+        // check that there are two aircraft type entries
+        assertEquals("Expected number of initial aircraft types not found", 2, aircraftTypes.size());
+    }
+}
diff --git a/examples/json-moxy/README.MD b/examples/json-moxy/README.MD
new file mode 100644
index 0000000..bd63590
--- /dev/null
+++ b/examples/json-moxy/README.MD
@@ -0,0 +1,41 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+MOXy JAX-RS JSON Provider Example
+=================================
+
+This example demonstrates how to produce/consume JSON representations from Java objects.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path       | Resource class   | HTTP method
+-------------- | ---------------- | -------------
+**_/test_**    | JsonResource     | GET
+**_/test_**    | JsonResource     | POST
+
+(See JsonResourceTest for more details.)
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/)
+
+A [WADL description](http://wadl.java.net/#spec) may be accessed at the URL:
+
+-   <http://localhost:9998/jsonmoxy/application.wadl>
+
+The resource is available at
+
+-   <http://localhost:9998/jsonmoxy/test>
\ No newline at end of file
diff --git a/examples/json-moxy/pom.xml b/examples/json-moxy/pom.xml
new file mode 100644
index 0000000..815ae38
--- /dev/null
+++ b/examples/json-moxy/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>json-moxy</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-json-moxy</name>
+
+    <description>Jersey JSON with MOXy example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.jsonmoxy.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/json-moxy/src/main/java/org/glassfish/jersey/examples/jsonmoxy/App.java b/examples/json-moxy/src/main/java/org/glassfish/jersey/examples/jsonmoxy/App.java
new file mode 100644
index 0000000..3715d11
--- /dev/null
+++ b/examples/json-moxy/src/main/java/org/glassfish/jersey/examples/jsonmoxy/App.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonmoxy;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.ext.ContextResolver;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Michal Gajdos
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:9998/jsonmoxy/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("JSON with MOXy Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, createApp(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.%nStop the application using CTRL+C"));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    public static ResourceConfig createApp() {
+        return new ResourceConfig()
+                .packages("org.glassfish.jersey.examples.jsonmoxy")
+                .register(createMoxyJsonResolver());
+    }
+
+    public static ContextResolver<MoxyJsonConfig> createMoxyJsonResolver() {
+        final MoxyJsonConfig moxyJsonConfig = new MoxyJsonConfig();
+        Map<String, String> namespacePrefixMapper = new HashMap<String, String>(1);
+        namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
+        moxyJsonConfig.setNamespacePrefixMapper(namespacePrefixMapper).setNamespaceSeparator(':');
+        return moxyJsonConfig.resolver();
+    }
+}
+
+
+
diff --git a/examples/json-moxy/src/main/java/org/glassfish/jersey/examples/jsonmoxy/JsonResource.java b/examples/json-moxy/src/main/java/org/glassfish/jersey/examples/jsonmoxy/JsonResource.java
new file mode 100644
index 0000000..a26e430
--- /dev/null
+++ b/examples/json-moxy/src/main/java/org/glassfish/jersey/examples/jsonmoxy/JsonResource.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonmoxy;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("test")
+public class JsonResource {
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public TestBean createSimpleBean() {
+        return new TestBean("a", 1, 1L);
+    }
+
+    @POST
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public TestBean roundTrip(TestBean s) {
+        return s;
+    }
+}
diff --git a/examples/json-moxy/src/main/java/org/glassfish/jersey/examples/jsonmoxy/TestBean.java b/examples/json-moxy/src/main/java/org/glassfish/jersey/examples/jsonmoxy/TestBean.java
new file mode 100644
index 0000000..74f1e38
--- /dev/null
+++ b/examples/json-moxy/src/main/java/org/glassfish/jersey/examples/jsonmoxy/TestBean.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonmoxy;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@XmlRootElement
+public class TestBean {
+
+    public String a;
+    public int b;
+    public long c;
+
+    public TestBean() {
+    }
+
+    public TestBean(String a, int b, long c) {
+        this.a = a;
+        this.b = b;
+        this.c = c;
+    }
+
+    public String getA() {
+        return a;
+    }
+
+    public int getB() {
+        return b;
+    }
+
+    public long getC() {
+        return c;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        TestBean that = (TestBean) o;
+
+        if (b != that.b) {
+            return false;
+        }
+        if (c != that.c) {
+            return false;
+        }
+        if (a != null ? !a.equals(that.a) : that.a != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = a != null ? a.hashCode() : 0;
+        result = 31 * result + b;
+        result = 31 * result + (int) (c ^ (c >>> 32));
+        return result;
+    }
+}
diff --git a/examples/json-moxy/src/test/java/org/glassfish/jersey/examples/jsonmoxy/JsonResourceTest.java b/examples/json-moxy/src/test/java/org/glassfish/jersey/examples/jsonmoxy/JsonResourceTest.java
new file mode 100644
index 0000000..3f6000a
--- /dev/null
+++ b/examples/json-moxy/src/test/java/org/glassfish/jersey/examples/jsonmoxy/JsonResourceTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonmoxy;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Michal Gajdos
+ */
+public class JsonResourceTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        return App.createApp();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(App.createMoxyJsonResolver());
+    }
+
+    @Test
+    public void testGet() {
+        final WebTarget target = target("test");
+        final TestBean testBean = target.request(MediaType.APPLICATION_JSON_TYPE).get(TestBean.class);
+
+        assertEquals(testBean, new TestBean("a", 1, 1L));
+    }
+
+    @Test
+    public void roundTripTest() {
+        final WebTarget target = target("test");
+        final TestBean testBean = target
+                .request(MediaType.APPLICATION_JSON_TYPE)
+                .post(Entity.entity(new TestBean("a", 1, 1L), MediaType.APPLICATION_JSON_TYPE), TestBean.class);
+
+        assertEquals(testBean, new TestBean("a", 1, 1L));
+    }
+}
diff --git a/examples/json-processing-webapp/README.MD b/examples/json-processing-webapp/README.MD
new file mode 100644
index 0000000..1d1750f
--- /dev/null
+++ b/examples/json-processing-webapp/README.MD
@@ -0,0 +1,61 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+JSON-P JAX-RS JSON Provider Example
+===================================
+
+This example demonstrates how to produce/consume JSON representations
+defined by [JSR-353](http://jcp.org/en/jsr/detail?id=353).
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                    | Resource class              | HTTP methods   | Allowed values
+--------------------------- | --------------------------- | -------------- | ------------------------------
+**_/document_**             | DocumentResource            | GET            |
+**_/document_**             | DocumentResource            | DELETE         | 
+**_/document_**             | DocumentResource            | POST           | `JsonObject`
+**_/document/{id: \d+}_**   | DocumentResource            | GET            | 
+**_/document/{id: \d+}_**   | DocumentResource            | DELETE         | 
+**_/document/multiple_**    | DocumentResource            | POST           | `JsonArray` of `JsonObject`s
+**_/document/filter_**      | DocumentFilteringResource   | POST           | `JsonArray` of string values
+
+(See JsonProcessingResourceTest for more details.)
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean package jetty:run
+
+A [WADL description](http://wadl.java.net/#spec) may be accessed at the URL:
+
+-   <http://localhost:8080/jsonp-webapp/application.wadl>
+
+This deploys current example using Jetty. You can access the application at <http://localhost:8080/jsonp-webapp/document>
+
+You can access resources of this application also using curl:
+
+### Store some documents.
+
+>     curl -H "Content-Type: application/json" -X POST --data '[{"name":"Jersey","site":"http://jersey.java.net"},{"age":33,"phone":"158158158","name":"Foo"},{"name":"JSON-P","site":"http://jsonp.java.net"}]' http://localhost:8080/jsonp-webapp/document/multiple
+
+### Retrieve stored documents.
+
+>     curl http://localhost:8080/jsonp-webapp/document
+
+### Retrieve a document with id 1.
+
+>     curl http://localhost:8080/jsonp-webapp/document/1
+
+### Retrieve `site` attributes with values of documents containing this attribute.
+
+>     curl -H "Content-Type: application/json" -X POST --data '["site"]' http://localhost:8080/jsonp-webapp/document/filter
diff --git a/examples/json-processing-webapp/pom.xml b/examples/json-processing-webapp/pom.xml
new file mode 100644
index 0000000..e84003b
--- /dev/null
+++ b/examples/json-processing-webapp/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>json-processing-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-json-processing-webapp</name>
+
+    <description>Jersey JSON-P (JSR 353) example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-processing</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>maven-jetty-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/MyApplication.java b/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/MyApplication.java
new file mode 100644
index 0000000..965da71
--- /dev/null
+++ b/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/MyApplication.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonp;
+
+import javax.json.stream.JsonGenerator;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * @author Michal Gajdos
+ */
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        packages("org.glassfish.jersey.examples.jsonp.resource");
+        register(LoggingFeature.class);
+        property(JsonGenerator.PRETTY_PRINTING, true);
+    }
+}
+
+
+
diff --git a/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/resource/DocumentFilteringResource.java b/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/resource/DocumentFilteringResource.java
new file mode 100644
index 0000000..5ca9299
--- /dev/null
+++ b/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/resource/DocumentFilteringResource.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonp.resource;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonString;
+
+import org.glassfish.jersey.examples.jsonp.service.DocumentStorage;
+
+/**
+ * Resource filtering stored documents based on the presence of given attributes.
+ *
+ * @author Michal Gajdos
+ */
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public class DocumentFilteringResource {
+
+    @POST
+    public JsonArray filter(final JsonArray properties) {
+        final JsonArrayBuilder documents = Json.createArrayBuilder();
+        final List<JsonString> propertyList = properties.getValuesAs(JsonString.class);
+
+        for (final JsonObject jsonObject : DocumentStorage.getAll()) {
+            final JsonObjectBuilder documentBuilder = Json.createObjectBuilder();
+
+            for (final JsonString property : propertyList) {
+                final String key = property.getString();
+
+                if (jsonObject.containsKey(key)) {
+                    documentBuilder.add(key, jsonObject.get(key));
+                }
+            }
+
+            final JsonObject document = documentBuilder.build();
+            if (!document.isEmpty()) {
+                documents.add(document);
+            }
+        }
+
+        return documents.build();
+    }
+
+}
diff --git a/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/resource/DocumentResource.java b/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/resource/DocumentResource.java
new file mode 100644
index 0000000..a2a42b9
--- /dev/null
+++ b/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/resource/DocumentResource.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonp.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObject;
+
+import org.glassfish.jersey.examples.jsonp.service.DocumentStorage;
+
+/**
+ * Document Resource.
+ *
+ * @author Michal Gajdos
+ */
+@Path("document")
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public class DocumentResource {
+
+    @GET
+    public JsonArray getAll() {
+        final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+        for (final JsonObject document : DocumentStorage.getAll()) {
+            arrayBuilder.add(document);
+        }
+        return arrayBuilder.build();
+    }
+
+    @GET
+    @Path("{id: \\d+}")
+    public JsonObject get(@PathParam("id") final int id) {
+        return DocumentStorage.get(id);
+    }
+
+    @DELETE
+    @Path("{id: \\d+}")
+    public JsonObject remove(@PathParam("id") final int id) {
+        return DocumentStorage.remove(id);
+    }
+
+    @DELETE
+    public void removeAll() {
+        DocumentStorage.removeAll();
+    }
+
+    @POST
+    public JsonArray store(final JsonObject document) {
+        return Json.createArrayBuilder().add(DocumentStorage.store(document)).build();
+    }
+
+    @POST
+    @Path("multiple")
+    public JsonArray store(final JsonArray documents) {
+        final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+        for (final JsonObject document : documents.getValuesAs(JsonObject.class)) {
+            arrayBuilder.add(DocumentStorage.store(document));
+        }
+        return arrayBuilder.build();
+    }
+
+    @Path("filter")
+    public Class<DocumentFilteringResource> getFilteringResource() {
+        return DocumentFilteringResource.class;
+    }
+}
diff --git a/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/service/DocumentStorage.java b/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/service/DocumentStorage.java
new file mode 100644
index 0000000..2c587d2
--- /dev/null
+++ b/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/service/DocumentStorage.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonp.service;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.json.JsonObject;
+
+/**
+ * Storage of documents.
+ *
+ * @author Michal Gajdos
+ */
+public final class DocumentStorage {
+
+    private static final Map<Integer, JsonObject> storage = new LinkedHashMap<>();
+    private static final AtomicInteger counter = new AtomicInteger(0);
+
+    public static int store(final JsonObject document) {
+        final int id = counter.addAndGet(1);
+        storage.put(id, document);
+        return id;
+    }
+
+    public static JsonObject get(final int id) {
+        return storage.get(id);
+    }
+
+    public static Collection<JsonObject> getAll() {
+        return storage.values();
+    }
+
+    public static JsonObject remove(final int id) {
+        return storage.remove(id);
+    }
+
+    public static void removeAll() {
+        storage.clear();
+    }
+
+    /**
+     * Prevent initialization.
+     */
+    private DocumentStorage() {
+    }
+}
diff --git a/examples/json-processing-webapp/src/main/webapp/WEB-INF/web.xml b/examples/json-processing-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..fea17eb
--- /dev/null
+++ b/examples/json-processing-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.examples.jsonp.MyApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.examples.jsonp.MyApplication</param-value>
+        </init-param>
+        <init-param>
+            <param-name>javax.json.stream.JsonGenerator.prettyPrinting</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.examples.jsonp.MyApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/examples/json-processing-webapp/src/test/java/org/glassfish/jersey/examples/jsonp/JsonProcessingResourceTest.java b/examples/json-processing-webapp/src/test/java/org/glassfish/jersey/examples/jsonp/JsonProcessingResourceTest.java
new file mode 100644
index 0000000..9b686c1
--- /dev/null
+++ b/examples/json-processing-webapp/src/test/java/org/glassfish/jersey/examples/jsonp/JsonProcessingResourceTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonp;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Michal Gajdos
+ */
+public class JsonProcessingResourceTest extends JerseyTest {
+
+    private static final List<JsonObject> documents = new ArrayList<>();
+
+    static {
+        documents.add(Json.createObjectBuilder()
+                        .add("name", "Jersey")
+                        .add("site", "http://jersey.github.io")
+                        .build()
+        );
+        documents.add(Json.createObjectBuilder()
+                        .add("age", 33)
+                        .add("phone", "158158158")
+                        .add("name", "Foo")
+                        .build()
+        );
+        documents.add(Json.createObjectBuilder()
+                        .add("name", "JSON-P")
+                        .add("site", "https://javaee.github.io/jsonp/")
+                        .build()
+        );
+    }
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("json-processing-webapp").build();
+    }
+
+    @Test
+    public void testStoreGetRemoveDocument() throws Exception {
+        final JsonObject document = documents.get(0);
+
+        // Store.
+        final Response response = target("document").request(MediaType.APPLICATION_JSON).post(Entity.json(document));
+        assertEquals(200, response.getStatus());
+
+        final List<JsonNumber> ids = response.readEntity(JsonArray.class).getValuesAs(JsonNumber.class);
+        assertEquals(1, ids.size());
+
+        // Get.
+        final String id = ids.get(0).toString();
+        final WebTarget documentTarget = target("document").path(id);
+        final JsonObject storedDocument = documentTarget.request(MediaType.APPLICATION_JSON)
+                .get(JsonObject.class);
+        assertEquals(document, storedDocument);
+
+        // Remove.
+        final JsonObject removedDocument = documentTarget.request(MediaType.APPLICATION_JSON).delete(JsonObject.class);
+        assertEquals(document, removedDocument);
+
+        // Get.
+        final Response errorResponse = documentTarget.request(MediaType.APPLICATION_JSON).get();
+        assertEquals(204, errorResponse.getStatus());
+    }
+
+    private JsonArray getDocumentJsonArray() {
+        final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+        for (final JsonObject document : documents) {
+            arrayBuilder.add(document);
+        }
+        return arrayBuilder.build();
+    }
+
+    @Test
+    public void testStoreDocuments() throws Exception {
+        final Response response = target("document/multiple")
+                .request(MediaType.APPLICATION_JSON).post(Entity.json(getDocumentJsonArray()));
+
+        assertEquals(200, response.getStatus());
+
+        final List<JsonNumber> ids = response.readEntity(JsonArray.class).getValuesAs(JsonNumber.class);
+        assertEquals(documents.size(), ids.size());
+
+        // Remove All.
+        target("document").request().delete();
+    }
+
+    @Test
+    public void testFilterDocuments() throws Exception {
+        // Store documents.
+        target("document/multiple").request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(getDocumentJsonArray()));
+
+        // Filter.
+        JsonArray filter = Json.createArrayBuilder().add("site").build();
+        JsonArray filtered = target("document/filter").request(MediaType.APPLICATION_JSON)
+                .post(Entity.json(filter), JsonArray.class);
+
+        checkFilteredDocuments(filtered, 2, "site");
+
+        filter = Json.createArrayBuilder().add("site").add("age").build();
+        filtered = target("document/filter").request(MediaType.APPLICATION_JSON)
+                .post(Entity.json(filter), JsonArray.class);
+
+        checkFilteredDocuments(filtered, 3, "site", "age");
+
+        // Remove All.
+        target("document").request().delete();
+    }
+
+    private void checkFilteredDocuments(final JsonArray filtered, final int size, final String... properties) {
+        assertEquals(size, filtered.size());
+
+        Set<String> strings = Arrays.stream(properties).collect(Collectors.toSet());
+        for (final JsonObject document : filtered.getValuesAs(JsonObject.class)) {
+            for (final String property : document.keySet()) {
+                assertTrue(strings.contains(property));
+            }
+        }
+    }
+}
diff --git a/examples/json-with-padding/README.MD b/examples/json-with-padding/README.MD
new file mode 100644
index 0000000..0712a30
--- /dev/null
+++ b/examples/json-with-padding/README.MD
@@ -0,0 +1,71 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+JSONP Example
+=============
+
+This example demonstrates how to use JSONP wrapper in order to serve not
+only JSON but also a JavaScript expression representing a function call
+with the original JSON data as a parameter. The technique is well
+described e.g. at <http://en.wikipedia.org/wiki/JSON#JSONP>.
+
+A simple web application is presented with just one resource: a change
+record list. A client can obtain the list using XML or JSON or
+JavaScript representation. The last change record is also available in
+all three formats as a subresource of the previously mentioned list
+resource.
+
+Contents
+--------
+
+The change record list web resource is implemented by
+`org.glassfish.jersey.examples.jsonp.ChangeListResource` class.
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                | Resource class   | HTTP methods
+----------------------- | ---------------- | --------------
+**_/changes_**          | ChangeList       | GET
+**_/changes/latest_**   | ChangeList       | GET
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the JSONP example using [Grizzly](http://grizzly.java.net/)
+
+A [WADL description](http://wadl.java.net/#spec) may be then accessed at the URL:
+
+- <http://localhost:8080/jsonp/application.wadl>
+
+Following steps are using [cURL](http://curl.haxx.se/) command line tool:
+
+Get the list of changes in JSON format:
+
+>     curl -HAccept:application/json http://localhost:8080/jsonp/changes
+
+Get the list of changes in XML format:
+
+>     curl -HAccept:application/xml http://localhost:8080/jsonp/changes
+
+Get the list of changes as a JavaScript callback:
+
+>     curl -HAccept:application/x-javascript http://localhost:8080/jsonp/changes
+
+You can also see test classes at `src/main/test` subdirectory for detail
+information how to consume the service using Jersey client API
+
+### JavaScript Client
+
+After starting the service and refreshing this page, a list of changes
+should be displayed below:
+
+`You need to run the service first!`
\ No newline at end of file
diff --git a/examples/json-with-padding/pom.xml b/examples/json-with-padding/pom.xml
new file mode 100644
index 0000000..dc814a3
--- /dev/null
+++ b/examples/json-with-padding/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>json-with-padding</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-json-with-padding</name>
+
+    <description>Jersey JSON with Padding example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-impl</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.jsonp.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/json-with-padding/src/main/assembly/src.xml b/examples/json-with-padding/src/main/assembly/src.xml
new file mode 100644
index 0000000..6e47a4b
--- /dev/null
+++ b/examples/json-with-padding/src/main/assembly/src.xml
@@ -0,0 +1,28 @@
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<assembly>
+  <id>project</id>
+  <formats>
+    <format>zip</format>
+  </formats>
+  <fileSets>
+    <fileSet>
+      <directory>.</directory>
+      <outputDirectory></outputDirectory>
+      <useDefaultExcludes>true</useDefaultExcludes>
+      <excludes>
+        <exclude>**/target/**</exclude>
+      </excludes>
+    </fileSet>
+  </fileSets>
+</assembly>
diff --git a/examples/json-with-padding/src/main/java/org/glassfish/jersey/examples/jsonp/App.java b/examples/json-with-padding/src/main/java/org/glassfish/jersey/examples/jsonp/App.java
new file mode 100644
index 0000000..7dbcc51
--- /dev/null
+++ b/examples/json-with-padding/src/main/java/org/glassfish/jersey/examples/jsonp/App.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonp;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Utility class which can create {@link org.glassfish.jersey.server.ApplicationHandler} instance and provides support
+ * for running this sample from command line.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/jsonp/");
+    public static final String ROOT_PATH = "changes";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("JSONP Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, createApp(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(
+                    String.format("Application started.%nTry out %s%s%nStop the application using CTRL+C", BASE_URI, ROOT_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+
+    public static ResourceConfig createApp() {
+        return new ResourceConfig(ChangeListResource.class);
+    }
+}
diff --git a/examples/json-with-padding/src/main/java/org/glassfish/jersey/examples/jsonp/ChangeListResource.java b/examples/json-with-padding/src/main/java/org/glassfish/jersey/examples/jsonp/ChangeListResource.java
new file mode 100644
index 0000000..e0b24a6
--- /dev/null
+++ b/examples/json-with-padding/src/main/java/org/glassfish/jersey/examples/jsonp/ChangeListResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonp;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+
+import org.glassfish.jersey.server.JSONP;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path(App.ROOT_PATH)
+@Produces({"application/x-javascript", "application/json", "application/xml"})
+public class ChangeListResource {
+
+    static final List<ChangeRecordBean> changes = new LinkedList<ChangeRecordBean>();
+
+    static {
+        changes.add(new ChangeRecordBean(false, 2, "title \"User Guide\" updated"));
+        changes.add(new ChangeRecordBean(true, 1, "fixed metadata"));
+        changes.add(new ChangeRecordBean(false, 91, "added index"));
+        changes.add(new ChangeRecordBean(false, 650, "\"Troubleshoothing\" chapter"));
+        changes.add(new ChangeRecordBean(false, 1, "fixing typo"));
+    }
+
+    @GET
+    @JSONP(queryParam = JSONP.DEFAULT_QUERY)
+    public List<ChangeRecordBean> getChanges(@QueryParam(JSONP.DEFAULT_QUERY) String callback, @QueryParam("type") int type) {
+        return changes;
+    }
+
+    @GET
+    @Path("latest")
+    @JSONP
+    public ChangeRecordBean getLastChange(@QueryParam("callback") String callback, @QueryParam("type") int type) {
+        return changes.get(changes.size() - 1);
+    }
+}
diff --git a/examples/json-with-padding/src/main/java/org/glassfish/jersey/examples/jsonp/ChangeRecordBean.java b/examples/json-with-padding/src/main/java/org/glassfish/jersey/examples/jsonp/ChangeRecordBean.java
new file mode 100644
index 0000000..cef2479
--- /dev/null
+++ b/examples/json-with-padding/src/main/java/org/glassfish/jersey/examples/jsonp/ChangeRecordBean.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonp;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@XmlRootElement(name = "change")
+public class ChangeRecordBean {
+
+    @XmlAttribute
+    public boolean madeByAdmin;
+    public int linesChanged;
+    public String logMessage;
+
+    /**
+     * No-arg constructor for JAXB
+     */
+    public ChangeRecordBean() {}
+
+    public ChangeRecordBean(boolean madeByAdmin, int linesChanged, String logMessage) {
+        this.madeByAdmin = madeByAdmin;
+        this.linesChanged = linesChanged;
+        this.logMessage = logMessage;
+    }
+}
diff --git a/examples/json-with-padding/src/test/java/org/glassfish/jersey/examples/jsonp/JsonWithPaddingTest.java b/examples/json-with-padding/src/test/java/org/glassfish/jersey/examples/jsonp/JsonWithPaddingTest.java
new file mode 100644
index 0000000..d589dc1
--- /dev/null
+++ b/examples/json-with-padding/src/test/java/org/glassfish/jersey/examples/jsonp/JsonWithPaddingTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.jsonp;
+
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.GenericType;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JsonWithPaddingTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        return App.createApp();
+    }
+
+    /**
+     * Test checks that the application.wadl is reachable.
+     * <p/>
+     */
+    @Test
+    public void testApplicationWadl() {
+        WebTarget target = target();
+        String applicationWadl = target.path("application.wadl").request().get(String.class);
+        assertTrue("Something wrong. Returned wadl length is not > 0", applicationWadl.length() > 0);
+    }
+
+    /**
+     * Test check GET on the "changes" resource in "application/json" format.
+     */
+    @Test
+    public void testGetOnChangesJSONFormat() {
+        WebTarget target = target();
+        GenericType<List<ChangeRecordBean>> genericType = new GenericType<List<ChangeRecordBean>>() {};
+        // get the initial representation
+        List<ChangeRecordBean> changes = target.path("changes").request("application/json").get(genericType);
+        // check that there are two changes entries
+        assertEquals("Expected number of initial changes not found", 5, changes.size());
+    }
+
+    /**
+     * Test check GET on the "changes" resource in "application/xml" format.
+     */
+    @Test
+    public void testGetOnLatestChangeXMLFormat() {
+        WebTarget target = target();
+        ChangeRecordBean lastChange = target.path("changes/latest").request("application/xml").get(ChangeRecordBean.class);
+        assertEquals(1, lastChange.linesChanged);
+    }
+
+    /**
+     * Test check GET on the "changes" resource in "application/javascript" format.
+     */
+    @Test
+    public void testGetOnLatestChangeJavascriptFormat() {
+        WebTarget target = target();
+        String js = target.path("changes").request("application/x-javascript").get(String.class);
+        assertTrue(js.startsWith("callback"));
+    }
+
+    @Test
+    public void testGetOnLatestChangeJavascriptFormatDifferentCallback() {
+        WebTarget target = target();
+        String js = target.path("changes").queryParam("__callback", "parse").request("application/x-javascript")
+                .get(String.class);
+        assertTrue(js.startsWith("parse"));
+    }
+}
diff --git a/examples/managed-beans-webapp/README.MD b/examples/managed-beans-webapp/README.MD
new file mode 100644
index 0000000..9de8b1a
--- /dev/null
+++ b/examples/managed-beans-webapp/README.MD
@@ -0,0 +1,42 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Managed Beans Example
+=====================
+
+This example demonstrates managed bean support in Jersey. JAX-RS root
+resource classes are annotated with @ManagedBean, which means EE-related
+resources may be injected into instances of those classes.
+
+Content
+-------
+
+The example application includes two root resource classes that are
+treated as Java EE managed beans. One root resource class is managed in
+the default JAX-RS life-cycle (one instance per request) and the other
+is managed in the singleton life-cycle (one instance per web
+application).
+
+Two Java EE artifacts are injected into the singleton root resource. The
+first is a resource constant defined in the web.xml. The second is an
+entity manager factory to allow integration with JPA layer.
+
+Running the Example
+-------------------
+
+This sample utilizes Java EE features in GlassFish application server.
+
+The easiest way to get the application running is to build it and deploy
+as follows:
+
+    mvn clean package
+    $AS_HOME/asadmin deploy target/managed-beans-webapp.war
+
+From a web browser, visit:
+
+-   <http://localhost:8080/managed-beans-webapp>
diff --git a/examples/managed-beans-webapp/pom.xml b/examples/managed-beans-webapp/pom.xml
new file mode 100644
index 0000000..6703aa6
--- /dev/null
+++ b/examples/managed-beans-webapp/pom.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>managed-beans-webapp</artifactId>
+    <name>jersey-examples-managed-beans-webapp</name>
+    <packaging>war</packaging>
+
+    <description>Jersey Managed Beans Web Application Example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.persistence</groupId>
+            <artifactId>persistence-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>run-external-tests</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <jersey.config.test.container.factory>${external.container.factory}</jersey.config.test.container.factory>
+                                <jersey.config.test.container.port>${external.container.port}</jersey.config.test.container.port>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <properties>
+                <!-- External test container configuration is done via properties to allow overriding via command line. -->
+                <external.container.factory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</external.container.factory>
+                <external.container.port>8080</external.container.port>
+                <maven.test.skip>false</maven.test.skip>
+            </properties>
+        </profile>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <maven.test.skip>true</maven.test.skip>
+    </properties>
+
+</project>
diff --git a/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/ManagedBeanException.java b/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/ManagedBeanException.java
new file mode 100644
index 0000000..6b24520
--- /dev/null
+++ b/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/ManagedBeanException.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedbeans.resources;
+
+/**
+ * Exception to be thrown and mapped by the web application.
+ *
+ * @author Paul Sandoz
+ */
+public class ManagedBeanException extends RuntimeException {
+
+}
diff --git a/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/ManagedBeanExceptionMapper.java b/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/ManagedBeanExceptionMapper.java
new file mode 100644
index 0000000..0d1d452
--- /dev/null
+++ b/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/ManagedBeanExceptionMapper.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedbeans.resources;
+
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import javax.annotation.ManagedBean;
+import javax.annotation.PostConstruct;
+
+/**
+ * Custom exception mapper.
+ *
+ * @author Paul Sandoz
+ */
+@Provider
+@ManagedBean
+public class ManagedBeanExceptionMapper implements ExceptionMapper<ManagedBeanException> {
+
+    @Context
+    private UriInfo uiFieldInject;
+
+    @Context
+    private ResourceContext rc;
+
+    private UriInfo uiMethodInject;
+
+    private UriInfo ui;
+
+    @Context
+    public void set(UriInfo ui) {
+        this.uiMethodInject = ui;
+    }
+
+    @PostConstruct
+    public void postConstruct() {
+        ensureInjected();
+        this.ui = uiMethodInject;
+    }
+
+    @Override
+    public Response toResponse(ManagedBeanException exception) {
+        ensureInjected();
+        return Response.serverError().entity(String.format("ManagedBeanException from %s", ui.getPath())).build();
+    }
+
+    private void ensureInjected() throws IllegalStateException {
+        if (uiFieldInject == null || uiMethodInject == null || rc == null) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/ManagedBeanPerRequestResource.java b/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/ManagedBeanPerRequestResource.java
new file mode 100644
index 0000000..1b7387d
--- /dev/null
+++ b/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/ManagedBeanPerRequestResource.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedbeans.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import javax.annotation.ManagedBean;
+
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptors;
+import javax.interceptor.InvocationContext;
+
+/**
+ * JAX-RS root resource treated as managed bean.
+ *
+ * @author Paul Sandoz
+ */
+@Path("/managedbean/per-request")
+@ManagedBean
+public class ManagedBeanPerRequestResource {
+
+    @Context UriInfo ui;
+
+    @QueryParam("x") String x;
+
+    public static class MyInterceptor {
+
+        @AroundInvoke
+        public Object around(InvocationContext ctx) throws Exception {
+            return String.format("INTERCEPTED: %s", ctx.proceed());
+        }
+    }
+
+    @GET
+    @Produces("text/plain")
+    @Interceptors(MyInterceptor.class)
+    public String getMessage() {
+        return String.format("echo from %s: %s", ui.getPath(), x);
+    }
+
+    @Path("exception")
+    public String getException() {
+        throw new ManagedBeanException();
+    }
+}
diff --git a/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/ManagedBeanSingletonResource.java b/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/ManagedBeanSingletonResource.java
new file mode 100644
index 0000000..3b79391
--- /dev/null
+++ b/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/ManagedBeanSingletonResource.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedbeans.resources;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import javax.annotation.ManagedBean;
+import javax.annotation.Resource;
+import javax.inject.Singleton;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.PersistenceUnit;
+
+/**
+ * JAX-RS root resource treated as Java EE managed bean in singleton scope.
+ *
+ * @author Paul Sandoz
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/managedbean/singleton")
+@Singleton
+@ManagedBean
+public class ManagedBeanSingletonResource {
+
+    /**
+     * Initial value should get injected by Java EE container.
+     */
+    @Resource(name = "injectedResource")
+    int counter = 0;
+
+    @Context
+    UriInfo ui;
+
+    private EntityManager entityManager;
+
+    /**
+     * Set entity manager based on injected entity manager factory.
+     *
+     * @param em entity manager factory injected by Java EE container.
+     */
+    @PersistenceUnit(unitName = "ManagedBeansPU")
+    void setEntityManager(final EntityManagerFactory em) {
+        entityManager = em.createEntityManager();
+    }
+
+    /**
+     * Provide textual representation of the internal counter.
+     * @return internal counter {@code String} representation
+     */
+    @GET
+    @Produces("text/plain")
+    public String getMessage() {
+        return Integer.toString(counter++);
+    }
+
+    /**
+     * Reset internal counter.
+     * @param i new counter value to be set.
+     */
+    @PUT
+    @Produces("text/plain")
+    public void putMessage(final int i) {
+        counter = i;
+    }
+
+    /**
+     * Throw a runtime exception, so that it could be mapped.
+     *
+     * @return nothing, just throw the custom exception.
+     */
+    @Path("exception")
+    public String getException() {
+        throw new ManagedBeanException();
+    }
+
+    /**
+     * Get JPA widget value.
+     *
+     * @param id widget id.
+     * @return value of the widget or 404 if given widget id is not found.
+     */
+    @Path("widget/{id: \\d+}")
+    @GET
+    public String getWidget(@PathParam("id") final int id) {
+        try {
+            return entityManager.find(Widget.class, id).val;
+        } catch (final NullPointerException ignored) {
+            throw new WebApplicationException(Response.Status.NOT_FOUND);
+        }
+    }
+
+    /**
+     * Set new widget value.
+     * @param id widget id.
+     * @param val new value to be associated with above specified id.
+     */
+    @Path("widget/{id: \\d+}")
+    @PUT
+    public void putWidget(@PathParam("id") final int id, final String val) {
+        entityManager.merge(new Widget(id, val));
+    }
+
+    /**
+     * Remove widget with given id.
+     * @param id id of the widget to be removed.
+     */
+    @Path("widget/{id: \\d+}")
+    @DELETE
+    public void deleteWidget(@PathParam("id") final int id) {
+        entityManager.remove(entityManager.find(Widget.class, id));
+    }
+}
diff --git a/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/MyApplication.java b/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/MyApplication.java
new file mode 100644
index 0000000..4bb428f
--- /dev/null
+++ b/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/MyApplication.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedbeans.resources;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application.
+ *
+ * @author Jonathan Benoit
+ */
+@ApplicationPath("/app/*")
+public class MyApplication extends Application {
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> classes = new HashSet<Class<?>>();
+        classes.add(ManagedBeanException.class);
+        classes.add(ManagedBeanSingletonResource.class);
+        classes.add(ManagedBeanPerRequestResource.class);
+        classes.add(ManagedBeanExceptionMapper.class);
+        return classes;
+    }
+}
diff --git a/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/Widget.java b/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/Widget.java
new file mode 100644
index 0000000..fff3dcd
--- /dev/null
+++ b/examples/managed-beans-webapp/src/main/java/org/glassfish/jersey/examples/managedbeans/resources/Widget.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedbeans.resources;
+
+import java.io.Serializable;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+/**
+ * Simple JPA entity made accessible via {@link ManagedBeanSingletonResource}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Entity
+public class Widget implements Serializable {
+
+    @Id
+    int id;
+
+    String val;
+
+    /**
+     * No-arg constructor to make JPA happy.
+     */
+    public Widget() {
+    }
+
+    /**
+     * Create a new widget with given id and value.
+     * @param id widget id
+     * @param val widget value
+     */
+    public Widget(int id, String val) {
+        this.id = id;
+        this.val = val;
+    }
+}
diff --git a/examples/managed-beans-webapp/src/main/resources/META-INF/persistence.xml b/examples/managed-beans-webapp/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000..11ccdd5
--- /dev/null
+++ b/examples/managed-beans-webapp/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
+    <persistence-unit name="ManagedBeansPU" transaction-type="JTA">
+        <jta-data-source>jdbc/__default</jta-data-source>
+        <properties>
+            <!-- drop and create tables at deployment -->
+            <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
+            <!-- log sqls executed in server.log -->
+            <property name="eclipselink.logging.level" value="FINE"/>
+            <!-- Instruct EclipseLink to not log execeptions it throws. Lets the application decide about it-->
+            <property name="eclipselink.logging.exceptions" value="false"/>
+        </properties>
+    </persistence-unit>
+</persistence>
diff --git a/examples/managed-beans-webapp/src/main/webapp/WEB-INF/web.xml b/examples/managed-beans-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..1e3283b
--- /dev/null
+++ b/examples/managed-beans-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+    <env-entry>
+        <env-entry-name>injectedResource</env-entry-name>
+        <env-entry-type>java.lang.Integer</env-entry-type>
+        <env-entry-value>3</env-entry-value>
+    </env-entry>
+</web-app>
diff --git a/examples/managed-beans-webapp/src/main/webapp/index.jsp b/examples/managed-beans-webapp/src/main/webapp/index.jsp
new file mode 100644
index 0000000..69c02f6
--- /dev/null
+++ b/examples/managed-beans-webapp/src/main/webapp/index.jsp
@@ -0,0 +1,32 @@
+<%--
+
+    Copyright (c) 2009, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+--%>
+
+<%--
+    Document   : index
+    Created on : Oct 22, 2009, 9:16:47 AM
+    Author     : paulsandoz
+--%>
+
+<%@page contentType="text/html" pageEncoding="UTF-8"%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>Managed Beans WebApp</title>
+    </head>
+    <body>
+        <p>Per-request resource <a href="app/managedbean/per-request">managedbean/per-request</a></p>
+        <p>Singleton resource <a href="app/managedbean/singleton">managedbean/singleton</a></p>
+    </body>
+</html>
diff --git a/examples/managed-beans-webapp/src/test/java/org/glassfish/jersey/examples/managedbeans/ManagedBeanWebAppTest.java b/examples/managed-beans-webapp/src/test/java/org/glassfish/jersey/examples/managedbeans/ManagedBeanWebAppTest.java
new file mode 100644
index 0000000..a7e86b6
--- /dev/null
+++ b/examples/managed-beans-webapp/src/test/java/org/glassfish/jersey/examples/managedbeans/ManagedBeanWebAppTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedbeans;
+
+import java.net.URI;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.examples.managedbeans.resources.MyApplication;
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Main test for the Managed Beans web application.
+ * The application must be deployed and running on a standalone GlassFish container.
+ * To run the tests then, you just launch the following command:
+ * <pre>
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Naresh Srinivas Bhimisetty
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class ManagedBeanWebAppTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("managed-beans-webapp").path("app").build();
+    }
+
+    /**
+     * Test that provided query parameter makes it back.
+     */
+    @Test
+    public void testPerRequestResource() {
+        WebTarget perRequest = target().path("managedbean/per-request");
+
+        String responseMsg = perRequest.queryParam("x", "X").request().get(String.class);
+        assertThat(responseMsg, containsString("X"));
+        assertThat(responseMsg, startsWith("INTERCEPTED"));
+
+        responseMsg = perRequest.queryParam("x", "hi there").request().get(String.class);
+        assertThat(responseMsg, containsString("hi there"));
+        assertThat(responseMsg, startsWith("INTERCEPTED"));
+    }
+
+    /**
+     * Test that singleton counter gets incremented with each call and can be reset.
+     */
+    @Test
+    public void testSingletonResource() {
+        WebTarget singleton = target().path("managedbean/singleton");
+
+        String responseMsg = singleton.request().get(String.class);
+        assertThat(responseMsg, containsString("3"));
+
+        responseMsg = singleton.request().get(String.class);
+        assertThat(responseMsg, containsString("4"));
+
+        singleton.request().put(Entity.text("1"));
+
+        responseMsg = singleton.request().get(String.class);
+        assertThat(responseMsg, containsString("1"));
+
+        responseMsg = singleton.request().get(String.class);
+        assertThat(responseMsg, containsString("2"));
+    }
+
+    /**
+     * Test the JPA backend.
+     */
+    @Test
+    public void testWidget() {
+        WebTarget target = target().path("managedbean/singleton/widget");
+
+        final WebTarget widget = target.path("1");
+
+        assertThat(widget.request().get().getStatus(), is(404));
+
+        widget.request().put(Entity.text("One"));
+        assertThat(widget.request().get(String.class), is("One"));
+
+        widget.request().put(Entity.text("Two"));
+        assertThat(widget.request().get(String.class), is("Two"));
+
+        assertThat(widget.request().delete().getStatus(), is(204));
+
+        assertThat(widget.request().get().getStatus(), is(404));
+    }
+
+    /**
+     * Test exceptions are properly mapped.
+     */
+    @Test
+    public void testExceptionMapper() {
+
+        WebTarget singletonTarget = target().path("managedbean/singleton/exception");
+        WebTarget perRequestTarget = target().path("managedbean/per-request/exception");
+
+        _testExceptionOutput(singletonTarget, "singleton");
+        _testExceptionOutput(perRequestTarget, "per-request");
+    }
+
+    /**
+     * Test a non empty WADL is generated.
+     */
+    @Test
+    public void testApplicationWadl() {
+        WebTarget wadl = target().path("application.wadl");
+        String wadlDoc = wadl.request(MediaTypes.WADL_TYPE).get(String.class);
+
+        assertThat(wadlDoc.length(), is(not(0)));
+    }
+
+
+    private void _testExceptionOutput(WebTarget exceptionTarget, String thatShouldBePresentInResponseBody) {
+
+        Response exceptionResponse = exceptionTarget.request().get();
+        assertThat(exceptionResponse.getStatus(), is(500));
+
+        final String responseBody = exceptionResponse.readEntity(String.class);
+
+        assertThat(responseBody, containsString("ManagedBeanException"));
+        assertThat(responseBody, containsString(thatShouldBePresentInResponseBody));
+    }
+}
diff --git a/examples/managed-client-simple-webapp/README.MD b/examples/managed-client-simple-webapp/README.MD
new file mode 100644
index 0000000..1792137
--- /dev/null
+++ b/examples/managed-client-simple-webapp/README.MD
@@ -0,0 +1,43 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Managed Client Simple Example
+=============================
+
+Description
+-----------
+
+This sample shows usage of injected client managed by jersey container
+into a resource methods. The annotation org.glassfish.jersey.server.Uri
+is used to inject client for specified path. The jax-rs application
+contains standard resources and also resources which use injected client
+to query these standard resources. In other words the injected client
+invokes requests to the other resources from the same jax-rs
+application. Also there is a example of client requesting resource from
+outside of the application.
+
+Front page shows the table with resources exposed in deployed
+application with description. Simply just click the links to go to these
+resources.
+
+Key Features
+------------
+
+-   `@Uri`
+-   `@Path`, `@PathParam`
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean package jetty:run
+
+This deploys current example on the local host. You can then access
+frontpage at <http://localhost:8080/managed-client-simple-webapp/index.html>.
+This page contians description of all resources available in the application.
diff --git a/examples/managed-client-simple-webapp/pom.xml b/examples/managed-client-simple-webapp/pom.xml
new file mode 100644
index 0000000..2644e0b
--- /dev/null
+++ b/examples/managed-client-simple-webapp/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>managed-client-simple-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-managed-client-simple-webapp</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>maven-jetty-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/examples/managed-client-simple-webapp/src/main/java/org/glassfish/jersey/examples/managedclientsimple/resources/ClientResource.java b/examples/managed-client-simple-webapp/src/main/java/org/glassfish/jersey/examples/managedclientsimple/resources/ClientResource.java
new file mode 100644
index 0000000..daa752d
--- /dev/null
+++ b/examples/managed-client-simple-webapp/src/main/java/org/glassfish/jersey/examples/managedclientsimple/resources/ClientResource.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclientsimple.resources;
+
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.Uri;
+
+/**
+ * A resource which use managed client injected by {@link org.glassfish.jersey.server.Uri @Uri annotation} to query
+ * external resources and resource from {@link StandardResource}.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+@Path("client")
+public class ClientResource {
+
+    /**
+     * Make request to external web site using injected client. The response from the injected client is then
+     * returned as a response from this resource method.
+     *
+     * @param webTarget Injected web target.
+     * @return Response.
+     */
+    @GET
+    @Produces(MediaType.TEXT_HTML)
+    @Path("glassfish")
+    public Response glassfish(@Uri("http://glassfish.java.net/") WebTarget webTarget) {
+        final Response response = webTarget.request().get();
+        return Response.fromResponse(response).build();
+    }
+
+    /**
+     * Query {@link StandardResource} and return result based on the results from methods of the {@link StandardResource}.
+     *
+     * @param dogWebTarget Injected client.
+     * @param catWebTarget Injected client.
+     * @param elefantWebTarget Injected client.
+     * @return String entity.
+     */
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    @Path("animals")
+    public String animals(@Uri("resource/dog") WebTarget dogWebTarget,
+                          @Uri("resource/cat") WebTarget catWebTarget,
+                          @Uri("resource/elefant") WebTarget elefantWebTarget) {
+
+        final String dog = dogWebTarget.request().get(String.class);
+        final String cat = catWebTarget.request().get(String.class);
+        final String elefant = elefantWebTarget.request().get(String.class);
+        return "Queried animals: " + dog + " and " + cat + " and " + elefant;
+    }
+
+    /**
+     * Query {@link StandardResource} using a injected client. The client injection is using a template parameter {@code id}
+     * which is filled by JAX-RS implementation using a path parameter of this resource method.
+     *
+     * @param webTarget Injected client.
+     * @param id Path parameter.
+     * @return String entity.
+     */
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    @Path("car/{id}")
+    public String car(@Uri("resource/car/{id}") WebTarget webTarget, @PathParam("id") String id) {
+        final Response response = webTarget.request().get();
+        return "Response from resource/car/" + id + ": " + response.readEntity(String.class);
+    }
+}
diff --git a/examples/managed-client-simple-webapp/src/main/java/org/glassfish/jersey/examples/managedclientsimple/resources/ManagedClientApplication.java b/examples/managed-client-simple-webapp/src/main/java/org/glassfish/jersey/examples/managedclientsimple/resources/ManagedClientApplication.java
new file mode 100644
index 0000000..d008eed
--- /dev/null
+++ b/examples/managed-client-simple-webapp/src/main/java/org/glassfish/jersey/examples/managedclientsimple/resources/ManagedClientApplication.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclientsimple.resources;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * {@link ResourceConfig resource config} registering resource for managed client simple sample.
+ *
+ * @author Miroslav Fuksa
+ */
+@ApplicationPath("/app")
+public class ManagedClientApplication extends ResourceConfig {
+
+    public ManagedClientApplication() {
+        super(ClientResource.class, StandardResource.class);
+    }
+
+}
diff --git a/examples/managed-client-simple-webapp/src/main/java/org/glassfish/jersey/examples/managedclientsimple/resources/StandardResource.java b/examples/managed-client-simple-webapp/src/main/java/org/glassfish/jersey/examples/managedclientsimple/resources/StandardResource.java
new file mode 100644
index 0000000..57c4ee7
--- /dev/null
+++ b/examples/managed-client-simple-webapp/src/main/java/org/glassfish/jersey/examples/managedclientsimple/resources/StandardResource.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclientsimple.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+
+/**
+ * Standard example resource exposing  GET methods. This resource can be accessed directly by GET and is also
+ * accessed from {@link ClientResource} using injected client.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+@Path("resource")
+public class StandardResource {
+    @GET
+    @Path("dog")
+    public String get() {
+        return "Max";
+    }
+
+    @GET
+    @Path("cat")
+    public String cat() {
+        return "Lucy";
+    }
+
+    @GET
+    @Path("elefant")
+    public String elefant() {
+        return "Bobo";
+    }
+
+    /**
+     * Returns resource based on the id which is passed as path parameter. For purpose of this sample the method
+     * just use the path parameter id to construct a string which is returned.
+     *
+     * @param id Injected path parameter.
+     * @return Resource constructed from given id.
+     */
+    @GET
+    @Path("car/{id}")
+    public String car(@PathParam("id") String id) {
+        return "CAR with id=" + id;
+    }
+}
diff --git a/examples/managed-client-simple-webapp/src/main/webapp/WEB-INF/web.xml b/examples/managed-client-simple-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..1c36ab2
--- /dev/null
+++ b/examples/managed-client-simple-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>managedclientsimple.resources.ManagedClientApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.examples.managedclientsimple.resources.ManagedClientApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>managedclientsimple.resources.ManagedClientApplication</servlet-name>
+        <url-pattern>/app/*</url-pattern>
+    </servlet-mapping>
+
+    <welcome-file-list>
+	<welcome-file>
+            index.html
+        </welcome-file>
+    </welcome-file-list>
+</web-app>
diff --git a/examples/managed-client-simple-webapp/src/main/webapp/index.html b/examples/managed-client-simple-webapp/src/main/webapp/index.html
new file mode 100644
index 0000000..beb6acc
--- /dev/null
+++ b/examples/managed-client-simple-webapp/src/main/webapp/index.html
@@ -0,0 +1,97 @@
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+    <title>Managed client sample</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=MacRoman">
+
+    <style>
+        table, tr, th, td {
+            border: 1px solid;
+            border-collapse: collapse;
+            padding: 2px;
+        }
+    </style>
+
+</head>
+
+<body>
+    <h1>Managed client simple sample</h1>
+    <p>This sample shows using client injected by @Uri into the resource methods. The following table contains description
+    of all accessible resource.</p>
+    <table>
+        <tr>
+            <td>uri</td>
+            <td>http method</td>
+            <td>description</td>
+        </tr>
+        <tr>
+            <td>/index.html</td>
+            <td>GET</td>
+            <td>this page</td>
+        </tr>
+        <tr>
+            <td>/app/*</td>
+            <td></td>
+            <td>rest application is deployed under this path</td>
+        </tr>
+        <tr>
+            <td>/app/resource/*</td>
+            <td></td>
+            <td>standard sample resource with sub resource methods</td>
+        </tr>
+        <tr>
+            <td><a href="./app/resource/dog">./app/resource/dog</a></td>
+            <td>GET</td>
+            <td>resource (plain/text)</td>
+        </tr>
+        <tr>
+            <td><a href="./app/resource/cat">./app/resource/cat</a></td>
+            <td>GET</td>
+            <td>resource (plain/text)</td>
+        </tr>
+        <tr>
+            <td><a href="./app/resource/elefant">./app/resource/elefant</a></td>
+            <td>GET</td>
+            <td>resource (plain/text)</td>
+        </tr>
+        <tr>
+            <td><a href="./app/resource/car/80">./app/resource/car/{id}</a></td>
+            <td>GET</td>
+            <td>resource with path param (plain/text). {id} is a string path parameter. Example: ./app/resource/car/80</td>
+        </tr>
+
+        <tr>
+            <td>/app/client/*</td>
+            <td></td>
+            <td>Resource with sub resources that use injected clients to query sub resources of <b>/app/resource/*</b> and other resources</td>
+        </tr>
+        <tr>
+            <td><a href="./app/client/glassfish">./app/client/glassfish</a></td>
+            <td>GET</td>
+            <td>Queries by GET <a href="http://glassfish.java.net">http://glassfish.java.net</a> and returns target html page.</td>
+        </tr>
+        <tr>
+            <td><a href="./app/client/animals">./app/client/animals</a></td>
+            <td>GET</td>
+            <td>Queries <a href="./app/resource/dog">./app/resource/dog</a>, <a href="./app/resource/cat">./app/resource/cat</a> and <a href="./app/resource/elefant">./app/resource/elefant</a> and returns merged result</td>
+        </tr>
+        <tr>
+        <td><a href="./app/client/car/15">./app/client/car/{id}</a></td>
+            <td>GET</td>
+            <td>Queries /app/resource/car/{id} and returns result. {id} is a string path parameter. Example: ./app/resource/client/car/15</td>
+        </tr>
+    </table>
+</body>
+</html>
diff --git a/examples/managed-client-simple-webapp/src/test/java/org/glassfish/jersey/examples/managedclientsimple/ManagedClientSimpleTest.java b/examples/managed-client-simple-webapp/src/test/java/org/glassfish/jersey/examples/managedclientsimple/ManagedClientSimpleTest.java
new file mode 100644
index 0000000..8610f89
--- /dev/null
+++ b/examples/managed-client-simple-webapp/src/test/java/org/glassfish/jersey/examples/managedclientsimple/ManagedClientSimpleTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclientsimple;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import java.net.URI;
+
+import org.glassfish.jersey.examples.managedclientsimple.resources.ManagedClientApplication;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Miroslav Fuksa
+ *
+ */
+public class ManagedClientSimpleTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ManagedClientApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        final UriBuilder baseUriBuilder = UriBuilder.fromUri(super.getBaseUri()).path("managed-client-simple-webapp");
+        final boolean externalFactoryInUse = getTestContainerFactory() instanceof ExternalTestContainerFactory;
+        return externalFactoryInUse ? baseUriBuilder.path("app").build() : baseUriBuilder.build();
+    }
+
+    @Test
+    public void testManagedClientSimple() throws Exception {
+        final WebTarget resource = target().path("client");
+        Response response;
+
+        response = resource.path("animals").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+        assertEquals("Queried animals: Max and Lucy and Bobo", response.readEntity(String.class));
+
+        response = resource.path("car/1").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+        assertEquals("Response from resource/car/1: CAR with id=1", response.readEntity(String.class));
+    }
+
+}
+
diff --git a/examples/managed-client-webapp/README.MD b/examples/managed-client-webapp/README.MD
new file mode 100644
index 0000000..4937d1b
--- /dev/null
+++ b/examples/managed-client-webapp/README.MD
@@ -0,0 +1,81 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Managed Client Example
+======================
+
+This example demonstrates a simple usage of Jersey Managed Client
+feature.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path            | Resource class     | HTTP methods
+------------------- | ------------------ | --------------
+**_/public/a_**     | PublicResource     | GET
+**_/public/b_**     | PublicResource     | GET
+**_/internal/a_**   | InternalResource   | GET
+**_/internal/b_**   | InternalResource   | GET
+
+In the example, the requests to a *public resource* deployed on
+`/public/` path are forwarded to an *internal resource* (deployed on
+`/internal/` path) using injected
+[javax.ws.rs.client.WebTarget](https://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/client/WebTarget.html)
+instances produced using 2 separate managed clients each of the managed
+clients using it's own custom configuration.
+
+An access to internal resource methods is guarded by a container request
+filter (`CustomHeaderFilter`) which rejects any request that does not
+contain expected custom header set to an expected value. In the example,
+the 2 managed clients (used by a public resource to access the methods
+on the internal resource) are configured with custom configurations,
+each containing a registration of a custom client request filter that is
+instructed to append a required custom header and value to every
+outgoing request. Only with managed client support working properly, the
+public resource is able to successfully retrieve data from the internal
+resource.
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean package jetty:run
+
+This deploys current example on the local host. You can then access WADL
+description of the deployed application at
+<http://localhost:8080/managed-client-webapp/application.wadl>.
+
+You can access public resource of this application using curl:
+
+>     curl -v -H "Accept: text/plain" http://localhost:8080/managed-client-webapp/public/a
+
+>     curl -v -H "Accept: text/plain" http://localhost:8080/managed-client-webapp/public/b
+
+In this example you should see the returned response message body
+contains "a" or "b" respectively upon successful invocation.
+
+You may also verify that access to internal resource is not possible
+without including a proper header in the request. First try to access
+the internal resource without any custom header:
+
+>     curl -v -H "Accept: text/plain" http://localhost:8080/managed-client-webapp/internal/a
+
+>     curl -v -H "Accept: text/plain" http://localhost:8080/managed-client-webapp/internal/b
+
+In both cases a `HTTP 403 Forbidden.` response is returned. Now lets try
+to access the resource once again, but this time we'll include also the
+expected custom headers:
+
+>     curl -v -H "Accept: text/plain" -H "custom-header:a" http://localhost:8080/managed-client-webapp/internal/a
+
+>     curl -v -H "Accept: text/plain" -H "custom-header:b" http://localhost:8080/managed-client-webapp/internal/b
+
+Finally, you should see the invocation succeeded and the returned response message body contains "a" or "b" respectively.
\ No newline at end of file
diff --git a/examples/managed-client-webapp/pom.xml b/examples/managed-client-webapp/pom.xml
new file mode 100644
index 0000000..9f9287f
--- /dev/null
+++ b/examples/managed-client-webapp/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>managed-client-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-managed-client-webapp</name>
+
+    <description>Jersey managed client web application example.</description>
+
+    <dependencies>
+        <!-- uncomment to use Grizzly client -->
+        <!--dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+            <scope>test</scope>
+        </dependency-->
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>maven-jetty-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/ClientA.java b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/ClientA.java
new file mode 100644
index 0000000..48fda2b
--- /dev/null
+++ b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/ClientA.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.server.ClientBinding;
+
+/**
+ * Managed client configuration for client A.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@ClientBinding(configClass = MyApplication.MyClientAConfig.class)
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.PARAMETER})
+public @interface ClientA {
+}
diff --git a/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/ClientB.java b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/ClientB.java
new file mode 100644
index 0000000..f78229f
--- /dev/null
+++ b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/ClientB.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.server.ClientBinding;
+
+/**
+ * Managed client configuration for client B.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@ClientBinding(configClass = MyApplication.MyClientBConfig.class)
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.PARAMETER})
+public @interface ClientB {
+}
diff --git a/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/CustomHeaderFeature.java b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/CustomHeaderFeature.java
new file mode 100644
index 0000000..1b73af6
--- /dev/null
+++ b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/CustomHeaderFeature.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.FeatureContext;
+
+/**
+ * Dynamic feature that appends a properly configured {@link CustomHeaderFilter} instance
+ * to every method that is annotated with {@link org.glassfish.jersey.examples.managedclient.CustomHeaderFeature.Require &#64;Require} internal feature
+ * annotation.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class CustomHeaderFeature implements DynamicFeature {
+
+    /**
+     * A method annotation to be placed on those resource methods to which a validating
+     * {@link CustomHeaderFilter} instance should be added.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Documented
+    @Target(ElementType.METHOD)
+    public static @interface Require {
+        /**
+         * Expected custom header name to be validated by the {@link CustomHeaderFilter}.
+         */
+        public String headerName();
+
+        /**
+         * Expected custom header value to be validated by the {@link CustomHeaderFilter}.
+         */
+        public String headerValue();
+    }
+
+    @Override
+    public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+        final Require va = resourceInfo.getResourceMethod().getAnnotation(Require.class);
+        if (va != null) {
+            context.register(new CustomHeaderFilter(va.headerName(), va.headerValue()));
+        }
+    }
+}
diff --git a/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/CustomHeaderFilter.java b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/CustomHeaderFilter.java
new file mode 100644
index 0000000..a10414b
--- /dev/null
+++ b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/CustomHeaderFilter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import java.io.IOException;
+
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * A filter for appending and validating custom headers.
+ * <p>
+ * On the client side, appends a new custom request header with a configured name and value to each outgoing request.
+ * </p>
+ * <p>
+ * On the server side, validates that each request has a custom header with a configured name and value.
+ * If the validation fails a HTTP 403 response is returned.
+ * </p>
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class CustomHeaderFilter implements ContainerRequestFilter, ClientRequestFilter {
+    private final String headerName;
+    private final String headerValue;
+
+    public CustomHeaderFilter(String headerName, String headerValue) {
+        if (headerName == null || headerValue == null) {
+            throw new IllegalArgumentException("Header name and value must not be null.");
+        }
+        this.headerName = headerName;
+        this.headerValue = headerValue;
+    }
+
+    @Override
+    public void filter(ContainerRequestContext ctx) throws IOException { // validate
+        if (!headerValue.equals(ctx.getHeaderString(headerName))) {
+            ctx.abortWith(Response.status(Response.Status.FORBIDDEN)
+                    .type(MediaType.TEXT_PLAIN)
+                    .entity(String.format("Expected header '%s' not present or value not equal to '%s'", headerName, headerValue))
+                    .build());
+        }
+    }
+
+    @Override
+    public void filter(ClientRequestContext ctx) throws IOException { // append
+        ctx.getHeaders().putSingle(headerName, headerValue);
+    }
+}
diff --git a/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/InternalResource.java b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/InternalResource.java
new file mode 100644
index 0000000..f715129
--- /dev/null
+++ b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/InternalResource.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * Internal resource accessed from the managed client resource.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("internal")
+public class InternalResource {
+
+    @GET
+    @Path("a")
+    @CustomHeaderFeature.Require(headerName = "custom-header", headerValue = "a")
+    public String getA() {
+        return "a";
+    }
+
+    @GET
+    @Path("b")
+    @CustomHeaderFeature.Require(headerName = "custom-header", headerValue = "b")
+    public String getB() {
+        return "b";
+    }
+}
diff --git a/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/MyApplication.java b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/MyApplication.java
new file mode 100644
index 0000000..1c8ccc6
--- /dev/null
+++ b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/MyApplication.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Jersey managed client example application.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class MyApplication extends ResourceConfig {
+
+    /**
+     * Create JAX-RS application for the example.
+     */
+    public MyApplication() {
+        super(PublicResource.class, InternalResource.class, CustomHeaderFeature.class);
+    }
+
+    public static class MyClientAConfig extends ClientConfig {
+        public MyClientAConfig() {
+            this.register(new CustomHeaderFilter("custom-header", "a"));
+        }
+    }
+
+    public static class MyClientBConfig extends ClientConfig {
+        public MyClientBConfig() {
+            this.register(new CustomHeaderFilter("custom-header", "b"));
+        }
+    }
+}
diff --git a/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/PublicResource.java b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/PublicResource.java
new file mode 100644
index 0000000..17454bc
--- /dev/null
+++ b/examples/managed-client-webapp/src/main/java/org/glassfish/jersey/examples/managedclient/PublicResource.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.Uri;
+
+/**
+ * A resource that uses managed clients to retrieve values of internal
+ * resources 'A' and 'B', which are protected by a {@link CustomHeaderFilter}
+ * and require a specific custom header in a request to be set to a specific value.
+ * <p>
+ * Properly configured managed clients have a {@code CustomHeaderFilter} instance
+ * configured to insert the {@link CustomHeaderFeature.Require required} custom header
+ * with a proper value into the outgoing client requests.
+ * </p>
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("public")
+public class PublicResource {
+
+    @Uri("a") @ClientA // resolves to <base>/internal/a
+    private WebTarget targetA;
+
+    @GET
+    @Produces("text/plain")
+    @Path("a")
+    public String getTargetA() {
+        return targetA.request(MediaType.TEXT_PLAIN).get(String.class);
+    }
+
+    @GET
+    @Produces("text/plain")
+    @Path("b")
+    public Response getTargetB(@Uri("internal/b") @ClientB WebTarget targetB) {
+        return targetB.request(MediaType.TEXT_PLAIN).get();
+    }
+}
diff --git a/examples/managed-client-webapp/src/main/webapp/WEB-INF/web.xml b/examples/managed-client-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..dcc06b8
--- /dev/null
+++ b/examples/managed-client-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.examples.managedclient.MyApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.examples.managedclient.MyApplication</param-value>
+        </init-param>
+        <init-param>
+            <param-name>org.glassfish.jersey.examples.managedclient.ClientA.baseUri</param-name>
+            <param-value>http://localhost:8080/managed-client-webapp/internal</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.examples.managedclient.MyApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/examples/managed-client-webapp/src/test/java/org/glassfish/jersey/examples/managedclient/ManagedClientTest.java b/examples/managed-client-webapp/src/test/java/org/glassfish/jersey/examples/managedclient/ManagedClientTest.java
new file mode 100644
index 0000000..adc3acd
--- /dev/null
+++ b/examples/managed-client-webapp/src/test/java/org/glassfish/jersey/examples/managedclient/ManagedClientTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import java.net.URI;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Jersey managed client example tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ManagedClientTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory
+        enable(TestProperties.LOG_TRAFFIC);
+        // enable(TestProperties.DUMP_ENTITY);
+
+        final MyApplication app = new MyApplication();
+        // overriding ClientA base Uri property for test purposes
+        app.property(ClientA.class.getName() + ".baseUri", UriBuilder.fromUri(getBaseUri()).path("internal").build());
+        return app;
+    }
+
+    @Override
+    protected URI getBaseUri() {
+       return UriBuilder.fromUri(super.getBaseUri()).path("managed-client-webapp").build();
+    }
+
+//    Uncomment to use Grizzly async client
+//    @Override
+//    protected void configureClient(ClientConfig clientConfig) {
+//        clientConfig.connector(new GrizzlyConnector(clientConfig));
+//    }
+
+    /**
+     * Test that a connection via managed clients works properly.
+     *
+     * @throws Exception in case of test failure.
+     */
+    @Test
+    public void testManagedClient() throws Exception {
+        final WebTarget resource = target().path("public").path("{name}");
+        Response response;
+
+        response = resource.resolveTemplate("name", "a").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+        assertEquals("a", response.readEntity(String.class));
+
+        response = resource.resolveTemplate("name", "b").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+        assertEquals("b", response.readEntity(String.class));
+    }
+}
diff --git a/examples/managed-client/README.MD b/examples/managed-client/README.MD
new file mode 100644
index 0000000..249eeb9
--- /dev/null
+++ b/examples/managed-client/README.MD
@@ -0,0 +1,81 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Managed Client Example
+======================
+
+This example demonstrates a simple usage of Jersey Managed Client
+feature.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path            | Resource class     | HTTP methods
+------------------- | ------------------ | --------------
+**_/public/a_**     | PublicResource     | GET
+**_/public/b_**     | PublicResource     | GET
+**_/internal/a_**   | InternalResource   | GET
+**_/internal/b_**   | InternalResource   | GET
+
+In the example, the requests to a *public resource* deployed on
+`/public/` path are forwarded to an *internal resource* (deployed on
+`/internal/` path) using injected
+[javax.ws.rs.client.WebTarget](https://jax-rs-spec.java.net/nonav/2.0/apidocs/javax/ws/rs/client/WebTarget.html)
+instances produced using 2 separate managed clients each of the managed
+clients using it's own custom configuration.
+
+An access to internal resource methods is guarded by a container request
+filter (`CustomHeaderFilter`) which rejects any request that does not
+contain expected custom header set to an expected value. In the example,
+the 2 managed clients (used by a public resource to access the methods
+on the internal resource) are configured with custom configurations,
+each containing a registration of a custom client request filter that is
+instructed to append a required custom header and value to every
+outgoing request. Only with managed client support working properly, the
+public resource is able to successfully retrieve data from the internal
+resource.
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean package exec:java
+
+This deploys current example on the local host. You can then access WADL
+description of the deployed application at
+<http://localhost:8080/managed-client-webapp/application.wadl>.
+
+You can access public resource of this application using curl:
+
+>     curl -v -H "Accept: text/plain" http://localhost:8080/managed-client-webapp/public/a
+
+>     curl -v -H "Accept: text/plain" http://localhost:8080/managed-client-webapp/public/b
+
+In this example you should see the returned response message body
+contains "a" or "b" respectively upon successful invocation.
+
+You may also verify that access to internal resource is not possible
+without including a proper header in the request. First try to access
+the internal resource without any custom header:
+
+>     curl -v -H "Accept: text/plain" http://localhost:8080/managed-client-webapp/internal/a
+
+>     curl -v -H "Accept: text/plain" http://localhost:8080/managed-client-webapp/internal/b
+
+In both cases a `HTTP 403 Forbidden.` response is returned. Now lets try
+to access the resource once again, but this time we'll include also the
+expected custom headers:
+
+>     curl -v -H "Accept: text/plain" -H "custom-header:a" http://localhost:8080/managed-client-webapp/internal/a
+
+>     curl -v -H "Accept: text/plain" -H "custom-header:b" http://localhost:8080/managed-client-webapp/internal/b
+
+Finally, you should see the invocation succeeded and the returned response message body contains "a" or "b" respectively.
diff --git a/examples/managed-client/pom.xml b/examples/managed-client/pom.xml
new file mode 100644
index 0000000..ad922d4
--- /dev/null
+++ b/examples/managed-client/pom.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>managed-client</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-managed-client</name>
+
+    <description>Jersey managed client example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <!-- uncomment to use Grizzly client -->
+        <!--dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency-->
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.managedclient.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/App.java b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/App.java
new file mode 100644
index 0000000..dc23e6d
--- /dev/null
+++ b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/App.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Jersey programmatic managed client example application.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/managedclient/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Managed Client\" Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, create(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.\nTry out public endpoints:\n  %s%s\n  %s%s\n"
+                            + "Stop the application using CTRL+C",
+                    BASE_URI, "public/a",
+                    BASE_URI, "public/b"));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+
+    /**
+     * Create JAX-RS application for the example.
+     *
+     * @return create JAX-RS application for the example.
+     */
+    public static ResourceConfig create() {
+        return new ResourceConfig(PublicResource.class, InternalResource.class, CustomHeaderFeature.class)
+                .property(ClientA.class.getName() + ".baseUri", BASE_URI.toString() + "internal");
+    }
+
+    public static class MyClientAConfig extends ClientConfig {
+
+        public MyClientAConfig() {
+            this.register(new CustomHeaderFilter("custom-header", "a"));
+        }
+    }
+
+    public static class MyClientBConfig extends ClientConfig {
+
+        public MyClientBConfig() {
+            this.register(new CustomHeaderFilter("custom-header", "b"));
+        }
+    }
+}
diff --git a/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/ClientA.java b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/ClientA.java
new file mode 100644
index 0000000..c67dd1e
--- /dev/null
+++ b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/ClientA.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.server.ClientBinding;
+
+/**
+ * Managed client configuration for client A.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@ClientBinding(configClass = App.MyClientAConfig.class)
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.PARAMETER})
+public @interface ClientA {
+}
diff --git a/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/ClientB.java b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/ClientB.java
new file mode 100644
index 0000000..3813df9
--- /dev/null
+++ b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/ClientB.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.server.ClientBinding;
+
+/**
+ * Managed client configuration for client B.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@ClientBinding(configClass = App.MyClientBConfig.class)
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.PARAMETER})
+public @interface ClientB {
+}
diff --git a/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/CustomHeaderFeature.java b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/CustomHeaderFeature.java
new file mode 100644
index 0000000..7c249a1
--- /dev/null
+++ b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/CustomHeaderFeature.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.FeatureContext;
+
+/**
+ *Dynamic feature that appends a properly configured {@link CustomHeaderFilter} instance
+ * to every method that is annotated with {@link Require &#64;Require} internal feature
+ * annotation.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class CustomHeaderFeature implements DynamicFeature {
+
+    /**
+     * A method annotation to be placed on those resource methods to which a validating
+     * {@link CustomHeaderFilter} instance should be added.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Documented
+    @Target(ElementType.METHOD)
+    public static @interface Require {
+        /**
+         * Expected custom header name to be validated by the {@link CustomHeaderFilter}.
+         */
+        public String headerName();
+
+        /**
+         * Expected custom header value to be validated by the {@link CustomHeaderFilter}.
+         */
+        public String headerValue();
+    }
+
+    @Override
+    public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+        final Require va = resourceInfo.getResourceMethod().getAnnotation(Require.class);
+        if (va != null) {
+            context.register(new CustomHeaderFilter(va.headerName(), va.headerValue()));
+        }
+    }
+}
diff --git a/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/CustomHeaderFilter.java b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/CustomHeaderFilter.java
new file mode 100644
index 0000000..a10414b
--- /dev/null
+++ b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/CustomHeaderFilter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import java.io.IOException;
+
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * A filter for appending and validating custom headers.
+ * <p>
+ * On the client side, appends a new custom request header with a configured name and value to each outgoing request.
+ * </p>
+ * <p>
+ * On the server side, validates that each request has a custom header with a configured name and value.
+ * If the validation fails a HTTP 403 response is returned.
+ * </p>
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class CustomHeaderFilter implements ContainerRequestFilter, ClientRequestFilter {
+    private final String headerName;
+    private final String headerValue;
+
+    public CustomHeaderFilter(String headerName, String headerValue) {
+        if (headerName == null || headerValue == null) {
+            throw new IllegalArgumentException("Header name and value must not be null.");
+        }
+        this.headerName = headerName;
+        this.headerValue = headerValue;
+    }
+
+    @Override
+    public void filter(ContainerRequestContext ctx) throws IOException { // validate
+        if (!headerValue.equals(ctx.getHeaderString(headerName))) {
+            ctx.abortWith(Response.status(Response.Status.FORBIDDEN)
+                    .type(MediaType.TEXT_PLAIN)
+                    .entity(String.format("Expected header '%s' not present or value not equal to '%s'", headerName, headerValue))
+                    .build());
+        }
+    }
+
+    @Override
+    public void filter(ClientRequestContext ctx) throws IOException { // append
+        ctx.getHeaders().putSingle(headerName, headerValue);
+    }
+}
diff --git a/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/InternalResource.java b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/InternalResource.java
new file mode 100644
index 0000000..f715129
--- /dev/null
+++ b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/InternalResource.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * Internal resource accessed from the managed client resource.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("internal")
+public class InternalResource {
+
+    @GET
+    @Path("a")
+    @CustomHeaderFeature.Require(headerName = "custom-header", headerValue = "a")
+    public String getA() {
+        return "a";
+    }
+
+    @GET
+    @Path("b")
+    @CustomHeaderFeature.Require(headerName = "custom-header", headerValue = "b")
+    public String getB() {
+        return "b";
+    }
+}
diff --git a/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/PublicResource.java b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/PublicResource.java
new file mode 100644
index 0000000..3340c16
--- /dev/null
+++ b/examples/managed-client/src/main/java/org/glassfish/jersey/examples/managedclient/PublicResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.Uri;
+
+/**
+ * A resource that uses managed clients to retrieve values of internal
+ * resources 'A' and 'B', which are protected by a {@link CustomHeaderFilter}
+ * and require a specific custom header in a request to be set to a specific value.
+ * <p>
+ * Properly configured managed clients have a {@code CustomHeaderFilter} instance
+ * configured to insert the {@link CustomHeaderFeature.Require required} custom header
+ * with a proper value into the outgoing client requests.
+ * </p>
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("public")
+public class PublicResource {
+    @Uri("a") @ClientA // resolves to <base>/internal/a
+    private WebTarget targetA;
+
+    @GET
+    @Produces("text/plain")
+    @Path("a")
+    public String getTargetA() {
+        return targetA.request(MediaType.TEXT_PLAIN).get(String.class);
+    }
+
+    @GET
+    @Produces("text/plain")
+    @Path("b")
+    public Response getTargetB(@Uri("internal/b") @ClientB WebTarget targetB) {
+        return targetB.request(MediaType.TEXT_PLAIN).get();
+    }
+}
diff --git a/examples/managed-client/src/test/java/org/glassfish/jersey/examples/managedclient/ManagedClientTest.java b/examples/managed-client/src/test/java/org/glassfish/jersey/examples/managedclient/ManagedClientTest.java
new file mode 100644
index 0000000..566aedd
--- /dev/null
+++ b/examples/managed-client/src/test/java/org/glassfish/jersey/examples/managedclient/ManagedClientTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.managedclient;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Jersey managed client example tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ManagedClientTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory
+        enable(TestProperties.LOG_TRAFFIC);
+        // enable(TestProperties.DUMP_ENTITY);
+
+        // overriding ClientA base Uri property for test purposes
+        return App.create().property(ClientA.class.getName() + ".baseUri", this.getBaseUri().toString() + "internal");
+    }
+
+//    Uncomment to use Grizzly async client
+//    @Override
+//    protected void configureClient(ClientConfig clientConfig) {
+//        clientConfig.connector(new GrizzlyConnector(clientConfig));
+//    }
+
+    /**
+     * Test that a connection via managed clients works properly.
+     *
+     * @throws Exception in case of test failure.
+     */
+    @Test
+    public void testManagedClient() throws Exception {
+        final WebTarget resource = target().path("public").path("{name}");
+        Response response;
+
+        response = resource.resolveTemplate("name", "a").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+        assertEquals("a", response.readEntity(String.class));
+
+        response = resource.resolveTemplate("name", "b").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+        assertEquals("b", response.readEntity(String.class));
+    }
+}
diff --git a/examples/multipart-webapp/README.MD b/examples/multipart-webapp/README.MD
new file mode 100644
index 0000000..656c05b
--- /dev/null
+++ b/examples/multipart-webapp/README.MD
@@ -0,0 +1,35 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Multipart Web app Example
+=========================
+
+This example demonstrates how to develop RESTful web service with
+demonstrating JAX-RS Integration with MIME MultiPart Message Formats and
+an EE 6 compliant Web container.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                     | Description                                    | Sample request using curl
+---------------------------- | ---------------------------------------------- | -----------------------------------------------------------------------------------------------
+**_/form/part_**             | POST message returning entire string           | `curl -X POST -F "part=part1"  http://localhost:8080/multipart-webapp/form/part`
+**_/form/part-file-name_**   | POST message returning part filename string.   | Be sure to execute this curl from project directory where pom.xml resides
+                             |                                                | `curl -X POST -F "part=@pom.xml"  http://localhost:8080/multipart-webapp/form/part-file-name`
+**_/form/xml-jaxb-part_**    | POST message returning xml jaxb part string.   | No curl sample available, please check test sources.
+
+Running the Example
+-------------------
+
+You can run the example using Jetty as follows:
+
+> `mvn clean package jetty:run`
+
+Following steps are using [cURL](http://curl.haxx.se/) command line tool:
\ No newline at end of file
diff --git a/examples/multipart-webapp/pom.xml b/examples/multipart-webapp/pom.xml
new file mode 100644
index 0000000..64a2057
--- /dev/null
+++ b/examples/multipart-webapp/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>multipart-webapp</artifactId>
+    <name>jersey-examples-multipart-webapp</name>
+    <packaging>war</packaging>
+
+    <description>Jersey Multipart example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>maven-jetty-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/Bean.java b/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/Bean.java
new file mode 100644
index 0000000..dc59cd5
--- /dev/null
+++ b/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/Bean.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.multipart.webapp;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class Bean {
+
+    public String value;
+
+    public Bean() {
+    }
+
+    public Bean(String str) {
+        value = str;
+    }
+
+}
diff --git a/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartFieldInjectedResource.java b/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartFieldInjectedResource.java
new file mode 100644
index 0000000..cddba3f
--- /dev/null
+++ b/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartFieldInjectedResource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.multipart.webapp;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.glassfish.jersey.media.multipart.FormDataParam;
+
+/**
+ * Field-injected version of the {@link FormDataParam} injection testing resource.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("/form-field-injected")
+public class MultiPartFieldInjectedResource {
+    @FormDataParam("string") private String s;
+    @FormDataParam("string") private FormDataContentDisposition sd;
+    @FormDataParam("bean") private Bean b;
+    @FormDataParam("bean") private FormDataContentDisposition bd;
+
+    @POST
+    @Path("xml-jaxb-part")
+    @Consumes(MediaType.MULTIPART_FORM_DATA)
+    public String post() {
+        return s + ":" + sd.getFileName() + "," + b.value + ":" + bd.getFileName();
+    }
+}
diff --git a/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartResource.java b/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartResource.java
new file mode 100644
index 0000000..5c73956
--- /dev/null
+++ b/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartResource.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.multipart.webapp;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.media.multipart.FormDataParam;
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("/form")
+public class MultiPartResource {
+
+    @POST
+    @Path("part")
+    @Consumes(MediaType.MULTIPART_FORM_DATA)
+    public String post(@FormDataParam("part") String s) {
+        return s;
+    }
+
+    @POST
+    @Path("part-file-name")
+    @Consumes(MediaType.MULTIPART_FORM_DATA)
+    public String post(
+            @FormDataParam("part") String s,
+            @FormDataParam("part") FormDataContentDisposition d) {
+        return s + ":" + d.getFileName();
+    }
+
+    @POST
+    @Path("xml-jaxb-part")
+    @Consumes(MediaType.MULTIPART_FORM_DATA)
+    public String post(
+            @FormDataParam("string") String s,
+            @FormDataParam("string") FormDataContentDisposition sd,
+            @FormDataParam("bean") Bean b,
+            @FormDataParam("bean") FormDataContentDisposition bd) {
+        return s + ":" + sd.getFileName() + "," + b.value + ":" + bd.getFileName();
+    }
+}
diff --git a/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MyApplication.java b/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MyApplication.java
new file mode 100644
index 0000000..120de05
--- /dev/null
+++ b/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MyApplication.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.multipart.webapp;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * @author Michal Gajdos
+ */
+@ApplicationPath("/")
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        super(MultiPartResource.class, MultiPartFieldInjectedResource.class, MultiPartFeature.class);
+    }
+}
diff --git a/examples/multipart-webapp/src/main/webapp/WEB-INF/web.xml b/examples/multipart-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..f601eea
--- /dev/null
+++ b/examples/multipart-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="2.5"
+        xmlns="http://java.sun.com/xml/ns/javaee"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <servlet>
+        <servlet-name>org.glassfish.jersey.examples.multipart.resources.MyApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.examples.multipart.webapp.MyApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.examples.multipart.resources.MyApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
diff --git a/examples/multipart-webapp/src/test/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartWebAppTest.java b/examples/multipart-webapp/src/test/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartWebAppTest.java
new file mode 100644
index 0000000..8407396
--- /dev/null
+++ b/examples/multipart-webapp/src/test/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartWebAppTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.multipart.webapp;
+
+import java.io.File;
+import java.net.URI;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.util.SaxHelper;
+import org.glassfish.jersey.internal.util.SimpleNamespaceResolver;
+import org.glassfish.jersey.media.multipart.FormDataBodyPart;
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import org.w3c.dom.Document;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for {@code MultipartResource} class.
+ *
+ * @author Naresh (srinivas.bhimisetty at oracle.com)
+ * @author Michal Gajdos
+ */
+public class MultiPartWebAppTest extends JerseyTest {
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("multipart-webapp").build();
+    }
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(MultiPartFeature.class);
+    }
+
+    @Test
+    public void testApplicationWadl() throws Exception {
+        final WebTarget target = target().path("application.wadl");
+
+        final Response response = target.request().get();
+        assertEquals(200, response.getStatus());
+        final File tmpFile = response.readEntity(File.class);
+
+        final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+        bf.setNamespaceAware(true);
+        bf.setValidating(false);
+
+        if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) {
+            bf.setXIncludeAware(false);
+        }
+
+        final DocumentBuilder b = bf.newDocumentBuilder();
+        final Document d = b.parse(tmpFile);
+
+        final XPath xp = XPathFactory.newInstance().newXPath();
+        xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+        String val = (String) xp.evaluate(
+                "//wadl:resource[@path='part']/wadl:method[@name='POST']/wadl:request/wadl:representation/@mediaType",
+                d, XPathConstants.STRING);
+
+        assertEquals("multipart/form-data", val);
+    }
+
+    @Test
+    public void testPart() {
+        final WebTarget target = target().path("form/part");
+
+        final FormDataMultiPart mp = new FormDataMultiPart();
+        final FormDataBodyPart p = new FormDataBodyPart(FormDataContentDisposition.name("part").build(), "CONTENT");
+        mp.bodyPart(p);
+
+        final String s = target.request().post(Entity.entity(mp, MediaType.MULTIPART_FORM_DATA_TYPE), String.class);
+        assertEquals("CONTENT", s);
+    }
+
+    @Test
+    public void testPartWithFileName() {
+        final WebTarget target = target().path("form/part-file-name");
+
+        final FormDataMultiPart mp = new FormDataMultiPart();
+        final FormDataBodyPart p = new FormDataBodyPart(FormDataContentDisposition.name("part").fileName("file").build(),
+                "CONTENT");
+        mp.bodyPart(p);
+
+        final String s = target.request().post(Entity.entity(mp, MediaType.MULTIPART_FORM_DATA_TYPE), String.class);
+        assertEquals("CONTENT:file", s);
+    }
+
+    @Test
+    public void testXmlJAXBPart() {
+        final WebTarget target = target().path("form/xml-jaxb-part");
+
+        final FormDataMultiPart mp = new FormDataMultiPart();
+        mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("bean").fileName("bean").build(),
+                new Bean("BEAN"),
+                MediaType.APPLICATION_XML_TYPE));
+        mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("string").fileName("string").build(),
+                "STRING"));
+
+        final String s = target.request().post(Entity.entity(mp, MediaType.MULTIPART_FORM_DATA_TYPE), String.class);
+        assertEquals("STRING:string,BEAN:bean", s);
+    }
+
+    @Test
+    public void testFieldInjectedXmlJAXBPart() {
+        final WebTarget target = target().path("form-field-injected/xml-jaxb-part");
+
+        final FormDataMultiPart mp = new FormDataMultiPart();
+        mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("bean").fileName("bean").build(),
+                new Bean("BEAN"),
+                MediaType.APPLICATION_XML_TYPE));
+        mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("string").fileName("string").build(),
+                "STRING"));
+
+        final String s = target.request().post(Entity.entity(mp, MediaType.MULTIPART_FORM_DATA_TYPE), String.class);
+        assertEquals("STRING:string,BEAN:bean", s);
+    }
+}
diff --git a/examples/oauth-client-twitter/README.MD b/examples/oauth-client-twitter/README.MD
new file mode 100644
index 0000000..ead17fc
--- /dev/null
+++ b/examples/oauth-client-twitter/README.MD
@@ -0,0 +1,80 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+OAuth 1 Client Sample - Twitter Client
+======================================
+
+This example demonstrates how to develop a simple web app client
+utilizing JAX-RS client API and Jersey OAuth support.
+
+Contents
+--------
+
+This example consists of the main class (`App`) and two JAXB beans used
+for unmarshaling the responses from Twitter (`Status` and `User`).
+
+Running the Example
+-------------------
+
+### Registering with Twitter
+
+Before running the example, you have to register your own application
+with Twitter (to obtain consumer key and consumer secret you have to use
+to configure this client). You can do it as follows:
+
+1.  Go to [Twitter Developers Page](http://dev.twitter.com/). You need
+    to sign in to the Twitter (if you are new to Twitter you need to
+    sign up). Then in the right upper corner you will see your profile
+    photo and drop down list in which you select "My Application". Click
+    on **Create a new application** button.
+
+2.  Fill out the "Create an application" form - you have to pick a
+    unique application name (e.g. app12345678), put in some description,
+    put in something for app website (e.g. http://jersey.java.net),
+    Accept terms by selecting **Yes, I agree** in "Twitter Content"
+    checkbox, enter captcha.
+
+3.  Click **Create your twitter application** button.
+
+4.  You will be presented with consumer key, consumer secret and other
+    details for your registered application.
+
+### Running for the First Time
+
+Enter the following on the command-line:
+
+>    mvn clean compile exec:java -DconsumerKey=<consumer key assigned to your app> -DconsumerSecret=<consumer secret assigned to your app>
+
+### Subsequent Runs
+
+After you run the application for the first time, it stores the consumer
+key, secret and token and token secret in a property file for future
+use. So, for subsequent runs you don't have to specify these as system
+properties. The following command is good enough for running the
+application after it was built, run for the first time and the property
+file was created:
+
+>    mvn exec:java
+
+Troubleshooting
+---------------
+
+### HTTP Proxy
+
+Behind a HTTP proxy, you will need to setup the following system
+properties to communicate with the Twitter server:
+
+-   http.proxyHost - the name of the proxy host.
+-   http.proxyPort - the port of the proxy host.
+-   http.proxyAuth - the user name and password to use when connecting
+    to the proxy; this string should be the user name and password
+    separated by a colon (e.g., rickhall:mypassword).
+
+These system properties can be set directly on the command line when
+starting the JVM using the standard "-D&lt;prop&gt;=&lt;value&gt;"
+syntax.
diff --git a/examples/oauth-client-twitter/pom.xml b/examples/oauth-client-twitter/pom.xml
new file mode 100644
index 0000000..8cc99cc
--- /dev/null
+++ b/examples/oauth-client-twitter/pom.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>oauth-client-twitter</artifactId>
+    <packaging>jar</packaging>
+
+
+    <description>
+        Twitter client using OAuth 1 support for Jersey that retrieves Tweets from the home timeline of a
+        registered Twitter account.
+    </description>
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.oauth.twitterclient.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/App.java b/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/App.java
new file mode 100644
index 0000000..f015aa5
--- /dev/null
+++ b/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/App.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.oauth.twitterclient;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Properties;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.oauth1.AccessToken;
+import org.glassfish.jersey.client.oauth1.ConsumerCredentials;
+import org.glassfish.jersey.client.oauth1.OAuth1AuthorizationFlow;
+import org.glassfish.jersey.client.oauth1.OAuth1ClientSupport;
+import org.glassfish.jersey.jackson.JacksonFeature;
+
+/** Simple command-line application that uses Jersey OAuth client library to authenticate
+ * with Twitter.
+ *
+ * @author Martin Matula
+ * @author Miroslav Fuksa
+ */
+public class App {
+    private static final BufferedReader IN = new BufferedReader(new InputStreamReader(System.in, Charset.forName("UTF-8")));
+    private static final String FRIENDS_TIMELINE_URI = "https://api.twitter.com/1.1/statuses/home_timeline.json";
+    private static final Properties PROPERTIES = new Properties();
+    private static final String PROPERTIES_FILE_NAME = "twitterclient.properties";
+    private static final String PROPERTY_CONSUMER_KEY = "consumerKey";
+    private static final String PROPERTY_CONSUMER_SECRET = "consumerSecret";
+    private static final String PROPERTY_TOKEN = "token";
+    private static final String PROPERTY_TOKEN_SECRET = "tokenSecret";
+
+    /**
+     * Main method that creates a {@link Client client} and initializes the OAuth support with
+     * configuration needed to connect to the Twitter and retrieve statuses.
+     * <p/>
+     * Execute this method to demo
+     *
+     * @param args Command line arguments.
+     * @throws Exception Thrown when error occurs.
+     */
+    public static void main(final String[] args) throws Exception {
+        // retrieve consumer key/secret and token/secret from the property file
+        // or system properties
+        loadSettings();
+
+        final ConsumerCredentials consumerCredentials = new ConsumerCredentials(
+                PROPERTIES.getProperty(PROPERTY_CONSUMER_KEY),
+                PROPERTIES.getProperty(PROPERTY_CONSUMER_SECRET));
+
+        final Feature filterFeature;
+        if (PROPERTIES.getProperty(PROPERTY_TOKEN) == null) {
+
+            // we do not have Access Token yet. Let's perfom the Authorization Flow first,
+            // let the user approve our app and get Access Token.
+            final OAuth1AuthorizationFlow authFlow = OAuth1ClientSupport.builder(consumerCredentials)
+                    .authorizationFlow(
+                            "https://api.twitter.com/oauth/request_token",
+                            "https://api.twitter.com/oauth/access_token",
+                            "https://api.twitter.com/oauth/authorize")
+                    .build();
+            final String authorizationUri = authFlow.start();
+
+            System.out.println("Enter the following URI into a web browser and authorize me:");
+            System.out.println(authorizationUri);
+            System.out.print("Enter the authorization code: ");
+            final String verifier;
+            try {
+                verifier = IN.readLine();
+            } catch (final IOException ex) {
+                throw new RuntimeException(ex);
+            }
+            final AccessToken accessToken = authFlow.finish(verifier);
+
+            // store access token for next application execution
+            PROPERTIES.setProperty(PROPERTY_TOKEN, accessToken.getToken());
+            PROPERTIES.setProperty(PROPERTY_TOKEN_SECRET, accessToken.getAccessTokenSecret());
+
+            // get the feature that will configure the client with consumer credentials and
+            // received access token
+            filterFeature = authFlow.getOAuth1Feature();
+        } else {
+            final AccessToken storedToken = new AccessToken(PROPERTIES.getProperty(PROPERTY_TOKEN),
+                    PROPERTIES.getProperty(PROPERTY_TOKEN_SECRET));
+            // build a new feature from the stored consumer credentials and access token
+            filterFeature = OAuth1ClientSupport.builder(consumerCredentials).feature()
+                    .accessToken(storedToken).build();
+        }
+
+
+        // create a new Jersey client and register filter feature that will add OAuth signatures and
+        // JacksonFeature that will process returned JSON data.
+        final Client client = ClientBuilder.newBuilder()
+                .register(filterFeature)
+                .register(JacksonFeature.class)
+                .build();
+
+        // make requests to protected resources
+        // (no need to care about the OAuth signatures)
+        final Response response = client.target(FRIENDS_TIMELINE_URI).request().get();
+        if (response.getStatus() != 200) {
+            String errorEntity = null;
+            if (response.hasEntity()) {
+                errorEntity = response.readEntity(String.class);
+            }
+            throw new RuntimeException("Request to Twitter was not successful. Response code: "
+                    + response.getStatus() + ", reason: " + response.getStatusInfo().getReasonPhrase()
+                    + ", entity: " + errorEntity);
+        }
+
+        // persist the current consumer key/secret and token/secret for future use
+        storeSettings();
+
+        final List<Status> statuses = response.readEntity(new GenericType<List<Status>>() {
+        });
+
+        System.out.println("Tweets:\n");
+        for (final Status s : statuses) {
+            System.out.println(s.getText());
+            System.out.println("[posted by " + s.getUser().getName() + " at " + s.getCreatedAt() + "]");
+        }
+
+
+    }
+
+    private static void loadSettings() {
+        FileInputStream st = null;
+        try {
+            st = new FileInputStream(PROPERTIES_FILE_NAME);
+            PROPERTIES.load(st);
+        } catch (final IOException e) {
+            // ignore
+        } finally {
+            if (st != null) {
+                try {
+                    st.close();
+                } catch (final IOException ex) {
+                    // ignore
+                }
+            }
+        }
+
+        for (final String name : new String[]{PROPERTY_CONSUMER_KEY, PROPERTY_CONSUMER_SECRET,
+                PROPERTY_TOKEN, PROPERTY_TOKEN_SECRET}) {
+            final String value = System.getProperty(name);
+            if (value != null) {
+                PROPERTIES.setProperty(name, value);
+            }
+        }
+
+        if (PROPERTIES.getProperty(PROPERTY_CONSUMER_KEY) == null
+                || PROPERTIES.getProperty(PROPERTY_CONSUMER_SECRET) == null) {
+            System.out.println("No consumerKey and/or consumerSecret found in twitterclient.properties file. "
+                    + "You have to provide these as system properties. See README.html for more information.");
+            System.exit(1);
+        }
+    }
+
+    private static void storeSettings() {
+        FileOutputStream st = null;
+        try {
+            st = new FileOutputStream(PROPERTIES_FILE_NAME);
+            PROPERTIES.store(st, null);
+        } catch (final IOException e) {
+            // ignore
+        } finally {
+            try {
+                if (st != null) {
+                    st.close();
+                }
+            } catch (final IOException ex) {
+                // ignore
+            }
+        }
+    }
+
+}
diff --git a/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/Status.java b/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/Status.java
new file mode 100644
index 0000000..34f2abe
--- /dev/null
+++ b/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/Status.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.oauth.twitterclient;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+/**
+ * Entity into which the Twitter status (tweet) is deserialized
+ *
+ *  @author Martin Matula
+ */
+@XmlRootElement
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Status {
+
+    @XmlElement(name = "created_at")
+    private String createdAt;
+    @XmlElement(name = "text")
+    private String text;
+    @XmlElement(name = "user")
+    private User user;
+
+    public String getCreatedAt() {
+        return createdAt;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public User getUser() {
+        return user;
+    }
+}
diff --git a/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/User.java b/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/User.java
new file mode 100644
index 0000000..9be2571
--- /dev/null
+++ b/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/User.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.oauth.twitterclient;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+/**
+ * Entity into which the Twitter user is deserialized.
+ *
+ * @author Martin Matula
+ */
+@XmlRootElement
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class User {
+    @XmlElement(name = "name")
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+}
diff --git a/examples/open-tracing/README.MD b/examples/open-tracing/README.MD
new file mode 100644
index 0000000..7fa3a82
--- /dev/null
+++ b/examples/open-tracing/README.MD
@@ -0,0 +1,84 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Jersey OpenTracing Example
+==========================
+
+This example demonstrates Jersey OpenTracing integration. The JAX-RS resource contains several resource method showing various
+possibilities.
+
+This particular example is configured with Jaeger as the GlobalTracer, but can be easily adjusted to use any other tracer
+implementation.
+
+Running the Example
+-------------------
+Example can be launched as is, but does not perform any interesting action out of the box.
+To be able to visualise the traces, it is recommended to start Jaeger (and th UI) locally, e.g. in Docker:
+
+>     docker run -d -p 5775:5775/udp -p 16686:16686 jaegertracing/all-in-one:travis-1278
+
+Check the UI on [localhost:16686](http://localhost:16686), there should be no traces visible.
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
+
+Try to access resources using the URIs listed bellow and look into Jager UI what traces are produced.
+The first example should be visible in the Jaeger UI right after the example application is started. Others can be created by
+doing rest calls to the exposed resource.
+
+1. On start, the example application sends one request demonstrating more complex tracing
+- jersey client (w/ registered OpenTracingFeature) creates the "jersey-client-GET" span
+- the span is propagated to server (via http headers), jersey server (w/ registered OpenTracinfFeature) automatically creates a
+ child span of the client span
+- in the resource method, child span of the server span is manually created and propagated into managed client calls
+- the managed clients are also tracing-enabled, so each one creates its own child span, that is propagated to the server, and
+so on
+
+2. No explicit (user-defined) spans.
+- one automatically created "root" span, that measures the processing on the Jersey side and one "resource-level" span to be
+used for logging events on the application level
+- the "root" contains several tags with request and response metadata, such as request headers, status code, etc
+- also, several technical Jersey-level events have been logged
+- to see this simple case, call
+>     curl localhost:8080/opentracing/resource/defaultTrace
+
+3. Explicit logging into resource-level span
+- same as above, but the "resource-level" span was resolved within the application logic, an event was logged and a tag was added
+>     curl localhost:8080/opentracing/resource/appLevelLogging
+
+- similar call with POST:
+>     curl -X POST -d "Jersey Rocks" localhost:8080/opentracing/resource/appLevelPost
+
+
+4. Explicit child span creation
+- same as above, but instead of "resource-level" span, a child span was created and used for logging and adding tags
+- note, that such a span needs to be finished manually, otherwise it won't propagate (the tracing info will be lost)
+>     curl localhost:8080/opentracing/resource/childSpan
+
+
+4. Client calls from the resource
+- more complex case, the resource method invokes two client calls using managed client
+- one request via managed client is triggered within the "provided", resource-level span
+- child span is created and another request via manged client is triggered within the child span
+- the span context needs to be propagated manually using the OpenTracingFeature.SPAN_CONTEXT_PROPERTY
+- in both cases, the span context is propagated using http headers to the server and back to the client
+- the child span (created in the resource) needs to be explicitly finished in the resource (or elsewhere, but needs to be finished)
+>     curl localhost:8080/opentracing/resource/traceWithManagedClient
+
+5. Asynchronous processing
+- either basic case, but using asynchronous request processing
+- there should be no practical difference visible in the tracing info
+>     curl localhost:8080/opentracing/resource/async
+
+6. Failing resource
+- demonstrates exception thrown in the resource method
+- Jaeger shows failed spans with the exclamation mark symbol and the exception is logged in the request span
+>     curl localhost:8080/opentracing/resource/error
\ No newline at end of file
diff --git a/examples/open-tracing/pom.xml b/examples/open-tracing/pom.xml
new file mode 100644
index 0000000..d3083e3
--- /dev/null
+++ b/examples/open-tracing/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>open-tracing</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-open-tracing</name>
+
+    <description>Jersey OpenTracing example</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.incubator</groupId>
+            <artifactId>jersey-open-tracing</artifactId>
+            <!-- not a part of bom pom.xml as long as it resides in incubator -->
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.opentracing</groupId>
+            <artifactId>opentracing-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.opentracing</groupId>
+            <artifactId>opentracing-util</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.uber.jaeger</groupId>
+            <artifactId>jaeger-core</artifactId>
+            <version>0.20.0</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.opentracing.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/open-tracing/src/main/java/org/glassfish/jersey/examples/opentracing/App.java b/examples/open-tracing/src/main/java/org/glassfish/jersey/examples/opentracing/App.java
new file mode 100644
index 0000000..b7c48e5
--- /dev/null
+++ b/examples/open-tracing/src/main/java/org/glassfish/jersey/examples/opentracing/App.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.opentracing;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.opentracing.OpenTracingFeature;
+import org.glassfish.jersey.opentracing.OpenTracingUtils;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import com.uber.jaeger.Configuration;
+
+import io.opentracing.Span;
+import io.opentracing.util.GlobalTracer;
+
+/**
+ * Open tracing example application.
+ * <p>
+ * Exposes OpenTracing-enabled REST application (with Jaeger registered as the Tracer)
+ * and invokes one request from (also OpenTracing-enabled) Jersey client.
+ * <p>
+ * To visualise the traces, start Jaeger locally:
+ * <pre>
+ * docker run -d -p 5775:5775/udp -p 16686:16686 jaegertracing/all-in-one:travis-1278
+ * </pre>
+ * and go to {@code localhost:16686}.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ * @since 2.26
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/opentracing");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Hello World\" Jersey OpenTracing Example App");
+            prepare();
+
+            final ResourceConfig resourceConfig = new ResourceConfig(TracedResource.class,
+                    OpenTracingFeature.class,
+                    ReqFilterA.class, ReqFilterB.class,
+                    RespFilterA.class, RespFilterB.class);
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, false);
+            Runtime.getRuntime().addShutdownHook(new Thread(server::shutdownNow));
+            server.start();
+
+            System.out.println(String.format("Application started.\nTry out %s/application.wadl\n"
+                    + "Stop the application using CTRL+C", BASE_URI));
+
+            // do the first "example" request with tracing-enabled client to show something in Jaegger UI,
+            // include some weird headers and accepted types, that will be visible in the span's tags
+            Client client = ClientBuilder.newBuilder().register(OpenTracingFeature.class).build();
+            client.target(BASE_URI).path("resource/managedClient").request()
+                    .accept("text/plain", "application/json", "*/*")
+                    .header("foo", "bar")
+                    .header("foo", "baz")
+                    .header("Hello", "World").get();
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    /**
+     * Configures Jaeger tracer as the {@link GlobalTracer}.
+     */
+    private static void prepare() {
+        GlobalTracer.register(
+                new com.uber.jaeger.Configuration(
+                        "OpenTracingTemporaryTest",
+                        new Configuration.SamplerConfiguration("const", 1),
+                        new Configuration.ReporterConfiguration(
+                                true,
+                                "localhost",
+                                5775,
+                                1000,
+                                10000)
+                ).getTracer()
+        );
+    }
+
+    /**
+     * No-op request filter, just to test, that it will be listed in the root level span's logs.
+     * For demonstrating purposes, it resolves the "root" request span and logs into it. If it fails to resolve the span, it
+     * creates a new ad-hoc span.
+     */
+    static class ReqFilterA implements ContainerRequestFilter {
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            Span span = OpenTracingUtils
+                    .getRequestSpan(requestContext)
+                    .orElse(GlobalTracer.get().buildSpan("ad-hoc-span-reqA").startManual());
+            span.log("ReqFilterA.filter() invoked");
+        }
+    }
+
+    /**
+     * No-op request filter, just to test, that it will be listed in the root level span's logs.
+     * For demonstrating purposes, it resolves the "root" request span and logs into it. If it fails to resolve the span, it
+     * creates a new ad-hoc span.
+     */
+    static class ReqFilterB implements ContainerRequestFilter {
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            Span span = OpenTracingUtils
+                    .getRequestSpan(requestContext)
+                    .orElse(GlobalTracer.get().buildSpan("ad-hoc-span-reqB").startManual());
+            span.log("ReqFilterB.filter() invoked");
+        }
+    }
+
+    /**
+     * No-op response filter, just to test, that it will be listed in the root level span's logs.
+     */
+    static class RespFilterA implements ContainerResponseFilter {
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+            Span span = OpenTracingUtils
+                    .getRequestSpan(requestContext)
+                    .orElse(GlobalTracer.get().buildSpan("ad-hoc-span-respA").startManual());
+            span.log("RespFilterA.filter() invoked");
+        }
+    }
+
+    /**
+     * No-op response filter, just to test, that it will be listed in the root level span's logs.
+     */
+    static class RespFilterB implements ContainerResponseFilter {
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+            Span span = OpenTracingUtils
+                    .getRequestSpan(requestContext)
+                    .orElse(GlobalTracer.get().buildSpan("ad-hoc-span-respB").startManual());
+            span.log("RespFilterB.filter() invoked");
+        }
+    }
+}
+
diff --git a/examples/open-tracing/src/main/java/org/glassfish/jersey/examples/opentracing/TracedResource.java b/examples/open-tracing/src/main/java/org/glassfish/jersey/examples/opentracing/TracedResource.java
new file mode 100644
index 0000000..ce8e83b
--- /dev/null
+++ b/examples/open-tracing/src/main/java/org/glassfish/jersey/examples/opentracing/TracedResource.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.opentracing;
+
+import java.util.concurrent.Executors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.opentracing.OpenTracingFeature;
+import org.glassfish.jersey.opentracing.OpenTracingUtils;
+import org.glassfish.jersey.server.ManagedAsync;
+import org.glassfish.jersey.server.Uri;
+
+import io.opentracing.Span;
+
+
+/**
+ * OpenTracing example resource.
+ * <p>
+ * Jersey (with registered {@link OpenTracingFeature} will automatically
+ * create and start span for each request ("root" span or "request" span) and a child span to be used in the resource method
+ * ("resource" span). The root span is used for Jersey-level event logging (resource matching started, request filters applied,
+ * etc). The resource span serves for application-level event logging purposes (used-defined). Both are automatically created
+ * and also automatically finished.
+ * <p>
+ * Resource span is created right before the resource method invocation and finished right after resource method finishes. It
+ * can be resolved by calling {@link OpenTracingUtils#getRequestSpan(ContainerRequestContext)}.
+ * <p>
+ * Application code can also create ad-hoc spans as child spans of the resource span. This can be achieved by calling one of the
+ * convenience methods {@link OpenTracingUtils#getRequestChildSpan(ContainerRequestContext)}.
+ * <p>
+ * {@link ContainerRequestContext} can be obtained via injection.
+ * <p>
+ * All the ad-hoc created spans MUST be {@link Span#finish() finished} explicitly.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path(value = "/resource")
+public class TracedResource {
+
+    /**
+     * Resource method with no explicit tracing.
+     * <p>
+     * One span (jersey-server) will be created and finished automatically.
+     *
+     * @return dummy response
+     */
+    @GET
+    @Path("defaultTrace")
+    public Response defaultTrace() {
+        return Response.ok("foo").build();
+    }
+
+    /**
+     * Resource method with explicit logging into resource span.
+     *
+     * @param context injected request context with resource-level span reference
+     * @return dummy response
+     * @throws InterruptedException if interrupted
+     */
+    @GET
+    @Path("appLevelLogging")
+    public Response appLevelLogging(@Context ContainerRequestContext context) throws InterruptedException {
+        final Span resourceSpan = OpenTracingUtils
+                .getRequestSpan(context)
+                .orElseThrow(() -> new RuntimeException("Tracing has failed"));
+
+        resourceSpan.log("Starting expensive operation.");
+        // Do the business
+        Thread.sleep(200);
+        resourceSpan.log("Expensive operation finished.");
+        resourceSpan.setTag("expensiveOperationSuccess", true);
+
+        return Response.ok("SUCCESS").build();
+    }
+
+    /**
+     * Similar as {@link #appLevelLogging(ContainerRequestContext)}, just with {@code POST} method.
+     *
+     * @param entity  posted entity
+     * @param context injected context
+     * @return dummy response
+     */
+    @POST
+    @Path("appLevelPost")
+    public Response tracePost(String entity, @Context ContainerRequestContext context) {
+        final Span resourceSpan = OpenTracingUtils
+                .getRequestSpan(context)
+                .orElseThrow(() -> new RuntimeException("Tracing has failed"));
+
+        resourceSpan.setTag("result", "42");
+        resourceSpan.setBaggageItem("entity", entity);
+        return Response.ok("Done!").build();
+    }
+
+    /**
+     * Resource method with explicit child span creation.
+     *
+     * @param context injected request context with resource-level (parent) span reference
+     * @return dummy response
+     * @throws InterruptedException if interrupted
+     */
+    @GET
+    @Path("childSpan")
+    public Response childSpan(@Context ContainerRequestContext context) throws InterruptedException {
+        final Span childSpan = OpenTracingUtils.getRequestChildSpan(context, "AppCreatedSpan");
+        childSpan.log("Starting expensive operation.");
+        // Do the business
+        Thread.sleep(200);
+        childSpan.log("Expensive operation finished.");
+        childSpan.setTag("expensiveOperationSuccess", true);
+
+        childSpan.finish();
+        return Response.ok("SUCCESS").build();
+    }
+
+
+    /**
+     * Resource method with explicit span creation and propagation into injected managed client.
+     * <p>
+     * Shows how to propagate the server-side span into managed client (or any common Jersey client).
+     * This way, the client span will be child of the resource span.
+     *
+     * @param context injected context
+     * @param wt      injected web target
+     * @return dummy response
+     */
+    @GET
+    @Path("managedClient")
+    public Response traceWithManagedClient(@Context ContainerRequestContext context,
+                                           @Uri("resource/appLevelPost") WebTarget wt) {
+        final Span providedSpan = OpenTracingUtils
+                .getRequestSpan(context)
+                .orElseThrow(() -> new RuntimeException("Tracing failed"));
+
+        providedSpan.log("Resource method started.");
+
+        final Response response = wt.request()
+                .property(OpenTracingFeature.SPAN_CONTEXT_PROPERTY, providedSpan.context())  // <--- span propagation
+                .post(Entity.text("Hello"));
+
+        providedSpan.log("1st Response received from managed client");
+        providedSpan.log("Firing 1st request from managed client");
+
+        providedSpan.log("Creating child span");
+        final Span childSpan = OpenTracingUtils.getRequestChildSpan(context, "jersey-resource-child-span");
+
+
+        childSpan.log("Firing 2nd request from managed client");
+        final Response response2 = wt.request()
+                .property(OpenTracingFeature.SPAN_CONTEXT_PROPERTY, childSpan.context())  // <--- span propagation
+                .post(Entity.text("World"));
+        childSpan.log("2st Response received from managed client");
+
+        childSpan.finish();
+        return Response.ok("Result: " + response.getStatus() + ", " + response2.getStatus()).build();
+    }
+
+    @GET
+    @Path("async")
+    @ManagedAsync
+    public void traceWithAsync(@Suspended final AsyncResponse asyncResponse, @Context ContainerRequestContext context) {
+        final Span span = OpenTracingUtils.getRequestSpan(context).orElseThrow(() -> new RuntimeException("tracing failed"));
+        span.log("In the resource method.");
+        Executors.newSingleThreadExecutor().submit(() -> {
+            try {
+                Thread.sleep(200);
+            } catch (InterruptedException e) {
+                span.log("Interrupted");
+                e.printStackTrace();
+            }
+            span.log("Resuming");
+            asyncResponse.resume("OK");
+        });
+        span.log("Before exiting the resource method");
+    }
+
+    @GET
+    @Path("error")
+    public String failTrace(@Context ContainerRequestContext context) {
+        throw new RuntimeException("Failing just for fun.");
+    }
+}
diff --git a/examples/osgi-helloworld-webapp/README.MD b/examples/osgi-helloworld-webapp/README.MD
new file mode 100644
index 0000000..40b98f6
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/README.MD
@@ -0,0 +1,47 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+HelloWorld OSGi Example
+=======================
+
+This example demonstrates how to develop a simple OSGi WAR bundle
+containing a RESTful hello world web service
+
+Contents
+--------
+
+The example WAR (see the `war-bundle` module) consists of two Jersey
+resources:
+
+`org.glassfish.jersey.examples.osgi.helloworld.resource.HelloWorldResource`
+
+that produces a textual response to an HTTP GET
+
+`org.glassfish.jersey.examples.osgi.helloworld.resource.AnotherResource`
+
+that produces a different textual response to an HTTP GET. The
+purpose of this resource is to show how to define multiple web
+resources within a web application.
+
+The mapping of the URI path space is presented in the following table:
+
+URI path           | Resource class       | HTTP method
+------------------ | -------------------- | -------------
+**_/helloworld_**  | HelloWorldResource   | GET
+**_/another_**     | AnotherResource      | GET
+
+Running the Example
+-------------------
+
+To run the example, you would need to build the WAR file and install it
+to an OSGi runtime (e.g. Apache Felix) together with other OSGi modules.
+Look at the attached `functional-test` module for details on the
+programatical runtime configuration. To build the war archive and run
+the tests, you can just launch
+
+>     mvn clean install
diff --git a/examples/osgi-helloworld-webapp/additional-bundle/pom.xml b/examples/osgi-helloworld-webapp/additional-bundle/pom.xml
new file mode 100644
index 0000000..3e0fa05
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/additional-bundle/pom.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>osgi-helloworld-webapp</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
+    <artifactId>additional-bundle</artifactId>
+    <name>jersey-examples-osgi-helloworld-webapp-additional</name>
+
+    <description>OSGi Helloworld Webapp - additional bundle</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- to generate the MANIFEST-FILE required by the bundle -->
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <manifestLocation>${project.build.directory}/META-INF</manifestLocation>
+                    <supportedProjectTypes>
+                        <supportedProjectType>bundle</supportedProjectType>
+                        <supportedProjectType>jar</supportedProjectType>
+                    </supportedProjectTypes>
+                    <instructions>
+                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                        <Import-Package>*</Import-Package>
+                        <Export-Package>org.glassfish.jersey.examples.osgi.helloworld.additional.resource</Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <!-- do not create source zip bundles -->
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <configuration>
+                            <skipAssembly>true</skipAssembly>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/osgi-helloworld-webapp/additional-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/additional/resource/AdditionalResource.java b/examples/osgi-helloworld-webapp/additional-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/additional/resource/AdditionalResource.java
new file mode 100644
index 0000000..c18d5c4
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/additional-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/additional/resource/AdditionalResource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgi.helloworld.additional.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Test resource for osgi-helloworld-webapp example;
+ * It is aimed to ensure, that the package scanning works in OSGi for multiple packages defined in web.xml
+ *
+ * There is also an alternate version of the resource with the same class name within the same package which
+ * should not be visible to Jersey via OSGi.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("/additional")
+public class AdditionalResource {
+    @GET
+    @Produces("text/plain")
+    public String getAdditionalResourceMessage() {
+        return "Additional Bundle!";
+    }
+}
diff --git a/examples/osgi-helloworld-webapp/additional-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/additional/resource/subpackage/AdditionalSubPackagedResource.java b/examples/osgi-helloworld-webapp/additional-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/additional/resource/subpackage/AdditionalSubPackagedResource.java
new file mode 100644
index 0000000..35318cc
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/additional-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/additional/resource/subpackage/AdditionalSubPackagedResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgi.helloworld.additional.resource.subpackage;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * This resource is located in a sub-package and will be detected by OSGI framework only if recursive scanning is turned on.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("/subadditional")
+public class AdditionalSubPackagedResource {
+
+    @GET
+    @Produces("text/plain")
+    public String getSubPackagedAdditionalMessage() {
+        return "Sub-packaged Additional Bundle!";
+    }
+
+}
diff --git a/examples/osgi-helloworld-webapp/alternate-version-bundle/pom.xml b/examples/osgi-helloworld-webapp/alternate-version-bundle/pom.xml
new file mode 100644
index 0000000..d958fdc
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/alternate-version-bundle/pom.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>osgi-helloworld-webapp</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
+    <artifactId>alternate-version-bundle</artifactId>
+    <name>jersey-examples-osgi-helloworld-webapp-alternate-version</name>
+
+    <description>OSGi Helloworld Webapp - alternate version bundle</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- to generate the MANIFEST-FILE required by the bundle -->
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <manifestLocation>${project.build.directory}/META-INF</manifestLocation>
+                    <supportedProjectTypes>
+                        <supportedProjectType>bundle</supportedProjectType>
+                        <supportedProjectType>jar</supportedProjectType>
+                    </supportedProjectTypes>
+                    <instructions>
+                        <Bundle-SymbolicName>additional-bundle</Bundle-SymbolicName>
+                        <Bundle-Name>jersey-examples-osgi-helloworld-webapp-additional</Bundle-Name>
+                        <!-- this version has to be different from the 'additional-bundle'
+                        version in order to test the correct bundle resolution within OSGi -->
+                        <Bundle-Version>2.2.1</Bundle-Version>
+                        <Import-Package>*</Import-Package>
+                        <Export-Package>org.glassfish.jersey.examples.osgi.helloworld.additional.resource</Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <!-- do not create source zip bundles -->
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <configuration>
+                            <skipAssembly>true</skipAssembly>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/osgi-helloworld-webapp/alternate-version-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/additional/resource/AdditionalResource.java b/examples/osgi-helloworld-webapp/alternate-version-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/additional/resource/AdditionalResource.java
new file mode 100644
index 0000000..214b37f
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/alternate-version-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/additional/resource/AdditionalResource.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgi.helloworld.additional.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Alternate version of test resource for osgi-helloworld-webapp example;
+ * The original class within additional-bundle module has the same name and resides withing the same package.
+ *
+ * The containing module's (alternate-version-bundle) pom.xml configures the MANIFEST bundle headers as an older version of
+ * additional-bundle.
+ *
+ * Both versions are then explicitly loaded into OSGi runtime and the correct version (the one in additional-bundle module)
+ * should be used by Jersey.
+ *
+ * If this version of the resource is used, the test will fail.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("/additional")
+public class AdditionalResource {
+    @GET
+    @Produces("text/plain")
+    public String getAdditionalResourceMessage() {
+        return "WRONG VERSION of additional Bundle!";
+    }
+}
diff --git a/examples/osgi-helloworld-webapp/functional-test/pom.xml b/examples/osgi-helloworld-webapp/functional-test/pom.xml
new file mode 100644
index 0000000..de4aeaf
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/functional-test/pom.xml
@@ -0,0 +1,256 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>osgi-helloworld-webapp</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
+    <artifactId>functional-test</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-examples-osgi-helloworld-webapp-test</name>
+
+    <dependencies>
+        <!-- example war bundle -->
+        <dependency>
+            <groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
+            <artifactId>war-bundle</artifactId>
+            <type>war</type>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
+            <artifactId>additional-bundle</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
+            <artifactId>alternate-version-bundle</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.hk2.external</groupId>
+            <artifactId>aopalliance-repackaged</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- logging -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>1.6.4</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- JUnit and Pax-Exam dependencies -->
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-junit4</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-container-forked</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-link-mvn</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.web</groupId>
+            <artifactId>pax-web-jetty-bundle</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.web</groupId>
+            <artifactId>pax-web-extender-war</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- OSGi runtime - Felix -->
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.eventadmin</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+            <scope>test</scope>
+         </dependency>
+         <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework.security</artifactId>
+            <scope>test</scope>
+         </dependency>
+         <!-- uncomment the following dependency to get ability
+              to run felix console in the test -->
+         <!--dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.shell.remote</artifactId>
+            <version>1.1.2</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <artifactId>org.osgi.core</artifactId>
+                </exclusion>
+            </exclusions>
+         </dependency-->
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.ops4j.pax.exam</groupId>
+                <artifactId>maven-paxexam-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>generate-config</id>
+                        <goals>
+                            <goal>generate-depends-file</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>test-compile</id>
+                        <phase>test-compile</phase>
+                        <goals>
+                            <goal>testCompile</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-resources-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>process-test-resources</phase>
+                        <goals>
+                            <goal>testResources</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkMode>always</forkMode>
+                    <enableAssertions>false</enableAssertions>
+                    <classpathDependencyExcludes>
+                        <classpathDepenencyExclude>org.apache.felix:org.osgi.core</classpathDepenencyExclude>
+                        <classpathDepenencyExclude>org.osgi:org.osgi.core</classpathDepenencyExclude>
+                    </classpathDependencyExcludes>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>test</id>
+                        <phase>test</phase>
+                        <goals>
+                            <goal>test</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <!-- do not create source zip bundles -->
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <configuration>
+                            <skipAssembly>true</skipAssembly>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <surefire.security.argline>-Dfelix.policy=${project.build.directory}/test-classes/felix.policy -Druntime.policy=${project.build.directory}/test-classes/runtime.policy</surefire.security.argline>
+    </properties>
+
+</project>
diff --git a/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/AbstractWebAppTest.java b/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/AbstractWebAppTest.java
new file mode 100644
index 0000000..86bd28a
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/AbstractWebAppTest.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.test;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.URI;
+import java.security.AccessController;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.UriBuilder;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
+import org.osgi.service.condpermadmin.ConditionalPermissionInfo;
+import org.osgi.service.condpermadmin.ConditionalPermissionUpdate;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+import static org.junit.Assert.assertNotNull;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public abstract class AbstractWebAppTest {
+
+    @Inject
+    BundleContext bundleContext;
+    /**
+     * maximum waiting time for runtime initialization and Jersey deployment
+     */
+    public static final long MAX_WAITING_SECONDS = 10L;
+    /**
+     * Latch for blocking the testing thread until the runtime is ready and
+     * Jersey deployed
+     */
+    final CountDownLatch countDownLatch = new CountDownLatch(1);
+    private static final int port = getProperty("jersey.config.test.container.port", 8080);
+    private static final String runtimePolicy = AccessController.doPrivileged(
+            PropertiesHelper.getSystemProperty("runtime.policy"));
+    private static final String felixPolicy = AccessController.doPrivileged(PropertiesHelper.getSystemProperty("felix.policy"));
+    private static final String CONTEXT = "/helloworld";
+    private static final URI baseUri = UriBuilder.fromUri("http://localhost").port(port).path(CONTEXT).build();
+    private static final Logger LOGGER = Logger.getLogger(AbstractWebAppTest.class.getName());
+
+    /**
+     * Allow subclasses to define additional OSGi configuration - called after
+     * genericOsgiOptions() and jettyOptions()
+     *
+     * @return list of pax exam Options
+     */
+    public abstract List<Option> osgiRuntimeOptions();
+
+    /**
+     * Generic OSGi options - defines which dependencies (bundles) should be
+     * loaded into runtime
+     */
+    public List<Option> genericOsgiOptions() {
+
+        // uncomment for debugging using felix console (lookup gogo string in the commnented lines below)
+        String gogoVersion = "0.8.0";
+
+        @SuppressWarnings("RedundantStringToString")
+        List<Option> options = Arrays.asList(options(
+                // vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"),
+                // vmOption("-Djava.security.debug=scl"),
+
+                // uncomment for verbose class loading info
+                // vmOption("-verbose:class"),
+
+                // bootDelegationPackage("org.glassfish.jersey.client.*"),
+
+                systemProperty("java.security.manager").value(""),
+                systemProperty("felix.policy").value(felixPolicy),
+                systemProperty("java.security.policy").value(runtimePolicy),
+                systemProperty(org.osgi.framework.Constants.FRAMEWORK_SECURITY)
+                        .value(org.osgi.framework.Constants.FRAMEWORK_SECURITY_OSGI),
+                systemProperty("org.osgi.service.http.port").value(String.valueOf(port)),
+                systemProperty("org.osgi.framework.system.packages.extra").value("javax.annotation"),
+                systemProperty("jersey.config.test.container.port").value(String.valueOf(port)),
+                // systemProperty(BundleLocationProperty).value(bundleLocation),
+
+                // do not remove the following line
+                systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("FINEST"),
+                // uncomment the following 4 lines should you need to debug from th felix console
+                // mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.gogo.runtime").version(gogoVersion),
+                // mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.gogo.shell").version(gogoVersion),
+                // mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.gogo.command").version(gogoVersion),
+                // mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.shell.remote").versionAsInProject(),
+                mavenBundle("org.apache.felix", "org.apache.felix.framework.security").versionAsInProject(),
+                // uncomment for logging (do not remove the following two lines)
+                //                 mavenBundle("org.ops4j.pax.logging", "pax-logging-api", "1.4"),
+                //                 mavenBundle("org.ops4j.pax.logging", "pax-logging-service", "1.4"),
+
+                // javax.annotation must go first!
+                mavenBundle().groupId("javax.annotation").artifactId("javax.annotation-api").versionAsInProject(),
+                // pax exam dependencies
+                mavenBundle("org.ops4j.pax.url", "pax-url-mvn"),
+                junitBundles(), // adds junit classes to the OSGi context
+
+                // HK2
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("hk2-api").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("osgi-resource-locator").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("hk2-locator").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("hk2-utils").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2.external").artifactId("javax.inject").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2.external").artifactId("aopalliance-repackaged").versionAsInProject(),
+                mavenBundle().groupId("org.javassist").artifactId("javassist").versionAsInProject(),
+                // JAX-RS API
+                mavenBundle().groupId("javax.ws.rs").artifactId("javax.ws.rs-api").versionAsInProject(),
+                // validation - required by jersey-container-servlet-core
+                mavenBundle().groupId("javax.validation").artifactId("validation-api").versionAsInProject(),
+                // Jersey bundles
+                mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-common").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-server").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-client").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.containers").artifactId("jersey-container-servlet-core")
+                        .versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.inject").artifactId("jersey-hk2").versionAsInProject(),
+                // Those two bundles have different (unique) maven coordinates, but represent the same OSGi bundle in two
+                // different versions.
+                // (see the maven bundle plugin configuration in each of the two pom.xml files
+                // Both bundles are explicitly loaded here to ensure, that both co-exist within the OSGi runtime;
+                mavenBundle().groupId("org.glassfish.jersey.examples.osgi-helloworld-webapp")
+                             .artifactId("additional-bundle").versionAsInProject(),
+                // The alternate-version-bundle contains the same resource in the same package
+                // (org.glassfish.jersey.examples.osgi.helloworld.additional.resource.AdditionalResource),
+                // mapped to the same URI (/additional), but returning a different string as a response.
+                // ---> if the test passes, it ensures, that Jersey sees/uses the correct version of the bundle
+                mavenBundle().groupId("org.glassfish.jersey.examples.osgi-helloworld-webapp")
+                             .artifactId("alternate-version-bundle").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.examples.osgi-helloworld-webapp")
+                             .artifactId("war-bundle").type("war").versionAsInProject()
+                // uncomment for debugging
+                // vmOption( "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005" )
+                ));
+
+        final String localRepository = AccessController.doPrivileged(PropertiesHelper.getSystemProperty("localRepository"));
+        if (localRepository != null) {
+            options = new ArrayList<Option>(options);
+            options.add(systemProperty("org.ops4j.pax.url.mvn.localRepository").value(localRepository));
+        }
+
+        return options;
+    }
+
+    public List<Option> jettyOptions() {
+        return Arrays.asList(options(
+                mavenBundle().groupId("org.ops4j.pax.web").artifactId("pax-web-jetty-bundle").versionAsInProject(),
+                mavenBundle().groupId("org.ops4j.pax.web").artifactId("pax-web-extender-war").versionAsInProject()));
+    }
+
+    private void updatePermissionsFromFile() throws IOException {
+
+        final ServiceReference cpaRef = bundleContext.getServiceReference(ConditionalPermissionAdmin.class.getName());
+        final ConditionalPermissionAdmin conditionalPermissionAdmin = (ConditionalPermissionAdmin) bundleContext
+                .getService(cpaRef);
+        final ConditionalPermissionUpdate permissionUpdate = conditionalPermissionAdmin.newConditionalPermissionUpdate();
+        final List conditionalPermissionInfos = permissionUpdate.getConditionalPermissionInfos();
+
+        try {
+
+            final BufferedReader reader = new BufferedReader(new FileReader(felixPolicy));
+            String line;
+            final Set<String> cpiNames = new HashSet<String>();
+
+            while ((line = reader.readLine()) != null) {
+                if (!line.startsWith("//")) {
+                    final ConditionalPermissionInfo cpi = conditionalPermissionAdmin.newConditionalPermissionInfo(line);
+                    final String cpiName = cpi.getName();
+                    if (cpiNames.contains(cpiName)) {
+                        throw new RuntimeException("Redundant policy name!");
+                    }
+                    cpiNames.add(cpiName);
+                    conditionalPermissionInfos.add(cpi);
+                }
+            }
+            reader.close();
+            permissionUpdate.commit();
+
+        } finally {
+            bundleContext.ungetService(cpaRef);
+        }
+    }
+
+    /**
+     * After the war bundle is loaded and initialized, it sends custom OSGi
+     * event "jersey/test/DEPLOYED"; This class handles the event (releases the
+     * waiting lock)
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public class WebEventHandler implements EventHandler {
+
+        @Override
+        public void handleEvent(Event event) {
+            countDownLatch.countDown();
+        }
+
+        public WebEventHandler(String handlerName) {
+            this.handlerName = handlerName;
+        }
+        private final String handlerName;
+
+        protected String getHandlerName() {
+            return handlerName;
+        }
+    }
+
+    /**
+     * Configuration method called by pax-exam framework
+     *
+     * @return
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    @Configuration
+    public Option[] configuration() {
+        List<Option> options = new LinkedList<Option>();
+
+        options.addAll(genericOsgiOptions());
+        options.addAll(jettyOptions());
+        options.addAll(osgiRuntimeOptions());
+
+        return options.toArray(new Option[options.size()]);
+    }
+
+    /**
+     * Registers the event handler for custom jersey/test/DEPLOYED event
+     */
+    public void defaultMandatoryBeforeMethod() throws Exception {
+        bundleContext.registerService(EventHandler.class.getName(),
+                new WebEventHandler("Deploy Handler"), getHandlerServiceProperties("jersey/test/DEPLOYED"));
+
+        assertNotNull(System.getSecurityManager());
+
+        updatePermissionsFromFile();
+    }
+
+    /**
+     * The test method itself - installs the war-bundle and sends two testing
+     * requests
+     *
+     * @throws Exception
+     */
+    public WebTarget webAppTestTarget(String appRoot) throws Exception {
+
+        LOGGER.info(bundleList());
+
+        // restart war bundle...
+        final Bundle warBundle = lookupWarBundle();
+        warBundle.stop();
+        warBundle.start();
+
+        // and wait until it's ready
+        LOGGER.fine("Waiting for jersey/test/DEPLOYED event with timeout " + MAX_WAITING_SECONDS + " seconds...");
+        if (!countDownLatch.await(MAX_WAITING_SECONDS, TimeUnit.SECONDS)) {
+            throw new TimeoutException("The event jersey/test/DEPLOYED did not arrive in "
+                    + MAX_WAITING_SECONDS
+                    + " seconds. Waiting timed out.");
+        }
+        final Client c = ClientBuilder.newClient();
+
+        // server should be listening now and everything should be initialized
+        return c.target(baseUri + appRoot);
+
+    }
+
+    private Bundle lookupWarBundle() {
+        for (Bundle b : bundleContext.getBundles()) {
+            if (b.getSymbolicName().contains("war-bundle")) {
+                return b;
+            }
+        }
+        return null;
+    }
+
+    private static int getProperty(final String varName, int defaultValue) {
+        if (null == varName) {
+            return defaultValue;
+        }
+        String varValue = AccessController.doPrivileged(PropertiesHelper.getSystemProperty(varName));
+        if (null != varValue) {
+            try {
+                return Integer.parseInt(varValue);
+            } catch (NumberFormatException e) {
+                // will return default value below
+            }
+        }
+        return defaultValue;
+    }
+
+    @SuppressWarnings({"UseOfObsoleteCollectionType", "unchecked"})
+    private Dictionary getHandlerServiceProperties(String... topics) {
+        Dictionary result = new Hashtable();
+        result.put(EventConstants.EVENT_TOPIC, topics);
+        return result;
+    }
+
+    private String bundleList() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("-- Bundle list -- \n");
+        for (Bundle b : bundleContext.getBundles()) {
+            sb.append(String.format("%1$5s", "[" + b.getBundleId() + "]")).append(" ")
+                    .append(String.format("%1$-70s", b.getSymbolicName())).append(" | ")
+                    .append(String.format("%1$-20s", b.getVersion())).append(" |");
+            sb.append(stateString(b)).append(" |");
+            sb.append(b.getLocation()).append("\n");
+        }
+        sb.append("-- \n\n");
+        return sb.toString();
+    }
+
+    private String stateString(Bundle b) {
+        switch (b.getState()) {
+            case Bundle.ACTIVE:
+                return "ACTIVE";
+            case Bundle.INSTALLED:
+                return "INSTALLED";
+            case Bundle.RESOLVED:
+                return "RESOLVED";
+            default:
+                return "???";
+        }
+    }
+}
diff --git a/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/WebAppFelixTest.java b/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/WebAppFelixTest.java
new file mode 100644
index 0000000..4a6c251
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/WebAppFelixTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.helloworld.test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerSuite;
+import static org.junit.Assert.assertEquals;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerSuite.class)
+public class WebAppFelixTest extends AbstractWebAppTest {
+
+    private static final Logger LOGGER = Logger.getLogger(WebAppFelixTest.class.getName());
+
+    @Override
+    public List<Option> osgiRuntimeOptions() {
+        return Arrays.asList(CoreOptions.options(
+                mavenBundle()
+                        .groupId("org.apache.felix").artifactId("org.apache.felix.eventadmin")
+                        .versionAsInProject()
+        )
+        );
+    }
+
+    @Before
+    public void before() throws Exception {
+        defaultMandatoryBeforeMethod();
+    }
+
+    @Test
+    public void testWebResources() throws Exception {
+        final WebTarget target = webAppTestTarget("/webresources");
+
+        // send request and check response - helloworld resource
+        final String helloResult = target.path("/helloworld").request().build("GET").invoke().readEntity(
+                String.class);
+        LOGGER.info("HELLO RESULT = " + helloResult);
+        assertEquals("Hello World", helloResult);
+
+        // send request and check response - another resource
+        final String anotherResult = target.path("/another").request().build("GET").invoke()
+                .readEntity(String.class);
+
+        LOGGER.info("ANOTHER RESULT = " + anotherResult);
+        assertEquals("Another", anotherResult);
+
+        // send request and check response for the additional bundle - should fail now
+        final String additionalResult = target.path("/additional").request().build("GET").invoke()
+                .readEntity(String.class);
+
+        LOGGER.info("ADDITIONAL RESULT = " + additionalResult);
+        assertEquals("Additional Bundle!", additionalResult);
+
+        // send request and check response for the sub-packaged additional bundle
+        final String subAdditionalResult = target.path("/subadditional").request().build("GET").invoke()
+                .readEntity(String.class);
+
+        LOGGER.info("SUB-PACKAGED ADDITIONAL RESULT = " + subAdditionalResult);
+        assertEquals("Sub-packaged Additional Bundle!", subAdditionalResult);
+
+        // send request and check response for the WEB-INF classes located resource
+        final String webInfClassesResourceResult = target.path("/webinf").request().build("GET").invoke()
+                .readEntity(String.class);
+
+        LOGGER.info("WEB-INF CLASSES RESOURCE RESULT = " + webInfClassesResourceResult);
+        assertEquals("WebInfClassesResource", webInfClassesResourceResult);
+
+        // send request and check response for the WEB-INF classes located resource
+        final String webInfClassesSubPackagedResourceResult = target.path("/subwebinf").request().build("GET")
+                .invoke().readEntity(String.class);
+
+        LOGGER.info("WEB-INF CLASSES SUB-PACKAGED RESOURCE RESULT = " + webInfClassesSubPackagedResourceResult);
+        assertEquals("WebInfClassesSubPackagedResource", webInfClassesSubPackagedResourceResult);
+    }
+
+    @Test
+    public void testNonRecursiveWebResources() throws Exception {
+        final WebTarget target = webAppTestTarget("/n-webresources");
+
+        // send request and check response - helloworld resource
+        final String helloResult = target.path("/helloworld").request().build("GET").invoke().readEntity(
+                String.class);
+        LOGGER.info("HELLO RESULT = " + helloResult);
+        assertEquals("Hello World", helloResult);
+
+        // send request and check response - another resource
+        final String anotherResult = target.path("/another").request().build("GET").invoke()
+                .readEntity(String.class);
+
+        LOGGER.info("ANOTHER RESULT = " + anotherResult);
+        assertEquals("Another", anotherResult);
+
+        // send request and check response for the additional bundle - should fail now
+        final String additionalResult = target.path("/additional").request().build("GET").invoke()
+                .readEntity(String.class);
+
+        LOGGER.info("ADDITIONAL RESULT = " + additionalResult);
+        assertEquals("Additional Bundle!", additionalResult);
+
+        // send request and check response for the sub-packaged additional bundle
+        final Response subAdditionalResponse = target.path("/subadditional").request().build("GET").invoke();
+
+        LOGGER.info("SUB-PACKAGED ADDITIONAL http status = " + subAdditionalResponse.getStatus());
+        assertEquals(Response.Status.NOT_FOUND.getStatusCode(), subAdditionalResponse.getStatus());
+
+        // send request and check response for the WEB-INF classes located resource
+        final String webInfClassesResourceResult = target.path("/webinf").request().build("GET").invoke()
+                .readEntity(String.class);
+
+        LOGGER.info("WEB-INF CLASSES RESOURCE RESULT = " + webInfClassesResourceResult);
+        assertEquals("WebInfClassesResource", webInfClassesResourceResult);
+
+        // send request and check response for the WEB-INF classes located resource
+        final Response webInfClassesSubPackagedResourceResponse = target.path("/subwebinf").request().build("GET")
+                .invoke();
+
+        LOGGER.info("WEB-INF CLASSES SUB-PACKAGED http status = " + webInfClassesSubPackagedResourceResponse.getStatus());
+        assertEquals(Response.Status.NOT_FOUND.getStatusCode(), webInfClassesSubPackagedResourceResponse.getStatus());
+    }
+}
diff --git a/examples/osgi-helloworld-webapp/functional-test/src/test/resources/felix.policy b/examples/osgi-helloworld-webapp/functional-test/src/test/resources/felix.policy
new file mode 100644
index 0000000..abe0d50
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/functional-test/src/test/resources/felix.policy
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+ 
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*System*"] (java.security.AllPermission) } "allToSystem";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*System*"] (org.osgi.framework.AdminPermission) } "adminToSystem";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*System*"] (org.osgi.framework.PackagePermission) } "packageToSystem";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*additional-bundle*"] (java.security.AllPermission) } "allToAdditional";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*javassist*"] (java.security.AllPermission) } "allToJavassist";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*javax.ws.rs*"] (java.security.AllPermission) } "allToJavaxWsRs";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jetty*"] (java.security.AllPermission) } "allToJetty";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*hk2*"] (java.security.AllPermission) } "allToHk2";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*osgi*"] (java.security.AllPermission) } "allToOsgi";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*org.apache*"] (java.security.AllPermission) } "allToApache";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*org.apache*"] (org.osgi.framework.AdminPermission) } "adminToApache";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*org.apache*"] (org.osgi.framework.PackagePermission) } "PackageToApache";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*org.ops4j*"] (java.security.AllPermission) } "allToOps4J";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*org.ops4j*"] (org.osgi.framework.AdminPermission) } "adminToOps4J";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*org.ops4j*"] (org.osgi.framework.PackagePermission) } "packageToOps4J";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*client-bundle*"] (java.security.AllPermission) } "allToCB";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*war-bundle*"] (java.security.AllPermission) } "allToWar";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*war-bundle*"] (org.osgi.framework.AdminPermission) } "adminToWar";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "local"] (java.security.AllPermission) } "allToLocal";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "local"] (org.osgi.framework.AdminPermission) } "adminToLocal";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "local"] (org.osgi.framework.PackagePermission) } "packageToLocal";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-common*"] (org.osgi.framework.AdminPermission "*" "class,resolve,resource") } "adminPermissionToJerseyCommon";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-common*"] (org.osgi.framework.PackagePermission) } "packagePermissionToJerseyCommon";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-common*"] (java.lang.reflect.ReflectPermission "suppressAccessChecks") } "suppressAccessChecksToJerseyCommon";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-common*"] (java.net.SocketPermission "*" "connect,resolve") } "socketPermissionToJerseyCommon";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-common*"] (java.lang.RuntimePermission "accessDeclaredMembers") } "accessDeclaredMembersToJerseyCommon";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-common*"] (java.lang.RuntimePermission "accessClassInPackage.sun.misc") } "accessClassInPackageSunMisc";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-common*"] (java.lang.RuntimePermission "getClassLoader") } "getCLToJerseyCommon";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-common*"] (java.lang.RuntimePermission "modifyThread") } "modifyThreadToJerseyCommon";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-common*"] (java.util.PropertyPermission "*" "read") } "propReadToJerseyCommon";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-common*"] (java.io.FilePermission "<<ALL FILES>>" "read,write") } "fileReadToJerseyCommon";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-client*"] (java.security.AllPermission) } "allToJerseyClient";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-server*"] (org.osgi.framework.AdminPermission "*" "class,resolve,resource") } "adminPermissionToJerseyServer";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-server*"] (java.lang.reflect.ReflectPermission "suppressAccessChecks") } "suppressAccessChecksToJerseyServer";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-server*"] (java.lang.RuntimePermission "accessDeclaredMembers") } "accessDeclaredMembersToJerseyServer";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-server*"] (java.lang.RuntimePermission "setContextClassLoader") } "setContextCLToJerseyServer";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-server*"] (java.lang.RuntimePermission "getClassLoader") } "getClassLoaderToJerseyServer";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-server*"] (java.util.PropertyPermission "*" "read") } "propReadToJerseyServer";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-server*"] (java.io.FilePermission "<<ALL FILES>>" "read,write") } "fileReadToJerseyServer";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-server*"] (java.lang.RuntimePermission "modifyThread") } "modifyThreadToJerseyServer";
+allow { [org.osgi.service.condpermadmin.BundleLocationCondition "*jersey-container*"] (java.security.AllPermission) } "allToJerseyContainer";
diff --git a/examples/osgi-helloworld-webapp/functional-test/src/test/resources/log4j.properties b/examples/osgi-helloworld-webapp/functional-test/src/test/resources/log4j.properties
new file mode 100644
index 0000000..4fd23df
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/functional-test/src/test/resources/log4j.properties
@@ -0,0 +1,14 @@
+#
+# Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Distribution License v. 1.0, which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+log4j.rootCategory=ERROR, stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout.ConversionPattern=[%30.30c{1}] - %m%n
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
diff --git a/examples/osgi-helloworld-webapp/functional-test/src/test/resources/runtime.policy b/examples/osgi-helloworld-webapp/functional-test/src/test/resources/runtime.policy
new file mode 100644
index 0000000..dcd1277
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/functional-test/src/test/resources/runtime.policy
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+grant {
+  permission java.security.AllPermission;
+};
diff --git a/examples/osgi-helloworld-webapp/lib-bundle/pom.xml b/examples/osgi-helloworld-webapp/lib-bundle/pom.xml
new file mode 100644
index 0000000..18586bf
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/lib-bundle/pom.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>osgi-helloworld-webapp</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
+    <artifactId>lib-bundle</artifactId>
+    <name>jersey-examples-osgi-helloworld-webapp-lib</name>
+
+    <description>OSGi Helloworld Webapp - lib bundle</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- to generate the MANIFEST-FILE required by the bundle -->
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <manifestLocation>${project.build.directory}/META-INF</manifestLocation>
+                    <supportedProjectTypes>
+                        <supportedProjectType>bundle</supportedProjectType>
+                        <supportedProjectType>jar</supportedProjectType>
+                    </supportedProjectTypes>
+                    <instructions>
+                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                        <Import-Package>*</Import-Package>
+                        <Export-Package>org.glassfish.jersey.examples.osgi.helloworld.resource</Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <!-- do not create source zip bundles -->
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <configuration>
+                            <skipAssembly>true</skipAssembly>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/osgi-helloworld-webapp/lib-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/AnotherResource.java b/examples/osgi-helloworld-webapp/lib-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/AnotherResource.java
new file mode 100644
index 0000000..7d050e2
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/lib-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/AnotherResource.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgi.helloworld.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+@Path("/another")
+public class AnotherResource {
+
+    @GET
+    @Produces("text/plain")
+    public String getAnotherMessage() {
+        return "Another";
+    }
+}
diff --git a/examples/osgi-helloworld-webapp/lib-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/HelloWorldResource.java b/examples/osgi-helloworld-webapp/lib-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/HelloWorldResource.java
new file mode 100644
index 0000000..3998982
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/lib-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/HelloWorldResource.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgi.helloworld.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+@Path("/helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String getClichedMessage() {
+        return "Hello World";
+    }
+}
diff --git a/examples/osgi-helloworld-webapp/lib-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/MyApplication.java b/examples/osgi-helloworld-webapp/lib-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/MyApplication.java
new file mode 100644
index 0000000..d779561
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/lib-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/MyApplication.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgi.helloworld.resource;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+public class MyApplication extends Application {
+
+    static final Set<Class<?>> classes = new HashSet<Class<?>>() {
+        {
+            add(HelloWorldResource.class);
+            add(AnotherResource.class);
+        }};
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return classes;
+    }
+}
diff --git a/examples/osgi-helloworld-webapp/pom.xml b/examples/osgi-helloworld-webapp/pom.xml
new file mode 100644
index 0000000..1461b5f
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/pom.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>osgi-helloworld-webapp</artifactId>
+    <name>jersey-examples-osgi-helloworld-webapp</name>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>war-bundle</module>
+        <module>functional-test</module>
+        <module>lib-bundle</module>
+        <module>additional-bundle</module>
+        <module>alternate-version-bundle</module>
+    </modules>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/osgi-helloworld-webapp/war-bundle/pom.xml b/examples/osgi-helloworld-webapp/war-bundle/pom.xml
new file mode 100644
index 0000000..5bf8b16
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/war-bundle/pom.xml
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>osgi-helloworld-webapp</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
+    <artifactId>war-bundle</artifactId>
+    <name>jersey-examples-osgi-helloworld-webapp-wab</name>
+    <packaging>war</packaging>
+
+    <description>OSGi Helloworld Webapp WAR bundle</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
+            <artifactId>lib-bundle</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
+            <artifactId>additional-bundle</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.examples.osgi-helloworld-webapp</groupId>
+            <artifactId>alternate-version-bundle</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version><!-- the current project's ${servlet2.version} is 2.4 and that's not enough -->
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.eventadmin</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- to generate the MANIFEST-FILE required by the bundle -->
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <manifestLocation>${project.build.directory}/META-INF</manifestLocation>
+                    <supportedProjectTypes>
+                        <supportedProjectType>bundle</supportedProjectType>
+                        <supportedProjectType>war</supportedProjectType>
+                    </supportedProjectTypes>
+                    <instructions>
+                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                        <Bundle-ClassPath>.,WEB-INF/classes</Bundle-ClassPath>
+                        <Import-Package>
+                            org.glassfish.jersey.servlet,
+                            <!--
+                                This package is exported by two different bundle versions of 'additional-bundle'
+                                - one is in the maven module 'additional-bundle' with the current ${project.version},
+                                - the other one is in the maven module 'alternate-version-bundle' with Bundle-Version 2.2.1
+                                (the names of maven artifacts have to be unique, but the name of bundles
+                                (defined in the maven-bundle-plugin configuration) is the same for both.
+
+                                Both bundles (additional-bundle 2.2.1 and additional-bundle ${project.version} are loaded into
+                                OSGi runtime, so that there are two versions of the same resource available, each one returning
+                                different string as a response. If the import version is changed to [2.2,2.3) as prepared below,
+                                the test will fail (the response returned from the alternate version of the bundle is different).
+                                NOTE - to test this, the version has to be restricted from above, otherwise OSGi can choose any of
+                                the two bundles, as version="2.2" actually means "version 2.2. or higher".
+                            -->
+                            <!-- TODO: newer bundle plugin (3.2.0) generates version string from SNAPSHOTS differently than 2.x version -->
+                            org.glassfish.jersey.examples.osgi.helloworld.additional.resource,
+                            <!-- org.glassfish.jersey.examples.osgi.helloworld.additional.resource;version="${project.version}",-->
+                            <!-- org.glassfish.jersey.examples.osgi.helloworld.additional.resource;version="[2.2,2.3)", -->
+                            *
+                        </Import-Package>
+                        <Export-Package>
+                            org.glassfish.jersey.examples.osgi.helloworld,
+                            org.glassfish.jersey.examples.osgi.helloworld.resource,
+                            org.glassfish.jersey.examples.osgi.helloworld.additional.resource
+                        </Export-Package>
+                        <Embed-Directory>WEB-INF/lib</Embed-Directory>
+                        <Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>
+                        <Embed-Transitive>true</Embed-Transitive>
+                        <Webapp-Context>helloworld</Webapp-Context>
+                        <Web-ContextPath>helloworld</Web-ContextPath>
+                        <Bundle-Activator>org.glassfish.jersey.examples.osgi.helloworld.WebAppContextListener</Bundle-Activator>
+                        <_wab>src/main/webapp</_wab>
+                    </instructions>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <attachClasses>true</attachClasses>
+                    <archive>
+                        <manifestFile>${project.build.directory}/META-INF/MANIFEST.MF</manifestFile>
+                        <manifestEntries>
+                            <Bundle-ClassPath>WEB-INF/classes</Bundle-ClassPath>
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <!-- do not create source zip bundles -->
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <configuration>
+                            <skipAssembly>true</skipAssembly>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+          <id>moxy</id>
+          <activation>
+                <property>
+                    <name>moxy</name>
+                </property>
+          </activation>
+          <dependencies>
+                <dependency>
+                    <groupId>org.eclipse.persistence</groupId>
+                    <artifactId>org.eclipse.persistence.moxy</artifactId>
+                    <version>${moxy.version}</version>
+                    <scope>provided</scope>
+                </dependency>
+            </dependencies>
+         </profile>
+    </profiles>
+
+</project>
diff --git a/examples/osgi-helloworld-webapp/war-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/WebAppContextListener.java b/examples/osgi-helloworld-webapp/war-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/WebAppContextListener.java
new file mode 100644
index 0000000..a7da75b
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/war-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/WebAppContextListener.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgi.helloworld;
+
+import java.util.HashMap;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+
+/**
+ * This is to make sure we signal the application has been deployed/un-deployed
+ * via the OSGi EventAdmin service.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class WebAppContextListener implements BundleActivator, ServletContextListener {
+
+    static EventAdmin ea;
+
+    BundleContext bc;
+    ServiceReference eaRef;
+
+    static synchronized EventAdmin getEa() {
+        return ea;
+    }
+
+    static synchronized void setEa(EventAdmin ea) {
+        WebAppContextListener.ea = ea;
+    }
+
+    @Override
+    public void contextInitialized(final ServletContextEvent sce) {
+        if (getEa() != null) {
+            final String contextPath = sce.getServletContext().getContextPath();
+            getEa().sendEvent(new Event("jersey/test/DEPLOYED", new HashMap<String, String>() {{
+                put("context-path", contextPath);
+            }}));
+        }
+    }
+
+    @Override
+    public void contextDestroyed(final ServletContextEvent sce) {
+        if (getEa() != null) {
+            getEa().sendEvent(new Event("jersey/test/UNDEPLOYED", new HashMap<String, String>() {{
+                put("context-path", sce.getServletContext().getContextPath());
+            }}));
+        }
+    }
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        bc = context;
+        eaRef = bc.getServiceReference(EventAdmin.class.getName());
+        if (eaRef != null) {
+            setEa((EventAdmin) bc.getService(eaRef));
+        }
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        if (eaRef != null) {
+            setEa(null);
+            bc.ungetService(eaRef);
+        }
+    }
+}
diff --git a/examples/osgi-helloworld-webapp/war-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/WebInfClassesResource.java b/examples/osgi-helloworld-webapp/war-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/WebInfClassesResource.java
new file mode 100644
index 0000000..fd096c3
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/war-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/WebInfClassesResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgi.helloworld.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * This resource is physically located in WEB-INF/classes of a OSGI bundle.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("/webinf")
+public class WebInfClassesResource {
+
+    @GET
+    @Produces("text/plain")
+    public String getWebInfMessage() {
+        return "WebInfClassesResource";
+    }
+
+}
diff --git a/examples/osgi-helloworld-webapp/war-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/subpackage/WebInfClassesSubPackagedResource.java b/examples/osgi-helloworld-webapp/war-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/subpackage/WebInfClassesSubPackagedResource.java
new file mode 100644
index 0000000..cf5a9a6
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/war-bundle/src/main/java/org/glassfish/jersey/examples/osgi/helloworld/resource/subpackage/WebInfClassesSubPackagedResource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgi.helloworld.resource.subpackage;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * This resource is located in a sub-package and will be detected by OSGI framework only if recursive scanning is turned on.<br/>
+ * As a matter of fact, this resource is physically located in WEB-INF/classes which needs to be tested as well.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("/subwebinf")
+public class WebInfClassesSubPackagedResource {
+
+    @GET
+    @Produces("text/plain")
+    public String getSubPackagedWebInfMessage() {
+        return "WebInfClassesSubPackagedResource";
+    }
+
+}
diff --git a/examples/osgi-helloworld-webapp/war-bundle/src/main/webapp/WEB-INF/web.xml b/examples/osgi-helloworld-webapp/war-bundle/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..3408caa
--- /dev/null
+++ b/examples/osgi-helloworld-webapp/war-bundle/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+    <listener>
+        <listener-class>org.glassfish.jersey.examples.osgi.helloworld.WebAppContextListener</listener-class>
+    </listener>
+    <!-- We do not really need the welcome files, but the jetty bundled with pax exam requires it -->
+    <welcome-file-list>
+        <welcome-file>index.jsp</welcome-file>
+    </welcome-file-list>
+
+    <!-- Jersey APP that has enabled recursive scanning (the default value) -->
+    <servlet>
+        <servlet-name>Jersey Web Application</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>
+                    org.glassfish.jersey.examples.osgi.helloworld.resource,
+                    org.glassfish.jersey.examples.osgi.helloworld.additional.resource,
+            </param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>Jersey Web Application</servlet-name>
+        <url-pattern>/webresources/*</url-pattern>
+    </servlet-mapping>
+
+    <!-- Jersey APP with disabled recursive scanning -->
+    <servlet>
+        <servlet-name>Jersey Web Non-recursive Application</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>
+                org.glassfish.jersey.examples.osgi.helloworld.resource,
+                org.glassfish.jersey.examples.osgi.helloworld.additional.resource,
+                <!-- 'package' package path prevents regression in matching packages implemented in OsgiRegistry as it matches
+                string 'org.glassfish.jersey.examples.osgi.helloworld.resource.sub[package]' -->
+                package
+            </param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.server.provider.scanning.recursive</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>Jersey Web Non-recursive Application</servlet-name>
+        <url-pattern>/n-webresources/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/examples/osgi-http-service/README.MD b/examples/osgi-http-service/README.MD
new file mode 100644
index 0000000..0e09ad0
--- /dev/null
+++ b/examples/osgi-http-service/README.MD
@@ -0,0 +1,41 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+OSGi HttpService Example
+========================
+
+This example demonstrates how to develop a simple OSGi bundle
+registering a Jersey based RESTful web service as an [OSGi
+HttpService](http://www.osgi.org/javadoc/r4v42/org/osgi/service/http/HttpService.html)
+
+Contents
+--------
+
+The example bundle (see the `bundle` module) consists of just one Jersey resource:
+
+`org.glassfish.jersey.examples.osgihttpservice.StatusResource`
+
+that produces a textual response to an HTTP GET
+
+The mapping of the URI path space is presented in the following table:
+
+URI path        | Resource class   | HTTP method
+--------------- | ---------------- | -------------
+**_/status_**   | StatusResource   | GET
+
+Running the Example
+-------------------
+
+To run the example, you would need to build the OSGi bundle in the
+`bundle` module and install it to an OSGi runtime (e.g. Apache Felix)
+together with other OSGi bundles. Look at the attached `functional-test`
+module for details on the programatical runtime configuration
+
+To build the bundle jar file and run the tests, you can just launch
+
+>     mvn clean install
diff --git a/examples/osgi-http-service/bundle/pom.xml b/examples/osgi-http-service/bundle/pom.xml
new file mode 100644
index 0000000..4ee914e
--- /dev/null
+++ b/examples/osgi-http-service/bundle/pom.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>osgi-http-service</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.examples.osgi-http-service</groupId>
+    <artifactId>bundle</artifactId>
+    <name>jersey-examples-osgi-http-service-bundle</name>
+    <packaging>jar</packaging>
+
+    <description>OSGi HttpService example bundle</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.http.bundle</artifactId>
+            <version>2.2.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.eventadmin</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Export-Package>org.glassfish.jersey.examples.osgihttpservice</Export-Package>
+                        <Import-Package>javax.servlet.*;version="[2.4,5.0)",*</Import-Package>
+                        <Bundle-Activator>org.glassfish.jersey.examples.osgihttpservice.Activator</Bundle-Activator>
+                        <Implementation-Title>jersey-osgi-http-service-bundle</Implementation-Title>
+                        <Implementation-Version>${project.version}</Implementation-Version>
+                    </instructions>
+                    <unpackBundle>true</unpackBundle>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <!-- do not create source zip bundles -->
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <configuration>
+                            <skipAssembly>true</skipAssembly>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/osgi-http-service/bundle/src/main/java/org/glassfish/jersey/examples/osgihttpservice/Activator.java b/examples/osgi-http-service/bundle/src/main/java/org/glassfish/jersey/examples/osgihttpservice/Activator.java
new file mode 100644
index 0000000..7e26f36
--- /dev/null
+++ b/examples/osgi-http-service/bundle/src/main/java/org/glassfish/jersey/examples/osgihttpservice/Activator.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgihttpservice;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletException;
+
+import org.glassfish.jersey.servlet.ServletContainer;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.NamespaceException;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class Activator implements BundleActivator {
+
+    private BundleContext bc;
+    private ServiceTracker tracker;
+    private HttpService httpService = null;
+    private static final Logger logger = Logger.getLogger(Activator.class.getName());
+
+    @Override
+    public synchronized void start(BundleContext bundleContext) throws Exception {
+        this.bc = bundleContext;
+
+        logger.info("STARTING HTTP SERVICE BUNDLE");
+
+        this.tracker = new ServiceTracker(this.bc, HttpService.class.getName(), null) {
+
+            @Override
+            public Object addingService(ServiceReference serviceRef) {
+                httpService = (HttpService) super.addingService(serviceRef);
+                registerServlets();
+                return httpService;
+            }
+
+            @Override
+            public void removedService(ServiceReference ref, Object service) {
+                if (httpService == service) {
+                    unregisterServlets();
+                    httpService = null;
+                }
+                super.removedService(ref, service);
+            }
+        };
+
+        this.tracker.open();
+
+        logger.info("HTTP SERVICE BUNDLE STARTED");
+    }
+
+    @Override
+    public synchronized void stop(BundleContext bundleContext) throws Exception {
+        this.tracker.close();
+    }
+
+    private void registerServlets() {
+        try {
+            rawRegisterServlets();
+        } catch (InterruptedException | NamespaceException | ServletException ie) {
+            throw new RuntimeException(ie);
+        }
+    }
+
+    private void rawRegisterServlets() throws ServletException, NamespaceException, InterruptedException {
+        logger.info("JERSEY BUNDLE: REGISTERING SERVLETS");
+        logger.info("JERSEY BUNDLE: HTTP SERVICE = " + httpService.toString());
+
+        // TODO - temporary workaround
+        // This is a workaround related to issue JERSEY-2093; grizzly (1.9.5) needs to have the correct context
+        // classloader set
+        ClassLoader myClassLoader = getClass().getClassLoader();
+        ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
+        try {
+            Thread.currentThread().setContextClassLoader(myClassLoader);
+            httpService.registerServlet("/jersey-http-service", new ServletContainer(), getJerseyServletParams(), null);
+        } finally {
+            Thread.currentThread().setContextClassLoader(originalContextClassLoader);
+        }
+        // END of workaround - after grizzly updated to the recent version, only the inner call from try block will remain:
+        // httpService.registerServlet("/jersey-http-service", new ServletContainer(), getJerseyServletParams(), null);
+
+        sendAdminEvent();
+        logger.info("JERSEY BUNDLE: SERVLETS REGISTERED");
+    }
+
+    private void sendAdminEvent() {
+        ServiceReference eaRef = bc.getServiceReference(EventAdmin.class.getName());
+        if (eaRef != null) {
+            EventAdmin ea = (EventAdmin) bc.getService(eaRef);
+            ea.sendEvent(new Event("jersey/test/DEPLOYED", new HashMap<String, String>() {
+                {
+                    put("context-path", "/");
+                }
+            }));
+            bc.ungetService(eaRef);
+        }
+    }
+
+    private void unregisterServlets() {
+        if (this.httpService != null) {
+            logger.info("JERSEY BUNDLE: UNREGISTERING SERVLETS");
+            httpService.unregister("/jersey-http-service");
+            logger.info("JERSEY BUNDLE: SERVLETS UNREGISTERED");
+        }
+    }
+
+    @SuppressWarnings("UseOfObsoleteCollectionType")
+    private Dictionary<String, String> getJerseyServletParams() {
+        Dictionary<String, String> jerseyServletParams = new Hashtable<>();
+        jerseyServletParams.put("javax.ws.rs.Application", JerseyApplication.class.getName());
+        return jerseyServletParams;
+    }
+}
diff --git a/examples/osgi-http-service/bundle/src/main/java/org/glassfish/jersey/examples/osgihttpservice/JerseyApplication.java b/examples/osgi-http-service/bundle/src/main/java/org/glassfish/jersey/examples/osgihttpservice/JerseyApplication.java
new file mode 100644
index 0000000..1c8278c
--- /dev/null
+++ b/examples/osgi-http-service/bundle/src/main/java/org/glassfish/jersey/examples/osgihttpservice/JerseyApplication.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgihttpservice;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.ws.rs.core.Application;
+
+public class JerseyApplication extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        Set<Class<?>> result = new HashSet<Class<?>>();
+        result.add(StatusResource.class);
+        return result;
+    }
+
+}
diff --git a/examples/osgi-http-service/bundle/src/main/java/org/glassfish/jersey/examples/osgihttpservice/StatusResource.java b/examples/osgi-http-service/bundle/src/main/java/org/glassfish/jersey/examples/osgihttpservice/StatusResource.java
new file mode 100644
index 0000000..4ea3361
--- /dev/null
+++ b/examples/osgi-http-service/bundle/src/main/java/org/glassfish/jersey/examples/osgihttpservice/StatusResource.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgihttpservice;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("status")
+public class StatusResource {
+
+    @GET
+    @Produces("text/plain")
+    public String getStatus() {
+        return "active";
+    }
+}
diff --git a/examples/osgi-http-service/functional-test/pom.xml b/examples/osgi-http-service/functional-test/pom.xml
new file mode 100644
index 0000000..7d147fc
--- /dev/null
+++ b/examples/osgi-http-service/functional-test/pom.xml
@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>osgi-http-service</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.examples.osgi-http-service</groupId>
+    <artifactId>functional-test</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-examples-osgi-http-service-test</name>
+
+    <dependencies>
+        <!-- Example bundle -->
+        <dependency>
+            <groupId>org.glassfish.jersey.examples.osgi-http-service</groupId>
+            <artifactId>bundle</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.hk2.external</groupId>
+            <artifactId>aopalliance-repackaged</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- OSGi runtime - Felix -->
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.eventadmin</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Pax-Exam and JUnit dependencies -->
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-junit4</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-container-forked</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-link-mvn</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.web</groupId>
+            <artifactId>pax-web-jetty-bundle</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.web</groupId>
+            <artifactId>pax-web-extender-war</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Grizzly dependencies-->
+        <dependency>
+            <groupId>com.sun.grizzly.osgi</groupId>
+            <artifactId>grizzly-httpservice-bundle</artifactId>
+            <scope>test</scope>
+            <version>1.9.45</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-servlet</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Logging dependencies-->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>1.6.4</version>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.ops4j.pax.exam</groupId>
+                <artifactId>maven-paxexam-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>generate-config</id>
+                        <goals>
+                            <goal>generate-depends-file</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>test-compile</id>
+                        <phase>test-compile</phase>
+                        <goals>
+                            <goal>testCompile</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>process-test-resources</phase>
+                        <goals>
+                            <goal>testResources</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkMode>always</forkMode>
+                    <enableAssertions>false</enableAssertions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>test</id>
+                        <phase>test</phase>
+                        <goals>
+                            <goal>test</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <!-- do not create source zip bundles -->
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <configuration>
+                            <skipAssembly>true</skipAssembly>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/osgi-http-service/functional-test/src/test/java/org/glassfish/jersey/examples/osgihttpservice/test/AbstractHttpServiceTest.java b/examples/osgi-http-service/functional-test/src/test/java/org/glassfish/jersey/examples/osgihttpservice/test/AbstractHttpServiceTest.java
new file mode 100644
index 0000000..8ec4109
--- /dev/null
+++ b/examples/osgi-http-service/functional-test/src/test/java/org/glassfish/jersey/examples/osgihttpservice/test/AbstractHttpServiceTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgihttpservice.test;
+
+import java.net.URI;
+import java.security.AccessController;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.UriBuilder;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+import static org.junit.Assert.assertEquals;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public abstract class AbstractHttpServiceTest {
+
+    @Inject
+    BundleContext bundleContext;
+
+    /** maximum waiting time for runtime initialization and Jersey deployment */
+    public static final long MAX_WAITING_SECONDS = 10L;
+
+    /** Latch for blocking the testing thread until the runtime is ready and Jersey deployed */
+    final CountDownLatch countDownLatch = new CountDownLatch(1);
+
+    private static final int port = getProperty("jersey.config.test.container.port", 8080);
+    private static final String CONTEXT = "/jersey-http-service";
+    private static final URI baseUri = UriBuilder.fromUri("http://localhost").port(port).path(CONTEXT).build();
+    private static final String BundleLocationProperty = "jersey.bundle.location";
+
+    private static final Logger LOGGER = Logger.getLogger(AbstractHttpServiceTest.class.getName());
+
+    public abstract List<Option> httpServiceProviderOptions();
+
+    public abstract List<Option> osgiRuntimeOptions();
+
+    public List<Option> genericOsgiOptions() {
+        @SuppressWarnings("RedundantStringToString")
+        final String bundleLocation = mavenBundle()
+                .groupId("org.glassfish.jersey.examples.osgi-http-service")
+                .artifactId("bundle")
+                .versionAsInProject().getURL().toString();
+
+        List<Option> options = Arrays.asList(options(
+                systemProperty("org.osgi.service.http.port").value(String.valueOf(port)),
+                systemProperty(BundleLocationProperty).value(bundleLocation),
+                systemProperty("jersey.config.test.container.port").value(String.valueOf(port)),
+                systemProperty("org.osgi.framework.system.packages.extra").value("javax.annotation"),
+
+                // do not remove the following line
+                // systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("FINEST"),
+
+                // uncomment for logging (do not remove the following two lines)
+                // mavenBundle("org.ops4j.pax.logging", "pax-logging-api", "1.4"),
+                // mavenBundle("org.ops4j.pax.logging", "pax-logging-service", "1.4"),
+
+                // javax.annotation has to go first!
+                mavenBundle().groupId("javax.annotation").artifactId("javax.annotation-api").versionAsInProject(),
+
+                mavenBundle("org.ops4j.pax.url", "pax-url-mvn"),
+                junitBundles(),
+
+                // HK2
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("hk2-api").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("osgi-resource-locator").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("hk2-locator").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("hk2-utils").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2.external").artifactId("javax.inject").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2.external").artifactId("aopalliance-repackaged").versionAsInProject(),
+                mavenBundle().groupId("org.javassist").artifactId("javassist").versionAsInProject(),
+
+                // JAX-RS API
+                mavenBundle().groupId("javax.ws.rs").artifactId("javax.ws.rs-api").versionAsInProject(),
+
+                // validation
+                mavenBundle().groupId("javax.validation").artifactId("validation-api").versionAsInProject(),
+
+                // Jersey bundles
+                mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-common").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-server").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-client").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.containers").artifactId("jersey-container-servlet-core")
+                .versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.inject").artifactId("jersey-hk2").versionAsInProject()
+                ));
+
+        final String localRepository = AccessController.doPrivileged(PropertiesHelper.getSystemProperty("localRepository"));
+        if (localRepository != null) {
+            options = new ArrayList<>(options);
+            options.add(systemProperty("org.ops4j.pax.url.mvn.localRepository").value(localRepository));
+        }
+
+        return options;
+    }
+
+    public List<Option> grizzlyOptions() {
+        return Arrays.asList(options(
+                mavenBundle().groupId("com.sun.grizzly.osgi").artifactId("grizzly-httpservice-bundle").versionAsInProject()
+        ));
+    }
+
+    public List<Option> jettyOptions() {
+        return Arrays.asList(options(
+                mavenBundle().groupId("org.ops4j.pax.web").artifactId("pax-web-jetty-bundle").versionAsInProject(),
+                mavenBundle().groupId("org.ops4j.pax.web").artifactId("pax-web-extender-war").versionAsInProject()));
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    public class WebEventHandler implements EventHandler {
+
+        @Override
+        public void handleEvent(Event event) {
+            countDownLatch.countDown();
+        }
+
+        public WebEventHandler(String handlerName) {
+            this.handlerName = handlerName;
+        }
+
+        private final String handlerName;
+
+        protected String getHandlerName() {
+            return handlerName;
+        }
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    @Configuration
+    public Option[] configuration() {
+        final List<Option> options = new LinkedList<>();
+
+        options.addAll(genericOsgiOptions());
+        options.addAll(httpServiceProviderOptions());
+        options.addAll(osgiRuntimeOptions());
+
+        return options.toArray(new Option[options.size()]);
+    }
+
+    public void defaultMandatoryBeforeMethod() {
+        LOGGER.fine("Registering event handler for jersey/test/DEPLOYED");
+        bundleContext.registerService(EventHandler.class.getName(), new WebEventHandler("Deploy Handler"),
+                getHandlerServiceProperties("jersey/test/DEPLOYED"));
+    }
+
+    public void defaultHttpServiceTestMethod() throws Exception {
+        // log the list of bundles and result of the attempt to start
+        StringBuilder sb = new StringBuilder();
+        sb.append("-- Bundle list -- \n");
+        for (Bundle b : bundleContext.getBundles()) {
+            sb.append(String.format("%1$5s", "[" + b.getBundleId() + "]")).append(" ")
+                    .append(String.format("%1$-70s", b.getSymbolicName())).append(" | ")
+                    .append(String.format("%1$-20s", b.getVersion())).append(" |");
+            try {
+                b.start();
+                sb.append(" STARTED  | ");
+            } catch (BundleException e) {
+                sb.append(" *FAILED* | ").append(e.getMessage());
+            }
+            sb.append(b.getLocation()).append("\n");
+        }
+        sb.append("-- \n\n");
+        LOGGER.fine(sb.toString());
+
+        // start the example bundle
+        bundleContext.installBundle(
+                AccessController.doPrivileged(PropertiesHelper.getSystemProperty(BundleLocationProperty))
+        ).start();
+
+        LOGGER.fine("Waiting for jersey/test/DEPLOYED event with timeout " + MAX_WAITING_SECONDS + " seconds...");
+        if (!countDownLatch.await(MAX_WAITING_SECONDS, TimeUnit.SECONDS)) {
+            throw new TimeoutException("The event jersey/test/DEPLOYED did not arrive in "
+                    + MAX_WAITING_SECONDS
+                    + " seconds. Waiting timed out.");
+        }
+
+        Client c = ClientBuilder.newClient();
+        final WebTarget target = c.target(baseUri);
+
+        String result = target.path("/status").request().build("GET").invoke().readEntity(String.class);
+
+        LOGGER.info("JERSEY RESULT = " + result);
+        assertEquals("active", result);
+    }
+
+    public static int getProperty(final String varName, int defaultValue) {
+        if (null == varName) {
+            return defaultValue;
+        }
+        String varValue = AccessController.doPrivileged(PropertiesHelper.getSystemProperty(varName));
+        if (null != varValue) {
+            try {
+                return Integer.parseInt(varValue);
+            } catch (NumberFormatException e) {
+                // will return default value below
+            }
+        }
+        return defaultValue;
+    }
+
+    @SuppressWarnings({"UseOfObsoleteCollectionType", "unchecked"})
+    private Dictionary getHandlerServiceProperties(String... topics) {
+        Dictionary result = new Hashtable();
+        result.put(EventConstants.EVENT_TOPIC, topics);
+        return result;
+    }
+}
diff --git a/examples/osgi-http-service/functional-test/src/test/java/org/glassfish/jersey/examples/osgihttpservice/test/GrizzlyHttpServiceFelixTest.java b/examples/osgi-http-service/functional-test/src/test/java/org/glassfish/jersey/examples/osgihttpservice/test/GrizzlyHttpServiceFelixTest.java
new file mode 100644
index 0000000..7e77916
--- /dev/null
+++ b/examples/osgi-http-service/functional-test/src/test/java/org/glassfish/jersey/examples/osgihttpservice/test/GrizzlyHttpServiceFelixTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgihttpservice.test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+@RunWith(PaxExam.class)
+public class GrizzlyHttpServiceFelixTest extends AbstractHttpServiceTest {
+
+    @Override
+    public List<Option> osgiRuntimeOptions() {
+        return Arrays.asList(CoreOptions.options(
+                mavenBundle()
+                        .groupId("org.apache.felix").artifactId("org.apache.felix.eventadmin")
+                        .versionAsInProject()
+        )
+        );
+    }
+
+    @Override
+    public List<Option> httpServiceProviderOptions() {
+        return grizzlyOptions();
+    }
+
+    @Before
+    public void before() {
+        defaultMandatoryBeforeMethod();
+    }
+
+    @Test
+    public void testHttpServiceMethod() throws Exception {
+        defaultHttpServiceTestMethod();
+    }
+}
+
diff --git a/examples/osgi-http-service/functional-test/src/test/java/org/glassfish/jersey/examples/osgihttpservice/test/JettyHttpServiceFelixTest.java b/examples/osgi-http-service/functional-test/src/test/java/org/glassfish/jersey/examples/osgihttpservice/test/JettyHttpServiceFelixTest.java
new file mode 100644
index 0000000..f73bb11
--- /dev/null
+++ b/examples/osgi-http-service/functional-test/src/test/java/org/glassfish/jersey/examples/osgihttpservice/test/JettyHttpServiceFelixTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.osgihttpservice.test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+@RunWith(PaxExam.class)
+public class JettyHttpServiceFelixTest extends AbstractHttpServiceTest {
+
+    @Override
+    public List<Option> osgiRuntimeOptions() {
+        return Arrays.asList(CoreOptions.options(
+                mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.eventadmin").versionAsInProject()
+        ));
+    }
+
+    @Override
+    public List<Option> httpServiceProviderOptions() {
+        return jettyOptions();
+    }
+
+    @Before
+    public void before() {
+        defaultMandatoryBeforeMethod();
+    }
+
+    @Test
+    public void testHttpServiceMethod() throws Exception {
+        defaultHttpServiceTestMethod();
+    }
+}
+
diff --git a/examples/osgi-http-service/functional-test/src/test/resources/log4j.properties b/examples/osgi-http-service/functional-test/src/test/resources/log4j.properties
new file mode 100644
index 0000000..4fd23df
--- /dev/null
+++ b/examples/osgi-http-service/functional-test/src/test/resources/log4j.properties
@@ -0,0 +1,14 @@
+#
+# Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Distribution License v. 1.0, which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+log4j.rootCategory=ERROR, stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout.ConversionPattern=[%30.30c{1}] - %m%n
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
diff --git a/examples/osgi-http-service/pom.xml b/examples/osgi-http-service/pom.xml
new file mode 100644
index 0000000..26b2b0f
--- /dev/null
+++ b/examples/osgi-http-service/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>osgi-http-service</artifactId>
+    <name>jersey-examples-osgi-http-service</name>
+    <packaging>pom</packaging>
+
+    <description>OSGi HttpService example</description>
+
+    <modules>
+        <module>bundle</module>
+        <module>functional-test</module>
+    </modules>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/pom.xml b/examples/pom.xml
new file mode 100644
index 0000000..20d8a70
--- /dev/null
+++ b/examples/pom.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <licenses>
+        <license>
+            <name>Eclipse Distribution License v. 1.0</name>
+            <url>http://www.eclipse.org/org/documents/edl-v10.php</url>
+            <distribution>repo</distribution>
+            <comments>Except for AngularJS, bootstrap.js, jQuery, jQuery Barcode plugin, KineticJS
+                See also https://github.com/eclipse-ee4j/jersey/blob/master/NOTICE.md</comments>
+        </license>
+        <license>
+            <name>jQuery license</name>
+            <url>jquery.org/license</url>
+            <distribution>repo</distribution>
+            <comments>jQuery v1.12.4</comments>
+        </license>
+        <license>
+            <name>MIT license</name>
+            <url>http://www.opensource.org/licenses/mit-license.php</url>
+            <distribution>repo</distribution>
+            <comments>AngularJS, Bootstrap v3.3.7 (http://getbootstrap.com), 
+                      jQuery Barcode plugin 0.3, KineticJS v4.7.1</comments>
+        </license>
+    </licenses>
+
+    <groupId>org.glassfish.jersey.examples</groupId>
+    <artifactId>project</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-examples</name>
+
+    <description>Jersey examples</description>
+
+    <modules>
+        <module>assemblies</module>
+        <module>bean-validation-webapp</module>
+        <module>bookmark</module>
+        <module>bookmark-em</module>
+        <module>bookstore-webapp</module>
+        <module>cdi-webapp</module>
+        <module>clipboard</module>
+        <module>clipboard-programmatic</module>
+        <module>declarative-linking</module>
+        <module>entity-filtering</module>
+        <module>entity-filtering-selectable</module>
+        <module>entity-filtering-security</module>
+        <module>extended-wadl-webapp</module>
+        <module>exception-mapping</module>
+        <module>feed-combiner-java8-webapp</module>
+        <module>freemarker-webapp</module>
+        <module>flight-mgmt-webapp</module>
+        <module>groovy</module>
+        <module>helloworld</module>
+        <module>helloworld-benchmark</module>
+        <module>helloworld-cdi2-se</module>
+        <module>helloworld-netty</module>
+        <module>helloworld-programmatic</module>
+        <module>helloworld-pure-jax-rs</module>
+        <module>helloworld-webapp</module>
+        <module>helloworld-weld</module>
+        <module>helloworld-spring-webapp</module>
+        <module>helloworld-spring-annotations</module>
+        <module>http-patch</module>
+        <module>http-trace</module>
+        <module>https-clientserver-grizzly</module>
+        <module>https-server-glassfish</module>
+        <module>java8-webapp</module>
+        <module>jaxb</module>
+        <module>jaxrs-types-injection</module>
+        <module>jersey-ejb</module>
+        <module>json-binding-webapp</module>
+        <module>json-jackson</module>
+        <module>json-jackson1</module>
+        <module>json-jettison</module>
+        <module>json-moxy</module>
+        <module>json-processing-webapp</module>
+        <module>json-with-padding</module>
+        <module>managed-beans-webapp</module>
+        <module>managed-client</module>
+        <module>managed-client-webapp</module>
+        <module>managed-client-simple-webapp</module>
+        <module>monitoring-webapp</module>
+        <module>multipart-webapp</module>
+        <module>open-tracing</module>
+        <module>osgi-helloworld-webapp</module>
+        <module>osgi-http-service</module>
+        <module>oauth-client-twitter</module>
+        <module>oauth2-client-google-webapp</module>
+        <module>reload</module>
+        <module>rx-client-webapp</module>
+        <module>server-async</module>
+        <module>server-async-managed</module>
+        <module>server-async-standalone</module>
+        <module>server-sent-events-jersey</module>
+        <module>server-sent-events-jaxrs</module>
+        <module>servlet3-webapp</module>
+        <module>simple-console</module>
+        <module>shortener-webapp</module>
+        <module>sparklines</module>
+        <module>sse-item-store-jersey-webapp</module>
+        <module>sse-item-store-jaxrs-webapp</module>
+        <module>sse-twitter-aggregator</module>
+        <module>system-properties-example</module>
+        <module>tone-generator</module>
+        <module>webapp-example-parent</module>
+        <module>xml-moxy</module>
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.glassfish.jersey</groupId>
+                <artifactId>jersey-bom</artifactId>
+                <version>${project.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-assembly-plugin</artifactId>
+                    <dependencies>
+                        <!-- Contains shared Jersey example assembly descriptors-->
+                        <dependency>
+                            <groupId>org.glassfish.jersey.examples</groupId>
+                            <artifactId>assemblies</artifactId>
+                            <version>${project.version}</version>
+                        </dependency>
+                    </dependencies>
+                    <executions>
+                        <execution>
+                            <id>assemble-src-zip</id>
+                            <phase>package</phase>
+                            <goals>
+                                <goal>single</goal>
+                            </goals>
+                            <configuration>
+                                <descriptorRefs>
+                                    <!-- Reference to a descriptor in org.glassfish.jersey.examples:assemblies module -->
+                                    <descriptorRef>src-zip</descriptorRef>
+                                </descriptorRefs>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>exec-maven-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <goals>
+                                <goal>java</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+</project>
diff --git a/examples/reload/README.MD b/examples/reload/README.MD
new file mode 100644
index 0000000..57a857b
--- /dev/null
+++ b/examples/reload/README.MD
@@ -0,0 +1,69 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Resource Configuration Reload Example
+=====================================
+
+This example demonstrates how to create a simple Jersey application with
+resource reloading capability using
+[Grizzly HTTP Server container](http://grizzly.java.net).
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                    | Resource class                                            | Content                                          | HTTP methods
+--------------------------- | --------------------------------------------------------- | ------------------------------------------------ | --------------
+**_/flights/arrivals_**     | org.glassfish.jersey.examples.reload.ArrivalsResource     | Dummy Arrivals Information                       | GET
+**_/flights/departures_**   | org.glassfish.jersey.examples.reload.DeparturesResource   | Dummy Departures Information                     | GET
+**_/flights/stats_**        | org.glassfish.jersey.examples.reload.StatsResource        | Statistics on application resource utilization   | GET
+
+The application gets configured via a plain text file named `resources`.
+The file contains a list of resource classes to be published by the
+application. After the application gets started, it watches the file system
+and reloads the application as file updates are being detected.
+Changes in JAX-RS resource source files and updates of the above mentioned resources file
+are being detected. Java code re-compilation is done as required.
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys current example using[Grizzly
+container](http://grizzly.java.net/). StatsResource should be available
+at <http://localhost:8080/flights/stats>
+
+You may want to check the other resources as well:
+
+-   <http://localhost:8080/flights/departures>
+-   <http://localhost:8080/flights/arrivals>
+
+Updated information on number of hits should be available at
+<http://localhost:8080/flights/stats>.
+Now you can try to edit the `resources` file and comment the stats
+resource out by using the `#` prefix. The point is you leave the
+application running while editing the file. The changes wil get picked
+up automatically. No need to manually restart the application. The
+`resource` file content should now look like follows:
+
+    org.glassfish.jersey.examples.reload.DeparturesResource
+    org.glassfish.jersey.examples.reload.ArrivalsResource
+    #org.glassfish.jersey.examples.reload.StatsResource
+
+Once you save the change, the stats resource should not be available,
+but the other, departures/arrivals, resources should remain functional.
+
+Now you can revert the change back and check the stats resource is back
+providing actual statistics information.
+
+You can also try to change the JAX-RS resources source code. These changes
+will be also picked up automatically.
diff --git a/examples/reload/pom.xml b/examples/reload/pom.xml
new file mode 100644
index 0000000..ebf097d
--- /dev/null
+++ b/examples/reload/pom.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>reload</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-reload</name>
+
+    <description>Jersey resource configuration reload example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <!--<version>2.2</version>-->
+                <executions>
+                    <execution>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>build-classpath</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <outputFile>${project.build.directory}/classpath.properties</outputFile>
+                    <outputFilterFile>true</outputFilterFile>
+                    <includeScope>compile</includeScope>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>properties-maven-plugin</artifactId>
+                <version>1.0-alpha-2</version>
+                <executions>
+                    <execution>
+                        <id>read-project-properties</id>
+                        <goals>
+                            <goal>read-project-properties</goal>
+                        </goals>
+                        <!--<phase>initialize</phase>-->
+                        <phase>generate-sources</phase>
+                        <configuration>
+                            <files>
+                                <file>${project.build.directory}/classpath.properties</file>
+                            </files>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.reload.App</mainClass>
+                    <arguments><argument>-cp=${classpath}</argument></arguments>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/reload/resources b/examples/reload/resources
new file mode 100644
index 0000000..0ad113a
--- /dev/null
+++ b/examples/reload/resources
@@ -0,0 +1,3 @@
+org.glassfish.jersey.examples.reload.DeparturesResource
+org.glassfish.jersey.examples.reload.ArrivalsResource
+#org.glassfish.jersey.examples.reload.StatsResource
diff --git a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/App.java b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/App.java
new file mode 100644
index 0000000..30244f6
--- /dev/null
+++ b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/App.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.reload;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.examples.reload.compiler.AppClassLoader;
+import org.glassfish.jersey.examples.reload.compiler.Compiler;
+import org.glassfish.jersey.examples.reload.compiler.JavaFile;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.ContainerLifecycleListener;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import com.sun.nio.file.SensitivityWatchEventModifier;
+
+/**
+ * Reload example application.
+ * <p/>
+ * A {@link ContainerLifecycleListener container listener} gets registered
+ * with the application. Upon application startup notification, the listener schedules
+ * a new {@link TimerTask timer task} to check a text file called {@code resources}
+ * every 2 seconds. When the text file change is detected, the application gets reloaded with
+ * a new {@link ResourceConfig resource configuration} including all
+ * resource classes listed in that file.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class App {
+
+    private static final Logger LOGGER = Logger.getLogger(App.class.getName());
+    private static final URI BASE_URI = URI.create("http://localhost:8080/flights/");
+    public static final String ROOT_PATH = "arrivals";
+    public static final String CONFIG_FILENAME = "resources";
+    public static final String SRC_MAIN_JAVA = "src/main/java";
+
+    static Container container;
+
+    static class FileCheckTask extends TimerTask {
+
+        @Override
+        public void run() {
+
+            WatchService watcher;
+
+            try {
+                watcher = FileSystems.getDefault().newWatchService();
+
+                Path srcDir = Paths.get("src/main/java/org/glassfish/jersey/examples/reload");
+                registerWatcher(watcher, srcDir);
+
+                Path configFilePath = Paths.get(".");
+                registerWatcher(watcher, configFilePath);
+
+            } catch (IOException e) {
+                e.printStackTrace();
+                throw new RuntimeException("Could not initialize watcher service!");
+            }
+
+            for (;;) {
+
+                try {
+                    final WatchKey watchKey = watcher.take();
+
+                    try {
+                        for (WatchEvent<?> event : watchKey.pollEvents()) {
+                            final WatchEvent.Kind<?> kind = event.kind();
+                            if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
+                                WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
+                                Path modifiedFile = pathEvent.context();
+                                System.out.printf("FILE MODIFIED: %s\n", modifiedFile);
+                            }
+                        }
+                    } finally {
+                        watchKey.reset(); // so that consecutive events could be processed
+                    }
+
+                    final File configFile = new File(CONFIG_FILENAME);
+                    reloadApp(configFile);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+
+        private void registerWatcher(WatchService watcher, Path directory) throws IOException {
+            directory.register(watcher,
+                    new WatchEvent.Kind[]{
+                            StandardWatchEventKinds.ENTRY_MODIFY
+                    },
+                    SensitivityWatchEventModifier.HIGH);
+        }
+
+        private void reloadApp(final File configFile) {
+            LOGGER.info("Reloading resource classes:");
+            final ResourceConfig rc = createResourceConfig(configFile);
+            App.container.reload(rc);
+        }
+
+    }
+
+    private static ResourceConfig createResourceConfig(File configFile) {
+        final ResourceConfig rc = new ResourceConfig();
+
+        try {
+            final AppClassLoader appClassLoader = new AppClassLoader(Thread.currentThread().getContextClassLoader());
+            final List<JavaFile> javaFiles = getJavaFiles(configFile);
+
+            Compiler.compile(appClassLoader, javaFiles);
+
+            for (JavaFile javaFile : javaFiles) {
+                try {
+                    rc.registerClasses(appClassLoader.loadClass(javaFile.getClassName()));
+                } catch (final ClassNotFoundException ex) {
+                    LOGGER.info(String.format(" ! class %s not found.\n", javaFile.getClassName()));
+                }
+            }
+        } catch (final Exception ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+        return rc;
+    }
+
+    private static List<JavaFile> getJavaFiles(File configFile) throws Exception {
+
+        final List<JavaFile> javaFiles = new LinkedList<>();
+
+        try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(configFile), "UTF-8"))) {
+            while (r.ready()) {
+                final String className = r.readLine();
+                if (!className.startsWith("#")) {
+                    javaFiles.add(new JavaFile(className, SRC_MAIN_JAVA));
+                    LOGGER.info(String.format(" + included class %s.\n", className));
+                } else {
+                    LOGGER.info(String.format(" - ignored class %s\n", className.substring(1)));
+                }
+            }
+        }
+        return javaFiles;
+    }
+
+    public static void main(final String[] args) throws Exception {
+        try {
+            LOGGER.info("Resource Config Reload Jersey Example App");
+
+            for (String s : args) {
+                if (s.startsWith("-cp=")) {
+                    Compiler.classpath = s.substring(4);
+                }
+            }
+
+            final ResourceConfig resourceConfig = createResourceConfig(new File(CONFIG_FILENAME));
+            registerReloader(resourceConfig);
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, true);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(
+                    String.format("Application started.\nTry out %s%s\nStop the application using CTRL+C", BASE_URI, ROOT_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    private static Class<?> loadClass(String className) throws Exception {
+        final JavaFile javaFile = new JavaFile(className, SRC_MAIN_JAVA);
+        return Compiler.compile(className, javaFile);
+    }
+
+    private static void registerReloader(ResourceConfig resourceConfig) {
+        resourceConfig.registerInstances(new ContainerLifecycleListener() {
+            @Override
+            public void onStartup(final Container container) {
+                App.container = container;
+                final Timer t = new Timer(true);
+                t.schedule(new FileCheckTask(), 0);
+            }
+
+            @Override
+            public void onReload(final Container container) {
+                System.out.println("Application has been reloaded!");
+            }
+
+            @Override
+            public void onShutdown(final Container container) {
+                // ignore
+            }
+        });
+    }
+}
diff --git a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/ArrivalsResource.java b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/ArrivalsResource.java
new file mode 100644
index 0000000..b849f5a
--- /dev/null
+++ b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/ArrivalsResource.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.reload;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("arrivals")
+public class ArrivalsResource {
+
+    @GET
+    @Produces("text/plain")
+    public String getArrivals() {
+        FlightsDB.arrivalsReqCount.incrementAndGet();
+        return "No arrival scheduled in the following days";
+    }
+}
diff --git a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/DeparturesResource.java b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/DeparturesResource.java
new file mode 100644
index 0000000..80f9098
--- /dev/null
+++ b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/DeparturesResource.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.reload;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("departures")
+public class DeparturesResource {
+
+    @GET
+    @Produces("text/plain")
+    public String getDepartures() {
+        FlightsDB.departuresReqCount.incrementAndGet();
+        return "No departure scheduled in the following days";
+    }
+}
diff --git a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/FlightsDB.java b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/FlightsDB.java
new file mode 100644
index 0000000..5de93db
--- /dev/null
+++ b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/FlightsDB.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.reload;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class FlightsDB {
+
+    static AtomicInteger departuresReqCount = new AtomicInteger();
+    static AtomicInteger arrivalsReqCount = new AtomicInteger();
+}
diff --git a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/StatsResource.java b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/StatsResource.java
new file mode 100644
index 0000000..b10fca6
--- /dev/null
+++ b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/StatsResource.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.reload;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("stats")
+@Produces("text/plain")
+public class StatsResource {
+
+    @GET
+    public String getStats() {
+        return String.format("Arrivals resource hits: %d\nDepartures resource hits: %d",
+                FlightsDB.arrivalsReqCount.get(), FlightsDB.departuresReqCount.get());
+    }
+}
diff --git a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/AppClassLoader.java b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/AppClassLoader.java
new file mode 100644
index 0000000..f9a8828
--- /dev/null
+++ b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/AppClassLoader.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.reload.compiler;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * In order to load re-compiled classes we need
+ * to have a separate class-loader for each reload.
+ *
+ * Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class AppClassLoader extends ClassLoader {
+
+    private final Map<String, ClassFile> classFiles = new HashMap<>();
+
+    public AppClassLoader(ClassLoader parent) {
+        super(parent);
+    }
+
+    public void setCode(ClassFile cc) {
+        classFiles.put(cc.getName(), cc);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        ClassFile cc = classFiles.get(name);
+        if (cc == null) {
+            return super.findClass(name);
+        }
+        byte[] byteCode = cc.getByteCode();
+        return defineClass(name, byteCode, 0, byteCode.length);
+    }
+
+    @Override
+    public Class<?> loadClass(String name) throws ClassNotFoundException {
+        // we are cheating here, the parent already has the class, but we prefer our bytecode to be used.
+        ClassFile cc = classFiles.get(name);
+        if (cc == null) {
+            return super.loadClass(name);
+        }
+        byte[] byteCode = cc.getByteCode();
+        return defineClass(name, byteCode, 0, byteCode.length);
+    }
+}
diff --git a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/ClassFile.java b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/ClassFile.java
new file mode 100644
index 0000000..b4511cf
--- /dev/null
+++ b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/ClassFile.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.reload.compiler;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.tools.SimpleJavaFileObject;
+
+/**
+ * Class file representation.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class ClassFile extends SimpleJavaFileObject {
+
+    private final String className;
+
+    private final ByteArrayOutputStream byteCode = new ByteArrayOutputStream();
+
+    /**
+     * Creates a new class file holder.
+     *
+     * @param className class name.
+     * @throws URISyntaxException in case given class name could not be represented as an URI.
+     */
+    public ClassFile(String className) throws URISyntaxException {
+        super(new URI(className), Kind.CLASS);
+        this.className = className;
+    }
+
+    /**
+     * Getter for class name associated with this class file.
+     *
+     * @return class name.
+     */
+    public String getClassName() {
+        return className;
+    }
+
+    @Override
+    public OutputStream openOutputStream() throws IOException {
+        return byteCode;
+    }
+
+    /**
+     * Returns byte code representation of the class after the class has been compiled.
+     *
+     * @return compiled byte code of the class.
+     */
+    public byte[] getByteCode() {
+        return byteCode.toByteArray();
+    }
+}
diff --git a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/Compiler.java b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/Compiler.java
new file mode 100644
index 0000000..33602e3
--- /dev/null
+++ b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/Compiler.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.reload.compiler;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.ToolProvider;
+
+/**
+ * Java compiler utility.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class Compiler {
+
+    private static JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
+
+    /**
+     * Compiler classpath.
+     */
+    public static String classpath;
+
+    /**
+     * Compiles a single class and loads the class using a new class loader.
+     *
+     * @param className class to compile.
+     * @param sourceCode source code of the class to compile.
+     * @return loaded class
+     * @throws Exception
+     */
+    public static Class<?> compile(String className, SimpleJavaFileObject sourceCode) throws Exception {
+        ClassFile classFile = new ClassFile(className);
+
+        Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(sourceCode);
+        AppClassLoader cl = new AppClassLoader(Thread.currentThread().getContextClassLoader());
+        FileManager fileManager = new FileManager(javac.getStandardFileManager(null, null, null), Arrays.asList(classFile), cl);
+        JavaCompiler.CompilationTask task = javac.getTask(null, fileManager, null, getClOptions(), null, compilationUnits);
+        task.call();
+        return cl.loadClass(className);
+    }
+
+
+    /**
+     * Compiles multiple source files at once.
+     *
+     * @param appClassLoader common class loader for the classes.
+     * @param javaFiles source files to compile.
+     * @throws Exception in case something goes wrong.
+     */
+    public static void compile(AppClassLoader appClassLoader, List<JavaFile> javaFiles) throws Exception {
+
+        List<ClassFile> classes = new LinkedList<>();
+
+        for (JavaFile javaFile : javaFiles) {
+            classes.add(new ClassFile(javaFile.getClassName()));
+        }
+        Iterable<? extends JavaFileObject> compilationUnits = javaFiles;
+
+        FileManager fileManager = new FileManager(javac.getStandardFileManager(null, null, null), classes, appClassLoader);
+        JavaCompiler.CompilationTask task = javac.getTask(null, fileManager, null, getClOptions(), null, compilationUnits);
+        task.call();
+    }
+
+    private static List<String> getClOptions() {
+        List<String> optionList = new ArrayList<>();
+        optionList.addAll(Arrays.asList("-classpath", classpath + File.pathSeparator + "target/classes"));
+        return optionList;
+    }
+}
diff --git a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/FileManager.java b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/FileManager.java
new file mode 100644
index 0000000..1bc611e
--- /dev/null
+++ b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/FileManager.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.reload.compiler;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+
+/**
+ * File manager delegator to control our source and class files.
+ *
+ * @author Jakub Podlesak (jakub.podlesak @ oracle.com)
+ */
+public class FileManager extends ForwardingJavaFileManager<JavaFileManager> {
+
+    private final Map<String, ClassFile> classFiles;
+    private final AppClassLoader cl;
+
+    /**
+     * Creates a new instance of FileManager.
+     *
+     * @param fileManager delegate to this file manager
+     * @param cl
+     */
+    protected FileManager(JavaFileManager fileManager, List<ClassFile> classFiles, AppClassLoader cl) {
+        super(fileManager);
+        this.classFiles = new HashMap<>();
+        this.cl = cl;
+        for (ClassFile classFile : classFiles) {
+            this.classFiles.put(classFile.getClassName(), classFile);
+        }
+    }
+
+    @Override
+    public JavaFileObject getJavaFileForOutput(
+            JavaFileManager.Location location,
+            String className,
+            JavaFileObject.Kind kind,
+            FileObject sibling) throws IOException {
+
+        final ClassFile classFile = classFiles.get(className);
+        if (classFile != null) {
+            this.cl.setCode(classFile);
+        }
+
+        return classFile;
+    }
+
+    @Override
+    public ClassLoader getClassLoader(JavaFileManager.Location location) {
+        return cl;
+    }
+}
diff --git a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/JavaFile.java b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/JavaFile.java
new file mode 100644
index 0000000..a7458ef
--- /dev/null
+++ b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/JavaFile.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.reload.compiler;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import javax.tools.SimpleJavaFileObject;
+
+/**
+ * Java source file representation.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JavaFile extends SimpleJavaFileObject {
+
+    private final String className;
+    private final String path;
+
+    public JavaFile(String className, String path) throws Exception {
+        super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
+        this.className = className;
+        this.path = path;
+    }
+
+    /**
+     * Class name getter.
+     *
+     * @return class name.
+     */
+    public String getClassName() {
+        return className;
+    }
+
+    @Override
+    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+
+        String filePath = path + File.separator + className.replace('.', '/') + Kind.SOURCE.extension;
+        final byte[] bytes = Files.readAllBytes(Paths.get(filePath));
+
+        return new String(bytes);
+    }
+}
diff --git a/examples/reload/src/test/java/org/glassfish/jersey/examples/reload/ReloadTest.java b/examples/reload/src/test/java/org/glassfish/jersey/examples/reload/ReloadTest.java
new file mode 100644
index 0000000..a9fd079
--- /dev/null
+++ b/examples/reload/src/test/java/org/glassfish/jersey/examples/reload/ReloadTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.reload;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.AbstractContainerLifecycleListener;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * This is to test the reload feature without updating the resources text file.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class ReloadTest extends JerseyTest {
+
+    private static Container container;
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+
+        final ResourceConfig result = new ResourceConfig(ArrivalsResource.class);
+
+        result.registerInstances(new AbstractContainerLifecycleListener() {
+            @Override
+            public void onStartup(Container container) {
+                ReloadTest.container = container;
+            }
+        });
+
+        return result;
+    }
+
+    @Test
+    public void testReload() {
+
+        // hit arrivals
+        Response response = target().path("arrivals").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+
+        // make sure stats resource is not found
+        response = target().path("stats").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(404, response.getStatus());
+
+        // add stats resource
+        container.reload(new ResourceConfig(ArrivalsResource.class, StatsResource.class));
+
+        // check stats
+        response = target().path("stats").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+        assertTrue("1 expected as number of arrivals hits in stats", response.readEntity(String.class).contains("1"));
+
+        // another arrivals hit
+        response = target().path("arrivals").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+
+        // check updated stats
+        response = target().path("stats").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+        assertTrue("2 expected as number of arrivals hits in stats", response.readEntity(String.class).contains("2"));
+
+        // remove stats
+        container.reload(new ResourceConfig(ArrivalsResource.class));
+
+        // make sure stats resource is not found
+        response = target().path("stats").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(404, response.getStatus());
+    }
+}
diff --git a/examples/rx-client-webapp/README.MD b/examples/rx-client-webapp/README.MD
new file mode 100644
index 0000000..56b53cb
--- /dev/null
+++ b/examples/rx-client-webapp/README.MD
@@ -0,0 +1,160 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Reactive Clients Jersey Reactive Client Extensions Example
+==========================================================
+
+This example demonstrates how data from multiple resources and in
+various formats (json, xml) can be combined into a form suitable to ones
+requirements. The example show and compares usage of standard JAX-RS
+sync client, JAX-RS async client, Jersey's Observable (RxJava) client
+extension, Jersey's CompletionStage (Java 8) client extension and
+Jersey's ListenableFuture (Guava) client extension.
+
+The application consists of two parts:
+
+-   **"Remote"**, that can be considered to be deployed on a
+    remote machine. In fact each resource from
+    `org.glassfish.jersey.examples.rx.remote` package can be deployed on
+    a separate remote machine as every resource represents a service of
+    it's own.
+
+-   **Note:** `DestinationResource` returns response entities as `JSON`
+    while `CalculationResource` and `ForecastResource` as `XML`.
+
+-   **"Agent"**, which is a synchronization/orchestration layer. This
+    layer, also deployed as a server application, fetches data from all
+    needed resources (using JAX-RS Client API) and combine
+    them together. The combined result is then sent to the client that
+    invoked the original request on the orchestration layer.
+
+Every "agent" resource (package `org.glassfish.jersey.examples.rx.agent`)
+invokes the following requests to the "remote" resource:
+
+-   Obtain **visited** destinations for a user. (User identification is
+    propagated via request header and basically everything about a user
+    maximally simplified since it's out of scope of this example)
+
+-   Obtain **recommended** destinations for a user. Requests obtaining
+    **visited** and **recommended** destinations are independent and can
+    be run in parallel.
+
+-   Obtain weather **forecasts** for recommended destinations. New
+    client request is invoked for every recommended destination.
+    Obtaining weather **forecasts** depend on actual
+    **recommended** destinations.
+
+-   Obtain trip **calculations** (prices) for recommended destinations.
+    New client request is invoked for every recommended destination.
+    Obtaining trip **calculations** depend on actual
+    **recommended** destinations. **forecast** and **calculation**
+    requests are independent on each other and can be invoked
+    in parallel.
+
+Contents
+--------
+
+> Please note that the example is not fully implemented right now.
+>
+> Available "agent" resources are _/rx/agent/sync_, _/rx/agent/async_ and _/rx/agent/completion_.
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                                           | Resource class                  | HTTP methods   | Allowed values
+-------------------------------------------------- | ------------------------------- | -------------- | ------------------------------------------------------------------------------
+**_/rx/agent/sync_**                               | SyncAgentResource               | GET            | returns JSON
+**_/rx/agent/async_**                              | AsyncAgentResource              | GET            | returns JSON
+**_/rx/agent/observable_** **(temporarily n/a)**   | ObservableAgentResource         | GET            | returns JSON
+**_/rx/agent/completion_**                         | CompletionStageAgentResource    | GET            | returns JSON
+**_/rx/agent/listenable_** **(temporarily n/a)**   | ListenableFutureAgentResource   | GET            | returns JSON
+**_/rx/remote/destination/visited_**               | DestinationResource             | GET            | returns JSON
+**_/rx/remote/destination/recommended_**           | DestinationResource             | GET            | returns JSON
+**_/rx/remote/forecast/{destination}_**            | ForecastResource                | GET            | destination - name of a country; returns XML (random value)
+**_/rx/remote/calculation/from/{from}/to/{to}_**   | CalculationResource             | GET            | from - name of a country, to - name of a country; returns XML (random value)
+
+Application is Servlet 3 based, web.xml-less. Everything needed (resources/providers) is registered in `RxApplication`.
+
+Sample Response
+---------------
+
+Agent responses look similar to the following one:
+
+```javascript
+{
+  "visited" : [ {
+    "destination" : "Antigua & Barbuda"
+  }, {
+    "destination" : "Guinea"
+  }, {
+    "destination" : "Malta"
+  }, {
+    "destination" : "Denmark"
+  }, {
+    "destination" : "Tajikistan"
+  } ],
+  "recommended" : [ {
+    "destination" : "Bolivia",
+    "forecast" : "Showers",
+    "price" : 1359
+  }, {
+    "destination" : "Yemen",
+    "forecast" : "Haze",
+    "price" : 8032
+  }, {
+    "destination" : "Dominican Republic",
+    "forecast" : "Cloudy",
+    "price" : 1141
+  }, {
+    "destination" : "Korea South",
+    "forecast" : "Mostly Sunny",
+    "price" : 9853
+  }, {
+    "destination" : "Saudi Arabia",
+    "forecast" : "Fog",
+    "price" : 9063
+  } ],
+  "processingTime" : 877
+}
+```
+
+As can be seen the response entity contains 3 main elements: **visited**
+(list of visited destinations), **recommended** (list of recommended
+destinations + weather forecast and price calculation) and
+**processingTime** (describing how long it took to obtain previous two
+elements).
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean package jetty:run
+
+This deploys current example using Jetty. You can access the application at:
+
+-   <http://localhost:8080/rx/agent/sync>
+-   <http://localhost:8080/rx/agent/async>
+-   <http://localhost:8080/rx/agent/listenable> **(temporarily n/a)**
+-   <http://localhost:8080/rx/agent/observable> **(temporarily n/a)**
+-   <http://localhost:8080/rx/agent/completion>
+
+Resources
+---------
+
+This examples is using the following (3-rd party) libraries:
+
+**RxJava** by Netflix
+-   [GitHub](https://github.com/ReactiveX/RxJava)
+-   [GitHub.Wiki](https://github.com/ReactiveX/RxJava/wiki)
+-   [JavaDoc](http://reactivex.io/RxJava/javadoc/)
+
+**Guava** by Google
+-   [Homepage](https://code.google.com/p/guava-libraries/)
+-   [ListenableFuture
+    Explained](https://code.google.com/p/guava-libraries/wiki/ListenableFutureExplained)
+-   [JavaDoc](http://docs.guava-libraries.googlecode.com/git/javadoc/index.html?overview-summary.html)
diff --git a/examples/rx-client-webapp/pom.xml b/examples/rx-client-webapp/pom.xml
new file mode 100644
index 0000000..14ab43d
--- /dev/null
+++ b/examples/rx-client-webapp/pom.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>rx-client-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-rx-client-webapp</name>
+
+    <description>Jersey Reactive Client WebApp Example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.rx</groupId>
+            <artifactId>jersey-rx-client-guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.rx</groupId>
+            <artifactId>jersey-rx-client-rxjava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.rx</groupId>
+            <artifactId>jersey-rx-client-rxjava2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Run the application using "mvn jetty:run" -->
+            <plugin>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+                <configuration>
+                    <scanIntervalSeconds>5</scanIntervalSeconds>
+                    <stopPort>9999</stopPort>
+                    <stopKey>STOP</stopKey>
+                    <webApp>
+                        <contextPath>/</contextPath>
+                        <webInfIncludeJarPattern>.*/.*jersey-[^/]\.jar$</webInfIncludeJarPattern>
+                    </webApp>
+                    <systemProperties>
+                        <systemProperty>
+                            <name>jetty.port</name>
+                            <value>${jersey.config.test.container.port}</value>
+                        </systemProperty>
+                    </systemProperties>
+                    <war>${project.build.directory}/${project.build.finalName}.war</war>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <jersey.config.test.container.port>8080</jersey.config.test.container.port>
+    </properties>
+</project>
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/Helper.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/Helper.java
new file mode 100644
index 0000000..9e0f8c5
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/Helper.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * @author Michal Gajdos
+ */
+public class Helper {
+
+    public static void sleep() {
+        sleep(500);
+    }
+
+    public static void sleep(final long millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException e) {
+            // NOOP.
+        }
+    }
+
+    public static List<String> getCountries(final int limit) {
+        return getCountries(limit, Collections.<String>emptyList());
+    }
+
+    public static List<String> getCountries(final int limit, final List<String> exclusions) {
+        final List<String> diff = new ArrayList<>(COUNTRIES);
+        diff.removeAll(exclusions);
+
+        if (limit >= diff.size()) {
+            return new ArrayList<>(diff);
+        } else {
+            final List<String> countries = new ArrayList<>(limit);
+            final Random random = new Random();
+
+            for (int i = 0; i < limit; i++) {
+                countries.add(diff.remove(random.nextInt(diff.size())));
+            }
+
+            return countries;
+        }
+    }
+
+    public static String getForecast() {
+        return WEATHER_CONDITIONS.get(new Random().nextInt(WEATHER_CONDITIONS.size()));
+    }
+
+    private static final List<String> COUNTRIES = Arrays.asList("Afghanistan",
+            "Albania",
+            "Algeria",
+            "Andorra",
+            "Angola",
+            "Antigua & Barbuda",
+            "Argentina",
+            "Armenia",
+            "Australia",
+            "Austria",
+            "Azerbaijan",
+            "Bahamas",
+            "Bahrain",
+            "Bangladesh",
+            "Barbados",
+            "Belarus",
+            "Belgium",
+            "Belize",
+            "Benin",
+            "Bhutan",
+            "Bolivia",
+            "Bosnia & Herzegovina",
+            "Botswana",
+            "Brazil",
+            "Brunei",
+            "Bulgaria",
+            "Burkina Faso",
+            "Burundi",
+            "Cambodia",
+            "Cameroon",
+            "Canada",
+            "Cape Verde",
+            "Central African Republic",
+            "Chad",
+            "Chile",
+            "China",
+            "Colombia",
+            "Comoros",
+            "Congo",
+            "Congo Democratic Republic",
+            "Costa Rica",
+            "Cote d'Ivoire",
+            "Croatia",
+            "Cuba",
+            "Cyprus",
+            "Czech Republic",
+            "Denmark",
+            "Djibouti",
+            "Dominica",
+            "Dominican Republic",
+            "Ecuador",
+            "East Timor",
+            "Egypt",
+            "El Salvador",
+            "Equatorial Guinea",
+            "Eritrea",
+            "Estonia",
+            "Ethiopia",
+            "Fiji",
+            "Finland",
+            "France",
+            "Gabon",
+            "Gambia",
+            "Georgia",
+            "Germany",
+            "Ghana",
+            "Greece",
+            "Grenada",
+            "Guatemala",
+            "Guinea",
+            "Guinea-Bissau",
+            "Guyana",
+            "Haiti",
+            "Honduras",
+            "Hungary",
+            "Iceland",
+            "India",
+            "Indonesia",
+            "Iran",
+            "Iraq",
+            "Ireland",
+            "Israel",
+            "Italy",
+            "Jamaica",
+            "Japan",
+            "Jordan",
+            "Kazakhstan",
+            "Kenya",
+            "Kiribati",
+            "Korea North",
+            "Korea South",
+            "Kosovo",
+            "Kuwait",
+            "Kyrgyzstan",
+            "Laos",
+            "Latvia",
+            "Lebanon",
+            "Lesotho",
+            "Liberia",
+            "Libya",
+            "Liechtenstein",
+            "Lithuania",
+            "Luxembourg",
+            "Macedonia",
+            "Madagascar",
+            "Malawi",
+            "Malaysia",
+            "Maldives",
+            "Mali",
+            "Malta",
+            "Marshall Islands",
+            "Mauritania",
+            "Mauritius",
+            "Mexico",
+            "Micronesia",
+            "Moldova",
+            "Monaco",
+            "Mongolia",
+            "Montenegro",
+            "Morocco",
+            "Mozambique",
+            "Myanmar (Burma)",
+            "Namibia",
+            "Nauru",
+            "Nepal",
+            "The Netherlands",
+            "New Zealand",
+            "Nicaragua",
+            "Niger",
+            "Nigeria",
+            "Norway",
+            "Oman",
+            "Pakistan",
+            "Palau",
+            "Palestinian State",
+            "Panama",
+            "Papua New Guinea",
+            "Paraguay",
+            "Peru",
+            "The Philippines",
+            "Poland",
+            "Portugal",
+            "Qatar",
+            "Romania",
+            "Russia",
+            "Rwanda",
+            "St. Kitts & Nevis",
+            "St. Lucia",
+            "St. Vincent & The Grenadines",
+            "Samoa",
+            "San Marino",
+            "Sao Tome & Principe",
+            "Saudi Arabia",
+            "Senegal",
+            "Serbia",
+            "Seychelles",
+            "Sierra Leone",
+            "Singapore",
+            "Slovakia",
+            "Slovenia",
+            "Solomon Islands",
+            "Somalia",
+            "South Africa",
+            "South Sudan",
+            "Spain",
+            "Sri Lanka",
+            "Sudan",
+            "Suriname",
+            "Swaziland",
+            "Sweden",
+            "Switzerland",
+            "Syria",
+            "Taiwan",
+            "Tajikistan",
+            "Tanzania",
+            "Thailand",
+            "Togo",
+            "Tonga",
+            "Trinidad & Tobago",
+            "Tunisia",
+            "Turkey",
+            "Turkmenistan",
+            "Tuvalu",
+            "Uganda",
+            "Ukraine",
+            "United Arab Emirates",
+            "United Kingdom",
+            "United States of America",
+            "Uruguay",
+            "Uzbekistan",
+            "Vanuatu",
+            "Vatican City (Holy See)",
+            "Venezuela",
+            "Vietnam",
+            "Yemen",
+            "Zambia",
+            "Zimbabwe");
+
+    private static final List<String> WEATHER_CONDITIONS = Arrays.asList("Partly Sunny",
+            "Scattered Thunderstorms",
+            "Showers",
+            "Scattered Showers",
+            "Rain and Snow",
+            "Overcast",
+            "Light Snow",
+            "Freezing Drizzle",
+            "Chance of Rain",
+            "Sunny",
+            "Clear",
+            "Mostly Sunny",
+            "Partly Cloudy",
+            "Mostly Cloudy",
+            "Chance of Storm",
+            "Rain",
+            "Chance of Snow",
+            "Cloudy",
+            "Mist",
+            "Storm",
+            "Thunderstorm",
+            "Chance of TStorm",
+            "Sleet",
+            "Snow",
+            "Icy",
+            "Dust",
+            "Fog",
+            "Smoke",
+            "Haze",
+            "Flurries",
+            "Light Rain",
+            "Snow Showers",
+            "Hail");
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/RxApplication.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/RxApplication.java
new file mode 100644
index 0000000..d6e3011
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/RxApplication.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.ext.ContextResolver;
+
+import org.glassfish.jersey.examples.rx.agent.AsyncAgentResource;
+import org.glassfish.jersey.examples.rx.agent.CompletionStageAgentResource;
+import org.glassfish.jersey.examples.rx.agent.FlowableAgentResource;
+import org.glassfish.jersey.examples.rx.agent.ListenableFutureAgentResource;
+import org.glassfish.jersey.examples.rx.agent.ObservableAgentResource;
+import org.glassfish.jersey.examples.rx.agent.SyncAgentResource;
+import org.glassfish.jersey.examples.rx.remote.CalculationResource;
+import org.glassfish.jersey.examples.rx.remote.DestinationResource;
+import org.glassfish.jersey.examples.rx.remote.ForecastResource;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+/**
+ * @author Michal Gajdos
+ */
+@ApplicationPath("rx")
+public class RxApplication extends ResourceConfig {
+
+    public RxApplication() {
+        // Remote (Server) Resources.
+        register(DestinationResource.class);
+        register(CalculationResource.class);
+        register(ForecastResource.class);
+
+        // Agent (Client) Resources.
+        register(SyncAgentResource.class);
+        register(AsyncAgentResource.class);
+        register(ObservableAgentResource.class);
+        register(FlowableAgentResource.class);
+        register(ListenableFutureAgentResource.class);
+        register(CompletionStageAgentResource.class);
+
+        // Providers.
+        register(JacksonFeature.class);
+        register(ObjectMapperProvider.class);
+    }
+
+    public static class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
+
+        @Override
+        public ObjectMapper getContext(final Class<?> type) {
+            return new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
+        }
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/AsyncAgentResource.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/AsyncAgentResource.java
new file mode 100644
index 0000000..a7353a2
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/AsyncAgentResource.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.agent;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+import org.glassfish.jersey.examples.rx.domain.AgentResponse;
+import org.glassfish.jersey.examples.rx.domain.Calculation;
+import org.glassfish.jersey.examples.rx.domain.Destination;
+import org.glassfish.jersey.examples.rx.domain.Forecast;
+import org.glassfish.jersey.examples.rx.domain.Recommendation;
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
+import org.glassfish.jersey.server.ManagedAsync;
+import org.glassfish.jersey.server.Uri;
+
+/**
+ * Obtain information about visited (destination) and recommended (destination, forecast, price) places for "Async" user. Uses
+ * standard JAX-RS async approach to obtain the data.
+ *
+ * @author Michal Gajdos
+ */
+@Path("agent/async")
+@Produces("application/json")
+public class AsyncAgentResource {
+
+    @Uri("remote/destination")
+    private WebTarget destination;
+
+    @Uri("remote/calculation/from/{from}/to/{to}")
+    private WebTarget calculation;
+
+    @Uri("remote/forecast/{destination}")
+    private WebTarget forecast;
+
+    private final ExecutorService executor;
+
+    public AsyncAgentResource() {
+        executor = new ScheduledThreadPoolExecutor(20, new ThreadFactoryBuilder()
+                .setNameFormat("jersey-rx-client-async-%d")
+                .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+                .build());
+    }
+
+    @GET
+    @ManagedAsync
+    public void async(@Suspended final AsyncResponse async) {
+        final long time = System.nanoTime();
+
+        final AgentResponse response = new AgentResponse();
+
+        final CountDownLatch outerLatch = new CountDownLatch(2);
+        final Queue<String> errors = new ConcurrentLinkedQueue<>();
+
+        // Obtain visited destinations.
+        destination.path("visited").request()
+                // Identify the user.
+                .header("Rx-User", "Async")
+                // Async invoker.
+                .async()
+                // Return a list of destinations
+                .get(new InvocationCallback<List<Destination>>() {
+                    @Override
+                    public void completed(final List<Destination> destinations) {
+                        response.setVisited(destinations);
+                        outerLatch.countDown();
+                    }
+
+                    @Override
+                    public void failed(final Throwable throwable) {
+                        errors.offer("Visited: " + throwable.getMessage());
+                        outerLatch.countDown();
+                    }
+                });
+
+
+        // Obtain recommended destinations. (does not depend on visited ones)
+        destination.path("recommended").request()
+                // Identify the user.
+                .header("Rx-User", "Async")
+                // Async invoker.
+                .async()
+                // Return a list of destinations.
+                .get(new InvocationCallback<List<Destination>>() {
+                    @Override
+                    public void completed(final List<Destination> recommended) {
+                        final CountDownLatch innerLatch = new CountDownLatch(recommended.size() * 2);
+
+                        // Forecasts. (depend on recommended destinations)
+                        final Map<String, Forecast> forecasts = Collections.synchronizedMap(new HashMap<>());
+                        for (final Destination dest : recommended) {
+                            forecast.resolveTemplate("destination", dest.getDestination()).request()
+                                    .async()
+                                    .get(new InvocationCallback<Forecast>() {
+                                        @Override
+                                        public void completed(final Forecast forecast) {
+                                            forecasts.put(dest.getDestination(), forecast);
+                                            innerLatch.countDown();
+                                        }
+
+                                        @Override
+                                        public void failed(final Throwable throwable) {
+                                            errors.offer("Forecast: " + throwable.getMessage());
+                                            innerLatch.countDown();
+                                        }
+                                    });
+                        }
+
+                        // Calculations. (depend on recommended destinations)
+                        final List<Future<Calculation>> futures = recommended.stream()
+                                .map(dest -> calculation.resolveTemplate("from", "Moon").resolveTemplate("to",
+                                        dest.getDestination()).request().async().get(Calculation.class))
+                                .collect(Collectors.toList());
+
+                        final Map<String, Calculation> calculations = new HashMap<>();
+                        while (!futures.isEmpty()) {
+                            final Iterator<Future<Calculation>> iterator = futures.iterator();
+
+                            while (iterator.hasNext()) {
+                                final Future<Calculation> f = iterator.next();
+                                if (f.isDone()) {
+                                    try {
+                                        final Calculation calculation = f.get();
+                                        calculations.put(calculation.getTo(), calculation);
+
+                                        innerLatch.countDown();
+                                    } catch (final Throwable t) {
+                                        errors.offer("Calculation: " + t.getMessage());
+                                        innerLatch.countDown();
+                                    } finally {
+                                        iterator.remove();
+                                    }
+                                }
+                            }
+                        }
+
+                        // Have to wait here for dependent requests ...
+                        try {
+                            if (!innerLatch.await(10, TimeUnit.SECONDS)) {
+                                errors.offer("Inner: Waiting for requests to complete has timed out.");
+                            }
+                        } catch (final InterruptedException e) {
+                            errors.offer("Inner: Waiting for requests to complete has been interrupted.");
+                        }
+
+                        // Recommendations.
+                        final List<Recommendation> recommendations = new ArrayList<>(recommended.size());
+                        for (final Destination dest : recommended) {
+                            final Forecast fore = forecasts.get(dest.getDestination());
+                            final Calculation calc = calculations.get(dest.getDestination());
+
+                            recommendations.add(new Recommendation(dest.getDestination(),
+                                    fore != null ? fore.getForecast() : "N/A", calc != null ? calc.getPrice() : -1));
+                        }
+                        response.setRecommended(recommendations);
+
+                        outerLatch.countDown();
+                    }
+
+                    @Override
+                    public void failed(final Throwable throwable) {
+                        errors.offer("Recommended: " + throwable.getMessage());
+                        outerLatch.countDown();
+                    }
+                });
+
+        // ... and have to wait also here for independent requests.
+        try {
+            if (!outerLatch.await(10, TimeUnit.SECONDS)) {
+                errors.offer("Outer: Waiting for requests to complete has timed out.");
+            }
+        } catch (final InterruptedException e) {
+            errors.offer("Outer: Waiting for requests to complete has been interrupted.");
+        }
+
+        // Do something with errors.
+        // ...
+
+        response.setProcessingTime((System.nanoTime() - time) / 1000000);
+        async.resume(response);
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/CompletionStageAgentResource.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/CompletionStageAgentResource.java
new file mode 100644
index 0000000..fd9f410
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/CompletionStageAgentResource.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.agent;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.GenericType;
+
+import org.glassfish.jersey.examples.rx.domain.AgentResponse;
+import org.glassfish.jersey.examples.rx.domain.Calculation;
+import org.glassfish.jersey.examples.rx.domain.Destination;
+import org.glassfish.jersey.examples.rx.domain.Forecast;
+import org.glassfish.jersey.examples.rx.domain.Recommendation;
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.server.Uri;
+
+/**
+ * Obtain information about visited (destination) and recommended (destination, forecast, price) places for
+ * "CompletionStage" user. Uses Java 8 CompletionStage and Jersey Client to obtain the data.
+ *
+ * @author Michal Gajdos
+ */
+@Path("agent/completion")
+@Produces("application/json")
+public class CompletionStageAgentResource {
+
+    @Uri("remote/destination")
+    private WebTarget destinationTarget;
+
+    @Uri("remote/calculation/from/{from}/to/{to}")
+    private WebTarget calculationTarget;
+
+    @Uri("remote/forecast/{destination}")
+    private WebTarget forecastTarget;
+
+    private final ExecutorService executor;
+
+    public CompletionStageAgentResource() {
+        executor = new ScheduledThreadPoolExecutor(20,
+                new ThreadFactoryBuilder().setNameFormat("jersey-rx-client-completion-%d").build());
+    }
+
+    @GET
+    public void completion(@Suspended final AsyncResponse async) {
+        final long time = System.nanoTime();
+
+        final Queue<String> errors = new ConcurrentLinkedQueue<>();
+
+        CompletableFuture.completedFuture(new AgentResponse())
+                .thenCombine(visited(destinationTarget, executor, errors), AgentResponse::visited)
+                .thenCombine(recommended(destinationTarget, executor, errors), AgentResponse::recommended)
+                .whenCompleteAsync((response, throwable) -> {
+                    // Do something with errors.
+
+                    response.setProcessingTime((System.nanoTime() - time) / 1000000);
+                    async.resume(throwable == null ? response : throwable);
+                });
+    }
+
+    private CompletionStage<List<Destination>> visited(final WebTarget destinationTarget,
+                                                       final ExecutorService executor,
+                                                       final Queue<String> errors) {
+        return destinationTarget.path("visited").request()
+                // Identify the user.
+                .header("Rx-User", "CompletionStage")
+                // Reactive invoker.
+                .rx()
+                // Return a list of destinations.
+                .get(new GenericType<List<Destination>>() {})
+                .exceptionally(throwable -> {
+                    errors.offer("Visited: " + throwable.getMessage());
+                    return Collections.emptyList();
+                });
+    }
+
+    private CompletionStage<List<Recommendation>> recommended(final WebTarget destinationTarget,
+                                                              final ExecutorService executor,
+                                                              final Queue<String> errors) {
+        // Recommended places.
+        final CompletionStage<List<Destination>> recommended = destinationTarget.path("recommended")
+                .request()
+                // Identify the user.
+                .header("Rx-User", "CompletionStage")
+                // Reactive invoker.
+                .rx()
+                // Return a list of destinations.
+                .get(new GenericType<List<Destination>>() {})
+                .exceptionally(throwable -> {
+                    errors.offer("Recommended: " + throwable.getMessage());
+                    return Collections.emptyList();
+                });
+
+        return recommended.thenCompose(destinations -> {
+            final WebTarget finalForecast = forecastTarget;
+            final WebTarget finalCalculation = calculationTarget;
+
+            List<CompletionStage<Recommendation>> recommendations = destinations.stream().map(destination -> {
+                // For each destination, obtain a weather forecast ...
+                final CompletionStage<Forecast> forecast =
+                        finalForecast.resolveTemplate("destination", destination.getDestination())
+                                     .request().rx().get(Forecast.class)
+                                     .exceptionally(throwable -> {
+                                         errors.offer("Forecast: " + throwable.getMessage());
+                                         return new Forecast(destination.getDestination(), "N/A");
+                                     });
+                // ... and a price calculation
+                final CompletionStage<Calculation> calculation = finalCalculation.resolveTemplate("from", "Moon")
+                        .resolveTemplate("to", destination.getDestination())
+                        .request().rx().get(Calculation.class)
+                        .exceptionally(throwable -> {
+                            errors.offer("Calculation: " + throwable.getMessage());
+                            return new Calculation("Moon", destination.getDestination(), -1);
+                        });
+
+                //noinspection unchecked
+                return CompletableFuture.completedFuture(new Recommendation(destination))
+                        // Set forecast for recommended destination.
+                        .thenCombine(forecast, Recommendation::forecast)
+                        // Set calculation for recommended destination.
+                        .thenCombine(calculation, Recommendation::calculation);
+            }).collect(Collectors.toList());
+
+            // Transform List<CompletionStage<Recommendation>> to CompletionStage<List<Recommendation>>
+            return sequence(recommendations);
+        });
+    }
+
+    private <T> CompletionStage<List<T>> sequence(final List<CompletionStage<T>> stages) {
+        //noinspection SuspiciousToArrayCall
+        final CompletableFuture<Void> done = CompletableFuture.allOf(stages.toArray(new CompletableFuture[stages.size()]));
+
+        return done.thenApply(v -> stages.stream()
+                        .map(CompletionStage::toCompletableFuture)
+                        .map(CompletableFuture::join)
+                        .collect(Collectors.<T>toList())
+        );
+    }
+
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/FlowableAgentResource.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/FlowableAgentResource.java
new file mode 100644
index 0000000..d5ffaef
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/FlowableAgentResource.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.agent;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.GenericType;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.client.rx.rxjava2.RxFlowableInvoker;
+import org.glassfish.jersey.client.rx.rxjava2.RxFlowableInvokerProvider;
+import org.glassfish.jersey.examples.rx.domain.AgentResponse;
+import org.glassfish.jersey.examples.rx.domain.Calculation;
+import org.glassfish.jersey.examples.rx.domain.Destination;
+import org.glassfish.jersey.examples.rx.domain.Forecast;
+import org.glassfish.jersey.examples.rx.domain.Recommendation;
+import org.glassfish.jersey.server.Uri;
+
+import io.reactivex.Flowable;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Obtain information about visited (destination) and recommended (destination, forecast, price) places for "RxJava2"
+ * user. Uses RxJava2 Flowable and Jersey Client to obtain the data.
+ *
+ * @author Michal Gajdos
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Singleton
+@Path("agent/flowable")
+@Produces("application/json")
+public class FlowableAgentResource {
+
+    @Uri("remote/destination")
+    private WebTarget destination;
+
+    @Uri("remote/calculation/from/{from}/to/{to}")
+    private WebTarget calculation;
+
+    @Uri("remote/forecast/{destination}")
+    private WebTarget forecast;
+
+    @GET
+    public void flowable(@Suspended final AsyncResponse async) {
+        final long time = System.nanoTime();
+        final Queue<String> errors = new ConcurrentLinkedQueue<>();
+
+        Flowable.just(new AgentResponse())
+                // Obtain visited destinations.
+                .zipWith(visited(errors), (agentResponse, visited) -> {
+                    agentResponse.setVisited(visited);
+                    return agentResponse;
+                })
+                // Obtain recommended destinations. (does not depend on visited ones)
+                .zipWith(
+                        recommended(errors),
+                        (agentResponse, recommendations) -> {
+                            agentResponse.setRecommended(recommendations);
+                            return agentResponse;
+                        })
+                // Observe on another thread than the one processing visited or recommended destinations.
+                .observeOn(Schedulers.io())
+                // Subscribe.
+                .subscribe(agentResponse -> {
+                    agentResponse.setProcessingTime((System.nanoTime() - time) / 1000000);
+                    async.resume(agentResponse);
+
+                }, async::resume);
+    }
+
+    private Flowable<List<Destination>> visited(final Queue<String> errors) {
+        destination.register(RxFlowableInvokerProvider.class);
+
+        return destination.path("visited").request()
+                          // Identify the user.
+                          .header("Rx-User", "RxJava2")
+                          // Reactive invoker.
+                          .rx(RxFlowableInvoker.class)
+                          // Return a list of destinations.
+                          .get(new GenericType<List<Destination>>() {
+                          })
+                          // Handle Errors.
+                          .onErrorReturn(throwable -> {
+                              errors.offer("Visited: " + throwable.getMessage());
+                              return Collections.emptyList();
+                          });
+    }
+
+    private Flowable<List<Recommendation>> recommended(final Queue<String> errors) {
+        destination.register(RxFlowableInvokerProvider.class);
+
+        // Recommended places.
+        final Flowable<Destination> recommended = destination.path("recommended").request()
+                                                             // Identify the user.
+                                                             .header("Rx-User", "RxJava2")
+                                                             // Reactive invoker.
+                                                             .rx(RxFlowableInvoker.class)
+                                                             // Return a list of destinations.
+                                                             .get(new GenericType<List<Destination>>() {
+                                                             })
+                                                             // Handle Errors.
+                                                             .onErrorReturn(throwable -> {
+                                                                 errors.offer("Recommended: " + throwable
+                                                                         .getMessage());
+                                                                 return Collections.emptyList();
+                                                             })
+                                                             // Emit destinations one-by-one.
+                                                             .flatMap(Flowable::fromIterable)
+                                                             // Remember emitted items for dependant requests.
+                                                             .cache();
+
+        forecast.register(RxFlowableInvokerProvider.class);
+
+        // Forecasts. (depend on recommended destinations)
+        final Flowable<Forecast> forecasts = recommended.flatMap(destination ->
+                forecast
+                        .resolveTemplate("destination", destination.getDestination())
+                        .request().rx(RxFlowableInvoker.class).get(Forecast.class)
+                        .onErrorReturn(throwable -> {
+                            errors.offer("Forecast: " + throwable.getMessage());
+                            return new Forecast(destination.getDestination(), "N/A");
+                        }));
+
+        calculation.register(RxFlowableInvokerProvider.class);
+
+        // Calculations. (depend on recommended destinations)
+        final Flowable<Calculation> calculations = recommended.flatMap(destination -> {
+            return calculation.resolveTemplate("from", "Moon").resolveTemplate("to", destination.getDestination())
+                              .request().rx(RxFlowableInvoker.class).get(Calculation.class)
+                              .onErrorReturn(throwable -> {
+                                  errors.offer("Calculation: " + throwable.getMessage());
+                                  return new Calculation("Moon", destination.getDestination(), -1);
+                              });
+        });
+
+        return Flowable.zip(recommended, forecasts, calculations, Recommendation::new).buffer(Integer.MAX_VALUE);
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/ListenableFutureAgentResource.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/ListenableFutureAgentResource.java
new file mode 100644
index 0000000..9dc3d5f
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/ListenableFutureAgentResource.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.agent;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.GenericType;
+
+import org.glassfish.jersey.client.rx.guava.RxListenableFutureInvoker;
+import org.glassfish.jersey.client.rx.guava.RxListenableFutureInvokerProvider;
+import org.glassfish.jersey.examples.rx.domain.AgentResponse;
+import org.glassfish.jersey.examples.rx.domain.Calculation;
+import org.glassfish.jersey.examples.rx.domain.Destination;
+import org.glassfish.jersey.examples.rx.domain.Forecast;
+import org.glassfish.jersey.examples.rx.domain.Recommendation;
+import org.glassfish.jersey.server.ManagedAsync;
+import org.glassfish.jersey.server.Uri;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Obtain information about visited (destination) and recommended (destination, forecast, price) places for "Guava" user. Uses
+ * Guava ListenableFuture and Jersey Client to obtain the data.
+ *
+ * @author Michal Gajdos
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("agent/listenable")
+@Produces("application/json")
+public class ListenableFutureAgentResource {
+
+    @Uri("remote/destination")
+    private WebTarget destination;
+
+    @Uri("remote/calculation/from/{from}/to/{to}")
+    private WebTarget calculation;
+
+    @Uri("remote/forecast/{destination}")
+    private WebTarget forecast;
+
+    @GET
+    @ManagedAsync
+    public void listenable(@Suspended final AsyncResponse async) {
+        final long time = System.nanoTime();
+        final AgentResponse response = new AgentResponse();
+
+        // Obtain and set visited and recommended places to create a response ...
+        final ListenableFuture<List<AgentResponse>> successful = Futures.successfulAsList(
+                Arrays.asList(visited(response), recommended(response)));
+
+        // ... and when we have them, return response to the client.
+        Futures.addCallback(successful, new FutureCallback<List<AgentResponse>>() {
+
+            @Override
+            public void onSuccess(final List<AgentResponse> result) {
+                response.setProcessingTime((System.nanoTime() - time) / 1000000);
+                async.resume(response);
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                async.resume(t);
+            }
+        });
+    }
+
+    private ListenableFuture<AgentResponse> visited(final AgentResponse response) {
+        destination.register(RxListenableFutureInvokerProvider.class);
+
+        // Get a list of visited destinations ...
+        final ListenableFuture<List<Destination>> visited = destination.path("visited").request()
+                                                                       // Identify the user.
+                                                                       .header("Rx-User", "Guava")
+                                                                       // Reactive invoker.
+                                                                       .rx(RxListenableFutureInvoker.class)
+                                                                       // Return a list of destinations.
+                                                                       .get(new GenericType<List<Destination>>() {
+                                                                       });
+
+        // ... and set them to the final response.
+        return Futures.transform(visited, (AsyncFunction<List<Destination>, AgentResponse>) destinations -> {
+            response.setVisited(destinations);
+
+            return Futures.immediateFuture(response);
+        });
+    }
+
+    private ListenableFuture<AgentResponse> recommended(final AgentResponse response) {
+        destination.register(RxListenableFutureInvokerProvider.class);
+
+        // Get a list of recommended destinations ...
+        final ListenableFuture<List<Destination>> destinations = destination.path("recommended")
+                                                                            .request()
+                                                                            // Identify the user.
+                                                                            .header("Rx-User", "Guava")
+                                                                            // Reactive invoker.
+                                                                            .rx(RxListenableFutureInvoker.class)
+                                                                            // Return a list of destinations.
+                                                                            .get(new GenericType<List<Destination>>() {
+                                                                            });
+
+        // ... transform them to Recommendation instances ...
+        final ListenableFuture<List<Recommendation>> recommendations = Futures.transform(
+                destinations,
+                (AsyncFunction<List<Destination>, List<Recommendation>>) destinationList -> {
+                    // Create new array list to avoid multiple remote calls.
+                    final List<Recommendation> recommendationList = Lists.newArrayList(Lists.transform(
+                            destinationList,
+                            destination -> new Recommendation(destination.getDestination(), null, 0)));
+
+                    return Futures.immediateFuture(recommendationList);
+                });
+
+        // ... add forecasts and calculations ...
+        final ListenableFuture<List<List<Recommendation>>> filledRecommendations = Futures
+                .successfulAsList(Arrays.asList(
+                        // Add Forecasts to Recommendations.
+                        forecasts(recommendations),
+                        // Add Forecasts to Recommendations.
+                        calculations(recommendations)));
+
+        // ... and transform the list into agent response with filled recommendations.
+        return Futures
+                .transform(filledRecommendations, (AsyncFunction<List<List<Recommendation>>, AgentResponse>) input -> {
+                    response.setRecommended(input.get(0));
+
+                    return Futures.immediateFuture(response);
+                });
+    }
+
+    private ListenableFuture<List<Recommendation>> forecasts(final ListenableFuture<List<Recommendation>> recommendations) {
+        forecast.register(RxListenableFutureInvokerProvider.class);
+
+        // Fill the list with weather forecast.
+        return Futures.transform(recommendations, (AsyncFunction<List<Recommendation>, List<Recommendation>>) list ->
+                // For each recommendation ...
+                Futures.successfulAsList(Lists.transform(list, recommendation -> Futures.transform(
+                        // ... get the weather forecast ...
+                        forecast.resolveTemplate("destination", recommendation.getDestination()).request()
+                                .rx(RxListenableFutureInvoker.class)
+                                .get(Forecast.class),
+                        // ... and set it to the recommendation.
+                        (AsyncFunction<Forecast, Recommendation>) forecast -> {
+                            recommendation.setForecast(forecast.getForecast());
+                            return Futures.immediateFuture(recommendation);
+                        }))));
+    }
+
+    private ListenableFuture<List<Recommendation>> calculations(final ListenableFuture<List<Recommendation>> recommendations) {
+        calculation.register(RxListenableFutureInvokerProvider.class);
+
+        // Fill the list with price calculations.
+        return Futures.transform(recommendations, (AsyncFunction<List<Recommendation>, List<Recommendation>>) list ->
+                // For each recommendation ...
+                Futures.successfulAsList(Lists.transform(list, recommendation -> Futures.transform(
+                        // ... get the price calculation ...
+                        calculation.resolveTemplate("from", "Moon")
+                                   .resolveTemplate("to", recommendation.getDestination())
+                                   .request().rx(RxListenableFutureInvoker.class).get(Calculation.class),
+                        // ... and set it to the recommendation.
+                        (AsyncFunction<Calculation, Recommendation>) calculation -> {
+                            recommendation.setPrice(calculation.getPrice());
+                            return Futures.immediateFuture(recommendation);
+                        })))
+        );
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/ObservableAgentResource.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/ObservableAgentResource.java
new file mode 100644
index 0000000..f23aa66
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/ObservableAgentResource.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.agent;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.GenericType;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.client.rx.rxjava.RxObservableInvoker;
+import org.glassfish.jersey.client.rx.rxjava.RxObservableInvokerProvider;
+import org.glassfish.jersey.examples.rx.domain.AgentResponse;
+import org.glassfish.jersey.examples.rx.domain.Calculation;
+import org.glassfish.jersey.examples.rx.domain.Destination;
+import org.glassfish.jersey.examples.rx.domain.Forecast;
+import org.glassfish.jersey.examples.rx.domain.Recommendation;
+import org.glassfish.jersey.server.Uri;
+
+import rx.Observable;
+import rx.schedulers.Schedulers;
+
+/**
+ * Obtain information about visited (destination) and recommended (destination, forecast, price) places for "RxJava" user. Uses
+ * RxJava Observable and Jersey Client to obtain the data.
+ *
+ * @author Michal Gajdos
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Singleton
+@Path("agent/observable")
+@Produces("application/json")
+public class ObservableAgentResource {
+
+    @Uri("remote/destination")
+    private WebTarget destination;
+
+    @Uri("remote/calculation/from/{from}/to/{to}")
+    private WebTarget calculation;
+
+    @Uri("remote/forecast/{destination}")
+    private WebTarget forecast;
+
+    @GET
+    public void observable(@Suspended final AsyncResponse async) {
+        final long time = System.nanoTime();
+        final Queue<String> errors = new ConcurrentLinkedQueue<>();
+
+        Observable.just(new AgentResponse())
+                  // Obtain visited destinations.
+                  .zipWith(visited(errors), (response, visited) -> {
+                      response.setVisited(visited);
+                      return response;
+                  })
+                  // Obtain recommended destinations. (does not depend on visited ones)
+                  .zipWith(recommended(errors), (response, recommendations) -> {
+                      response.setRecommended(recommendations);
+                      return response;
+                  })
+                  // Observe on another thread than the one processing visited or recommended destinations.
+                  .observeOn(Schedulers.io())
+                  // Subscribe.
+                  .subscribe(response -> {
+                      // Do something with errors.
+
+                      response.setProcessingTime((System.nanoTime() - time) / 1000000);
+                      async.resume(response);
+                  }, async::resume);
+    }
+
+    private Observable<List<Destination>> visited(final Queue<String> errors) {
+        destination.register(RxObservableInvokerProvider.class);
+
+        return destination.path("visited").request()
+                          // Identify the user.
+                          .header("Rx-User", "RxJava")
+                          // Reactive invoker.
+                          .rx(RxObservableInvoker.class)
+                          // Return a list of destinations.
+                          .get(new GenericType<List<Destination>>() {
+                          })
+                          // Handle Errors.
+                          .onErrorReturn(throwable -> {
+                              errors.offer("Visited: " + throwable.getMessage());
+                              return Collections.emptyList();
+                          });
+    }
+
+    private Observable<List<Recommendation>> recommended(final Queue<String> errors) {
+        destination.register(RxObservableInvokerProvider.class);
+
+        // Recommended places.
+        final Observable<Destination> recommended = destination.path("recommended").request()
+                                                               // Identify the user.
+                                                               .header("Rx-User", "RxJava")
+                                                               // Reactive invoker.
+                                                               .rx(RxObservableInvoker.class)
+                                                               // Return a list of destinations.
+                                                               .get(new GenericType<List<Destination>>() {
+                                                               })
+                                                               // Handle Errors.
+                                                               .onErrorReturn(throwable -> {
+                                                                   errors.offer("Recommended: " + throwable
+                                                                           .getMessage());
+                                                                   return Collections.emptyList();
+                                                               })
+                                                               // Emit destinations one-by-one.
+                                                               .flatMap(Observable::from)
+                                                               // Remember emitted items for dependant requests.
+                                                               .cache();
+
+        forecast.register(RxObservableInvokerProvider.class);
+
+        // Forecasts. (depend on recommended destinations)
+        final Observable<Forecast> forecasts = recommended.flatMap(destination ->
+                forecast
+                        .resolveTemplate("destination", destination.getDestination())
+                        .request().rx(RxObservableInvoker.class).get(Forecast.class)
+                        .onErrorReturn(throwable -> {
+                            errors.offer("Forecast: " + throwable.getMessage());
+                            return new Forecast(destination.getDestination(), "N/A");
+                        }));
+
+        calculation.register(RxObservableInvokerProvider.class);
+
+        // Calculations. (depend on recommended destinations)
+        final Observable<Calculation> calculations = recommended.flatMap(destination ->
+                calculation.resolveTemplate("from", "Moon").resolveTemplate("to", destination.getDestination())
+                           .request().rx(RxObservableInvoker.class).get(Calculation.class)
+                           .onErrorReturn(throwable -> {
+                               errors.offer("Calculation: " + throwable.getMessage());
+                               return new Calculation("Moon", destination.getDestination(), -1);
+                           }));
+
+        return Observable.zip(recommended, forecasts, calculations, Recommendation::new).toList();
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/SyncAgentResource.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/SyncAgentResource.java
new file mode 100644
index 0000000..102d465
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/agent/SyncAgentResource.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.agent;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.GenericType;
+
+import org.glassfish.jersey.examples.rx.domain.AgentResponse;
+import org.glassfish.jersey.examples.rx.domain.Calculation;
+import org.glassfish.jersey.examples.rx.domain.Destination;
+import org.glassfish.jersey.examples.rx.domain.Forecast;
+import org.glassfish.jersey.examples.rx.domain.Recommendation;
+import org.glassfish.jersey.server.Uri;
+
+/**
+ * Obtain information about visited (destination) and recommended (destination, forecast, price) places for "Sync" user. Uses
+ * standard JAX-RS sync approach to obtain the data.
+ *
+ * @author Michal Gajdos
+ */
+@Path("agent/sync")
+@Produces("application/json")
+public class SyncAgentResource {
+
+    @Uri("remote/destination")
+    private WebTarget destination;
+
+    @Uri("remote/calculation/from/{from}/to/{to}")
+    private WebTarget calculation;
+
+    @Uri("remote/forecast/{destination}")
+    private WebTarget forecast;
+
+    @GET
+    public AgentResponse sync() {
+        final long time = System.nanoTime();
+
+        final AgentResponse response = new AgentResponse();
+        final Queue<String> errors = new ConcurrentLinkedQueue<>();
+
+        // Obtain visited destinations.
+        try {
+            response.setVisited(destination.path("visited").request()
+                    // Identify the user.
+                    .header("Rx-User", "Sync")
+                    // Return a list of destinations
+                    .get(new GenericType<List<Destination>>() {}));
+        } catch (final Throwable throwable) {
+            errors.offer("Visited: " + throwable.getMessage());
+        }
+
+        // Obtain recommended destinations. (does not depend on visited ones)
+        List<Destination> recommended = Collections.emptyList();
+        try {
+            recommended = destination.path("recommended").request()
+                    // Identify the user.
+                    .header("Rx-User", "Sync")
+                    // Return a list of destinations.
+                    .get(new GenericType<List<Destination>>() {});
+        } catch (final Throwable throwable) {
+            errors.offer("Recommended: " + throwable.getMessage());
+        }
+
+        // Forecasts. (depend on recommended destinations)
+        final Map<String, Forecast> forecasts = new HashMap<>();
+        for (final Destination dest : recommended) {
+            try {
+                forecasts.put(dest.getDestination(),
+                        forecast.resolveTemplate("destination", dest.getDestination()).request().get(Forecast.class));
+            } catch (final Throwable throwable) {
+                errors.offer("Forecast: " + throwable.getMessage());
+            }
+        }
+
+        // Calculations. (depend on recommended destinations)
+        final Map<String, Calculation> calculations = new HashMap<>();
+        recommended.stream().forEach(destination -> {
+            try {
+                calculations.put(destination.getDestination(), calculation.resolveTemplate("from", "Moon")
+                        .resolveTemplate("to", destination.getDestination())
+                        .request().get(Calculation.class));
+            } catch (final Throwable throwable) {
+                errors.offer("Calculation: " + throwable.getMessage());
+            }
+        });
+
+        // Recommendations.
+        final List<Recommendation> recommendations = new ArrayList<>(recommended.size());
+        for (final Destination dest : recommended) {
+            final Forecast fore = forecasts.get(dest.getDestination());
+            final Calculation calc = calculations.get(dest.getDestination());
+
+            recommendations.add(new Recommendation(dest.getDestination(),
+                    fore != null ? fore.getForecast() : "N/A", calc != null ? calc.getPrice() : -1));
+        }
+
+        // Do something with errors.
+        // ...
+
+        response.setRecommended(recommendations);
+        response.setProcessingTime((System.nanoTime() - time) / 1000000);
+        return response;
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/AgentResponse.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/AgentResponse.java
new file mode 100644
index 0000000..4b656d6
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/AgentResponse.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Michal Gajdos
+ */
+public class AgentResponse {
+
+    private List<Destination> visited = new ArrayList<>();
+    private List<Recommendation> recommended;
+    private long processingTime;
+
+    public AgentResponse() {
+    }
+
+    public List<Destination> getVisited() {
+        return visited;
+    }
+
+    public void setVisited(final List<Destination> visited) {
+        this.visited = visited;
+    }
+
+    public void setRecommended(final List<Recommendation> recommended) {
+        this.recommended = recommended;
+    }
+
+    public List<Recommendation> getRecommended() {
+        return recommended;
+    }
+
+    public void setProcessingTime(final long processingTime) {
+        this.processingTime = processingTime;
+    }
+
+    public long getProcessingTime() {
+        return processingTime;
+    }
+
+    public AgentResponse visited(final List<Destination> visited) {
+        setVisited(visited);
+        return this;
+    }
+
+    public AgentResponse recommended(final List<Recommendation> recommended) {
+        setRecommended(recommended);
+        return this;
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/Calculation.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/Calculation.java
new file mode 100644
index 0000000..1d5a141
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/Calculation.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.domain;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Michal Gajdos
+ */
+@XmlRootElement
+public class Calculation {
+
+    private String from;
+    private String to;
+    private int price;
+
+    public Calculation() {
+    }
+
+    public Calculation(final String from, final String to, final int price) {
+        this.from = from;
+        this.to = to;
+        this.price = price;
+    }
+
+    public String getFrom() {
+        return from;
+    }
+
+    public void setFrom(final String from) {
+        this.from = from;
+    }
+
+    public String getTo() {
+        return to;
+    }
+
+    public void setTo(final String to) {
+        this.to = to;
+    }
+
+    public int getPrice() {
+        return price;
+    }
+
+    public void setPrice(final int price) {
+        this.price = price;
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/Destination.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/Destination.java
new file mode 100644
index 0000000..f5a4276
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/Destination.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.domain;
+
+/**
+ * @author Michal Gajdos
+ */
+public class Destination {
+
+    private String destination;
+
+    public Destination() {
+    }
+
+    public Destination(final String destination) {
+        this.destination = destination;
+    }
+
+    public String getDestination() {
+        return destination;
+    }
+
+    public void setDestination(final String destination) {
+        this.destination = destination;
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/Forecast.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/Forecast.java
new file mode 100644
index 0000000..95ee20c
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/Forecast.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.domain;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Michal Gajdos
+ */
+@XmlRootElement
+public class Forecast {
+
+    private String forecast;
+    private String destination;
+
+    public Forecast() {
+    }
+
+    public Forecast(final String destination, final String forecast) {
+        this.destination = destination;
+        this.forecast = forecast;
+    }
+
+    public String getForecast() {
+        return forecast;
+    }
+
+    public void setForecast(final String forecast) {
+        this.forecast = forecast;
+    }
+
+    public String getDestination() {
+        return destination;
+    }
+
+    public void setDestination(final String destination) {
+        this.destination = destination;
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/Recommendation.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/Recommendation.java
new file mode 100644
index 0000000..3c2c7ff
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/domain/Recommendation.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.domain;
+
+/**
+ * @author Michal Gajdos
+ */
+public class Recommendation {
+
+    private String destination;
+    private String forecast;
+    private int price;
+
+    public Recommendation() {
+    }
+
+    public Recommendation(final String destination) {
+        this.destination = destination;
+    }
+
+    public Recommendation(final String destination, final String forecast, final int price) {
+        this.destination = destination;
+        this.forecast = forecast;
+        this.price = price;
+    }
+
+    public Recommendation(final Destination destination) {
+        this.destination = destination.getDestination();
+    }
+
+    public Recommendation(final Destination destination, final Forecast forecast, final Calculation calculation) {
+        this.destination = destination.getDestination();
+        this.forecast = forecast.getForecast();
+        this.price = calculation.getPrice();
+    }
+
+    public String getDestination() {
+        return destination;
+    }
+
+    public void setDestination(final String destination) {
+        this.destination = destination;
+    }
+
+    public String getForecast() {
+        return forecast;
+    }
+
+    public void setForecast(final String forecast) {
+        this.forecast = forecast;
+    }
+
+    public int getPrice() {
+        return price;
+    }
+
+    public void setPrice(final int price) {
+        this.price = price;
+    }
+
+    public Recommendation forecast(final Forecast forecast) {
+        setForecast(forecast.getForecast());
+        return this;
+    }
+
+    public Recommendation calculation(final Calculation calculation) {
+        setPrice(calculation.getPrice());
+        return this;
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/remote/CalculationResource.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/remote/CalculationResource.java
new file mode 100644
index 0000000..84792ea
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/remote/CalculationResource.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.remote;
+
+import java.util.Random;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+import org.glassfish.jersey.examples.rx.Helper;
+import org.glassfish.jersey.examples.rx.domain.Calculation;
+import org.glassfish.jersey.server.ManagedAsync;
+
+/**
+ * Obtain a calculation for a trip from one destination to another.
+ *
+ * @author Michal Gajdos
+ */
+@Path("remote/calculation")
+@Produces("application/json")
+public class CalculationResource {
+
+    @GET
+    @ManagedAsync
+    @Path("/from/{from}/to/{to}")
+    public Calculation calculation(@PathParam("from") @DefaultValue("Moon") final String from,
+                                   @PathParam("to") final String to) {
+        // Simulate long-running operation.
+        Helper.sleep(350);
+
+        return new Calculation(from, to, new Random().nextInt(10000));
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/remote/DestinationResource.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/remote/DestinationResource.java
new file mode 100644
index 0000000..6fa1d15
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/remote/DestinationResource.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.remote;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.examples.rx.Helper;
+import org.glassfish.jersey.examples.rx.domain.Destination;
+import org.glassfish.jersey.internal.util.collection.Views;
+import org.glassfish.jersey.server.ManagedAsync;
+
+/**
+ * Obtain a list of visited / recommended places for a given user.
+ *
+ * @author Michal Gajdos
+ */
+@Singleton
+@Path("remote/destination")
+@Produces("application/json")
+public class DestinationResource {
+
+    private static final Map<String, List<String>> VISITED = new HashMap<>();
+
+    static {
+        VISITED.put("Sync", Helper.getCountries(5));
+        VISITED.put("Async", Helper.getCountries(5));
+        VISITED.put("Guava", Helper.getCountries(5));
+        VISITED.put("RxJava", Helper.getCountries(5));
+        VISITED.put("RxJava2", Helper.getCountries(5));
+        VISITED.put("CompletionStage", Helper.getCountries(5));
+    }
+
+    @GET
+    @ManagedAsync
+    @Path("visited")
+    public List<Destination> visited(@HeaderParam("Rx-User") @DefaultValue("KO") final String user) {
+        // Simulate long-running operation.
+        Helper.sleep();
+
+        if (!VISITED.containsKey(user)) {
+            VISITED.put(user, Helper.getCountries(5));
+        }
+
+        return Views.listView(VISITED.get(user), Destination::new);
+    }
+
+    @GET
+    @ManagedAsync
+    @Path("recommended")
+    public List<Destination> recommended(@HeaderParam("Rx-User") @DefaultValue("KO") final String user,
+                                         @QueryParam("limit") @DefaultValue("5") final int limit) {
+        // Simulate long-running operation.
+        Helper.sleep();
+
+        if (!VISITED.containsKey(user)) {
+            VISITED.put(user, Helper.getCountries(5));
+        }
+
+        return Views.listView(Helper.getCountries(limit, VISITED.get(user)), Destination::new);
+    }
+}
diff --git a/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/remote/ForecastResource.java b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/remote/ForecastResource.java
new file mode 100644
index 0000000..09247fe
--- /dev/null
+++ b/examples/rx-client-webapp/src/main/java/org/glassfish/jersey/examples/rx/remote/ForecastResource.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx.remote;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+import org.glassfish.jersey.examples.rx.Helper;
+import org.glassfish.jersey.examples.rx.domain.Forecast;
+import org.glassfish.jersey.server.ManagedAsync;
+
+/**
+ * Obtain current weather conditions in a destination.
+ *
+ * @author Michal Gajdos
+ */
+@Path("remote/forecast")
+@Produces("application/json")
+public class ForecastResource {
+
+    @GET
+    @ManagedAsync
+    @Path("/{destination}")
+    public Forecast forecast(@PathParam("destination") final String destination) {
+        // Simulate long-running operation.
+        Helper.sleep(350);
+
+        return new Forecast(destination, Helper.getForecast());
+    }
+}
diff --git a/examples/rx-client-webapp/src/test/java/org/glassfish/jersey/examples/rx/RxClientsTest.java b/examples/rx-client-webapp/src/test/java/org/glassfish/jersey/examples/rx/RxClientsTest.java
new file mode 100644
index 0000000..0d6ae12
--- /dev/null
+++ b/examples/rx-client-webapp/src/test/java/org/glassfish/jersey/examples/rx/RxClientsTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.rx;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.examples.rx.domain.AgentResponse;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.ServletDeploymentContext;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Invoke clients in Agent part of the application.
+ *
+ * @author Michal Gajdos
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class RxClientsTest extends JerseyTest {
+
+    @Override
+    protected DeploymentContext configureDeployment() {
+        return ServletDeploymentContext.builder(RxApplication.class)
+                .contextPath("rx-client-webapp/rx").build();
+    }
+
+    @Test
+    public void testSyncClient() throws Exception {
+        // warmup
+        target("agent").path("sync").request().get();
+
+        final Response response = target("agent").path("sync").request().get();
+        response.bufferEntity();
+
+        final AgentResponse agentResponse = response.readEntity(AgentResponse.class);
+
+        assertThat(agentResponse.getVisited().size(), is(5));
+        assertThat(agentResponse.getRecommended().size(), is(5));
+
+        assertThat(agentResponse.getProcessingTime() > 4500, is(true));
+
+        System.out.println(response.readEntity(String.class));
+        System.out.println("Processing Time: " + agentResponse.getProcessingTime());
+    }
+
+    @Test
+    public void testAsyncClient() throws Exception {
+        // warmup
+        target("agent").path("async").request().get();
+
+        final Response response = target("agent").path("async").request().get();
+        response.bufferEntity();
+
+        final AgentResponse agentResponse = response.readEntity(AgentResponse.class);
+
+        assertThat(agentResponse.getVisited().size(), is(5));
+        assertThat(agentResponse.getRecommended().size(), is(5));
+
+        assertThat(agentResponse.getProcessingTime() > 850, is(true));
+        assertThat(agentResponse.getProcessingTime() < 4500, is(true));
+
+        System.out.println(response.readEntity(String.class));
+        System.out.println("Processing Time: " + agentResponse.getProcessingTime());
+    }
+
+    @Test
+    public void testRxObservableClient() throws Exception {
+        // warmup
+        target("agent").path("observable").request().get();
+
+        final Response response = target("agent").path("observable").request().get();
+        response.bufferEntity();
+
+        final AgentResponse agentResponse = response.readEntity(AgentResponse.class);
+
+        assertThat(agentResponse.getVisited().size(), is(5));
+        assertThat(agentResponse.getRecommended().size(), is(5));
+
+        assertThat(agentResponse.getProcessingTime() > 850, is(true));
+        assertThat(agentResponse.getProcessingTime() < 4500, is(true));
+
+        System.out.println(response.readEntity(String.class));
+        System.out.println("Processing Time: " + agentResponse.getProcessingTime());
+    }
+
+    @Test
+    public void testRxFlowableClient() throws Exception {
+        // warmup
+        target("agent").path("flowable").request().get();
+
+        final Response response = target("agent").path("flowable").request().get();
+        response.bufferEntity();
+
+        final AgentResponse agentResponse = response.readEntity(AgentResponse.class);
+
+        assertThat(agentResponse.getVisited().size(), is(5));
+        assertThat(agentResponse.getRecommended().size(), is(5));
+
+        assertThat(agentResponse.getProcessingTime() > 850, is(true));
+        assertThat(agentResponse.getProcessingTime() < 4500, is(true));
+
+        System.out.println(response.readEntity(String.class));
+        System.out.println("Processing Time: " + agentResponse.getProcessingTime());
+    }
+
+    @Test
+    public void testRxCompletionStageClient() throws Exception {
+        // warmup
+        target("agent").path("completion").request().get();
+
+        final Response response = target("agent").path("completion").request().get();
+        response.bufferEntity();
+
+        final AgentResponse agentResponse = response.readEntity(AgentResponse.class);
+
+        assertThat(agentResponse.getVisited().size(), is(5));
+        assertThat(agentResponse.getRecommended().size(), is(5));
+
+        assertThat(agentResponse.getProcessingTime() > 850, is(true));
+        assertThat(agentResponse.getProcessingTime() < 4500, is(true));
+
+        System.out.println(response.readEntity(String.class));
+        System.out.println("Processing Time: " + agentResponse.getProcessingTime());
+    }
+}
diff --git a/examples/server-async-managed/README.MD b/examples/server-async-managed/README.MD
new file mode 100644
index 0000000..1c63f4c
--- /dev/null
+++ b/examples/server-async-managed/README.MD
@@ -0,0 +1,74 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Server Asynchronous Managed Example
+===================================
+
+This example demonstrates JAX-RS 2.0 server-side non-blocking API using **ManagedAsync** annotation.
+
+The full description how to create an asynchronous resource can be found in Jersey User Guide, chapter 
+[Asynchronous Services and Clients](https://jersey.java.net/documentation/latest/async.html).
+
+Indicates that the resource method to which the annotation has been applied
+ * should be executed on a separate thread managed by an internal Jersey
+
+The  goal of the sample is to demonstrate that with limited I/O processing threads
+on the server the synchronous execution of a long-running task may lead to resource
+starvation caused by I/O processing threads being blocked by the long-running
+operation, unable to serve new incoming requests.
+
+OTOH, when the same long-running operation is executed asynchronously, the I/O
+threads do not need to block while waiting for the long-running operation to finish
+and the overall throughput of the system is greatly increased.
+
+- Look at the another example how to create a non-blocking API without **ManagedAsync**
+annotation <https://github.com/jersey/jersey/tree/master/examples/server-async>
+which also shown the way how to provide an immediate response for a client
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                        | Resource class            | HTTP methods
+--------------------------------| --------------------------| --------------
+**_chat_**                      | ChatResource              | POST
+**_chat_**                      | ChatResource              | GET
+**_managedasync/longrunning_**  | BlockingPostChatResource  | POST
+
+Sample Response
+---------------
+
+This section shortly describes what really happens during the calls in this chat example.
+
+We can start with POST request:
+```
+curl -v -X POST http://localhost:8080/base/chat -H "Content-Type: application/json" -d '{
+    "author": "Anonymous Author",
+    "message": "Secret Message"
+}'
+```
+
+We can notice that we haven't got any response yet. The called method is annotated **@ManagedAsync**
+which means that the I/O thread delegates a method processing to another Thread-Pool and is ready
+to accept another incoming request. But the processing is still stop. Why? **AsyncResponse** was added
+to the blocking queue and now it's waiting for GET call which will take the response and will finish the processing
+of the preceding call.
+
+```
+curl -v -X GET http://localhost:8080/base/chat
+```
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
diff --git a/examples/server-async-managed/pom.xml b/examples/server-async-managed/pom.xml
new file mode 100644
index 0000000..60905bb
--- /dev/null
+++ b/examples/server-async-managed/pom.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>server-async-managed</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-server-async-managed</name>
+
+    <description>Jersey JAX-RS asynchronous server-side example with custom Jersey executor providers.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.server.async.managed.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/server-async-managed/src/main/java/org/glassfish/jersey/examples/server/async/managed/App.java b/examples/server-async-managed/src/main/java/org/glassfish/jersey/examples/server/async/managed/App.java
new file mode 100644
index 0000000..3171046
--- /dev/null
+++ b/examples/server-async-managed/src/main/java/org/glassfish/jersey/examples/server/async/managed/App.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async.managed;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import static org.glassfish.jersey.logging.LoggingFeature.Verbosity.PAYLOAD_ANY;
+
+/**
+ * Jersey example application for custom executors managed async resources.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/base/");
+    public static final String ASYNC_LONG_RUNNING_MANAGED_OP_PATH = "managedasync/longrunning";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Custom Executor Managed Async Resources\" Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, create(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.\n"
+                    + "To test long-running asynchronous operation resource, try %s%s\n"
+                    + "To test async chat resource, try %s%s\n"
+                    + "Stop the application using CTRL+C", BASE_URI, ASYNC_LONG_RUNNING_MANAGED_OP_PATH, BASE_URI, "chat"));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    public static ResourceConfig create() {
+        return new ResourceConfig()
+                .registerClasses(JacksonFeature.class, ChatResource.class, SimpleJerseyExecutorManagedLongRunningResource.class)
+                .registerInstances(new LoggingFeature(Logger.getLogger(App.class.getName()), PAYLOAD_ANY));
+    }
+}
diff --git a/examples/server-async-managed/src/main/java/org/glassfish/jersey/examples/server/async/managed/ChatResource.java b/examples/server-async-managed/src/main/java/org/glassfish/jersey/examples/server/async/managed/ChatResource.java
new file mode 100644
index 0000000..5c86ad7
--- /dev/null
+++ b/examples/server-async-managed/src/main/java/org/glassfish/jersey/examples/server/async/managed/ChatResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async.managed;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+import org.glassfish.jersey.server.ManagedAsync;
+
+/**
+ * Example of a simple fire&forget point-to-point messaging resource.
+ *
+ * This version of the messaging resource does not block when POSTing a new message.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("chat")
+@Produces("application/json")
+public class ChatResource {
+
+    private static final BlockingQueue<AsyncResponse> suspended = new ArrayBlockingQueue<AsyncResponse>(5);
+
+    @GET
+    @ManagedAsync
+    public void getMessage(@Suspended final AsyncResponse ar) throws InterruptedException {
+        suspended.put(ar);
+    }
+
+    @POST
+    @ManagedAsync
+    public String postMessage(final Message message) throws InterruptedException {
+        final AsyncResponse asyncResponse = suspended.take();
+        asyncResponse.resume(message);
+        return "Sent!";
+    }
+}
diff --git a/examples/server-async-managed/src/main/java/org/glassfish/jersey/examples/server/async/managed/Message.java b/examples/server-async-managed/src/main/java/org/glassfish/jersey/examples/server/async/managed/Message.java
new file mode 100644
index 0000000..7f526d7
--- /dev/null
+++ b/examples/server-async-managed/src/main/java/org/glassfish/jersey/examples/server/async/managed/Message.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async.managed;
+
+import java.util.Date;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Chat message JAXB POJO.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@XmlRootElement
+public class Message {
+
+    public String author = "";
+    public String message = "";
+    public long time = new Date().getTime();
+
+    public Message(String author, String message) {
+        this.author = author;
+        this.message = message;
+    }
+
+    public Message() {
+    }
+
+    @Override
+    public String toString() {
+        return "Message{" + "author=" + author + ", message=" + message + ", time=" + time + '}';
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Message other = (Message) obj;
+        if ((this.author == null) ? (other.author != null) : !this.author.equals(other.author)) {
+            return false;
+        }
+        if ((this.message == null) ? (other.message != null) : !this.message.equals(other.message)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 47 * hash + (this.author != null ? this.author.hashCode() : 0);
+        hash = 47 * hash + (this.message != null ? this.message.hashCode() : 0);
+        return hash;
+    }
+}
diff --git a/examples/server-async-managed/src/main/java/org/glassfish/jersey/examples/server/async/managed/SimpleJerseyExecutorManagedLongRunningResource.java b/examples/server-async-managed/src/main/java/org/glassfish/jersey/examples/server/async/managed/SimpleJerseyExecutorManagedLongRunningResource.java
new file mode 100644
index 0000000..79f2851
--- /dev/null
+++ b/examples/server-async-managed/src/main/java/org/glassfish/jersey/examples/server/async/managed/SimpleJerseyExecutorManagedLongRunningResource.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async.managed;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+import org.glassfish.jersey.server.ManagedAsync;
+
+/**
+ * Example of a simple resource with a long-running operation executed in a
+ * custom Jersey container request processing thread.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path(App.ASYNC_LONG_RUNNING_MANAGED_OP_PATH)
+@Produces("text/plain")
+public class SimpleJerseyExecutorManagedLongRunningResource {
+
+    public static final String NOTIFICATION_RESPONSE = "Hello async world!";
+    //
+    private static final Logger LOGGER = Logger.getLogger(SimpleJerseyExecutorManagedLongRunningResource.class.getName());
+    private static final int SLEEP_TIME_IN_MILLIS = 1000;
+
+    @GET
+    @ManagedAsync
+    public void longGet(@Suspended final AsyncResponse ar, @QueryParam("id") int requestId) {
+        try {
+            Thread.sleep(SLEEP_TIME_IN_MILLIS);
+        } catch (InterruptedException ex) {
+            LOGGER.log(Level.SEVERE, "Response processing interrupted", ex);
+        }
+        ar.resume(requestId + " - " + NOTIFICATION_RESPONSE);
+    }
+}
diff --git a/examples/server-async-managed/src/test/java/org/glassfish/jersey/examples/server/async/managed/ManagedAsyncResourceTest.java b/examples/server-async-managed/src/test/java/org/glassfish/jersey/examples/server/async/managed/ManagedAsyncResourceTest.java
new file mode 100644
index 0000000..3eb6234
--- /dev/null
+++ b/examples/server-async-managed/src/test/java/org/glassfish/jersey/examples/server/async/managed/ManagedAsyncResourceTest.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async.managed;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Test for the asynchronous managed resources example.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ManagedAsyncResourceTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(ManagedAsyncResourceTest.class.getName());
+
+    @Override
+    protected ResourceConfig configure() {
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.external.ExternalTestContainerFactory
+
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+        return App.create();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(new JacksonFeature());
+    }
+
+    @Test
+    public void testLongRunningResource() throws InterruptedException {
+        final WebTarget resourceTarget = target().path(App.ASYNC_LONG_RUNNING_MANAGED_OP_PATH);
+        final String expectedResponse = SimpleJerseyExecutorManagedLongRunningResource.NOTIFICATION_RESPONSE;
+
+        final int MAX_MESSAGES = 100;
+        final int LATCH_WAIT_TIMEOUT = 10 * getAsyncTimeoutMultiplier();
+        final boolean debugMode = false;
+        final boolean sequentialGet = false;
+        final Object sequentialGetLock = new Object();
+
+        final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+                .setNameFormat("async-resource-test-%d")
+                .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+                .build());
+
+        final Map<Integer, String> getResponses = new ConcurrentHashMap<Integer, String>();
+
+        final CountDownLatch getRequestLatch = new CountDownLatch(MAX_MESSAGES);
+
+        try {
+            for (int i = 0; i < MAX_MESSAGES; i++) {
+                final int requestId = i;
+                executor.submit(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        if (debugMode || sequentialGet) {
+                            synchronized (sequentialGetLock) {
+                                get();
+                            }
+                        } else {
+                            get();
+                        }
+                    }
+
+                    private void get() {
+                        try {
+                            int attemptCounter = 0;
+                            while (true) {
+                                attemptCounter++;
+                                try {
+                                    final String response = resourceTarget.queryParam("id", requestId).request()
+                                            .get(String.class);
+                                    getResponses.put(requestId, response);
+                                    break;
+                                } catch (Throwable t) {
+                                    LOGGER.log(Level.SEVERE, String.format("Error sending GET request <%s> for %d. time.",
+                                            requestId, attemptCounter), t);
+                                }
+                                if (attemptCounter > 3) {
+                                    break;
+                                }
+                                Thread.sleep(10);
+                            }
+                        } catch (InterruptedException ignored) {
+                            LOGGER.log(Level.WARNING,
+                                    String.format("Error sending GET message <%s>: Interrupted", requestId), ignored);
+                        } finally {
+                            getRequestLatch.countDown();
+                        }
+                    }
+                });
+            }
+
+            if (debugMode) {
+                getRequestLatch.await();
+            } else {
+                if (!getRequestLatch.await(LATCH_WAIT_TIMEOUT, TimeUnit.SECONDS)) {
+                    LOGGER.log(Level.SEVERE, "Waiting for all GET requests to complete has timed out.");
+                }
+            }
+        } finally {
+            executor.shutdownNow();
+        }
+
+        StringBuilder messageBuilder = new StringBuilder("GET responses received: ").append(getResponses.size()).append("\n");
+        for (Map.Entry<Integer, String> getResponseEntry : getResponses.entrySet()) {
+            messageBuilder.append("GET response for message ")
+                    .append(getResponseEntry.getKey()).append(": ")
+                    .append(getResponseEntry.getValue()).append('\n');
+        }
+        LOGGER.info(messageBuilder.toString());
+
+        for (Map.Entry<Integer, String> entry : getResponses.entrySet()) {
+            assertTrue(
+                    "Unexpected GET notification response for message " + entry.getKey(),
+                    entry.getValue().contains(expectedResponse));
+        }
+        assertEquals(MAX_MESSAGES, getResponses.size());
+    }
+
+    @Test
+    public void testChatResource() throws InterruptedException {
+        final WebTarget resourceTarget = target().path("chat");
+        final int MAX_MESSAGES = 100;
+        final int LATCH_WAIT_TIMEOUT = 10 * getAsyncTimeoutMultiplier();
+        final boolean debugMode = false;
+        final boolean sequentialGet = false;
+        final boolean sequentialPost = false;
+        final Object sequentialGetLock = new Object();
+        final Object sequentialPostLock = new Object();
+
+        final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+                .setNameFormat("async-resource-test-%d")
+                .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+                .build());
+
+        final Map<Integer, Integer> postResponses = new ConcurrentHashMap<Integer, Integer>();
+        final Map<Integer, Message> getResponses = new ConcurrentHashMap<Integer, Message>();
+
+        final CountDownLatch postRequestLatch = new CountDownLatch(MAX_MESSAGES);
+        final CountDownLatch getRequestLatch = new CountDownLatch(MAX_MESSAGES);
+
+        try {
+            for (int i = 0; i < MAX_MESSAGES; i++) {
+                final int requestId = i;
+                executor.submit(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        if (debugMode || sequentialPost) {
+                            synchronized (sequentialPostLock) {
+                                post();
+                            }
+                        } else {
+                            post();
+                        }
+                    }
+
+                    private void post() {
+                        try {
+                            int attemptCounter = 0;
+                            while (true) {
+                                attemptCounter++;
+                                try {
+                                    final Response response = resourceTarget.request()
+                                            .post(Entity.json(new Message("" + requestId, "" + requestId)));
+                                    postResponses.put(requestId, response.getStatus());
+                                    break;
+                                } catch (Throwable t) {
+                                    LOGGER.log(Level.WARNING, String.format("Error POSTING message <%s> for %d. time.",
+                                            requestId, attemptCounter), t);
+                                }
+                                if (attemptCounter > 3) {
+                                    break;
+                                }
+                                Thread.sleep(10);
+                            }
+                        } catch (InterruptedException ignored) {
+                            LOGGER.log(Level.WARNING,
+                                    String.format("Error POSTING message <%s>: Interrupted", requestId), ignored);
+                        } finally {
+                            postRequestLatch.countDown();
+                        }
+                    }
+                });
+                executor.submit(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        if (debugMode || sequentialGet) {
+                            synchronized (sequentialGetLock) {
+                                get();
+                            }
+                        } else {
+                            get();
+                        }
+                    }
+
+                    private void get() {
+                        try {
+                            int attemptCounter = 0;
+                            while (true) {
+                                attemptCounter++;
+                                try {
+                                    final Message response = resourceTarget.request("application/json").get(Message.class);
+                                    getResponses.put(requestId, response);
+                                    break;
+                                } catch (Throwable t) {
+                                    LOGGER.log(Level.SEVERE, String.format("Error sending GET request <%s> for %d. time.",
+                                            requestId, attemptCounter), t);
+                                }
+                                if (attemptCounter > 3) {
+                                    break;
+                                }
+                                Thread.sleep(10);
+                            }
+                        } catch (InterruptedException ignored) {
+                            LOGGER.log(Level.WARNING,
+                                    String.format("Error sending GET message <%s>: Interrupted", requestId), ignored);
+                        } finally {
+                            getRequestLatch.countDown();
+                        }
+                    }
+                });
+            }
+
+            if (debugMode) {
+                postRequestLatch.await();
+                getRequestLatch.await();
+            } else {
+                if (!postRequestLatch.await(LATCH_WAIT_TIMEOUT, TimeUnit.SECONDS)) {
+                    LOGGER.log(Level.SEVERE, "Waiting for all POST requests to complete has timed out.");
+                }
+                if (!getRequestLatch.await(LATCH_WAIT_TIMEOUT, TimeUnit.SECONDS)) {
+                    LOGGER.log(Level.SEVERE, "Waiting for all GET requests to complete has timed out.");
+                }
+            }
+        } finally {
+            executor.shutdownNow();
+        }
+
+        StringBuilder messageBuilder = new StringBuilder("POST responses received: ").append(postResponses.size()).append("\n");
+        for (Map.Entry<Integer, Integer> postResponseEntry : postResponses.entrySet()) {
+            messageBuilder.append("POST response for message ")
+                    .append(postResponseEntry.getKey()).append(": ")
+                    .append(postResponseEntry.getValue()).append('\n');
+        }
+        messageBuilder.append('\n');
+        messageBuilder.append("GET responses received: ").append(getResponses.size()).append("\n");
+        for (Map.Entry<Integer, Message> getResponseEntry : getResponses.entrySet()) {
+            messageBuilder.append("GET response for message ")
+                    .append(getResponseEntry.getKey()).append(": ")
+                    .append(getResponseEntry.getValue()).append('\n');
+        }
+        LOGGER.info(messageBuilder.toString());
+
+        for (Map.Entry<Integer, Integer> postResponseEntry : postResponses.entrySet()) {
+            assertEquals(
+                    "Unexpected POST notification response for message " + postResponseEntry.getKey(),
+                    200, postResponseEntry.getValue().intValue());
+        }
+
+        final List<Integer> lost = new LinkedList<Integer>();
+        final Collection<Message> getResponseValues = getResponses.values();
+        for (int i = 0; i < MAX_MESSAGES; i++) {
+            if (!getResponseValues.contains(new Message("" + i, "" + i))) {
+                lost.add(i);
+            }
+        }
+        if (!lost.isEmpty()) {
+            fail("Detected a posted message loss(es): " + lost.toString());
+        }
+        assertEquals(MAX_MESSAGES, postResponses.size());
+        assertEquals(MAX_MESSAGES, getResponses.size());
+    }
+}
diff --git a/examples/server-async-standalone/README.MD b/examples/server-async-standalone/README.MD
new file mode 100644
index 0000000..d5add0b
--- /dev/null
+++ b/examples/server-async-standalone/README.MD
@@ -0,0 +1,104 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Server Asynchronous Standalone Example
+======================================
+
+This example demonstrates JAX-RS 2.0 asynchronous API, both on the client and
+server side.
+
+The  goal of the sample is to demonstrate that with limited I/O processing threads
+on the server the synchronous execution of a long-running task may lead to resource
+starvation caused by I/O processing threads being blocked by the long-running
+operation, unable to serve new incoming requests.
+
+OTOH, when the same long-running operation is executed asynchronously, the I/O
+threads do not need to block while waiting for the long-running operation to finish
+and the overall throughput of the system is greatly increased.
+
+The example consists of 2 modules:
+
+  1. web application module "webapp" (produces server-async-standalone-webapp.war)
+
+     - the module contains an long-running echo service deployed under application context root on path templates:
+
+       - "long-running/sync/{echo}", served by a synchronously executed, blockingmethod.
+
+       - "long-running/async/{echo}", served by an asynchronously executed, non-blocking method.
+
+  2. client module "client" (produces client.jar)
+
+     - the module contains the web application client developed using JAX-RS 2.0
+       client API & leveraging the asynchronous client execution for spawning
+       multiple simultaneous client requests with subsequent asynchronous response
+       processing.
+
+To build the example, change to the example root directory and invoke maven build:
+
+>   mvn clean package
+
+This command builds the web application WAR as well as client JAR artifacts. Once
+built, the web application may be deployed to the application server. E.g. to deploy
+the application to a running GlassFish instance, you may do (while still being in
+the root example directory):
+
+>   $AS_HOME/bin/as-admin deploy ./webapp/target/server-async-standalone-webapp.war
+
+Once deployed, the client can be run as follows:
+
+>   cd client
+>   mvn exec:java
+
+This will start the main client GUI window. In the top panel, the GUI lets you to customize
+few variables such as number of requests to be sent, base web application URL or sync/async
+endpoint to be invoked. Once the client is set up, the "Run" button runs the requests. Each
+request is represented as a colored box in the lower window panel. The color indicates the
+request status:
+
+* yellow - running
+* green  - successfully completed
+* red    - request execution failed with an error
+
+Also, the tooltip of each box provides additional status information. After the execution is
+finished, short statistics about the run are displayed in the middle window panel.
+
+Executing command-line client:
+------------------------------
+Optionally, a command line Main.java client can be executed (by changing the mainClass of
+exec-maven-plugin in pom.xml to org.glassfish.jersey.examples.server.async.Main).
+Following properties may be customized in the command-line client runtime:
+
+  1. req=<n>
+
+     : number of requests to be sent by the client, defaults to 10
+
+  2. mode=(sync|async)
+
+     : execution mode defines which version of the service should be used, defaults to "async"
+
+  3. uri=<base_webapp_uri>
+
+     : base URI of the deployed web application, defaults to "http://localhost:8080/server-async-standalone-webapp"
+
+These properties may be passed throug maven to the client runtime as command-line arguments
+using the -Dexec.args="<arguments>" flag, e.g.:
+
+>   mvn exec:java -Dexec.args="req=20 mode=sync uri=http://foo.com/bar"
+
+When the example client is run, successfully executed requests are output as an
+asterisk '*', failed requests as an exclamation mark '!'. Any errors that occured
+during the client execution are reported at the end of the run including the
+statistics about the processing time and success ratio.
+
+Try running the client using many requests and compare the results when running
+against sync and async version of the resource, e.g.:
+
+>   mvn exec:java -Dexec.args="req=100 mode=sync"
+>   mvn exec:java -Dexec.args="req=100 mode=async"
+
+You should see a significant speed improvement in the latter run.
diff --git a/examples/server-async-standalone/client/pom.xml b/examples/server-async-standalone/client/pom.xml
new file mode 100644
index 0000000..06f779b
--- /dev/null
+++ b/examples/server-async-standalone/client/pom.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>server-async-standalone</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>server-async-standalone-client</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-server-async-standalone-client</name>
+
+    <description>Standalone Jersey JAX-RS asynchronous server-side processing example client.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.swinglabs</groupId>
+            <artifactId>swing-layout</artifactId>
+            <version>1.0.3</version>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <index>true</index>
+                        <manifest>
+                            <addClasspath>true</addClasspath>
+                            <mainClass>org.glassfish.jersey.examples.server.async.MainWindow</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+            <!-- Run the application using "mvn exec:java" -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.server.async.MainWindow</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>run-external-tests</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <jersey.config.test.container.factory>${external.container.factory}</jersey.config.test.container.factory>
+                                <jersey.config.test.container.port>${external.container.port}</jersey.config.test.container.port>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <properties>
+                <!-- External test container configuration is done via properties to allow overriding via command line. -->
+                <external.container.factory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</external.container.factory>
+                <external.container.port>8080</external.container.port>
+                <maven.test.skip>false</maven.test.skip>
+            </properties>
+        </profile>
+        <profile>
+            <id>release</id>
+            <!-- do not create source zip bundles -->
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <configuration>
+                            <skipAssembly>true</skipAssembly>
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <maven.test.skip>true</maven.test.skip>
+    </properties>
+
+</project>
diff --git a/examples/server-async-standalone/client/src/main/java/org/glassfish/jersey/examples/server/async/Main.java b/examples/server-async-standalone/client/src/main/java/org/glassfish/jersey/examples/server/async/Main.java
new file mode 100644
index 0000000..8f7c291
--- /dev/null
+++ b/examples/server-async-standalone/client/src/main/java/org/glassfish/jersey/examples/server/async/Main.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async;
+
+import java.io.IOException;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.client.WebTarget;
+
+/**
+ * Long-running asynchronous service client.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class Main {
+
+    /**
+     * Main client entry point.
+     *
+     * @param args command-line arguments.
+     */
+    public static void main(String[] args) {
+        System.exit(runClient(args));
+    }
+
+    /**
+     * Client - business logic.
+     *
+     * @param args command-line arguments.
+     * @return exit code of the utility. {@code 0} if everything completed without errors, {@code -1} otherwise.
+     */
+    static int runClient(final String[] args) {
+        // Parsing command-line arguments
+        final Config config = Config.parse(args);
+        System.out.println(String.format("\nStarting to execute %d requests:\n", config.requests));
+        // Creating JAX-RS client
+        final Client client = ClientBuilder.newClient();
+        // Targeting echo resource at URI "<baseUri>/long-running/(sync|async)/{echo}"
+        final WebTarget echoResource = client.target(config.baseUri).path("long-running/{mode}/{echo}")
+                .resolveTemplate("mode", (config.sync) ? "sync" : "async");
+
+        final CountDownLatch latch = new CountDownLatch(config.requests);
+        final Queue<String> errors = new ConcurrentLinkedQueue<String>();
+        final AtomicInteger requestCounter = new AtomicInteger(0);
+
+        final long tic = System.currentTimeMillis();
+        for (int i = 0; i < config.requests; i++) {
+            final int reqId = i;
+            echoResource.resolveTemplate("echo", reqId).request().async().get(new InvocationCallback<String>() {
+                private final AtomicInteger retries = new AtomicInteger(0);
+
+                @Override
+                public void completed(String response) {
+                    final String requestId = Integer.toString(reqId);
+                    if (requestId.equals(response)) {
+                        System.out.print("*");
+                        requestCounter.incrementAndGet();
+                    } else {
+                        System.out.print("!");
+                        errors.offer(String.format("Echo response '%s' not equal to request '%s'", response, requestId));
+                    }
+                    latch.countDown();
+                }
+
+                @Override
+                public void failed(Throwable error) {
+                    if (error.getCause() instanceof IOException && retries.getAndIncrement() < 3) {
+                        // resend
+                        echoResource.resolveTemplate("echo", reqId).request().async().get(this);
+                    } else {
+                        System.out.print("!");
+                        errors.offer(String.format("Request '%d' has failed: %s", reqId, error.toString()));
+                        latch.countDown();
+                    }
+                }
+            });
+        }
+
+        try {
+            if (!latch.await(60, TimeUnit.SECONDS)) {
+                errors.offer("Waiting for requests to complete has timed out.");
+            }
+        } catch (InterruptedException e) {
+            errors.offer("Waiting for requests to complete has been interrupted.");
+        }
+        final long toc = System.currentTimeMillis();
+
+        System.out.println(String.format("\n\nExecution finished in %d ms.\nSuccess rate: %6.2f %%",
+                toc - tic,
+                ((double) requestCounter.get() / config.requests) * 100));
+        if (errors.size() > 0) {
+            System.out.println("Following errors occurred during the request execution");
+            for (String error : errors) {
+                System.out.println("\t" + error);
+            }
+        }
+
+        client.close();
+
+        return errors.size() > 0 ? -1 : 0;
+    }
+
+    static class Config {
+
+        /**
+         * Default base URI of the async echo web application.
+         */
+        public static final String DEFAULT_BASE_URI = "http://localhost:8080/server-async-standalone-webapp/";
+
+        final String baseUri;
+        final boolean sync;
+        final int requests;
+
+        Config(String baseUri, boolean sync, int requests) {
+            this.baseUri = baseUri;
+            this.sync = sync;
+            this.requests = requests;
+        }
+
+        public static Config parse(String[] args) {
+            String baseUri = DEFAULT_BASE_URI;
+            boolean sync = false;
+            int requests = 10;
+
+            for (String arg : args) {
+                final String[] keyValuePair = arg.trim().split("=");
+
+                if ("uri".equals(keyValuePair[0])) {
+                    baseUri = keyValuePair[1];
+                } else if ("mode".equals(keyValuePair[0])) {
+                    sync = "sync".equals(keyValuePair[1]);
+                } else if ("req".equals(keyValuePair[0])) {
+                    requests = Integer.parseInt(keyValuePair[1]);
+                } else {
+                    System.out.println("WARNING: Unknown parameter: " + keyValuePair[0]);
+                }
+            }
+
+            return new Config(baseUri, sync, requests);
+        }
+
+        @Override
+        public String toString() {
+            return "Config{"
+                    + "baseUri='" + baseUri + '\''
+                    + ", sync=" + sync
+                    + ", requests=" + requests
+                    + '}';
+        }
+    }
+}
diff --git a/examples/server-async-standalone/client/src/main/java/org/glassfish/jersey/examples/server/async/MainWindow.form b/examples/server-async-standalone/client/src/main/java/org/glassfish/jersey/examples/server/async/MainWindow.form
new file mode 100644
index 0000000..b78a861
--- /dev/null
+++ b/examples/server-async-standalone/client/src/main/java/org/glassfish/jersey/examples/server/async/MainWindow.form
@@ -0,0 +1,256 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
+  <NonVisualComponents>
+    <Component class="javax.swing.ButtonGroup" name="syncAsyncSwitchGroup">
+    </Component>
+  </NonVisualComponents>
+  <Properties>
+    <Property name="defaultCloseOperation" type="int" value="3"/>
+    <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+      <Dimension value="[480, 600]"/>
+    </Property>
+    <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+      <Dimension value="[480, 600]"/>
+    </Property>
+    <Property name="resizable" type="boolean" value="false"/>
+  </Properties>
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="2"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="1" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="1" attributes="0">
+                  <Component id="jPanel2" max="32767" attributes="0"/>
+                  <Component id="jPanel1" alignment="0" max="32767" attributes="0"/>
+                  <Component id="messagePanel" alignment="1" min="-2" pref="468" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jPanel1" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jPanel2" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="messagePanel" pref="456" max="32767" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="jPanel1">
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel2" alignment="1" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace type="unrelated" max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Group type="102" attributes="0">
+                          <Component id="requestCountField" max="32767" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="syncRadio" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="asyncRadio" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace min="-2" pref="24" max="-2" attributes="0"/>
+                          <Component id="runButton" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <Group type="102" attributes="0">
+                          <Component id="uriField" max="32767" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                      </Group>
+                  </Group>
+              </Group>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="1" attributes="0">
+                  <EmptySpace max="32767" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="uriField" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel2" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="requestCountField" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="syncRadio" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="asyncRadio" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="runButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JLabel" name="jLabel1">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Number of requests"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JTextField" name="requestCountField">
+          <Properties>
+            <Property name="horizontalAlignment" type="int" value="4"/>
+            <Property name="text" type="java.lang.String" value="10"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JRadioButton" name="syncRadio">
+          <Properties>
+            <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+              <ComponentRef name="syncAsyncSwitchGroup"/>
+            </Property>
+            <Property name="text" type="java.lang.String" value="sync"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JRadioButton" name="asyncRadio">
+          <Properties>
+            <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+              <ComponentRef name="syncAsyncSwitchGroup"/>
+            </Property>
+            <Property name="selected" type="boolean" value="true"/>
+            <Property name="text" type="java.lang.String" value="async"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JTextField" name="uriField">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="jTextField2"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JLabel" name="jLabel2">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Base web app URL"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JButton" name="runButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Run"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="runButtonActionPerformed"/>
+          </Events>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Container class="javax.swing.JPanel" name="messagePanel">
+      <Properties>
+        <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+          <Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
+            <EtchetBorder/>
+          </Border>
+        </Property>
+      </Properties>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JLabel" name="jLabel3">
+          <Properties>
+            <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+              <Color blue="0" green="ff" red="ff" type="rgb"/>
+            </Property>
+            <Property name="text" type="java.lang.String" value="   "/>
+            <Property name="toolTipText" type="java.lang.String" value="Running..."/>
+            <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+              <Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
+                <LineBorder/>
+              </Border>
+            </Property>
+            <Property name="opaque" type="boolean" value="true"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JLabel" name="jLabel4">
+          <Properties>
+            <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+              <Color blue="0" green="ff" red="ff" type="rgb"/>
+            </Property>
+            <Property name="text" type="java.lang.String" value="   "/>
+            <Property name="toolTipText" type="java.lang.String" value="Running..."/>
+            <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+              <Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
+                <LineBorder/>
+              </Border>
+            </Property>
+            <Property name="opaque" type="boolean" value="true"/>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Container class="javax.swing.JPanel" name="jPanel2">
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Component id="finishStatusLabel" max="32767" attributes="0"/>
+                      <Component id="successRateStatusLabel" max="32767" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="finishStatusLabel" min="-2" max="-2" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="successRateStatusLabel" min="-2" max="-2" attributes="0"/>
+                  <EmptySpace max="32767" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JLabel" name="finishStatusLabel">
+          <Properties>
+            <Property name="text" type="java.lang.String" value=" "/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JLabel" name="successRateStatusLabel">
+          <Properties>
+            <Property name="text" type="java.lang.String" value=" "/>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>
diff --git a/examples/server-async-standalone/client/src/main/java/org/glassfish/jersey/examples/server/async/MainWindow.java b/examples/server-async-standalone/client/src/main/java/org/glassfish/jersey/examples/server/async/MainWindow.java
new file mode 100644
index 0000000..de9df4b
--- /dev/null
+++ b/examples/server-async-standalone/client/src/main/java/org/glassfish/jersey/examples/server/async/MainWindow.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async;
+
+import java.awt.Color;
+import java.awt.EventQueue;
+import java.awt.FlowLayout;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.client.WebTarget;
+
+import javax.swing.JLabel;
+
+/**
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class MainWindow extends javax.swing.JFrame {
+
+    /**
+     * Creates new form MainWindow
+     */
+    public MainWindow() {
+        initComponents();
+
+        uriField.setText(Main.Config.DEFAULT_BASE_URI);
+        ((FlowLayout) messagePanel.getLayout()).setAlignment(FlowLayout.LEADING);
+        messagePanel.removeAll();
+        messagePanel.revalidate();
+    }
+
+    /**
+     * This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        syncAsyncSwitchGroup = new javax.swing.ButtonGroup();
+        jPanel1 = new javax.swing.JPanel();
+        jLabel1 = new javax.swing.JLabel();
+        requestCountField = new javax.swing.JTextField();
+        syncRadio = new javax.swing.JRadioButton();
+        asyncRadio = new javax.swing.JRadioButton();
+        uriField = new javax.swing.JTextField();
+        jLabel2 = new javax.swing.JLabel();
+        runButton = new javax.swing.JButton();
+        messagePanel = new javax.swing.JPanel();
+        jLabel3 = new javax.swing.JLabel();
+        jLabel4 = new javax.swing.JLabel();
+        jPanel2 = new javax.swing.JPanel();
+        finishStatusLabel = new javax.swing.JLabel();
+        successRateStatusLabel = new javax.swing.JLabel();
+
+        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
+        setMaximumSize(new java.awt.Dimension(480, 600));
+        setMinimumSize(new java.awt.Dimension(480, 600));
+        setResizable(false);
+
+        jLabel1.setText("Number of requests");
+
+        requestCountField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
+        requestCountField.setText("10");
+
+        syncAsyncSwitchGroup.add(syncRadio);
+        syncRadio.setText("sync");
+
+        syncAsyncSwitchGroup.add(asyncRadio);
+        asyncRadio.setSelected(true);
+        asyncRadio.setText("async");
+
+        uriField.setText("jTextField2");
+
+        jLabel2.setText("Base web app URL");
+
+        runButton.setText("Run");
+        runButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                runButtonActionPerformed(evt);
+            }
+        });
+
+        org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1);
+        jPanel1.setLayout(jPanel1Layout);
+        jPanel1Layout.setHorizontalGroup(
+                jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(jPanel1Layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                                        .add(jLabel1)
+                                        .add(org.jdesktop.layout.GroupLayout.TRAILING, jLabel2))
+                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
+                                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                                        .add(jPanel1Layout.createSequentialGroup()
+                                                .add(requestCountField)
+                                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                                .add(syncRadio)
+                                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                                .add(asyncRadio)
+                                                .add(24, 24, 24)
+                                                .add(runButton))
+                                        .add(jPanel1Layout.createSequentialGroup()
+                                                .add(uriField)
+                                                .addContainerGap())))
+        );
+        jPanel1Layout.setVerticalGroup(
+                jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel1Layout.createSequentialGroup()
+                                .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
+                                        .add(uriField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
+                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
+                                        .add(jLabel2))
+                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
+                                        .add(requestCountField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
+                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
+                                        .add(jLabel1)
+                                        .add(syncRadio)
+                                        .add(asyncRadio)
+                                        .add(runButton)))
+        );
+
+        messagePanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
+
+        jLabel3.setBackground(new java.awt.Color(255, 255, 0));
+        jLabel3.setText("   ");
+        jLabel3.setToolTipText("Running...");
+        jLabel3.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
+        jLabel3.setOpaque(true);
+        messagePanel.add(jLabel3);
+
+        jLabel4.setBackground(new java.awt.Color(255, 255, 0));
+        jLabel4.setText("   ");
+        jLabel4.setToolTipText("Running...");
+        jLabel4.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
+        jLabel4.setOpaque(true);
+        messagePanel.add(jLabel4);
+
+        finishStatusLabel.setText(" ");
+
+        successRateStatusLabel.setText(" ");
+
+        org.jdesktop.layout.GroupLayout jPanel2Layout = new org.jdesktop.layout.GroupLayout(jPanel2);
+        jPanel2.setLayout(jPanel2Layout);
+        jPanel2Layout.setHorizontalGroup(
+                jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(jPanel2Layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                                        .add(finishStatusLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                        .add(successRateStatusLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                                .addContainerGap())
+        );
+        jPanel2Layout.setVerticalGroup(
+                jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(jPanel2Layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(finishStatusLabel)
+                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                .add(successRateStatusLabel)
+                                .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+        );
+
+        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
+        getContentPane().setLayout(layout);
+        layout.setHorizontalGroup(
+                layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
+                                        .add(jPanel2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                        .add(org.jdesktop.layout.GroupLayout.LEADING, jPanel1,
+                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                        .add(messagePanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 468,
+                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
+                                .addContainerGap())
+        );
+        layout.setVerticalGroup(
+                layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(jPanel1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
+                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                        org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
+                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                .add(jPanel2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
+                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                        org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
+                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                .add(messagePanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 456, Short.MAX_VALUE)
+                                .addContainerGap())
+        );
+
+        pack();
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void runButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_runButtonActionPerformed
+        final Main.Config config = new Main.Config(uriField.getText(), syncRadio.isSelected(),
+                Integer.parseInt(requestCountField.getText()));
+        runButton.setEnabled(false);
+        finishStatusLabel.setText(" ");
+        successRateStatusLabel.setText(" ");
+        messagePanel.removeAll();
+        messagePanel.revalidate();
+        messagePanel.repaint();
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+
+            @Override
+            public void run() {
+                sendMessages(config);
+            }
+        });
+    }//GEN-LAST:event_runButtonActionPerformed
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String args[]) {
+        /* Set the Nimbus look and feel */
+        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
+        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
+         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
+         */
+//        try {
+//            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
+//                if ("Nimbus".equals(info.getName())) {
+//                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
+//                    break;
+//                }
+//            }
+//        } catch (ClassNotFoundException ex) {
+//            java.util.logging.Logger.getLogger(MainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
+//        } catch (InstantiationException ex) {
+//            java.util.logging.Logger.getLogger(MainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
+//        } catch (IllegalAccessException ex) {
+//            java.util.logging.Logger.getLogger(MainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
+//        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
+//            java.util.logging.Logger.getLogger(MainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
+//        }
+        //</editor-fold>
+
+        /* Create and display the form */
+        java.awt.EventQueue.invokeLater(new Runnable() {
+            public void run() {
+                final MainWindow mainWindow = new MainWindow();
+                mainWindow.setLocationRelativeTo(null);
+                mainWindow.setVisible(true);
+            }
+        });
+    }
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JRadioButton asyncRadio;
+    private javax.swing.JLabel finishStatusLabel;
+    private javax.swing.JLabel jLabel1;
+    private javax.swing.JLabel jLabel2;
+    private javax.swing.JLabel jLabel3;
+    private javax.swing.JLabel jLabel4;
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JPanel jPanel2;
+    private javax.swing.JPanel messagePanel;
+    private javax.swing.JTextField requestCountField;
+    private javax.swing.JButton runButton;
+    private javax.swing.JLabel successRateStatusLabel;
+    private javax.swing.ButtonGroup syncAsyncSwitchGroup;
+    private javax.swing.JRadioButton syncRadio;
+    private javax.swing.JTextField uriField;
+    // End of variables declaration//GEN-END:variables
+
+    private final Color PROGRESS_COLOR = new Color(255, 255, 0);
+    private final Color ERROR_COLOR = new Color(255, 0, 0);
+    private final Color SUCCESS_COLOR = new Color(0, 255, 0);
+
+    private JLabel createRequestStatusLabel() {
+        final JLabel label = new JLabel("   ");
+        invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+
+                label.setBackground(PROGRESS_COLOR);
+                label.setToolTipText("Running...");
+                label.setBorder(javax.swing.BorderFactory.createLineBorder(new Color(0, 0, 0)));
+                label.setOpaque(true);
+
+                messagePanel.add(label);
+                messagePanel.revalidate();
+            }
+        });
+
+        return label;
+    }
+
+    private void invokeAndWait(Runnable event) {
+        try {
+            EventQueue.invokeAndWait(event);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void sendMessages(final Main.Config config) {
+        // Creating JAX-RS client
+        final Client client = ClientBuilder.newClient();
+        // Targeting echo resource at URI "<baseUri>/long-running/(sync|async)/{echo}"
+        final WebTarget echoResource = client.target(config.baseUri).path("long-running/{mode}/{echo}")
+                .resolveTemplate("mode", (config.sync) ? "sync" : "async");
+
+        final CountDownLatch latch = new CountDownLatch(config.requests);
+        final AtomicInteger requestCounter = new AtomicInteger(0);
+
+        final long tic = System.currentTimeMillis();
+        for (int i = 0; i < config.requests; i++) {
+            final int reqId = i;
+
+            final JLabel requestStatusLabel = createRequestStatusLabel();
+
+            echoResource.resolveTemplate("echo", reqId).request().async().get(new InvocationCallback<String>() {
+                private final AtomicInteger retries = new AtomicInteger(0);
+
+                @Override
+                public void completed(final String response) {
+                    invokeAndWait(new Runnable() {
+                        @Override
+                        public void run() {
+                            final String requestId = Integer.toString(reqId);
+                            if (requestId.equals(response)) {
+                                requestStatusLabel.setBackground(SUCCESS_COLOR);
+                                requestStatusLabel.setToolTipText(requestId);
+                                requestCounter.incrementAndGet();
+                            } else {
+                                requestStatusLabel.setBackground(ERROR_COLOR);
+                                requestStatusLabel.setToolTipText(String.format("Echo response '%s' not equal to request '%s'",
+                                        response, requestId));
+                            }
+                            latch.countDown();
+                        }
+                    });
+                }
+
+                @Override
+                public void failed(final Throwable error) {
+                    if (error.getCause() instanceof IOException && retries.getAndIncrement() < 3) {
+                        // resend
+                        echoResource.resolveTemplate("echo", reqId).request().async().get(this);
+                    } else {
+                        invokeAndWait(new Runnable() {
+                            @Override
+                            public void run() {
+                                requestStatusLabel.setBackground(ERROR_COLOR);
+                                requestStatusLabel.setToolTipText(String.format("Request '%d' has failed: %s",
+                                        reqId, error.toString()));
+                                latch.countDown();
+                            }
+                        });
+                    }
+                }
+            });
+        }
+
+        try {
+            if (!latch.await(60, TimeUnit.SECONDS)) {
+                System.out.println("Waiting for requests to complete has timed out.");
+            }
+        } catch (InterruptedException e) {
+            System.out.println("Waiting for requests to complete has been interrupted.");
+        }
+        final long toc = System.currentTimeMillis();
+
+        invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                finishStatusLabel.setText(String.format("Execution finished in %d ms.", toc - tic));
+                successRateStatusLabel.setText(String.format("Success rate: %6.2f %%",
+                        ((double) requestCounter.get() / config.requests) * 100));
+            }
+        });
+        client.close();
+
+        EventQueue.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                runButton.setEnabled(true);
+            }
+        });
+    }
+}
diff --git a/examples/server-async-standalone/client/src/test/java/org/glassfish/jersey/examples/server/async/MainTest.java b/examples/server-async-standalone/client/src/test/java/org/glassfish/jersey/examples/server/async/MainTest.java
new file mode 100644
index 0000000..2826e44
--- /dev/null
+++ b/examples/server-async-standalone/client/src/test/java/org/glassfish/jersey/examples/server/async/MainTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test whether cases described in {@code README} are passing.
+ *
+ * @author Michal Gajdos
+ */
+public class MainTest {
+
+    @Test
+    public void testSync() throws Exception {
+        assertEquals(0, Main.runClient(new String[]{"req=10", "mode=sync"}));
+    }
+
+    @Test
+    public void testAsync() throws Exception {
+        assertEquals(0, Main.runClient(new String[]{"req=10", "mode=async"}));
+    }
+
+    @Test
+    public void testAsyncWrongUri() throws Exception {
+        assertEquals(-1, Main.runClient(new String[]{"req=1", "mode=async", "uri=http://foo.bar"}));
+    }
+
+    @Test
+    public void testSyncWrongUri() throws Exception {
+        assertEquals(-1, Main.runClient(new String[]{"req=1", "mode=sync", "uri=http://foo.bar"}));
+    }
+}
diff --git a/examples/server-async-standalone/pom.xml b/examples/server-async-standalone/pom.xml
new file mode 100644
index 0000000..04c3e8a
--- /dev/null
+++ b/examples/server-async-standalone/pom.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>server-async-standalone</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-examples-server-async-standalone</name>
+
+    <description>Standalone Jersey JAX-RS asynchronous server-side processing example.</description>
+
+    <modules>
+        <module>client</module>
+        <module>webapp</module>
+    </modules>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/server-async-standalone/webapp/pom.xml b/examples/server-async-standalone/webapp/pom.xml
new file mode 100644
index 0000000..22bc1b8
--- /dev/null
+++ b/examples/server-async-standalone/webapp/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>server-async-standalone</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>server-async-standalone-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-server-async-standalone-webapp</name>
+
+    <description>Standalone Jersey JAX-RS asynchronous server-side processing example web application.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <!-- do not create source zip bundles -->
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <configuration>
+                            <skipAssembly>true</skipAssembly>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/server-async-standalone/webapp/src/main/java/org/glassfish/jersey/examples/server/async/AsyncJaxrsApplication.java b/examples/server-async-standalone/webapp/src/main/java/org/glassfish/jersey/examples/server/async/AsyncJaxrsApplication.java
new file mode 100644
index 0000000..505f327
--- /dev/null
+++ b/examples/server-async-standalone/webapp/src/main/java/org/glassfish/jersey/examples/server/async/AsyncJaxrsApplication.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+
+/**
+ * Jersey Async Webapp application class.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@ApplicationPath("/")
+public class AsyncJaxrsApplication extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final HashSet<Class<?>> classes = new HashSet<Class<?>>();
+        classes.add(LongRunningEchoResource.class);
+
+        return classes;
+    }
+
+    @Override
+    public Set<Object> getSingletons() {
+        final HashSet<Object> instances = new HashSet<Object>();
+
+        instances.add(new LoggingFeature(Logger.getLogger(AsyncJaxrsApplication.class.getName()),
+                LoggingFeature.Verbosity.PAYLOAD_ANY));
+
+        return instances;
+    }
+}
diff --git a/examples/server-async-standalone/webapp/src/main/java/org/glassfish/jersey/examples/server/async/LongRunningEchoResource.java b/examples/server-async-standalone/webapp/src/main/java/org/glassfish/jersey/examples/server/async/LongRunningEchoResource.java
new file mode 100644
index 0000000..f6d016b
--- /dev/null
+++ b/examples/server-async-standalone/webapp/src/main/java/org/glassfish/jersey/examples/server/async/LongRunningEchoResource.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.ServiceUnavailableException;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+/**
+ * Example of a simple resource with a long-running operation executed in a
+ * custom application thread.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("long-running")
+@Produces("text/plain")
+public class LongRunningEchoResource {
+
+    private static final int SLEEP_TIME_IN_MILLIS = 1000;
+    private static final ExecutorService TASK_EXECUTOR = Executors.newCachedThreadPool();
+
+    /**
+     * Synchronously echo the last path segment after sleeping for a long time.
+     *
+     * @param echo message to echo.
+     * @return echoed message.
+     */
+    @GET
+    @Path("sync/{echo}")
+    public String syncEcho(@PathParam("echo") final String echo) {
+        try {
+            Thread.sleep(SLEEP_TIME_IN_MILLIS);
+        } catch (final InterruptedException ex) {
+            throw new ServiceUnavailableException();
+        }
+        return echo;
+    }
+
+    /**
+     * Asynchronously echo the last path segment after sleeping for a long time.
+     *
+     * @param echo message to echo.
+     * @param ar AsynchronousResponse, will contain echoed message.
+     */
+    @GET
+    @Path("async/{echo}")
+    public void asyncEcho(@PathParam("echo") final String echo, @Suspended final AsyncResponse ar) {
+        TASK_EXECUTOR.submit(new Runnable() {
+
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(SLEEP_TIME_IN_MILLIS);
+                } catch (final InterruptedException ex) {
+                    ar.cancel();
+                }
+                ar.resume(echo);
+            }
+        });
+    }
+}
diff --git a/examples/server-async-standalone/webapp/src/main/webapp/WEB-INF/web.xml b/examples/server-async-standalone/webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..5f63815
--- /dev/null
+++ b/examples/server-async-standalone/webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+    <servlet>
+        <servlet-name>async-app</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.examples.server.async.AsyncJaxrsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+        <async-supported>true</async-supported>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>async-app</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/examples/server-async/README.MD b/examples/server-async/README.MD
new file mode 100644
index 0000000..1b4d180
--- /dev/null
+++ b/examples/server-async/README.MD
@@ -0,0 +1,80 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Server Asynchronous Example
+===========================
+
+The full description how to create an asynchronous resource can be found in Jersey User Guide, chapter 
+[Asynchronous Services and Clients](https://jersey.java.net/documentation/latest/async.html).
+
+This example demonstrates JAX-RS 2.0 server-side non-blocking API in comparison to blocking and long-running
+asynchronous operations.
+
+The  goal of the sample is to demonstrate that with limited I/O processing threads
+on the server the synchronous execution of a long-running task may lead to resource
+starvation caused by I/O processing threads being blocked by the long-running
+operation, unable to serve new incoming requests.
+
+OTOH, when the same long-running operation is executed asynchronously, the I/O
+threads do not need to block while waiting for the long-running operation to finish
+and the overall throughput of the system is greatly increased.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                             | Resource class            | HTTP methods
+------------------------------------ | --------------------------| --------------
+**_async/messaging/fireAndForget_**  | FireAndForgetChatResource | POST1
+**_async/messaging/fireAndForget_**  | FireAndForgetChatResource | GET
+**_async/messaging/blocking_**       | BlockingPostChatResource  | POST
+**_async/messaging/blocking_**       | BlockingPostChatResource  | GET
+**_async/longrunning_**              | SimpleLongRunningResource | GET
+
+Sample Response
+---------------
+
+This section shortly describes a main difference between blocking and non-blocking approach implemented
+in the example.
+
+#### Blocking Approach
+
+First you have to sent POST request which is shown below and repeat this call 5-times.
+
+>     curl -v -X POST http://localhost:8080/base/async/messaging/blocking -H "Content-Type: text/plain" -d "My message"
+
+All previous requests were processed using I/O Container threads, there was no problem with
+a returning response and a response's delay until now. But if you run the 6-th call, the server will
+be blocked. At this time I/O thread is blocked (because of ArrayBlockingQueue is full) and waits for
+the call which should read and remove one of the saved messages. Once the a below mentioned
+request is called, the Blocking has place for another message and waiting thread is processed.
+
+>     curl -v -X GET http://localhost:8080/base/async/messaging/blocking
+
+#### Non-blocking Approach
+
+We can start with the same procedure as in previous approach. Sent the POST request shown below 5-times
+
+>     curl -v -X POST http://localhost:8080/base/async/messaging/fireAndForget -H "Content-Type: text/plain" -d "My message"
+
+At this time blocking queue in resource should be full, but if you send another request, the client application won't
+be blocked and will receive the given response immediately. Since the server application doesn't block the I/O thread
+but blocks a thread which is dedicated for processing the request. After the GET call, the blocked dedicated thread
+is processed and returned to a cached thread pool.
+
+>     curl -v -X GET http://localhost:8080/base/async/messaging/fireAndForget
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
diff --git a/examples/server-async/pom.xml b/examples/server-async/pom.xml
new file mode 100644
index 0000000..90d0c23
--- /dev/null
+++ b/examples/server-async/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>server-async</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-server-async</name>
+
+    <description>Jersey JAX-RS asynchronous server-side example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.server.async.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/App.java b/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/App.java
new file mode 100644
index 0000000..12063f2
--- /dev/null
+++ b/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/App.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import static org.glassfish.jersey.logging.LoggingFeature.Verbosity.PAYLOAD_ANY;
+
+/**
+ * Jersey example application for custom executors managed async resources.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/base/");
+    public static final String ASYNC_MESSAGING_FIRE_N_FORGET_PATH = "async/messaging/fireAndForget";
+    public static final String ASYNC_MESSAGING_BLOCKING_PATH = "async/messaging/blocking";
+    public static final String ASYNC_LONG_RUNNING_OP_PATH = "async/longrunning";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Async resources\" Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, create(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format(
+                    "Application started.\n"
+                            + "To test simple, non-blocking asynchronous messaging resource, try %s%s\n"
+                            + "To test blocking version of asynchronous messaging resource, try %s%s\n"
+                            + "To test long-running asynchronous operation resource, try %s%s\n"
+                            + "Stop the application using CTRL+C",
+                    BASE_URI, ASYNC_MESSAGING_FIRE_N_FORGET_PATH,
+                    BASE_URI, ASYNC_MESSAGING_BLOCKING_PATH,
+                    BASE_URI, ASYNC_LONG_RUNNING_OP_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+
+    public static ResourceConfig create() {
+        final ResourceConfig resourceConfig = new ResourceConfig()
+                .registerClasses(BlockingPostChatResource.class, FireAndForgetChatResource.class, SimpleLongRunningResource.class)
+                .registerInstances(new LoggingFeature(Logger.getLogger(App.class.getName()), PAYLOAD_ANY));
+
+        return resourceConfig;
+    }
+}
diff --git a/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/BlockingPostChatResource.java b/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/BlockingPostChatResource.java
new file mode 100644
index 0000000..84b8348
--- /dev/null
+++ b/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/BlockingPostChatResource.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
+
+/**
+ * Example of a simple blocking point-to-point messaging resource.
+ *
+ * This version of the messaging resource blocks when POSTing a new message until
+ * the message is retrieved.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path(App.ASYNC_MESSAGING_BLOCKING_PATH)
+@Produces(MediaType.TEXT_PLAIN)
+@Consumes(MediaType.TEXT_PLAIN)
+public class BlockingPostChatResource {
+
+    public static final String POST_NOTIFICATION_RESPONSE = "Message stored.";
+    //
+    private static final Logger LOGGER = Logger.getLogger(BlockingPostChatResource.class.getName());
+    private static final Level DEBUG = Level.INFO;
+    //
+    private static final ExecutorService QUEUE_EXECUTOR = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+            .setNameFormat("blocking-post-chat-resource-executor-%d")
+            .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+            .build());
+    private static final BlockingQueue<String> messages = new ArrayBlockingQueue<String>(5);
+    //
+
+    @GET
+    public void pickUpMessage(@Suspended final AsyncResponse ar, @QueryParam("id") final String messageId) {
+        LOGGER.log(DEBUG, "Received GET <{0}> with context {1} on thread {2}.",
+                new Object[]{messageId, ar.toString(), Thread.currentThread().getName()});
+        QUEUE_EXECUTOR.submit(new Runnable() {
+
+            @Override
+            public void run() {
+                try {
+                    final String message = messages.take();
+                    LOGGER.log(DEBUG, "Resuming GET <{0}> context {1} with a message {2} on thread {3}.",
+                            new Object[]{messageId, ar.toString(), message, Thread.currentThread().getName()});
+                    ar.resume(message);
+                } catch (InterruptedException ex) {
+                    LOGGER.log(Level.SEVERE,
+                            "Waiting for a message pick-up interrupted. Cancelling context" + ar.toString(), ex);
+                    ar.cancel(); // close the open connection
+                }
+            }
+        });
+    }
+
+    @POST
+    public void postMessage(@Suspended final AsyncResponse ar, final String message) {
+        LOGGER.log(DEBUG, "Received POST <{0}> with context {1} on thread {2}. Suspending the context.",
+                new Object[]{message, ar.toString(), Thread.currentThread().getName()});
+        QUEUE_EXECUTOR.submit(new Runnable() {
+
+            @Override
+            public void run() {
+                try {
+                    messages.put(message);
+                    LOGGER.log(DEBUG, "POSTed message <{0}> successfully queued. Resuming POST with context {1} on thread {2}.",
+                            new Object[]{message, ar.toString(), Thread.currentThread().getName()});
+                    ar.resume(POST_NOTIFICATION_RESPONSE);
+                } catch (InterruptedException ex) {
+                    LOGGER.log(Level.SEVERE,
+                            "Waiting for a queueing a message '" + message + "' has been interrupted.", ex);
+                    ar.resume(ex); // propagate info about the problem
+                }
+            }
+        });
+    }
+}
diff --git a/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/FireAndForgetChatResource.java b/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/FireAndForgetChatResource.java
new file mode 100644
index 0000000..7a32d39
--- /dev/null
+++ b/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/FireAndForgetChatResource.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
+
+/**
+ * Example of a simple fire&forget point-to-point messaging resource.
+ *
+ * This version of the messaging resource does not block when POSTing a new message.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path(App.ASYNC_MESSAGING_FIRE_N_FORGET_PATH)
+@Produces(MediaType.TEXT_PLAIN)
+@Consumes(MediaType.TEXT_PLAIN)
+public class FireAndForgetChatResource {
+
+    public static final String POST_NOTIFICATION_RESPONSE = "Message sent";
+    //
+    private static final Logger LOGGER = Logger.getLogger(FireAndForgetChatResource.class.getName());
+    private static final Level DEBUG = Level.INFO;
+    //
+    private static final ExecutorService QUEUE_EXECUTOR = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+            .setNameFormat("fire&forget-chat-resource-executor-%d")
+            .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+            .build());
+    private static final BlockingQueue<AsyncResponse> suspended = new ArrayBlockingQueue<AsyncResponse>(5);
+
+    @GET
+    public void pickUpMessage(@Suspended final AsyncResponse ar, @QueryParam("id") final String messageId)
+            throws InterruptedException {
+        LOGGER.log(DEBUG, "Received GET <{0}> with context {1} on thread {2}.",
+                new Object[] {messageId, ar.toString(), Thread.currentThread().getName()});
+        QUEUE_EXECUTOR.submit(new Runnable() {
+
+            @Override
+            public void run() {
+                try {
+                    suspended.put(ar);
+                    LOGGER.log(DEBUG, "GET <{0}> context {1} scheduled for resume.",
+                            new Object[] {messageId, ar.toString()});
+                } catch (InterruptedException ex) {
+                    LOGGER.log(Level.SEVERE,
+                            "Waiting for a message pick-up interrupted. Cancelling context" + ar.toString(), ex);
+                    ar.cancel(); // close the open connection
+                }
+            }
+        });
+    }
+
+    @POST
+    public String postMessage(final String message) throws InterruptedException {
+        LOGGER.log(DEBUG, "Received POSTed message <{0}> on thread {1}.",
+                new Object[] {message, Thread.currentThread().getName()});
+        QUEUE_EXECUTOR.submit(new Runnable() {
+
+            @Override
+            public void run() {
+                try {
+                    final AsyncResponse suspendedResponse = suspended.take();
+                    LOGGER.log(DEBUG, "Resuming GET context {0} with a message <{1}> on thread {2}.",
+                            new Object[] {suspendedResponse.toString(), message, Thread.currentThread().getName()});
+                    suspendedResponse.resume(message);
+                } catch (InterruptedException ex) {
+                    LOGGER.log(Level.SEVERE,
+                            "Waiting for a sending a message <" + message + "> has been interrupted.", ex);
+                }
+            }
+        });
+
+        return POST_NOTIFICATION_RESPONSE;
+    }
+}
diff --git a/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/LongRunningAsyncOperationResource.java b/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/LongRunningAsyncOperationResource.java
new file mode 100644
index 0000000..15b3a08
--- /dev/null
+++ b/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/LongRunningAsyncOperationResource.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
+
+/**
+ * Example resource for long running async operations.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+//TODO move the test to integration tests.
+//@Path(App.LONG_RUNNING_ASYNC_OP_PATH)
+@Produces("text/plain")
+public class LongRunningAsyncOperationResource {
+
+    public static final String NOTIFICATION_RESPONSE = "Hello async world!";
+    //
+    private static final Logger LOGGER = Logger.getLogger(LongRunningAsyncOperationResource.class.getName());
+    private static final int SLEEP_TIME_IN_MILLIS = 1000;
+    private static final ExecutorService TASK_EXECUTOR = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+            .setNameFormat("long-running-resource-executor-%d")
+            .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+            .build());
+
+    @GET
+    @Path("basicSyncExample")
+    public String basicSyncExample() {
+        try {
+            Thread.sleep(SLEEP_TIME_IN_MILLIS);
+        } catch (InterruptedException ex) {
+            LOGGER.log(Level.SEVERE, "Response processing interrupted", ex);
+        }
+        return NOTIFICATION_RESPONSE;
+    }
+
+    @GET
+    @Path("async")
+    public void suspendViaContextExample(@Suspended final AsyncResponse ar, @QueryParam("query") final String query) {
+        TASK_EXECUTOR.submit(new Runnable() {
+
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(SLEEP_TIME_IN_MILLIS);
+                } catch (InterruptedException ex) {
+                    LOGGER.log(Level.SEVERE, "Response processing interrupted", ex);
+                }
+                ar.resume("Complex result for " + query);
+            }
+        });
+    }
+}
diff --git a/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/SimpleLongRunningResource.java b/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/SimpleLongRunningResource.java
new file mode 100644
index 0000000..76cae73
--- /dev/null
+++ b/examples/server-async/src/main/java/org/glassfish/jersey/examples/server/async/SimpleLongRunningResource.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
+
+/**
+ * Example of a simple resource with a long-running operation executed in a
+ * custom application thread.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path(App.ASYNC_LONG_RUNNING_OP_PATH)
+@Produces("text/plain")
+public class SimpleLongRunningResource {
+
+    public static final String NOTIFICATION_RESPONSE = "Hello async world!";
+    private static final Logger LOGGER = Logger.getLogger(SimpleLongRunningResource.class.getName());
+    private static final int SLEEP_TIME_IN_MILLIS = 1000;
+    private static final ExecutorService TASK_EXECUTOR = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+            .setNameFormat("long-running-resource-executor-%d")
+            .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+            .build());
+
+    @GET
+    public void longGet(@Suspended final AsyncResponse ar) {
+        TASK_EXECUTOR.submit(new Runnable() {
+
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(SLEEP_TIME_IN_MILLIS);
+                } catch (InterruptedException ex) {
+                    LOGGER.log(Level.SEVERE, "Response processing interrupted", ex);
+                }
+                ar.resume(NOTIFICATION_RESPONSE);
+            }
+        });
+    }
+}
diff --git a/examples/server-async/src/test/java/org/glassfish/jersey/examples/server/async/AsyncResourceTest.java b/examples/server-async/src/test/java/org/glassfish/jersey/examples/server/async/AsyncResourceTest.java
new file mode 100644
index 0000000..1e7c8dc
--- /dev/null
+++ b/examples/server-async/src/test/java/org/glassfish/jersey/examples/server/async/AsyncResourceTest.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.server.async;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class AsyncResourceTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(AsyncResourceTest.class.getName());
+
+    @Override
+    protected ResourceConfig configure() {
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+        return App.create();
+    }
+
+    @Test
+    public void testFireAndForgetChatResource() throws InterruptedException {
+        executeChatTest(target().path(App.ASYNC_MESSAGING_FIRE_N_FORGET_PATH),
+                FireAndForgetChatResource.POST_NOTIFICATION_RESPONSE);
+    }
+
+    @Test
+    public void testBlockingPostChatResource() throws InterruptedException {
+        executeChatTest(target().path(App.ASYNC_MESSAGING_BLOCKING_PATH), BlockingPostChatResource.POST_NOTIFICATION_RESPONSE);
+    }
+
+    private void executeChatTest(final WebTarget resourceTarget, final String expectedPostResponse) throws InterruptedException {
+        final int MAX_MESSAGES = 100;
+        final int LATCH_WAIT_TIMEOUT = 10 * getAsyncTimeoutMultiplier();
+        final boolean debugMode = false;
+        final boolean sequentialGet = false;
+        final boolean sequentialPost = false;
+        final Object sequentialGetLock = new Object();
+        final Object sequentialPostLock = new Object();
+
+        final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+                .setNameFormat("async-resource-test-%02d")
+                .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+                .build());
+
+        final Map<Integer, String> postResponses = new ConcurrentHashMap<Integer, String>();
+        final Map<Integer, String> getResponses = new ConcurrentHashMap<Integer, String>();
+
+        final CountDownLatch postRequestLatch = new CountDownLatch(MAX_MESSAGES);
+        final CountDownLatch getRequestLatch = new CountDownLatch(MAX_MESSAGES);
+
+        try {
+            for (int i = 0; i < MAX_MESSAGES; i++) {
+                final int requestId = i;
+                executor.submit(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        if (debugMode || sequentialPost) {
+                            synchronized (sequentialPostLock) {
+                                post();
+                            }
+                        } else {
+                            post();
+                        }
+                    }
+
+                    private void post() {
+                        try {
+                            int attemptCounter = 0;
+                            while (true) {
+                                attemptCounter++;
+                                try {
+                                    final String response = resourceTarget.request()
+                                            .post(Entity.text(String.format("%02d", requestId)), String.class);
+                                    postResponses.put(requestId, response);
+                                    break;
+                                } catch (Throwable t) {
+                                    LOGGER.log(Level.WARNING, String.format("Error POSTING message <%s> for %d. time.",
+                                            requestId, attemptCounter), t);
+                                }
+                                if (attemptCounter > 3) {
+                                    break;
+                                }
+                                Thread.sleep(10);
+                            }
+                        } catch (InterruptedException ignored) {
+                            LOGGER.log(Level.WARNING,
+                                    String.format("Error POSTING message <%s>: Interrupted", requestId), ignored);
+                        } finally {
+                            postRequestLatch.countDown();
+                        }
+                    }
+                });
+                executor.submit(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        if (debugMode || sequentialGet) {
+                            synchronized (sequentialGetLock) {
+                                get();
+                            }
+                        } else {
+                            get();
+                        }
+                    }
+
+                    private void get() {
+                        try {
+                            int attemptCounter = 0;
+                            while (true) {
+                                attemptCounter++;
+                                try {
+                                    final String response = resourceTarget.queryParam("id", requestId).request()
+                                            .get(String.class);
+                                    getResponses.put(requestId, response);
+                                    break;
+                                } catch (Throwable t) {
+                                    LOGGER.log(Level.SEVERE, String.format("Error sending GET request <%s> for %d. time.",
+                                            requestId, attemptCounter), t);
+                                }
+                                if (attemptCounter > 3) {
+                                    break;
+                                }
+                                Thread.sleep(10);
+                            }
+                        } catch (InterruptedException ignored) {
+                            LOGGER.log(Level.WARNING,
+                                    String.format("Error sending GET message <%s>: Interrupted", requestId), ignored);
+                        } finally {
+                            getRequestLatch.countDown();
+                        }
+                    }
+                });
+            }
+
+            if (debugMode) {
+                postRequestLatch.await();
+                getRequestLatch.await();
+            } else {
+                if (!postRequestLatch.await(LATCH_WAIT_TIMEOUT, TimeUnit.SECONDS)) {
+                    LOGGER.log(Level.SEVERE, "Waiting for all POST requests to complete has timed out.");
+                }
+                if (!getRequestLatch.await(LATCH_WAIT_TIMEOUT, TimeUnit.SECONDS)) {
+                    LOGGER.log(Level.SEVERE, "Waiting for all GET requests to complete has timed out.");
+                }
+            }
+        } finally {
+            executor.shutdownNow();
+        }
+
+        StringBuilder messageBuilder = new StringBuilder("POST responses received: ").append(postResponses.size()).append("\n");
+        for (Map.Entry<Integer, String> postResponseEntry : postResponses.entrySet()) {
+            messageBuilder.append(String.format("POST response for message %02d: ", postResponseEntry.getKey()))
+                    .append(postResponseEntry.getValue()).append('\n');
+        }
+        messageBuilder.append('\n');
+        messageBuilder.append("GET responses received: ").append(getResponses.size()).append("\n");
+        for (Map.Entry<Integer, String> getResponseEntry : getResponses.entrySet()) {
+            messageBuilder.append(String.format("GET response for message %02d: ", getResponseEntry.getKey()))
+                    .append(getResponseEntry.getValue()).append('\n');
+        }
+        LOGGER.info(messageBuilder.toString());
+
+        for (Map.Entry<Integer, String> postResponseEntry : postResponses.entrySet()) {
+            assertEquals(
+                    String.format("Unexpected POST notification response for message %02d", postResponseEntry.getKey()),
+                    expectedPostResponse, postResponseEntry.getValue());
+        }
+
+        final List<Integer> lost = new LinkedList<Integer>();
+        final Collection<String> getResponseValues = getResponses.values();
+        for (int i = 0; i < MAX_MESSAGES; i++) {
+            if (!getResponseValues.contains(String.format("%02d", i))) {
+                lost.add(i);
+            }
+        }
+        if (!lost.isEmpty()) {
+            fail("Detected a posted message loss(es): " + lost.toString());
+        }
+        assertEquals(MAX_MESSAGES, postResponses.size());
+        assertEquals(MAX_MESSAGES, getResponses.size());
+    }
+
+    @Test
+    public void testLongRunningResource() throws InterruptedException {
+        final WebTarget resourceTarget = target().path(App.ASYNC_LONG_RUNNING_OP_PATH);
+        final String expectedResponse = SimpleLongRunningResource.NOTIFICATION_RESPONSE;
+
+        final int MAX_MESSAGES = 100;
+        final int LATCH_WAIT_TIMEOUT = 25 * getAsyncTimeoutMultiplier();
+        final boolean debugMode = false;
+        final boolean sequentialGet = false;
+        final Object sequentialGetLock = new Object();
+
+        final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+                .setNameFormat("async-resource-test-%02d")
+                .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+                .build());
+
+        final Map<Integer, String> getResponses = new ConcurrentHashMap<Integer, String>();
+
+        final CountDownLatch getRequestLatch = new CountDownLatch(MAX_MESSAGES);
+
+        try {
+            for (int i = 0; i < MAX_MESSAGES; i++) {
+                final int requestId = i;
+                executor.submit(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        if (debugMode || sequentialGet) {
+                            synchronized (sequentialGetLock) {
+                                get();
+                            }
+                        } else {
+                            get();
+                        }
+                    }
+
+                    private void get() {
+                        try {
+                            int attemptCounter = 0;
+                            while (true) {
+                                attemptCounter++;
+                                try {
+                                    final String response = resourceTarget.request().get(String.class);
+                                    getResponses.put(requestId, response);
+                                    break;
+                                } catch (Throwable t) {
+                                    LOGGER.log(Level.SEVERE, String.format("Error sending GET request <%s> for %d. time.",
+                                            requestId, attemptCounter), t);
+                                }
+                                if (attemptCounter > 3) {
+                                    break;
+                                }
+                                Thread.sleep(10);
+                            }
+                        } catch (InterruptedException ignored) {
+                            LOGGER.log(Level.WARNING,
+                                    String.format("Error sending GET message <%s>: Interrupted", requestId), ignored);
+                        } finally {
+                            getRequestLatch.countDown();
+                        }
+                    }
+                });
+            }
+
+            if (debugMode) {
+                getRequestLatch.await();
+            } else {
+                if (!getRequestLatch.await(LATCH_WAIT_TIMEOUT, TimeUnit.SECONDS)) {
+                    LOGGER.log(Level.SEVERE, "Waiting for all GET requests to complete has timed out.");
+                }
+            }
+        } finally {
+            executor.shutdownNow();
+        }
+
+        final ArrayList<Map.Entry<Integer, String>> responseEntryList =
+                new ArrayList<Map.Entry<Integer, String>>(getResponses.entrySet());
+        Collections.sort(responseEntryList,
+                new Comparator<Map.Entry<Integer, String>>() {
+                    @Override
+                    public int compare(Map.Entry<Integer, String> o1, Map.Entry<Integer, String> o2) {
+                        return o1.getKey().compareTo(o2.getKey());
+                    }
+                });
+        StringBuilder messageBuilder = new StringBuilder("GET responses received: ").append(responseEntryList.size())
+                .append("\n");
+        for (Map.Entry<Integer, String> getResponseEntry : responseEntryList) {
+            messageBuilder.append(String.format("GET response for message %02d: ", getResponseEntry.getKey()))
+                    .append(getResponseEntry.getValue()).append('\n');
+        }
+        LOGGER.info(messageBuilder.toString());
+
+        for (Map.Entry<Integer, String> entry : responseEntryList) {
+            assertEquals(
+                    String.format("Unexpected GET notification response for message %02d", entry.getKey()),
+                    expectedResponse, entry.getValue());
+        }
+        assertEquals(MAX_MESSAGES, getResponses.size());
+    }
+
+}
diff --git a/examples/server-sent-events-jaxrs/README.MD b/examples/server-sent-events-jaxrs/README.MD
new file mode 100644
index 0000000..010baaa
--- /dev/null
+++ b/examples/server-sent-events-jaxrs/README.MD
@@ -0,0 +1,70 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Server-Sent Events (SSE) Example
+================================
+
+This example demonstrates JAX-RS 2.1 server-sent events support.
+
+The full description how to add support for server-sent events to your application can be found in Jersey User Guide, 
+chapter
+[Server-Sent Events (SSE) Support](https://jersey.java.net/documentation/latest/sse.html).
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                              | Resource class             | HTTP methods   | Description
+------------------------------------- | -------------------------- | -------------- | ------------
+**_server-sent-events_**              | ServerSentEventsResource   | GET            | Get entire EventOutput with all messages
+**_server-sent-events_**              | ServerSentEventsResource   | POST           | Insert a new message in EventOutput
+**_server-sent-events_**              | ServerSentEventsResource   | DELETE         | Reset EventOutput
+**_server-sent-events/domains/{id}_** | BlockingPostChatResource   | POST           | Generate several messages with a delay in EventOutput and return it
+
+Sample Response
+---------------
+
+A great example of Server-Sent Events is `server-sent-events/domains/{id}` which sends several messages with a delay 200ms
+between each other.
+
+>     curl -v -X POST http://localhost:8080/server-sent-events-jaxrs/domains/1 -H "Content-Type: text/event-stream"
+
+Look at a console how events are handled one after another in the right order.
+
+```
+event: domain-progress
+data: starting domain 1 ...
+
+event: domain-progress
+data: 50%
+
+event: domain-progress
+data: 60%
+
+event: domain-progress
+data: 70%
+
+event: domain-progress
+data: 99%
+
+event: domain-progress
+data: done
+```
+
+Running the Example
+-------------------
+
+Look at Jersey Documentation to learn how to consume Server-Sent Events using Jersey Client
+<https://jersey.java.net/documentation/latest/sse.html#d0e11786>
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
diff --git a/examples/server-sent-events-jaxrs/pom.xml b/examples/server-sent-events-jaxrs/pom.xml
new file mode 100644
index 0000000..ade2a8f
--- /dev/null
+++ b/examples/server-sent-events-jaxrs/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>server-sent-events-jaxrs</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-server-sent-events-jaxrs</name>
+
+    <description>Jersey JAX-RS 2.1 Server-Sent Events example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-sse</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/server-sent-events-jaxrs/src/main/java/org/glassfish/jersey/examples/sse/jaxrs/App.java b/examples/server-sent-events-jaxrs/src/main/java/org/glassfish/jersey/examples/sse/jaxrs/App.java
new file mode 100644
index 0000000..29d44ff
--- /dev/null
+++ b/examples/server-sent-events-jaxrs/src/main/java/org/glassfish/jersey/examples/sse/jaxrs/App.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sse.jaxrs;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Server sent event example.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/");
+    static final String ROOT_PATH = "server-sent-events";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"JAX-RS 2.1 Server-Sent Events\" Jersey Example App");
+
+            final ResourceConfig resourceConfig = new ResourceConfig(JaxRsServerSentEventsResource.class);
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, false);
+            Runtime.getRuntime().addShutdownHook(new Thread(server::shutdownNow));
+            server.start();
+
+            System.out.println(String.format("Application started.\nTry out %s%s\nStop the application using CTRL+C",
+                    BASE_URI, ROOT_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+}
diff --git a/examples/server-sent-events-jaxrs/src/main/java/org/glassfish/jersey/examples/sse/jaxrs/DomainResource.java b/examples/server-sent-events-jaxrs/src/main/java/org/glassfish/jersey/examples/sse/jaxrs/DomainResource.java
new file mode 100644
index 0000000..98a03e0
--- /dev/null
+++ b/examples/server-sent-events-jaxrs/src/main/java/org/glassfish/jersey/examples/sse/jaxrs/DomainResource.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sse.jaxrs;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.sse.Sse;
+import javax.ws.rs.sse.SseBroadcaster;
+import javax.ws.rs.sse.SseEventSink;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("domain")
+public class DomainResource {
+
+    private static final Map<Integer, Process> processes = new ConcurrentHashMap<>();
+
+    @Path("start")
+    @POST
+    public Response post(@DefaultValue("0") @QueryParam("testSources") int testSources, @Context Sse sse) {
+        final Process process = new Process(testSources, sse);
+        processes.put(process.getId(), process);
+
+        Executors.newSingleThreadExecutor().execute(process);
+
+        final URI processIdUri = UriBuilder.fromResource(DomainResource.class).path("process/{id}").build(process.getId());
+        return Response.created(processIdUri).build();
+    }
+
+    @Path("process/{id}")
+    @Produces(MediaType.SERVER_SENT_EVENTS)
+    @GET
+    public void getProgress(@PathParam("id") int id,
+                            @DefaultValue("false") @QueryParam("testSource") boolean testSource,
+                            @Context SseEventSink eventSink) {
+        final Process process = processes.get(id);
+
+        if (process != null) {
+            if (testSource) {
+                process.release();
+            }
+            process.getBroadcaster().register(eventSink);
+        } else {
+            throw new NotFoundException();
+        }
+    }
+
+    static class Process implements Runnable {
+
+        private static final AtomicInteger counter = new AtomicInteger(0);
+
+        private final int id;
+        private final CountDownLatch latch;
+        private final SseBroadcaster broadcaster;
+        private final Sse sse;
+
+        Process(int testReceivers, Sse sse) {
+            this.sse = sse;
+            this.broadcaster = sse.newBroadcaster();
+            id = counter.incrementAndGet();
+            latch = testReceivers > 0 ? new CountDownLatch(testReceivers) : null;
+        }
+
+        int getId() {
+            return id;
+        }
+
+        SseBroadcaster getBroadcaster() {
+            return broadcaster;
+        }
+
+        void release() {
+            if (latch != null) {
+                latch.countDown();
+            }
+        }
+
+        public void run() {
+            try {
+                if (latch != null) {
+                    // wait for all test EventSources to be registered
+                    latch.await(5, TimeUnit.SECONDS);
+                }
+
+                broadcaster.broadcast(sse.newEventBuilder()
+                                         .name("domain-progress")
+                                         .data(String.class, "starting domain " + id + " ...")
+                                         .build());
+                broadcaster.broadcast(sse.newEventBuilder().name("domain-progress").data(String.class, "50%").build());
+                broadcaster.broadcast(sse.newEventBuilder().name("domain-progress").data(String.class, "60%").build());
+                broadcaster.broadcast(sse.newEventBuilder().name("domain-progress").data(String.class, "70%").build());
+                broadcaster.broadcast(sse.newEventBuilder().name("domain-progress").data(String.class, "99%").build());
+                broadcaster.broadcast(sse.newEventBuilder().name("domain-progress").data(String.class, "done").build());
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}
diff --git a/examples/server-sent-events-jaxrs/src/main/java/org/glassfish/jersey/examples/sse/jaxrs/JaxRsServerSentEventsResource.java b/examples/server-sent-events-jaxrs/src/main/java/org/glassfish/jersey/examples/sse/jaxrs/JaxRsServerSentEventsResource.java
new file mode 100644
index 0000000..28ab750
--- /dev/null
+++ b/examples/server-sent-events-jaxrs/src/main/java/org/glassfish/jersey/examples/sse/jaxrs/JaxRsServerSentEventsResource.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sse.jaxrs;
+
+import java.io.IOException;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.sse.Sse;
+import javax.ws.rs.sse.SseEventSink;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("server-sent-events")
+public class JaxRsServerSentEventsResource {
+
+    private static volatile SseEventSink eventSink = null;
+
+    @GET
+    @Produces(MediaType.SERVER_SENT_EVENTS)
+    public void getMessageQueue(@Context SseEventSink sink) {
+        eventSink = sink;
+    }
+
+    @POST
+    public void addMessage(final String message, @Context Sse sse) throws IOException {
+        final SseEventSink localSink = eventSink;
+        if (localSink != null) {
+            localSink.send(sse.newEventBuilder().name("custom-message").data(String.class, message).build());
+        }
+    }
+
+    @DELETE
+    public void close() throws IOException {
+        final SseEventSink localSink = eventSink;
+        if (localSink != null) {
+            eventSink.close();
+        }
+        eventSink = null;
+    }
+
+    @POST
+    @Path("domains/{id}")
+    @Produces(MediaType.SERVER_SENT_EVENTS)
+    public void startDomain(@PathParam("id") final String id, @Context SseEventSink domainSink, @Context Sse sse) {
+        new Thread(() -> {
+            try {
+                domainSink.send(sse.newEventBuilder()
+                                    .name("domain-progress")
+                                    .data(String.class, "starting domain " + id + " ...")
+                                    .build());
+                Thread.sleep(200);
+                domainSink.send(sse.newEventBuilder().name("domain-progress").data(String.class, "50%").build());
+                Thread.sleep(200);
+                domainSink.send(sse.newEventBuilder().name("domain-progress").data(String.class, "60%").build());
+                Thread.sleep(200);
+                domainSink.send(sse.newEventBuilder().name("domain-progress").data(String.class, "70%").build());
+                Thread.sleep(200);
+                domainSink.send(sse.newEventBuilder().name("domain-progress").data(String.class, "99%").build());
+                Thread.sleep(200);
+                domainSink.send(sse.newEventBuilder().name("domain-progress").data(String.class, "done").build());
+                domainSink.close();
+
+            } catch (final InterruptedException e) {
+                e.printStackTrace();
+            }
+        }).start();
+    }
+}
diff --git a/examples/server-sent-events-jaxrs/src/test/java/org/glassfish/jersey/examples/sse/jaxrs/ServerSentEventsTest.java b/examples/server-sent-events-jaxrs/src/test/java/org/glassfish/jersey/examples/sse/jaxrs/ServerSentEventsTest.java
new file mode 100644
index 0000000..b0e234a
--- /dev/null
+++ b/examples/server-sent-events-jaxrs/src/test/java/org/glassfish/jersey/examples/sse/jaxrs/ServerSentEventsTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sse.jaxrs;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.sse.SseEventSource;
+
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * SSE example resources test.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class ServerSentEventsTest extends JerseyTest {
+
+    private final ExecutorService executorService =
+            Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("ServerSentEventsTest-%d").build());
+    private final Client client = ClientBuilder.newBuilder().executorService(executorService).build();
+
+    @Override
+    protected Application configure() {
+        // enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(JaxRsServerSentEventsResource.class, DomainResource.class);
+    }
+
+    @Override
+    protected Client getClient() {
+        return client;
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        client.close();
+        executorService.shutdown();
+    }
+
+    /**
+     * Test consuming a single SSE event via event source.
+     *
+     * @throws Exception in case of a failure during the test execution.
+     */
+    @Test
+    public void testEventSource() throws Exception {
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final AtomicReference<String> message = new AtomicReference<>();
+        final SseEventSource eventSource = SseEventSource.target(target().path(App.ROOT_PATH)).build();
+        eventSource.register((inboundEvent) -> {
+            final String value = inboundEvent.readData();
+            message.set(value);
+            latch.countDown();
+        });
+
+        eventSource.open();
+        target().path(App.ROOT_PATH).request().post(Entity.text("message"));
+
+        try {
+            assertTrue("Waiting for message to be delivered has timed out.",
+                    latch.await(5 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS));
+        } finally {
+            eventSource.close();
+        }
+        assertThat("Unexpected SSE event data value.", message.get(), equalTo("message"));
+    }
+
+
+    /**
+     * Test receiving all streamed messages in parallel by multiple event sources.
+     *
+     * @throws Exception in case of a failure during the test execution.
+     */
+    @Test
+    public void testCreateDomain() throws Exception {
+        final int MAX_CLIENTS = 25;
+        final int MESSAGE_COUNT = 6;
+
+        final Response response = target().path("domain/start")
+                .queryParam("testSources", MAX_CLIENTS)
+                .request().post(Entity.text("data"), Response.class);
+
+        assertThat("Unexpected start domain response status code.",
+                response.getStatus(), equalTo(Response.Status.CREATED.getStatusCode()));
+
+        final Map<Integer, Integer> messageCounts = new ConcurrentHashMap<>(MAX_CLIENTS);
+        final CountDownLatch doneLatch = new CountDownLatch(MAX_CLIENTS);
+        final SseEventSource[] sources = new SseEventSource[MAX_CLIENTS];
+
+        final String processUriString = target().getUri().relativize(response.getLocation()).toString();
+
+        final WebTarget sseTarget = target().path(processUriString).queryParam("testSource", "true");
+        for (int i = 0; i < MAX_CLIENTS; i++) {
+            final AtomicInteger messageCount = new AtomicInteger(0);  // will this work?
+            final int id = i;
+            sources[id] = SseEventSource.target(sseTarget).build();
+            sources[id].register((event) -> {
+                messageCount.incrementAndGet();
+                final String message = event.readData(String.class);
+                if ("done".equals(message)) {
+                    messageCounts.put(id, messageCount.get());
+                    doneLatch.countDown();
+                }
+            });
+            sources[i].open();
+        }
+
+        doneLatch.await(5 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+
+        for (SseEventSource source : sources) {
+            source.close();
+        }
+
+        for (int i = 0; i < MAX_CLIENTS; i++) {
+            final Integer count = messageCounts.get(i);
+            assertThat("Final message not received by event source " + i, count, notNullValue());
+            assertThat("Unexpected number of messages received by event source " + i,
+                    count, equalTo(MESSAGE_COUNT));
+        }
+    }
+}
diff --git a/examples/server-sent-events-jersey/README.MD b/examples/server-sent-events-jersey/README.MD
new file mode 100644
index 0000000..8c12344
--- /dev/null
+++ b/examples/server-sent-events-jersey/README.MD
@@ -0,0 +1,72 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Server-Sent Events (SSE) Example
+===================================
+
+This example demonstrates JAX-RS 2.0 server-sent events support sometimes also called one-way publish-subscribe model.
+
+The full description how to create a support for server-sent events can be found in Jersey User Guide, chapter
+[Server-Sent Events (SSE) Support](https://jersey.java.net/documentation/latest/sse.html).
+
+It is highly recommended to look at details of SSE API Specification on
+<http://www.w3.org/TR/2009/WD-eventsource-20091029/>
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                              | Resource class             | HTTP methods   | Description
+------------------------------------- | -------------------------- | -------------- | ------------
+**_server-sent-events_**              | ServerSentEventsResource   | GET            | Get entire EventOutput with all messages
+**_server-sent-events_**              | ServerSentEventsResource   | POST           | Insert a new message in EventOutput
+**_server-sent-events_**              | ServerSentEventsResource   | DELETE         | Reset EventOutput
+**_server-sent-events/domains/{id}_** | BlockingPostChatResource   | POST           | Generate several messages with a delay in EventOutput and return it
+
+Sample Response
+---------------
+
+A great example of Server-Sent Events is `server-sent-events/domains/{id}` which sends several messages with a delay 200ms
+between each other.
+
+>     curl -v -X POST http://localhost:8080/server-sent-events-jersey/domains/1 -H "Content-Type: text/event-stream"
+
+Look at a console how events are handled one after another in the right order.
+
+```
+event: domain-progress
+data: starting domain 1 ...
+
+event: domain-progress
+data: 50%
+
+event: domain-progress
+data: 60%
+
+event: domain-progress
+data: 70%
+
+event: domain-progress
+data: 99%
+
+event: domain-progress
+data: done
+```
+
+Running the Example
+-------------------
+
+Look at Jersey Documentation to learn how to consume Server-Sent Events using Jersey Client 
+<https://jersey.java.net/documentation/latest/sse.html#d0e11786>
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
diff --git a/examples/server-sent-events-jersey/pom.xml b/examples/server-sent-events-jersey/pom.xml
new file mode 100644
index 0000000..af580c1
--- /dev/null
+++ b/examples/server-sent-events-jersey/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>server-sent-events-jersey</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-server-sent-events-jersey</name>
+
+    <description>Jersey Server-Sent Events example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-sse</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/server-sent-events-jersey/src/main/java/org/glassfish/jersey/examples/sse/jersey/App.java b/examples/server-sent-events-jersey/src/main/java/org/glassfish/jersey/examples/sse/jersey/App.java
new file mode 100644
index 0000000..c253d6c
--- /dev/null
+++ b/examples/server-sent-events-jersey/src/main/java/org/glassfish/jersey/examples/sse/jersey/App.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sse.jersey;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Server sent event example.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/");
+    public static final String ROOT_PATH = "server-sent-events";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Server-Sent Events\" Jersey Example App");
+
+            final ResourceConfig resourceConfig = new ResourceConfig(ServerSentEventsResource.class, SseFeature.class);
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.\nTry out %s%s\nStop the application using CTRL+C",
+                    BASE_URI, ROOT_PATH));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+}
diff --git a/examples/server-sent-events-jersey/src/main/java/org/glassfish/jersey/examples/sse/jersey/DomainResource.java b/examples/server-sent-events-jersey/src/main/java/org/glassfish/jersey/examples/sse/jersey/DomainResource.java
new file mode 100644
index 0000000..d51e916
--- /dev/null
+++ b/examples/server-sent-events-jersey/src/main/java/org/glassfish/jersey/examples/sse/jersey/DomainResource.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sse.jersey;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.media.sse.EventOutput;
+import org.glassfish.jersey.media.sse.OutboundEvent;
+import org.glassfish.jersey.media.sse.SseBroadcaster;
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.server.ChunkedOutput;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("domain")
+public class DomainResource {
+
+    private static final Map<Integer, Process> processes = new ConcurrentHashMap<Integer, Process>();
+
+    @Path("start")
+    @POST
+    public Response post(@DefaultValue("0") @QueryParam("testSources") int testSources) {
+        final Process process = new Process(testSources);
+        processes.put(process.getId(), process);
+
+        Executors.newSingleThreadExecutor().execute(process);
+
+        final URI processIdUri = UriBuilder.fromResource(DomainResource.class).path("process/{id}").build(process.getId());
+        return Response.created(processIdUri).build();
+    }
+
+    @Path("process/{id}")
+    @Produces(SseFeature.SERVER_SENT_EVENTS)
+    @GET
+    public EventOutput getProgress(@PathParam("id") int id,
+                                   @DefaultValue("false") @QueryParam("testSource") boolean testSource) {
+        final Process process = processes.get(id);
+
+        if (process != null) {
+            if (testSource) {
+                process.release();
+            }
+            final EventOutput eventOutput = new EventOutput();
+            process.getBroadcaster().add(eventOutput);
+            return eventOutput;
+        } else {
+            throw new NotFoundException();
+        }
+    }
+
+    static class Process implements Runnable {
+
+        private static final AtomicInteger counter = new AtomicInteger(0);
+
+        private final int id;
+        private final CountDownLatch latch;
+        private final SseBroadcaster broadcaster = new SseBroadcaster() {
+            @Override
+            public void onException(ChunkedOutput<OutboundEvent> outboundEventChunkedOutput, Exception exception) {
+                exception.printStackTrace();
+            }
+        };
+
+        public Process(int testReceivers) {
+            id = counter.incrementAndGet();
+            latch = testReceivers > 0 ? new CountDownLatch(testReceivers) : null;
+        }
+
+        public int getId() {
+            return id;
+        }
+
+        public SseBroadcaster getBroadcaster() {
+            return broadcaster;
+        }
+
+        public boolean release() {
+            if (latch == null) {
+                return false;
+            }
+
+            latch.countDown();
+            return true;
+        }
+
+        public void run() {
+            try {
+                if (latch != null) {
+                    // wait for all test EventSources to be registered
+                    latch.await(5, TimeUnit.SECONDS);
+                }
+
+                broadcaster.broadcast(
+                        new OutboundEvent.Builder().name("domain-progress").data(String.class, "starting domain " + id + " ...")
+                                .build());
+                broadcaster.broadcast(new OutboundEvent.Builder().name("domain-progress").data(String.class, "50%").build());
+                broadcaster.broadcast(new OutboundEvent.Builder().name("domain-progress").data(String.class, "60%").build());
+                broadcaster.broadcast(new OutboundEvent.Builder().name("domain-progress").data(String.class, "70%").build());
+                broadcaster.broadcast(new OutboundEvent.Builder().name("domain-progress").data(String.class, "99%").build());
+                broadcaster.broadcast(new OutboundEvent.Builder().name("domain-progress").data(String.class, "done").build());
+                broadcaster.closeAll();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}
diff --git a/examples/server-sent-events-jersey/src/main/java/org/glassfish/jersey/examples/sse/jersey/ServerSentEventsResource.java b/examples/server-sent-events-jersey/src/main/java/org/glassfish/jersey/examples/sse/jersey/ServerSentEventsResource.java
new file mode 100644
index 0000000..1b8096d
--- /dev/null
+++ b/examples/server-sent-events-jersey/src/main/java/org/glassfish/jersey/examples/sse/jersey/ServerSentEventsResource.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sse.jersey;
+
+import java.io.IOException;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+import org.glassfish.jersey.media.sse.EventOutput;
+import org.glassfish.jersey.media.sse.OutboundEvent;
+import org.glassfish.jersey.media.sse.SseFeature;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("server-sent-events")
+public class ServerSentEventsResource {
+
+    private static volatile EventOutput eventOutput = new EventOutput();
+
+    @GET
+    @Produces(SseFeature.SERVER_SENT_EVENTS)
+    public EventOutput getMessageQueue() {
+        return eventOutput;
+    }
+
+    @POST
+    public void addMessage(final String message) throws IOException {
+        final EventOutput localOutput = eventOutput;
+        if (localOutput != null) {
+            eventOutput.write(new OutboundEvent.Builder().name("custom-message").data(String.class, message).build());
+        }
+    }
+
+    @DELETE
+    public void close() throws IOException {
+        final EventOutput localOutput = eventOutput;
+        if (localOutput != null) {
+            eventOutput.close();
+        }
+        ServerSentEventsResource.setEventOutput(new EventOutput());
+    }
+
+    @POST
+    @Path("domains/{id}")
+    @Produces(SseFeature.SERVER_SENT_EVENTS)
+    public EventOutput startDomain(@PathParam("id") final String id) {
+        final EventOutput seq = new EventOutput();
+
+        new Thread() {
+            public void run() {
+                try {
+                    seq.write(new OutboundEvent.Builder().name("domain-progress")
+                            .data(String.class, "starting domain " + id + " ...").build());
+                    Thread.sleep(200);
+                    seq.write(new OutboundEvent.Builder().name("domain-progress").data(String.class, "50%").build());
+                    Thread.sleep(200);
+                    seq.write(new OutboundEvent.Builder().name("domain-progress").data(String.class, "60%").build());
+                    Thread.sleep(200);
+                    seq.write(new OutboundEvent.Builder().name("domain-progress").data(String.class, "70%").build());
+                    Thread.sleep(200);
+                    seq.write(new OutboundEvent.Builder().name("domain-progress").data(String.class, "99%").build());
+                    Thread.sleep(200);
+                    seq.write(new OutboundEvent.Builder().name("domain-progress").data(String.class, "done").build());
+                    seq.close();
+
+                } catch (final InterruptedException | IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }.start();
+
+        return seq;
+    }
+
+    private static void setEventOutput(final EventOutput eventOutput) {
+        ServerSentEventsResource.eventOutput = eventOutput;
+    }
+}
diff --git a/examples/server-sent-events-jersey/src/test/java/org/glassfish/jersey/examples/sse/jersey/ServerSentEventsTest.java b/examples/server-sent-events-jersey/src/test/java/org/glassfish/jersey/examples/sse/jersey/ServerSentEventsTest.java
new file mode 100644
index 0000000..4782e7d
--- /dev/null
+++ b/examples/server-sent-events-jersey/src/test/java/org/glassfish/jersey/examples/sse/jersey/ServerSentEventsTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sse.jersey;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.media.sse.EventInput;
+import org.glassfish.jersey.media.sse.EventListener;
+import org.glassfish.jersey.media.sse.EventSource;
+import org.glassfish.jersey.media.sse.InboundEvent;
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * SSE example resources test.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ServerSentEventsTest extends JerseyTest {
+    // TODO - due to JdkConnector migration this was radically reduced. It deadlocks with 25 clients, find out why!
+    private static final int MAX_CLIENTS = 10;
+
+    @Override
+    protected Application configure() {
+        // enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(ServerSentEventsResource.class, DomainResource.class, SseFeature.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.property(ClientProperties.ASYNC_THREADPOOL_SIZE, MAX_CLIENTS + 2);
+    }
+
+    /**
+     * Test consuming a single SSE event via event source.
+     *
+     * @throws Exception in case of a failure during the test execution.
+     */
+    @Test
+    public void testEventSource() throws Exception {
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final AtomicReference<String> message = new AtomicReference<String>();
+        final EventSource eventSource = new EventSource(target().path(App.ROOT_PATH)) {
+            @Override
+            public void onEvent(InboundEvent inboundEvent) {
+                try {
+                    final String value = inboundEvent.readData();
+                    message.set(value);
+                    latch.countDown();
+                } catch (ProcessingException e) {
+                    e.printStackTrace();
+                }
+            }
+        };
+
+        target().path(App.ROOT_PATH).request().post(Entity.text("message"));
+
+        try {
+            assertTrue("Waiting for message to be delivered has timed out.",
+                    latch.await(5 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS));
+        } finally {
+            eventSource.close();
+        }
+        assertThat("Unexpected SSE event data value.", message.get(), equalTo("message"));
+    }
+
+    /**
+     * Test consuming multiple SSE events sequentially using event input.
+     *
+     * @throws Exception in case of a failure during the test execution.
+     */
+    @Test
+    public void testInboundEventReader() throws Exception {
+        final int MAX_MESSAGES = 5;
+        final CountDownLatch startLatch = new CountDownLatch(1);
+
+        final ExecutorService executor = Executors.newSingleThreadExecutor();
+        try {
+            final Future<List<String>> futureMessages =
+                    executor.submit(new Callable<List<String>>() {
+
+                        @Override
+                        public List<String> call() throws Exception {
+                            final EventInput eventInput = target(App.ROOT_PATH).register(SseFeature.class)
+                                    .request().get(EventInput.class);
+
+                            startLatch.countDown();
+
+                            final List<String> messages = new ArrayList<String>(MAX_MESSAGES);
+                            try {
+                                for (int i = 0; i < MAX_MESSAGES; i++) {
+                                    InboundEvent event = eventInput.read();
+                                    messages.add(event.readData());
+                                }
+                            } finally {
+                                if (eventInput != null) {
+                                    eventInput.close();
+                                }
+                            }
+
+                            return messages;
+                        }
+                    });
+
+            assertTrue("Waiting for receiver thread to start has timed out.",
+                    startLatch.await(5, TimeUnit.SECONDS));
+
+            for (int i = 0; i < MAX_MESSAGES; i++) {
+                target(App.ROOT_PATH).request().post(Entity.text("message " + i));
+            }
+
+            int i = 0;
+            for (String message : futureMessages.get(5000, TimeUnit.SECONDS)) {
+                assertThat("Unexpected SSE event data value.", message, equalTo("message " + i++));
+            }
+        } finally {
+            executor.shutdownNow();
+        }
+    }
+
+    /**
+     * Test receiving all streamed messages in parallel by multiple event sources.
+     *
+     * @throws Exception in case of a failure during the test execution.
+     */
+    @Test
+    public void testCreateDomain() throws Exception {
+        final int MESSAGE_COUNT = 6;
+
+        final Response response = target().path("domain/start")
+                .queryParam("testSources", MAX_CLIENTS)
+                .request().post(Entity.text("data"), Response.class);
+
+        assertThat("Unexpected start domain response status code.",
+                response.getStatus(), equalTo(Response.Status.CREATED.getStatusCode()));
+
+        final Map<Integer, Integer> messageCounts = new ConcurrentHashMap<Integer, Integer>(MAX_CLIENTS);
+        final CountDownLatch doneLatch = new CountDownLatch(MAX_CLIENTS);
+        final EventSource[] sources = new EventSource[MAX_CLIENTS];
+
+        final String processUriString = target().getUri().relativize(response.getLocation()).toString();
+
+        final WebTarget sseTarget = target().path(processUriString).queryParam("testSource", "true");
+        for (int i = 0; i < MAX_CLIENTS; i++) {
+            final int id = i;
+            sources[id] = EventSource.target(sseTarget).build();
+            sources[id].register(new EventListener() {
+
+                private final AtomicInteger messageCount = new AtomicInteger(0);
+
+                @Override
+                public void onEvent(InboundEvent inboundEvent) {
+                    messageCount.incrementAndGet();
+                    final String message = inboundEvent.readData(String.class);
+                    if ("done".equals(message)) {
+                        messageCounts.put(id, messageCount.get());
+                        doneLatch.countDown();
+                    }
+                }
+            });
+            sources[i].open();
+        }
+
+        doneLatch.await(5 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+
+        for (EventSource source : sources) {
+            source.close();
+        }
+
+        for (int i = 0; i < MAX_CLIENTS; i++) {
+            final Integer count = messageCounts.get(i);
+            assertThat("Final message not received by event source " + i, count, notNullValue());
+            assertThat("Unexpected number of messages received by event source " + i,
+                    count, equalTo(MESSAGE_COUNT));
+        }
+    }
+}
diff --git a/examples/servlet3-webapp/README.MD b/examples/servlet3-webapp/README.MD
new file mode 100644
index 0000000..839f789
--- /dev/null
+++ b/examples/servlet3-webapp/README.MD
@@ -0,0 +1,35 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Servlet 3 Example
+===================
+
+This example demonstrates how to use advantages of Servlet v3 and how to configure Jersey application with this version of Jersey.
+JAX-RS resource returns the usual text
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path             | Resource class      | HTTP methods
+-------------------- | ------------------- | ------------
+**_/cat_**           | CatResource         |  GET
+**_/dog_**           | DogResource         |  GET
+
+Running the Example
+-------------------
+
+Run the example using Jetty  as follows:
+
+>   mvn clean package jetty:run
+
+Go to the URL:
+
+-   <http://localhost:9998/animals/cat>
+-   <http://localhost:9998/animals/dog>
diff --git a/examples/servlet3-webapp/pom.xml b/examples/servlet3-webapp/pom.xml
new file mode 100644
index 0000000..13acfb2
--- /dev/null
+++ b/examples/servlet3-webapp/pom.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet3-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-servlet3-webapp</name>
+
+    <description>Jersey Servlet 3 example with missing servlet-class in the web.xml file</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-servlet</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>true</skipTests>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+                <configuration>
+                    <skip>${skip.tests}</skip>
+                    <scanIntervalSeconds>10</scanIntervalSeconds>
+                    <stopPort>9999</stopPort>
+                    <stopKey>STOP</stopKey>
+                    <webApp>
+                        <contextPath>/</contextPath>
+                        <webInfIncludeJarPattern>.*/.*jersey-[^/]\.jar$</webInfIncludeJarPattern>
+                    </webApp>
+                    <connectors>
+                        <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
+                            <port>${jersey.config.test.container.port}</port>
+                            <maxIdleTime>60000</maxIdleTime>
+                        </connector>
+                    </connectors>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>start-jetty</id>
+                        <phase>pre-integration-test</phase>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                        <configuration>
+                            <scanIntervalSeconds>0</scanIntervalSeconds>
+                            <daemon>true</daemon>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>stop-jetty</id>
+                        <phase>post-integration-test</phase>
+                        <goals>
+                            <goal>stop</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <jersey.config.test.container.port>9998</jersey.config.test.container.port>
+    </properties>
+
+</project>
diff --git a/examples/servlet3-webapp/src/main/java/org/glassfish/jersey/examples/servlet3/webapp/App.java b/examples/servlet3-webapp/src/main/java/org/glassfish/jersey/examples/servlet3/webapp/App.java
new file mode 100644
index 0000000..1e3f5d5
--- /dev/null
+++ b/examples/servlet3-webapp/src/main/java/org/glassfish/jersey/examples/servlet3/webapp/App.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.servlet3.webapp;
+
+import javax.ws.rs.core.Application;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Test Application subclass for servlet3-webapp example.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class App extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        HashSet<Class<?>> classes = new HashSet<Class<?>>();
+        classes.add(DogResource.class);
+        classes.add(CatResource.class);
+
+        return classes;
+    }
+}
diff --git a/examples/servlet3-webapp/src/main/java/org/glassfish/jersey/examples/servlet3/webapp/CatResource.java b/examples/servlet3-webapp/src/main/java/org/glassfish/jersey/examples/servlet3/webapp/CatResource.java
new file mode 100644
index 0000000..d42071d
--- /dev/null
+++ b/examples/servlet3-webapp/src/main/java/org/glassfish/jersey/examples/servlet3/webapp/CatResource.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.servlet3.webapp;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Test resource for the servlet3-webapp example.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("cat")
+public class CatResource {
+    @GET
+    @Produces("text/plain")
+    public String meow() {
+        return "Miaow!";
+    }
+}
diff --git a/examples/servlet3-webapp/src/main/java/org/glassfish/jersey/examples/servlet3/webapp/DogResource.java b/examples/servlet3-webapp/src/main/java/org/glassfish/jersey/examples/servlet3/webapp/DogResource.java
new file mode 100644
index 0000000..352576e
--- /dev/null
+++ b/examples/servlet3-webapp/src/main/java/org/glassfish/jersey/examples/servlet3/webapp/DogResource.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.servlet3.webapp;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Test resource for the servlet3-webapp example.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("dog")
+public class DogResource {
+    @GET
+    @Produces("text/plain")
+    public String bark() {
+        return "Woof!";
+    }
+}
diff --git a/examples/servlet3-webapp/src/main/webapp/WEB-INF/web.xml b/examples/servlet3-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..91a6614
--- /dev/null
+++ b/examples/servlet3-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.examples.servlet3.webapp.App</servlet-name>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.examples.servlet3.webapp.App</servlet-name>
+        <url-pattern>/animals/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/examples/servlet3-webapp/src/test/java/Servlet3WebappITCase.java b/examples/servlet3-webapp/src/test/java/Servlet3WebappITCase.java
new file mode 100644
index 0000000..0552dac
--- /dev/null
+++ b/examples/servlet3-webapp/src/test/java/Servlet3WebappITCase.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.UriBuilder;
+
+import java.net.URI;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests the servlet3-webapp example.
+ * Integration test launched by maven-jetty-plugin
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class Servlet3WebappITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new Application(); // dummy Application instance for test framework
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("animals").build();
+    }
+
+    @Test
+    public void testClientStringResponse() {
+        String s = target().path("dog").request().get(String.class);
+        assertEquals("Woof!", s);
+
+        s = target().path("cat").request().get(String.class);
+        assertEquals("Miaow!", s);
+    }
+}
diff --git a/examples/simple-console/README.MD b/examples/simple-console/README.MD
new file mode 100644
index 0000000..8581318
--- /dev/null
+++ b/examples/simple-console/README.MD
@@ -0,0 +1,73 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Simple Console Example
+======================
+
+This example demonstrates how to develop RESTful web services with the
+embedded Grizzly server.
+
+Contents
+--------
+
+The example consists of three Java classes and one HTML file:
+
+`org.glassfish.jersey.examples.console.App`
+
+Contains the main function which starts and stops the HTTP server.
+
+`org.glassfish.jersey.examples.console.resources.Colours`
+
+A resource class that can produce two different representations of a
+list of colors. The list can be filtered by a client-supplied
+query parameter. The resource references the Form resource using the
+Path annotation declared on the Colours.getColours method.
+
+`org.glassfish.jersey.examples.console.FormResource`
+
+A resource class that produces a form in response to an HTTP GET,
+processes the content of the form when submitted, and produces a
+table of the submitted values in respose to the form being posted.
+
+`form.html`
+
+A static HTML file that is returned by the Form resource.
+
+The mapping of the URI path space is presented in the following table:
+
+URI path              | Resource class   | HTTP methods
+--------------------- | ---------------- | --------------
+**_/form_**           | Form             | GET, POST
+**_/form/colours_**   | Colours          | GET
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container. You can access the application at:
+
+-   <http://127.0.0.1:9998/resources/form>
+
+Fill in the form and submit it. Note that the pull-down list of colors
+is populated dynamically. Enter part of a color name in the hint box to
+see a filtered list of colors containing the hint.
+
+You can see the complete list of colors at:
+
+-   <http://127.0.0.1:9998/resources/form/colours>
+
+Or see only those colors containing the letter 'r':
+
+-   <http://127.0.0.1:9998/resources/form/colours?match=r>
+
+A [WADL description](http://wadl.java.net/#spec) may be accessed at the URL:
+
+-   <http://127.0.0.1:9998/resources/application.wadl>
\ No newline at end of file
diff --git a/examples/simple-console/pom.xml b/examples/simple-console/pom.xml
new file mode 100644
index 0000000..fcb5fc8
--- /dev/null
+++ b/examples/simple-console/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>simple-console</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-simple-console</name>
+
+    <description>Jersey Simple Console example</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jettison</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.console.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/simple-console/src/main/java/org/glassfish/jersey/examples/console/App.java b/examples/simple-console/src/main/java/org/glassfish/jersey/examples/console/App.java
new file mode 100644
index 0000000..e350294
--- /dev/null
+++ b/examples/simple-console/src/main/java/org/glassfish/jersey/examples/console/App.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.console;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+public class App {
+
+    private static int getPort(int defaultPort) {
+        final String port = System.getProperty("jersey.config.test.container.port");
+        if (null != port) {
+            try {
+                return Integer.parseInt(port);
+            } catch (NumberFormatException e) {
+                System.out.println("Value of jersey.config.test.container.port property"
+                        + " is not a valid positive integer [" + port + "]."
+                        + " Reverting to default [" + defaultPort + "].");
+            }
+        }
+        return defaultPort;
+    }
+
+    private static URI getBaseURI() {
+        return UriBuilder.fromUri("http://localhost/resources").port(getPort(defaultPort)).build();
+    }
+
+    public static final URI BASE_URI = getBaseURI();
+    public static final int defaultPort = 9998;
+
+    public static ResourceConfig createApp() {
+        return new ResourceConfig()
+                .register(new JettisonFeature())
+                .packages("org.glassfish.jersey.examples.console");
+    }
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("Simple Console Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(getBaseURI(), createApp(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(
+                    String.format("Application started.%nTry out %s%nStop the application using CTRL+C", BASE_URI + "/form"));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+}
diff --git a/examples/simple-console/src/main/java/org/glassfish/jersey/examples/console/resources/Colours.java b/examples/simple-console/src/main/java/org/glassfish/jersey/examples/console/resources/Colours.java
new file mode 100644
index 0000000..048349a
--- /dev/null
+++ b/examples/simple-console/src/main/java/org/glassfish/jersey/examples/console/resources/Colours.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.console.resources;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+
+import org.codehaus.jettison.json.JSONArray;
+
+/**
+ * A web resource for a list of colours.
+ */
+public class Colours {
+
+    private static String colours[] = {"red", "orange", "yellow", "green", "blue", "indigo", "violet"};
+
+    /**
+     * Returns a list of colours as plain text, one colour per line.
+     * @param filter If not empty, constrains the list of colours to only
+     * those that contain this substring
+     * @return the list of colours matching the filter
+     */
+    @GET
+    @Produces("text/plain")
+    public String getColourListAsText(@QueryParam("match") String filter) {
+        StringBuilder buf = new StringBuilder();
+        for (String colour : getMatchingColours(filter)) {
+            buf.append(colour);
+            buf.append('\n');
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Returns a list of colours as a JSON array.
+     * @param filter If not empty, constrains the list of colours to only
+     * those that contain this substring
+     * @return the list of colours matching the filter
+     */
+    @GET
+    @Produces("application/json")
+    public JSONArray getColourListAsJSON(@QueryParam("match") String filter) {
+        return new JSONArray(getMatchingColours(filter));
+    }
+
+    /**
+     * Returns a list of colours.
+     * @param filter If not empty, constrains the list of colours to only
+     * those that contain this substring
+     * @return the list of colours matching the filter
+     */
+    public static List<String> getMatchingColours(String filter) {
+        List<String> matches = new ArrayList<>();
+
+        for (String colour : colours) {
+            if (filter == null || filter.length() == 0 || colour.contains(filter)) {
+                matches.add(colour);
+            }
+        }
+
+        return matches;
+    }
+}
diff --git a/examples/simple-console/src/main/java/org/glassfish/jersey/examples/console/resources/FormResource.java b/examples/simple-console/src/main/java/org/glassfish/jersey/examples/console/resources/FormResource.java
new file mode 100644
index 0000000..fb7d495
--- /dev/null
+++ b/examples/simple-console/src/main/java/org/glassfish/jersey/examples/console/resources/FormResource.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.console.resources;
+
+import java.io.InputStream;
+import java.util.Date;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
+
+/**
+ * A Web form resource, produces the form and processes the results of
+ * submitting it.
+ *
+ */
+@Path("/form")
+@Produces("text/html")
+public class FormResource {
+
+    private static final Colours coloursResource = new Colours();
+
+    @Context
+    HttpHeaders headers;
+
+    @Path("colours")
+    public Colours getColours() {
+        return coloursResource;
+    }
+
+    /**
+     * Produce a form from a static HTML file packaged with the compiled class
+     * @return a stream from which the HTML form can be read.
+     */
+    @GET
+    public Response getForm() {
+        Date now = new Date();
+
+        InputStream entity = this.getClass().getClassLoader().getResourceAsStream("form.html");
+        return Response.ok(entity)
+                .cookie(new NewCookie("date", now.toString())).build();
+    }
+
+    /**
+     * Process the form submission. Produces a table showing the form field
+     * values submitted.
+     * @return a dynamically generated HTML table.
+     * @param formData the data from the form submission
+     */
+    @POST
+    @Consumes("application/x-www-form-urlencoded")
+    public String processForm(MultivaluedMap<String, String> formData) {
+        StringBuilder buf = new StringBuilder();
+        buf.append("<html><head><title>Form results</title></head><body>");
+        buf.append("<p>Hello, you entered the following information: </p><table border='1'>");
+        for (String key : formData.keySet()) {
+            if (key.equals("submit")) {
+                continue;
+            }
+            buf.append("<tr><td>");
+            buf.append(key);
+            buf.append("</td><td>");
+            buf.append(formData.getFirst(key));
+            buf.append("</td></tr>");
+        }
+        for (Cookie c : headers.getCookies().values()) {
+            buf.append("<tr><td>Cookie: ");
+            buf.append(c.getName());
+            buf.append("</td><td>");
+            buf.append(c.getValue());
+            buf.append("</td></tr>");
+        }
+
+        buf.append("</table></body></html>");
+        return buf.toString();
+    }
+
+}
diff --git a/examples/simple-console/src/main/resources/form.html b/examples/simple-console/src/main/resources/form.html
new file mode 100644
index 0000000..4665455
--- /dev/null
+++ b/examples/simple-console/src/main/resources/form.html
@@ -0,0 +1,67 @@
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+<html>
+<head>
+    <title>A Form</title>
+    <script type="text/javascript">
+        function setColours() {
+            var http_request = new XMLHttpRequest();
+            if (document.bio.hint.value.length > 0)
+                http_request.open("GET", "form/colours?match="+document.bio.hint.value, false);
+            else
+                http_request.open("GET", "form/colours", false);
+            http_request.setRequestHeader("Accept", "application/json");
+            http_request.send(null);
+            if (http_request.status == 200) {
+                list = eval("(" + http_request.responseText + ")");
+                document.bio.colour.options.length = 0;
+                document.bio.colour.options[0] = new Option("Pick a colour", "", true, false);
+                for (var i = 0; i<list.length; i++) {
+                    document.bio.colour.options[i+1] = new Option(list[i], list[i], false, false);
+                }
+            } else {
+                alert("There was a problem fetching the list of colours.");
+            }
+            http_request = null;
+        }
+    </script>
+</head>
+<body>
+<p>Tell me about yourself:</p>
+<form name="bio" action="form" method="POST">
+    <table>
+        <tr>
+            <td align="right">Name:</td>
+            <td><input type="text" name="name" value="" size="30" /></td>
+        </tr>
+        <tr>
+            <td align="right">Favorite colour:</td>
+            <td>
+                <select name="colour" onmousedown="setColours()">
+                    <option>Pick a colour</option>
+                </select> <i>Populated dynamically when you click on the control</i><br/>
+                <input type="text" name="hint" value="" size="13"/> <i>Type a hint to reduce the number of options</i>
+            </td>
+        </tr>
+        <tr>
+            <td></td>
+            <td>
+                <input type="submit" value="Submit" name="submit" />
+            </td>
+        </tr>
+    </table>
+</form>
+</body>
+</html>
diff --git a/examples/simple-console/src/test/java/org/glassfish/jersey/examples/console/MainTest.java b/examples/simple-console/src/test/java/org/glassfish/jersey/examples/console/MainTest.java
new file mode 100644
index 0000000..14fe277
--- /dev/null
+++ b/examples/simple-console/src/test/java/org/glassfish/jersey/examples/console/MainTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.console;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.util.collection.MultivaluedStringMap;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.codehaus.jettison.json.JSONArray;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Naresh (srinivas.bhimisetty at oracle.com)
+ */
+public class MainTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return App.createApp();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(new JettisonFeature());
+    }
+
+    /**
+     * Test if a WADL document is available at the relative path
+     * "application.wadl".
+     */
+    @Test
+    public void testApplicationWadl() {
+        String serviceWadl = target().path("application.wadl").request(MediaTypes.WADL_TYPE).get(String.class);
+        assertTrue(!serviceWadl.isEmpty());
+    }
+
+    /**
+     * Test if GET on the resource "/form" gives response with status code 200.
+     */
+    @Test
+    public void testGetOnForm() {
+        Response response = target().path("form").request(MediaType.TEXT_HTML).get();
+        assertEquals("GET on the 'form' resource doesn't give expected response", Response.Status.OK.getStatusCode(),
+                response.getStatusInfo().getStatusCode());
+    }
+
+    /**
+     * Test checks that POST on the '/form' resource gives a response page
+     * with the entered data.
+     */
+    @Test
+    public void testPostOnForm() {
+        MultivaluedMap<String, String> formData = new MultivaluedStringMap();
+        formData.add("name", "testName");
+        formData.add("colour", "red");
+        formData.add("hint", "re");
+
+        Response response = target().path("form").request().post(Entity.entity(formData, MediaType.APPLICATION_FORM_URLENCODED));
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+        // check that the generated response is the expected one
+        InputStream responseInputStream = response.readEntity(InputStream.class);
+        try {
+            byte[] responseData = new byte[responseInputStream.available()];
+            final int read = responseInputStream.read(responseData);
+
+            assertTrue(read > 0);
+            assertTrue(new String(responseData).contains("Hello, you entered"));
+        } catch (IOException ex) {
+            Logger.getLogger(MainTest.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    /**
+     * Test checks that a GET on the resource "/form/colours" with mime-type "text/html"
+     * shows the appropriate colours based on the query param "match".
+     */
+    @Test
+    public void testGetColoursAsPlainText() {
+        // without the query param "match"
+        Response response = target().path("form").path("colours").request(MediaType.TEXT_PLAIN).get();
+        assertEquals("GET on path '/form/colours' with mime type 'text/html' doesn't give expected response",
+                Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+        String responseMsg = target().path("form").path("colours").request(MediaType.TEXT_PLAIN).get(String.class);
+        assertEquals("Response content doesn't match the expected value", "red\norange\nyellow\ngreen\nblue\nindigo\nviolet\n",
+                responseMsg);
+
+        // with the query param "match" value "re"
+        responseMsg = target("form/colours").queryParam("match", "re").request(MediaType.TEXT_PLAIN).get(String.class);
+        assertEquals("Response content doesn't match the expected value with the query param 'match=re'", "red\ngreen\n",
+                responseMsg);
+    }
+
+    /**
+     * Test checks that a GET on the resource "/form/colours" with mime-type "application/json"
+     * shows the appropriate colours based on the query param "match".
+     */
+    @Test
+    public void testGetColoursAsJson() {
+        Response response = target().path("form").path("colours").request(MediaType.APPLICATION_JSON).get();
+        assertEquals("GET on path '/form/colours' with mime type 'application/json' doesn't give expected response",
+                Response.Status.OK.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+        JSONArray jsonArray = target().path("form").path("colours").request(MediaType.APPLICATION_JSON).get(JSONArray.class);
+        assertEquals("Returned JSONArray doesn't have expected number of entries", 7, jsonArray.length());
+
+        // with the query param "match" value "re"
+        jsonArray = target("form/colours").queryParam("match", "re").request(MediaType.APPLICATION_JSON).get(JSONArray.class);
+        assertEquals("Returned JSONArray doesn't have expected number of entries with the query param 'match=re'", 2,
+                jsonArray.length());
+    }
+
+}
diff --git a/examples/sse-item-store-jaxrs-webapp/README.MD b/examples/sse-item-store-jaxrs-webapp/README.MD
new file mode 100644
index 0000000..2948b07
--- /dev/null
+++ b/examples/sse-item-store-jaxrs-webapp/README.MD
@@ -0,0 +1,67 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+JAX-RS SSE Item Store Jersey Example
+====================================
+
+An example demonstrating how new Server Sent Events (SSE) JAX-RS API can
+be used to notify clients about changes in server-side managed data
+(collection of items). The example also outlines how SSE events can be
+consumed using javascript browser-based clients.
+
+Contents
+--------
+
+The example consists of a web-based client application and a server-side
+application deployed on a [Jetty servlet container](http://www.eclipse.org/jetty/documentation/current/).
+
+The server side part of the application consists of a JAX-RS resource
+managing collection of string items and a simple HTML page that includes
+a browser SSE client written in Javascript as well as a basic CSS
+stylesheet. The SSE Javascript client connects to the JAX-RS resource
+and transforms the streamed messages into HTML code that is rendered by
+the browser. The javascript client also demonstrates how named and
+unnamed SSE events are handled by HTML5 `EventSource` component. The
+mapping of the URI path space of the server-side part of the application
+is presented in the following table:
+
+URI path                        |  Resource class      | HTTP methods
+------------------------------- |  ------------------- | --------------
+**_/resources/items_**          |  ItemStoreResource   | GET, POST
+**_/resources/items/events_**   |  ItemStoreResource   | GET (SSE)
+
+Application is configured to run using Jetty maven plugin under base
+path `sse-item-store-jaxrs-webapp`.
+
+Running the Example
+-------------------
+
+>     mvn clean compile jetty:run
+
+The command above deploys the current example. After successful
+deployment, you should be able to access the browser SSE client page at
+<http://localhost:8080/sse-item-store-jaxrs-webapp/index.html>. To see the raw
+SSE event stream, you may also point your browser directly at the
+[`ItemStoreResource`](http://localhost:8080/sse-item-store-jaxrs-webapp/resources/items/events).
+
+Deploying the example to another servlet container.
+---------------------------------------------------
+
+>     mvn clean package
+
+The command above creates a Servlet 3.x compliant WAR located in the
+target directory. The WAR can be then deployed to your Servlet 3.x
+compliant container.
+
+Running Test Client
+-------------------
+
+After deploying the application into a Servlet 3.x compliant container,
+you can run the attached test by executing:
+
+>     mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.external.ExternalTestContainerFactory -Djersey.config.test.container.port=<port>
diff --git a/examples/sse-item-store-jaxrs-webapp/pom.xml b/examples/sse-item-store-jaxrs-webapp/pom.xml
new file mode 100644
index 0000000..afba6c1
--- /dev/null
+++ b/examples/sse-item-store-jaxrs-webapp/pom.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>sse-item-store-jaxrs-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-sse-item-store-jaxrs-webapp</name>
+
+    <description>Jersey JAX-RS 2.1 SSE API-based item store example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-sse</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-apache-connector</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Run the application using "mvn jetty:run" -->
+            <plugin>
+                <!-- TODO unify Jetty in all examples -->
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+                <configuration>
+                    <scanIntervalSeconds>10</scanIntervalSeconds>
+                    <stopPort>9999</stopPort>
+                    <stopKey>STOP</stopKey>
+                    <webApp>
+                        <contextPath>/${project.artifactId}</contextPath>
+                    </webApp>
+                    <systemProperties>
+                        <systemProperty>
+                            <name>jetty.port</name>
+                            <value>${jersey.config.test.container.port}</value>
+                        </systemProperty>
+                    </systemProperties>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <jersey.config.test.container.port>8080</jersey.config.test.container.port>
+    </properties>
+</project>
diff --git a/examples/sse-item-store-jaxrs-webapp/src/main/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreApp.java b/examples/sse-item-store-jaxrs-webapp/src/main/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreApp.java
new file mode 100644
index 0000000..ed76ecf
--- /dev/null
+++ b/examples/sse-item-store-jaxrs-webapp/src/main/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreApp.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sseitemstore.jaxrs;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * SSE item store JAX-RS application class.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@ApplicationPath("resources")
+public class JaxrsItemStoreApp extends ResourceConfig {
+    /**
+     * Create new SSE Item Store Example JAX-RS application.
+     */
+    public JaxrsItemStoreApp() {
+        super(JaxrsItemStoreResource.class);
+    }
+}
diff --git a/examples/sse-item-store-jaxrs-webapp/src/main/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreResource.java b/examples/sse-item-store-jaxrs-webapp/src/main/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreResource.java
new file mode 100644
index 0000000..a8c193d
--- /dev/null
+++ b/examples/sse-item-store-jaxrs-webapp/src/main/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreResource.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sseitemstore.jaxrs;
+
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.logging.Logger;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.ServiceUnavailableException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.sse.OutboundSseEvent;
+import javax.ws.rs.sse.Sse;
+import javax.ws.rs.sse.SseBroadcaster;
+import javax.ws.rs.sse.SseEventSink;
+
+import org.glassfish.jersey.media.sse.SseFeature;
+
+
+/**
+ * A resource for storing named items.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("items")
+public class JaxrsItemStoreResource {
+    private static final Logger LOGGER = Logger.getLogger(JaxrsItemStoreResource.class.getName());
+
+    private static final ReentrantReadWriteLock storeLock = new ReentrantReadWriteLock();
+    private static final LinkedList<String> itemStore = new LinkedList<>();
+
+    private static final AtomicReference<SseBroadcaster> BROADCASTER = new AtomicReference<>(null);
+
+    @Context
+    private Sse sse;
+
+    private static volatile long reconnectDelay = 0;
+
+    /**
+     * List all stored items.
+     *
+     * @return list of all stored items.
+     */
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String listItems() {
+        try {
+            storeLock.readLock().lock();
+            return itemStore.toString();
+        } finally {
+            storeLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Receive & process commands sent by the test client that control the internal resource state.
+     * <p>
+     * Following is the list of recognized commands:
+     * <ul>
+     * <li><b>disconnect</b> - disconnect all registered event streams.</li>
+     * <li><b>reconnect now</b> - enable client reconnecting.</li>
+     * <li><b>reconnect &lt;seconds&gt;</b> - disable client reconnecting.
+     * Reconnecting clients will receive a HTTP 503 response with
+     * {@value javax.ws.rs.core.HttpHeaders#RETRY_AFTER} set to the amount of
+     * milliseconds specified.</li>
+     * </ul>
+     *
+     * @param command command to be processed.
+     * @return message about processing result.
+     * @throws BadRequestException in case the command is not recognized or not specified.
+     */
+    @POST
+    @Path("commands")
+    public String processCommand(String command) {
+        if (command == null || command.isEmpty()) {
+            throw new BadRequestException("No command specified.");
+        }
+
+        if ("disconnect".equals(command)) {
+            closeBroadcaster();
+            return "Disconnected.";
+        } else if ("reconnect ".length() < command.length() && command.startsWith("reconnect ")) {
+            final String when = command.substring("reconnect ".length());
+            try {
+                reconnectDelay = "now".equals(when) ? 0 : Long.parseLong(when);
+                return "Reconnect strategy updated: " + when;
+            } catch (NumberFormatException ignore) {
+                // ignored
+            }
+        }
+
+        throw new BadRequestException("Command not recognized: '" + command + "'");
+    }
+
+    /**
+     * TODO - rewrite Connect or re-connect to SSE event stream.
+     *
+     * @param lastEventId Value of custom SSE HTTP <tt>{@value SseFeature#LAST_EVENT_ID_HEADER}</tt> header.
+     *                    Defaults to {@code -1} if not set.
+     * @throws InternalServerErrorException in case replaying missed events to the reconnected output stream fails.
+     * @throws ServiceUnavailableException  in case the reconnect delay is set to a positive value.
+     */
+    @GET
+    @Path("events")
+    @Produces(MediaType.SERVER_SENT_EVENTS)
+    public void itemEvents(@HeaderParam(SseFeature.LAST_EVENT_ID_HEADER) @DefaultValue("-1") int lastEventId,
+                           @Context SseEventSink eventSink) {
+
+        if (lastEventId >= 0) {
+            LOGGER.info("Received last event id :" + lastEventId);
+
+            // decide the reconnect handling strategy based on current reconnect delay value.
+            final long delay = reconnectDelay;
+            if (0 < delay) {
+                LOGGER.info("Non-zero reconnect delay [" + delay + "] - responding with HTTP 503.");
+                throw new ServiceUnavailableException(delay);
+            } else {
+                LOGGER.info("Zero reconnect delay - reconnecting.");
+                replayMissedEvents(lastEventId, eventSink);
+            }
+        }
+
+        getBroadcaster().register(eventSink);
+    }
+
+    private void replayMissedEvents(int lastEventId, SseEventSink eventSink) {
+        try {
+            storeLock.readLock().lock();
+            final int firstUnreceived = lastEventId + 1;
+            final int missingCount = itemStore.size() - firstUnreceived;
+            if (missingCount > 0) {
+                LOGGER.info("Replaying events - starting with id " + firstUnreceived);
+                final ListIterator<String> it = itemStore.subList(firstUnreceived, itemStore.size()).listIterator();
+                while (it.hasNext()) {
+                    eventSink.send(createItemEvent(it.nextIndex() + firstUnreceived, it.next()));
+                }
+            } else {
+                LOGGER.info("No events to replay.");
+            }
+        } finally {
+            storeLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Add new item to the item store.
+     * <p>
+     * Invoking this method will fire 2 new SSE events - 1st about newly added item and 2nd about the new item store size.
+     *
+     * @param name item name.
+     */
+    @POST
+    public void addItem(@FormParam("name") String name) {
+        // Ignore if the request was sent without name parameter.
+        if (name == null) {
+            return;
+        }
+
+        final int eventId;
+        try {
+            storeLock.writeLock().lock();
+            eventId = itemStore.size();
+            itemStore.add(name);
+
+            SseBroadcaster sseBroadcaster = getBroadcaster();
+
+            // Broadcasting an un-named event with the name of the newly added item in data
+            sseBroadcaster.broadcast(createItemEvent(eventId, name));
+
+            // Broadcasting a named "size" event with the current size of the items collection in data
+            final OutboundSseEvent event = sse.newEventBuilder().name("size").data(Integer.class, eventId + 1).build();
+            sseBroadcaster.broadcast(event);
+        } finally {
+            storeLock.writeLock().unlock();
+        }
+    }
+
+    private OutboundSseEvent createItemEvent(final int eventId, final String name) {
+        Logger.getLogger(JaxrsItemStoreResource.class.getName())
+                .info("Creating event id [" + eventId + "] name [" + name + "]");
+        return sse.newEventBuilder().id("" + eventId).data(String.class, name).build();
+    }
+
+    /**
+     * Get stored broadcaster or create a new one and store it.
+     *
+     * @return broadcaster instance.
+     */
+    private SseBroadcaster getBroadcaster() {
+        SseBroadcaster sseBroadcaster = BROADCASTER.get();
+        if (sseBroadcaster == null) {
+            BROADCASTER.compareAndSet(null, sse.newBroadcaster());
+        }
+
+        return BROADCASTER.get();
+    }
+
+    /**
+     * Close currently stored broadcaster.
+     */
+    private void closeBroadcaster() {
+        SseBroadcaster sseBroadcaster = BROADCASTER.getAndSet(null);
+        if (sseBroadcaster == null) {
+            return;
+        }
+        sseBroadcaster.close();
+    }
+}
diff --git a/examples/sse-item-store-jaxrs-webapp/src/main/webapp/css/main.css b/examples/sse-item-store-jaxrs-webapp/src/main/webapp/css/main.css
new file mode 100644
index 0000000..5e31320
--- /dev/null
+++ b/examples/sse-item-store-jaxrs-webapp/src/main/webapp/css/main.css
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+div.message {
+    border-radius: 10px;
+    border: thin solid #444444;
+    margin: 10px;
+    width: 300px;
+    padding: 10px;
+    box-shadow: 5px 5px 5px #888888;
+    font-family: Helvetica, serif;
+    color: #333333;
+}
+
+div.items {
+    border-radius: 10px;
+    border: thin dotted #AAAAFF;
+    margin: 10px;
+    padding: 10px;
+    font-family: Helvetica, serif;
+    color: #4444FF;
+}
+
+div.messags {
+    border-radius: 10px;
+    border: thin dotted #888888;
+    margin: 10px;
+    padding: 10px;
+}
diff --git a/examples/sse-item-store-jaxrs-webapp/src/main/webapp/index.html b/examples/sse-item-store-jaxrs-webapp/src/main/webapp/index.html
new file mode 100644
index 0000000..0c488a2
--- /dev/null
+++ b/examples/sse-item-store-jaxrs-webapp/src/main/webapp/index.html
@@ -0,0 +1,27 @@
+<!--
+
+    Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <title>Jersey SSE Item Store Example</title>
+    <link rel="stylesheet" href="css/main.css"/>
+    <script src="js/engine.js" type="text/javascript"></script>
+</head>
+<body>
+<div>
+    Enter item: <input id="name" type="text"/><input value="Add" type="button" onclick="addItem()"/>
+</div>
+<div id="items" class="items"></div>
+<div id="messages" class="messages"></div>
+</body>
+</html>
diff --git a/examples/sse-item-store-jaxrs-webapp/src/main/webapp/js/engine.js b/examples/sse-item-store-jaxrs-webapp/src/main/webapp/js/engine.js
new file mode 100644
index 0000000..3b1f42d
--- /dev/null
+++ b/examples/sse-item-store-jaxrs-webapp/src/main/webapp/js/engine.js
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+"use strict";
+
+function addItem() {
+    var itemInput = document.getElementById("name");
+
+    var req = new XMLHttpRequest();
+    req.open("POST", "resources/items", true);
+    req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+    req.onreadystatechange = function () {
+        if (req.readyState == 4 && req.status == 204) {
+            //Call a function when the state changes.
+            itemInput.value = "";
+            getItems();
+        }
+    };
+    req.send("name=" + itemInput.value);
+}
+
+function getItems() {
+    var req = new XMLHttpRequest();
+    req.open("GET", "resources/items", true);
+    req.setRequestHeader("Accept", "text/plain");
+    req.onreadystatechange = function () {
+        //Call a function when the state changes.
+        if (req.readyState == 4 && req.status == 200) {
+            document.getElementById("items").innerHTML = req.responseText;
+        }
+    };
+    req.send();
+}
+
+function display(data, rgb) {
+    var msgSpan = document.createElement("span");
+    msgSpan.style.color = rgb;
+    msgSpan.innerHTML = data;
+    var msgDiv = document.createElement("div");
+    msgDiv.className = "message";
+    msgDiv.appendChild(msgSpan);
+
+    var messages = document.getElementById("messages");
+    messages.insertBefore(msgDiv, messages.firstChild);
+}
+
+function receiveMessages() {
+    if (typeof(EventSource) !== "undefined") {
+        // Yes! Server-sent events support!
+        var source = new EventSource("resources/items/events");
+        source.onmessage = function (event) {
+            console.log('Received unnamed event: ' + event.data);
+            display("Added new item: " + event.data, "#444444");
+        };
+
+        source.addEventListener("size", function(e) {
+            console.log('Received event ' + event.name + ': ' + event.data);
+            display("New items size: " + event.data, "#0000FF");
+        }, false);
+
+        source.onopen = function (event) {
+            console.log("event source opened");
+        };
+
+        source.onerror = function (event) {
+            console.log('Received error event: ' + event.data);
+            display(event.data, "#FF0000");
+        };
+    } else {
+        // Sorry! No server-sent events support..
+        display('SSE not supported by browser.', "#FF0000");
+    }
+}
+
+window.onload = receiveMessages;
diff --git a/examples/sse-item-store-jaxrs-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreResourceTest.java b/examples/sse-item-store-jaxrs-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreResourceTest.java
new file mode 100644
index 0000000..0f352a6
--- /dev/null
+++ b/examples/sse-item-store-jaxrs-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreResourceTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sseitemstore.jaxrs;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.sse.SseEventSource;
+
+import org.glassfish.jersey.apache.connector.ApacheClientProperties;
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.describedAs;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Item store test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class JaxrsItemStoreResourceTest extends JerseyTest {
+
+    private static final int RECONNECT_DEFAULT = 500;
+    private static final Logger LOGGER = Logger.getLogger(JaxrsItemStoreResourceTest.class.getName());
+    private static final int MAX_LISTENERS = 5;
+    private static final int MAX_ITEMS = 10;
+
+    private final ExecutorService executorService =
+            Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("JaxrsItemStoreResourceTest-%d").build());
+    private final AtomicReference<Client> client = new AtomicReference<>(null);
+
+    @Override
+    protected Application configure() {
+        return new JaxrsItemStoreApp();
+    }
+
+    protected void configureClient(ClientConfig config) {
+        // using AHC as a test client connector to avoid issues with HttpUrlConnection socket management.
+        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
+
+        // adjusting max. connections just to be safe - the testEventSourceReconnect is quite greedy...
+        cm.setMaxTotal(MAX_LISTENERS * MAX_ITEMS);
+        cm.setDefaultMaxPerRoute(MAX_LISTENERS * MAX_ITEMS);
+
+        config.property(ApacheClientProperties.CONNECTION_MANAGER, cm)
+                .property(ClientProperties.READ_TIMEOUT, 2000)
+                .connectorProvider(new ApacheConnectorProvider());
+    }
+
+    @Override
+    protected Client getClient() {
+        if (client.get() == null) {
+            ClientConfig clientConfig = new ClientConfig();
+            configureClient(clientConfig);
+            client.compareAndSet(null,
+                                 ClientBuilder.newBuilder()
+                                              .withConfig(clientConfig)
+                                              .executorService(executorService).build());
+        }
+
+        return client.get();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        client.get().close();
+        executorService.shutdown();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        final UriBuilder baseUriBuilder = UriBuilder.fromUri(super.getBaseUri()).path("sse-item-store-jaxrs-webapp");
+        final boolean externalFactoryInUse = getTestContainerFactory() instanceof ExternalTestContainerFactory;
+        return externalFactoryInUse ? baseUriBuilder.path("resources").build() : baseUriBuilder.build();
+    }
+
+    /**
+     * Test the item addition, addition event broadcasting and item retrieval from {@link JaxrsItemStoreResource}.
+     *
+     * @throws Exception in case of a test failure.
+     */
+    @Test
+    public void testItemsStore() throws Exception {
+        final List<String> items = Collections.unmodifiableList(Arrays.asList("foo", "bar", "baz"));
+        final WebTarget itemsTarget = target("items");
+        final CountDownLatch latch = new CountDownLatch(items.size() * MAX_LISTENERS * 2); // countdown on all events
+        final List<Queue<Integer>> indexQueues = new ArrayList<>(MAX_LISTENERS);
+        final SseEventSource[] sources = new SseEventSource[MAX_LISTENERS];
+        final AtomicInteger sizeEventsCount = new AtomicInteger(0);
+
+        for (int i = 0; i < MAX_LISTENERS; i++) {
+            final int id = i;
+            final SseEventSource es = SseEventSource.target(itemsTarget.path("events")).build();
+            sources[id] = es;
+
+            final Queue<Integer> indexes = new ConcurrentLinkedQueue<>();
+            indexQueues.add(indexes);
+
+            es.register(inboundEvent -> {
+                try {
+                    if (null == inboundEvent.getName()) {
+                        final String data = inboundEvent.readData();
+                        LOGGER.info("[-i-] SOURCE " + id + ": Received event id=" + inboundEvent.getId() + " data=" + data);
+                        indexes.add(items.indexOf(data));
+                    } else if ("size".equals(inboundEvent.getName())) {
+                        sizeEventsCount.incrementAndGet();
+                    }
+                } catch (Exception ex) {
+                    LOGGER.log(Level.SEVERE, "[-x-] SOURCE " + id + ": Error getting event data.", ex);
+                    indexes.add(-999);
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+
+        try {
+            open(sources);
+            items.forEach((item) -> postItem(itemsTarget, item));
+
+            assertTrue("Waiting to receive all events has timed out.",
+                    latch.await((1000 + MAX_LISTENERS * RECONNECT_DEFAULT) * getAsyncTimeoutMultiplier(),
+                            TimeUnit.MILLISECONDS));
+
+            // need to force disconnect on server in order for EventSource.close(...) to succeed with HttpUrlConnection
+            sendCommand(itemsTarget, "disconnect");
+        } finally {
+            close(sources);
+        }
+
+        String postedItems = itemsTarget.request().get(String.class);
+        items.forEach((item) -> assertTrue("Item '" + item + "' not stored on server.", postedItems.contains(item)));
+
+        final AtomicInteger queueId = new AtomicInteger(0);
+        indexQueues.forEach((indexes) -> {
+            for (int i = 0; i < items.size(); i++) {
+                assertTrue("Event for '" + items.get(i) + "' not received in queue " + queueId.get(), indexes.contains(i));
+            }
+            assertEquals("Not received the expected number of events in queue " + queueId.get(), items.size(), indexes.size());
+            queueId.incrementAndGet();
+        });
+
+        assertEquals("Number of received 'size' events does not match.", items.size() * MAX_LISTENERS, sizeEventsCount.get());
+    }
+
+    /**
+     * Test the {@link SseEventSource} reconnect feature.
+     *
+     * @throws Exception in case of a test failure.
+     */
+    @Test
+    public void testEventSourceReconnect() throws Exception {
+        final WebTarget itemsTarget = target("items");
+        final CountDownLatch latch = new CountDownLatch(MAX_ITEMS * MAX_LISTENERS * 2); // countdown only on new item events
+        final List<Queue<String>> receivedQueues = new ArrayList<>(MAX_LISTENERS);
+        final SseEventSource[] sources = new SseEventSource[MAX_LISTENERS];
+
+        for (int i = 0; i < MAX_LISTENERS; i++) {
+            final int id = i;
+            final SseEventSource es = SseEventSource.target(itemsTarget.path("events"))
+                    .reconnectingEvery(1, TimeUnit.MILLISECONDS).build();
+            sources[id] = es;
+
+            final Queue<String> received = new ConcurrentLinkedQueue<>();
+            receivedQueues.add(received);
+
+            es.register(inboundEvent -> {
+                try {
+                    if (null == inboundEvent.getName()) {
+                        final String data = inboundEvent.readData();
+                        LOGGER.info("[-i-] SOURCE " + id + ": Received event id=" + inboundEvent.getId() + " data=" + data);
+                        received.add(data);
+                        latch.countDown();
+                    }
+                } catch (Exception ex) {
+                    LOGGER.log(Level.SEVERE, "[-x-] SOURCE " + id + ": Error getting event data.", ex);
+                    received.add("[data processing error]");
+                }
+            });
+        }
+
+        final String[] postedItems = new String[MAX_ITEMS * 2];
+        try {
+            open(sources);
+
+            for (int i = 0; i < MAX_ITEMS; i++) {
+                final String item = String.format("round-1-%02d", i);
+                postItem(itemsTarget, item);
+                postedItems[i] = item;
+                sendCommand(itemsTarget, "disconnect");
+                Thread.sleep(200);
+            }
+
+            final int reconnectDelay = 1;
+            sendCommand(itemsTarget, "reconnect " + reconnectDelay);
+            sendCommand(itemsTarget, "disconnect");
+
+            Thread.sleep(reconnectDelay * 1000);
+
+            for (int i = 0; i < MAX_ITEMS; i++) {
+                final String item = String.format("round-2-%02d", i);
+                postedItems[i + MAX_ITEMS] = item;
+                postItem(itemsTarget, item);
+            }
+
+            sendCommand(itemsTarget, "reconnect now");
+
+            assertTrue("Waiting to receive all events has timed out.",
+                    latch.await((1 + MAX_LISTENERS * (MAX_ITEMS + 1) * reconnectDelay) * getAsyncTimeoutMultiplier(),
+                            TimeUnit.SECONDS));
+
+            // need to force disconnect on server in order for EventSource.close(...) to succeed with HttpUrlConnection
+            sendCommand(itemsTarget, "disconnect");
+        } finally {
+            close(sources);
+        }
+
+        final String storedItems = itemsTarget.request().get(String.class);
+        for (String item : postedItems) {
+            assertThat("Posted item '" + item + "' stored on server", storedItems, containsString(item));
+        }
+
+        int sourceId = 0;
+        for (Queue<String> queue : receivedQueues) {
+            assertThat("Received events in source " + sourceId, queue,
+                    describedAs("Collection containing %0", hasItems(postedItems), Arrays.asList(postedItems).toString()));
+            assertThat("Size of received queue for source " + sourceId, queue.size(), equalTo(postedItems.length));
+            sourceId++;
+        }
+    }
+
+    private static void postItem(final WebTarget itemsTarget, final String item) {
+        final Response response = itemsTarget.request().post(Entity.form(new Form("name", item)));
+        assertEquals("Posting new item has failed.", 204, response.getStatus());
+        LOGGER.info("[-i-] POSTed item: '" + item + "'");
+    }
+
+    private static void open(final SseEventSource[] sources) {
+        Arrays.stream(sources).forEach(SseEventSource::open);
+    }
+
+    private static void close(final SseEventSource[] sources) {
+        int i = 0;
+        for (SseEventSource source : sources) {
+            if (source.isOpen()) {
+                assertTrue("Waiting to close a source has timed out.", source.close(1, TimeUnit.SECONDS));
+//                    source.close(100, TimeUnit.MILLISECONDS);
+                LOGGER.info("[<--] SOURCE " + i++ + " closed.");
+            }
+        }
+    }
+
+    private static void sendCommand(final WebTarget itemsTarget, final String command) {
+        final Response response = itemsTarget.path("commands").request().post(Entity.text(command));
+        assertEquals("'" + command + "' command has failed.", 200, response.getStatus());
+        LOGGER.info("[-!-] COMMAND '" + command + "' has been processed.");
+    }
+}
diff --git a/examples/sse-item-store-jersey-webapp/README.MD b/examples/sse-item-store-jersey-webapp/README.MD
new file mode 100644
index 0000000..4850875
--- /dev/null
+++ b/examples/sse-item-store-jersey-webapp/README.MD
@@ -0,0 +1,67 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+SSE Item Store Jersey Example
+=============================
+
+An example demonstrating how Server Sent Events (SSE) Jersey support can
+be used to notify clients about changes in server-side managed data
+(collection of items). The example also outlines how SSE events can be
+consumed using javascript browser-based clients.
+
+Contents
+--------
+
+The example consists of a web-based client application and a server-side
+application deployed on a [Jetty servlet container](http://www.eclipse.org/jetty/documentation/current/).
+
+The server side part of the application consists of a JAX-RS resource
+managing collection of string items and a simple HTML page that includes
+a browser SSE client written in Javascript as well as a basic CSS
+stylesheet. The SSE Javascript client connects to the JAX-RS resource
+and transforms the streamed messages into HTML code that is rendered by
+the browser. The javascript client also demonstrates how named and
+unnamed SSE events are handled by HTML5 `EventSource` component. The
+mapping of the URI path space of the server-side part of the application
+is presented in the following table:
+
+URI path                        |  Resource class      | HTTP methods
+------------------------------- |  ------------------- | --------------
+**_/resources/items_**          |  ItemStoreResource   | GET, POST
+**_/resources/items/events_**   |  ItemStoreResource   | GET (SSE)
+
+Application is configured to run using Jetty maven plugin under base
+path `sse-item-store-webapp`.
+
+Running the Example
+-------------------
+
+>     mvn clean compile jetty:run
+
+The command above deploys the current example. After successful
+deployment, you should be able to access the browser SSE client page at
+<http://localhost:8080/sse-item-store-jersey-webapp/index.html>. To see the raw
+SSE event stream, you may also point your browser directly at the
+[`ItemStoreResource`](http://localhost:8080/sse-item-store-jersey-webapp/resources/items/events).
+
+Deploying the example to another servlet container.
+---------------------------------------------------
+
+>     mvn clean package
+
+The command above creates a Servlet 3.x compliant WAR located in the
+target directory. The WAR can be then deployed to your Servlet 3.x
+compliant container.
+
+Running Test Client
+-------------------
+
+After deploying the application into a Servlet 3.x compliant container,
+you can run the attached test by executing:
+
+>     mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.external.ExternalTestContainerFactory -Djersey.config.test.container.port=<port>
diff --git a/examples/sse-item-store-jersey-webapp/pom.xml b/examples/sse-item-store-jersey-webapp/pom.xml
new file mode 100644
index 0000000..fdadcd5
--- /dev/null
+++ b/examples/sse-item-store-jersey-webapp/pom.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>webapp-example-parent</artifactId>
+        <relativePath>../webapp-example-parent/pom.xml</relativePath>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>sse-item-store-jersey-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-examples-sse-item-store-jersey-webapp</name>
+
+    <description>Jersey SSE API-based item store example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-sse</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-apache-connector</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Run the application using "mvn jetty:run" -->
+            <plugin>
+                <!-- TODO unify Jetty in all examples -->
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+                <configuration>
+                    <scanIntervalSeconds>10</scanIntervalSeconds>
+                    <stopPort>9999</stopPort>
+                    <stopKey>STOP</stopKey>
+                    <webApp>
+                        <contextPath>/${project.artifactId}</contextPath>
+                    </webApp>
+                    <systemProperties>
+                        <systemProperty>
+                            <name>jetty.port</name>
+                            <value>${jersey.config.test.container.port}</value>
+                        </systemProperty>
+                    </systemProperties>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <jersey.config.test.container.port>8080</jersey.config.test.container.port>
+    </properties>
+</project>
diff --git a/examples/sse-item-store-jersey-webapp/src/main/java/org/glassfish/jersey/examples/sseitemstore/jersey/ItemStoreApp.java b/examples/sse-item-store-jersey-webapp/src/main/java/org/glassfish/jersey/examples/sseitemstore/jersey/ItemStoreApp.java
new file mode 100644
index 0000000..a8ca593
--- /dev/null
+++ b/examples/sse-item-store-jersey-webapp/src/main/java/org/glassfish/jersey/examples/sseitemstore/jersey/ItemStoreApp.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sseitemstore.jersey;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * SSE item store JAX-RS application class.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@ApplicationPath("resources")
+public class ItemStoreApp extends ResourceConfig {
+    /**
+     * Create new SSE Item Store Example JAX-RS application.
+     */
+    public ItemStoreApp() {
+        super(ItemStoreResource.class, SseFeature.class);
+    }
+}
diff --git a/examples/sse-item-store-jersey-webapp/src/main/java/org/glassfish/jersey/examples/sseitemstore/jersey/ItemStoreResource.java b/examples/sse-item-store-jersey-webapp/src/main/java/org/glassfish/jersey/examples/sseitemstore/jersey/ItemStoreResource.java
new file mode 100644
index 0000000..a8d1dc7
--- /dev/null
+++ b/examples/sse-item-store-jersey-webapp/src/main/java/org/glassfish/jersey/examples/sseitemstore/jersey/ItemStoreResource.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sseitemstore.jersey;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.ServiceUnavailableException;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.media.sse.EventOutput;
+import org.glassfish.jersey.media.sse.OutboundEvent;
+import org.glassfish.jersey.media.sse.SseBroadcaster;
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.server.BroadcasterListener;
+import org.glassfish.jersey.server.ChunkedOutput;
+
+/**
+ * A resource for storing named items.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("items")
+public class ItemStoreResource {
+    private static final Logger LOGGER = Logger.getLogger(ItemStoreResource.class.getName());
+
+    private static final ReentrantReadWriteLock storeLock = new ReentrantReadWriteLock();
+    private static final LinkedList<String> itemStore = new LinkedList<String>();
+    private static final SseBroadcaster broadcaster = new SseBroadcaster();
+
+    static {
+        broadcaster.add(new BroadcasterListener<OutboundEvent>() {
+            @Override
+            public void onException(ChunkedOutput<OutboundEvent> chunkedOutput, Exception exception) {
+                LOGGER.log(Level.WARNING,
+                        "An exception has been thrown while broadcasting to an event output.",
+                        exception);
+            }
+
+            @Override
+            public void onClose(ChunkedOutput<OutboundEvent> chunkedOutput) {
+                LOGGER.log(Level.INFO, "Chunked output has been closed.");
+            }
+        });
+    }
+
+    private static volatile long reconnectDelay = 0;
+
+    /**
+     * List all stored items.
+     *
+     * @return list of all stored items.
+     */
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String listItems() {
+        try {
+            storeLock.readLock().lock();
+            return itemStore.toString();
+        } finally {
+            storeLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Receive & process commands sent by the test client that control the internal resource state.
+     *
+     * Following is the list of recognized commands:
+     * <ul>
+     * <li><b>disconnect</b> - disconnect all registered event streams.</li>
+     * <li><b>reconnect now</b> - enable client reconnecting.</li>
+     * <li><b>reconnect &lt;seconds&gt;</b> - disable client reconnecting.
+     * Reconnecting clients will receive a HTTP 503 response with
+     * {@value javax.ws.rs.core.HttpHeaders#RETRY_AFTER} set to the amount of
+     * milliseconds specified.</li>
+     * </ul>
+     *
+     * @param command command to be processed.
+     * @return message about processing result.
+     * @throws BadRequestException in case the command is not recognized or not specified.
+     */
+    @POST
+    @Path("commands")
+    public String processCommand(String command) {
+        if (command == null || command.isEmpty()) {
+            throw new BadRequestException("No command specified.");
+        }
+
+        if ("disconnect".equals(command)) {
+            broadcaster.closeAll();
+            return "Disconnected.";
+        } else if (command.length() > "reconnect ".length() && command.startsWith("reconnect ")) {
+            final String when = command.substring("reconnect ".length());
+            try {
+                reconnectDelay = "now".equals(when) ? 0 : Long.parseLong(when);
+                return "Reconnect strategy updated: " + when;
+            } catch (NumberFormatException ignore) {
+                // ignored
+            }
+        }
+
+        throw new BadRequestException("Command not recognized: '" + command + "'");
+    }
+
+    /**
+     * Connect or re-connect to SSE event stream.
+     *
+     * @param lastEventId Value of custom SSE HTTP <tt>{@value SseFeature#LAST_EVENT_ID_HEADER}</tt> header.
+     *                    Defaults to {@code -1} if not set.
+     * @return new SSE event output stream representing the (re-)established SSE client connection.
+     * @throws InternalServerErrorException in case replaying missed events to the reconnected output stream fails.
+     * @throws ServiceUnavailableException  in case the reconnect delay is set to a positive value.
+     */
+    @GET
+    @Path("events")
+    @Produces(SseFeature.SERVER_SENT_EVENTS)
+    public EventOutput itemEvents(
+            @HeaderParam(SseFeature.LAST_EVENT_ID_HEADER) @DefaultValue("-1") int lastEventId) {
+        final EventOutput eventOutput = new EventOutput();
+
+        if (lastEventId >= 0) {
+            LOGGER.info("Received last event id :" + lastEventId);
+
+            // decide the reconnect handling strategy based on current reconnect delay value.
+            final long delay = reconnectDelay;
+            if (delay > 0) {
+                LOGGER.info("Non-zero reconnect delay [" + delay + "] - responding with HTTP 503.");
+                throw new ServiceUnavailableException(delay);
+            } else {
+                LOGGER.info("Zero reconnect delay - reconnecting.");
+                replayMissedEvents(lastEventId, eventOutput);
+            }
+        }
+
+        if (!broadcaster.add(eventOutput)) {
+            LOGGER.severe("!!! Unable to add new event output to the broadcaster !!!");
+            // let's try to force a 5s delayed client reconnect attempt
+            throw new ServiceUnavailableException(5L);
+        }
+
+        return eventOutput;
+    }
+
+    private void replayMissedEvents(final int lastEventId, final EventOutput eventOutput) {
+        try {
+            storeLock.readLock().lock();
+            final int firstUnreceived = lastEventId + 1;
+            final int missingCount = itemStore.size() - firstUnreceived;
+            if (missingCount > 0) {
+                LOGGER.info("Replaying events - starting with id " + firstUnreceived);
+                final ListIterator<String> it = itemStore.subList(firstUnreceived, itemStore.size()).listIterator();
+                while (it.hasNext()) {
+                    eventOutput.write(createItemEvent(it.nextIndex() + firstUnreceived, it.next()));
+                }
+            } else {
+                LOGGER.info("No events to replay.");
+            }
+        } catch (IOException ex) {
+            throw new InternalServerErrorException("Error replaying missed events", ex);
+        } finally {
+            storeLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Add new item to the item store.
+     *
+     * Invoking this method will fire 2 new SSE events - 1st about newly added item and 2nd about the new item store size.
+     *
+     * @param name item name.
+     */
+    @POST
+    public void addItem(@FormParam("name") String name) {
+        // Ignore if the request was sent without name parameter.
+        if (name == null) {
+            return;
+        }
+
+        final int eventId;
+        try {
+            storeLock.writeLock().lock();
+            eventId = itemStore.size();
+            itemStore.add(name);
+            // Broadcasting an un-named event with the name of the newly added item in data
+            broadcaster.broadcast(createItemEvent(eventId, name));
+            // Broadcasting a named "size" event with the current size of the items collection in data
+            broadcaster.broadcast(new OutboundEvent.Builder().name("size").data(Integer.class, eventId + 1).build());
+        } finally {
+            storeLock.writeLock().unlock();
+        }
+    }
+
+    private OutboundEvent createItemEvent(final int eventId, final String name) {
+        Logger.getLogger(ItemStoreResource.class.getName()).info("Creating event id [" + eventId + "] name [" + name + "]");
+        return new OutboundEvent.Builder().id("" + eventId).data(String.class, name).build();
+    }
+}
diff --git a/examples/sse-item-store-jersey-webapp/src/main/webapp/css/main.css b/examples/sse-item-store-jersey-webapp/src/main/webapp/css/main.css
new file mode 100644
index 0000000..6907c2c
--- /dev/null
+++ b/examples/sse-item-store-jersey-webapp/src/main/webapp/css/main.css
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+div.message {
+    border-radius: 10px;
+    border: thin solid #444444;
+    margin: 10px;
+    width: 300px;
+    padding: 10px;
+    box-shadow: 5px 5px 5px #888888;
+    font-family: Helvetica, serif;
+    color: #333333;
+}
+
+div.items {
+    border-radius: 10px;
+    border: thin dotted #AAAAFF;
+    margin: 10px;
+    padding: 10px;
+    font-family: Helvetica, serif;
+    color: #4444FF;
+}
+
+div.messags {
+    border-radius: 10px;
+    border: thin dotted #888888;
+    margin: 10px;
+    padding: 10px;
+}
diff --git a/examples/sse-item-store-jersey-webapp/src/main/webapp/index.html b/examples/sse-item-store-jersey-webapp/src/main/webapp/index.html
new file mode 100644
index 0000000..abd98be
--- /dev/null
+++ b/examples/sse-item-store-jersey-webapp/src/main/webapp/index.html
@@ -0,0 +1,27 @@
+<!--
+
+    Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <title>Jersey SSE Item Store Example</title>
+    <link rel="stylesheet" href="css/main.css"/>
+    <script src="js/engine.js" type="text/javascript"></script>
+</head>
+<body>
+<div>
+    Enter item: <input id="name" type="text"/><input value="Add" type="button" onclick="addItem()"/>
+</div>
+<div id="items" class="items"></div>
+<div id="messages" class="messages"></div>
+</body>
+</html>
diff --git a/examples/sse-item-store-jersey-webapp/src/main/webapp/js/engine.js b/examples/sse-item-store-jersey-webapp/src/main/webapp/js/engine.js
new file mode 100644
index 0000000..a01776d
--- /dev/null
+++ b/examples/sse-item-store-jersey-webapp/src/main/webapp/js/engine.js
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+"use strict";
+
+function addItem() {
+    var itemInput = document.getElementById("name");
+
+    var req = new XMLHttpRequest();
+    req.open("POST", "resources/items", true);
+    req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+    req.onreadystatechange = function () {
+        if (req.readyState == 4 && req.status == 204) {
+            //Call a function when the state changes.
+            itemInput.value = "";
+            getItems();
+        }
+    };
+    req.send("name=" + itemInput.value);
+}
+
+function getItems() {
+    var req = new XMLHttpRequest();
+    req.open("GET", "resources/items", true);
+    req.setRequestHeader("Accept", "text/plain");
+    req.onreadystatechange = function () {
+        //Call a function when the state changes.
+        if (req.readyState == 4 && req.status == 200) {
+            document.getElementById("items").innerHTML = req.responseText;
+        }
+    };
+    req.send();
+}
+
+function display(data, rgb) {
+    var msgSpan = document.createElement("span");
+    msgSpan.style.color = rgb;
+    msgSpan.innerHTML = data;
+    var msgDiv = document.createElement("div");
+    msgDiv.className = "message";
+    msgDiv.appendChild(msgSpan);
+
+    var messages = document.getElementById("messages");
+    messages.insertBefore(msgDiv, messages.firstChild);
+}
+
+function receiveMessages() {
+    if (typeof(EventSource) !== "undefined") {
+        // Yes! Server-sent events support!
+        var source = new EventSource("resources/items/events");
+        source.onmessage = function (event) {
+            console.log('Received unnamed event: ' + event.data);
+            display("Added new item: " + event.data, "#444444");
+        };
+
+        source.addEventListener("size", function(e) {
+            console.log('Received event ' + event.name + ': ' + event.data);
+            display("New items size: " + event.data, "#0000FF");
+        }, false);
+
+        source.onopen = function (event) {
+            console.log("event source opened");
+        };
+
+        source.onerror = function (event) {
+            console.log('Received error event: ' + event.data);
+            display(event.data, "#FF0000");
+        };
+    } else {
+        // Sorry! No server-sent events support..
+        display('SSE not supported by browser.', "#FF0000");
+    }
+}
+
+window.onload = receiveMessages;
diff --git a/examples/sse-item-store-jersey-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jersey/JerseyItemStoreResourceTest.java b/examples/sse-item-store-jersey-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jersey/JerseyItemStoreResourceTest.java
new file mode 100644
index 0000000..4122a3d
--- /dev/null
+++ b/examples/sse-item-store-jersey-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jersey/JerseyItemStoreResourceTest.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sseitemstore.jersey;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.apache.connector.ApacheClientProperties;
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.media.sse.EventSource;
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.describedAs;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Item store test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class JerseyItemStoreResourceTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(JerseyItemStoreResourceTest.class.getName());
+    private static final int MAX_LISTENERS = 5;
+    private static final int MAX_ITEMS = 10;
+
+
+    @Override
+    protected Application configure() {
+        return new ItemStoreApp();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        // using AHC as a test client connector to avoid issues with HttpUrlConnection socket management.
+        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
+
+        // adjusting max. connections just to be safe - the testEventSourceReconnect is quite greedy...
+        cm.setMaxTotal(MAX_LISTENERS * MAX_ITEMS);
+        cm.setDefaultMaxPerRoute(MAX_LISTENERS * MAX_ITEMS);
+
+        config.register(SseFeature.class)
+                .property(ApacheClientProperties.CONNECTION_MANAGER, cm)
+                .property(ClientProperties.READ_TIMEOUT, 2000)
+                .connectorProvider(new ApacheConnectorProvider());
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        final UriBuilder baseUriBuilder = UriBuilder.fromUri(super.getBaseUri()).path("sse-item-store-jersey-webapp");
+        final boolean externalFactoryInUse = getTestContainerFactory() instanceof ExternalTestContainerFactory;
+        return externalFactoryInUse ? baseUriBuilder.path("resources").build() : baseUriBuilder.build();
+    }
+
+    /**
+     * Test the item addition, addition event broadcasting and item retrieval from {@link ItemStoreResource}.
+     *
+     * @throws Exception in case of a test failure.
+     */
+    @Test
+    public void testItemsStore() throws Exception {
+        final List<String> items = Collections.unmodifiableList(Arrays.asList(
+                "foo",
+                "bar",
+                "baz"));
+        final WebTarget itemsTarget = target("items");
+        final CountDownLatch latch = new CountDownLatch(items.size() * MAX_LISTENERS * 2); // countdown on all events
+        final List<Queue<Integer>> indexQueues = new ArrayList<>(MAX_LISTENERS);
+        final EventSource[] sources = new EventSource[MAX_LISTENERS];
+        final AtomicInteger sizeEventsCount = new AtomicInteger(0);
+
+        for (int i = 0; i < MAX_LISTENERS; i++) {
+            final int id = i;
+            final EventSource es = EventSource.target(itemsTarget.path("events"))
+                    .named("SOURCE " + id).build();
+            sources[id] = es;
+
+            final Queue<Integer> indexes = new ConcurrentLinkedQueue<>();
+            indexQueues.add(indexes);
+
+            es.register(inboundEvent -> {
+                try {
+                    if (inboundEvent.getName() == null) {
+                        final String data = inboundEvent.readData();
+                        LOGGER.info("[-i-] SOURCE " + id + ": Received event id=" + inboundEvent.getId() + " data=" + data);
+                        indexes.add(items.indexOf(data));
+                    } else if ("size".equals(inboundEvent.getName())) {
+                        sizeEventsCount.incrementAndGet();
+                    }
+                } catch (Exception ex) {
+                    LOGGER.log(Level.SEVERE, "[-x-] SOURCE " + id + ": Error getting event data.", ex);
+                    indexes.add(-999);
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+
+        try {
+            open(sources);
+
+            for (String item : items) {
+                postItem(itemsTarget, item);
+            }
+
+            assertTrue("Waiting to receive all events has timed out.",
+                    latch.await((1000 + MAX_LISTENERS * EventSource.RECONNECT_DEFAULT) * getAsyncTimeoutMultiplier(),
+                            TimeUnit.MILLISECONDS));
+
+            // need to force disconnect on server in order for EventSource.close(...) to succeed with HttpUrlConnection
+            sendCommand(itemsTarget, "disconnect");
+        } finally {
+            close(sources);
+        }
+
+        String postedItems = itemsTarget.request().get(String.class);
+        for (String item : items) {
+            assertTrue("Item '" + item + "' not stored on server.", postedItems.contains(item));
+        }
+
+        int queueId = 0;
+        for (Queue<Integer> indexes : indexQueues) {
+            for (int i = 0; i < items.size(); i++) {
+                assertTrue("Event for '" + items.get(i) + "' not received in queue " + queueId, indexes.contains(i));
+            }
+            assertEquals("Not received the expected number of events in queue " + queueId, items.size(), indexes.size());
+            queueId++;
+        }
+
+        assertEquals("Number of received 'size' events does not match.", items.size() * MAX_LISTENERS, sizeEventsCount.get());
+    }
+
+    /**
+     * Test the {@link EventSource} reconnect feature.
+     *
+     * @throws Exception in case of a test failure.
+     */
+    @Test
+    public void testEventSourceReconnect() throws Exception {
+        final WebTarget itemsTarget = target("items");
+        final CountDownLatch latch = new CountDownLatch(MAX_ITEMS * MAX_LISTENERS * 2); // countdown only on new item events
+        final List<Queue<String>> receivedQueues = new ArrayList<>(MAX_LISTENERS);
+        final EventSource[] sources = new EventSource[MAX_LISTENERS];
+
+        for (int i = 0; i < MAX_LISTENERS; i++) {
+            final int id = i;
+            final EventSource es = EventSource.target(itemsTarget.path("events")).named("SOURCE " + id).build();
+            sources[id] = es;
+
+            final Queue<String> received = new ConcurrentLinkedQueue<>();
+            receivedQueues.add(received);
+
+            es.register(inboundEvent -> {
+                try {
+                    if (inboundEvent.getName() == null) {
+                        latch.countDown();
+                        final String data = inboundEvent.readData();
+                        LOGGER.info("[-i-] SOURCE " + id + ": Received event id=" + inboundEvent.getId() + " data=" + data);
+                        received.add(data);
+                    }
+                } catch (Exception ex) {
+                    LOGGER.log(Level.SEVERE, "[-x-] SOURCE " + id + ": Error getting event data.", ex);
+                    received.add("[data processing error]");
+                }
+            });
+        }
+
+        final String[] postedItems = new String[MAX_ITEMS * 2];
+        try {
+            open(sources);
+
+            for (int i = 0; i < MAX_ITEMS; i++) {
+                final String item = String.format("round-1-%02d", i);
+                postItem(itemsTarget, item);
+                postedItems[i] = item;
+                sendCommand(itemsTarget, "disconnect");
+                Thread.sleep(100);
+            }
+
+            final int reconnectDelay = 1;
+            sendCommand(itemsTarget, "reconnect " + reconnectDelay);
+            sendCommand(itemsTarget, "disconnect");
+
+            Thread.sleep(reconnectDelay * 1000);
+
+            for (int i = 0; i < MAX_ITEMS; i++) {
+                final String item = String.format("round-2-%02d", i);
+                postedItems[i + MAX_ITEMS] = item;
+                postItem(itemsTarget, item);
+            }
+
+            sendCommand(itemsTarget, "reconnect now");
+
+            assertTrue("Waiting to receive all events has timed out.",
+                    latch.await((1 + MAX_LISTENERS * (MAX_ITEMS + 1) * reconnectDelay) * getAsyncTimeoutMultiplier(),
+                            TimeUnit.SECONDS));
+
+            // need to force disconnect on server in order for EventSource.close(...) to succeed with HttpUrlConnection
+            sendCommand(itemsTarget, "disconnect");
+        } finally {
+            close(sources);
+        }
+
+        final String storedItems = itemsTarget.request().get(String.class);
+        for (String item : postedItems) {
+            assertThat("Posted item '" + item + "' stored on server", storedItems, containsString(item));
+        }
+
+        int sourceId = 0;
+        for (Queue<String> queue : receivedQueues) {
+            assertThat("Received events in source " + sourceId, queue,
+                    describedAs("Collection containing %0", hasItems(postedItems), Arrays.asList(postedItems).toString()));
+            assertThat("Size of received queue for source " + sourceId, queue.size(), equalTo(postedItems.length));
+            sourceId++;
+        }
+    }
+
+    private static void postItem(final WebTarget itemsTarget, final String item) {
+        final Response response = itemsTarget.request().post(Entity.form(new Form("name", item)));
+        assertEquals("Posting new item has failed.", 204, response.getStatus());
+        LOGGER.info("[-i-] POSTed item: '" + item + "'");
+    }
+
+    private static void open(final EventSource[] sources) {
+        int i = 0;
+        for (EventSource source : sources) {
+            source.open();
+            LOGGER.info("[-->] SOURCE " + i++ + " opened.");
+        }
+    }
+
+    private static void close(final EventSource[] sources) {
+        int i = 0;
+        for (EventSource source : sources) {
+            if (source.isOpen()) {
+                assertTrue("Waiting to close a source has timed out.", source.close(1, TimeUnit.SECONDS));
+//                    source.close(100, TimeUnit.MILLISECONDS);
+                LOGGER.info("[<--] SOURCE " + i++ + " closed.");
+            }
+        }
+    }
+
+    private static void sendCommand(final WebTarget itemsTarget, final String command) {
+        final Response response = itemsTarget.path("commands").request().post(Entity.text(command));
+        assertEquals("'" + command + "' command has failed.", 200, response.getStatus());
+        LOGGER.info("[-!-] COMMAND '" + command + "' has been processed.");
+    }
+}
diff --git a/examples/sse-twitter-aggregator/README.MD b/examples/sse-twitter-aggregator/README.MD
new file mode 100644
index 0000000..45cec61
--- /dev/null
+++ b/examples/sse-twitter-aggregator/README.MD
@@ -0,0 +1,90 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Message Streaming Jersey Example
+================================
+
+An example demonstrating how Server Sent Events (SSE) Jersey support can
+be used to create a message aggregating and streaming resource. The
+example also demonstrates how to consume data provided by a [Twitter
+Streaming API](https://dev.twitter.com/docs/streaming-apis) using Jersey
+`ChunkedInput` client-side support.
+
+Contents
+--------
+
+The example consists of a Swing GUI client application and a server-side
+application deployed on a [Grizzly container](http://grizzly.java.net).
+
+The Swing client application opens in a single window split into
+multiple configuration panels. The top-left panel allows to select which
+aggregators should be executed. It is possible to select a twitter
+message aggregator and/or a fake test message aggregator. The panel
+below provides means for adding new keywords tracked by the Twitter
+message aggregator (if enabled). All actively tracked keywords are
+listed in the right-side list box which supports removal of the keys
+using a `Delete` key on your keyboard. The last panel in the Swing
+client window contains a Start/Stop button for starting or stopping the
+selected aggregators as well as a text area for displaying the messages
+that the aggregators have sent to the message aggregating resource
+deployed on the server-side of the application.
+
+The server side part of the application consists of a message
+aggregating JAX-RS resource and a simple HTML page that includes a
+browser SSE client written in Javascript as well as a basic CSS
+stylesheet. The SSE Javascript client connects to the JAX-RS message
+aggregating resource and transforms the streamed message data into HTML
+code that is rendered by the browser. The mapping of the URI path space
+of the server-side part of the application is presented in the following
+table:
+
+URI path                           | Resource class          | HTTP methods
+---------------------------------- | ----------------------- | --------------
+`/aggregator-api/message/stream`   | MessageStreamResource   | GET, PUT
+
+Application is programmatically configured to run on Grizzly container
+under base paths `aggregator` and `aggregator-api`.
+
+Running the Example
+-------------------
+
+Before running the example, you need to copy the
+`twitter-api.properties` file available in the root directory of this
+example to your user home directory. Once done, you need to edit the
+file in your user home directory and provide a valid twitter account
+credentials. This step is necessary as the Twitter streaming API is
+protected using HTTP Basic Authentication mechanism and only an
+authenticated twitter account owner can access the API. The
+TwitterAggregator class in the example will use the credentials provided
+in the file to access the Twitter Streaming API.
+
+Once you have set up your Twitter credentials, you're ready to run the
+example. You can run the example by executing a main class via maven as
+follows:
+
+>     mvn clean compile exec:java
+
+This deploys the current example as well as it starts the Swing client
+application. You should be able to see the main Swing application window
+open as you run the application as well as you should see the note in
+the command line stating that the Grizzly container has started and the
+server side part of the application has been deployed.
+
+After successful deployment, you should be able to access the browser
+SSE client page at
+
+-   <http://localhost:8080/aggregator/index.html>
+
+To see the raw SSE event stream, you may also point your browser
+directly at the
+
+-   [MessageStreamResource](http://localhost:8080/aggregator-api/message/stream)
+
+Note that you first need to start the message aggregators via the Swing
+client application in order to be able to receive any SSE events from
+the message streaming resource.
diff --git a/examples/sse-twitter-aggregator/client.cert b/examples/sse-twitter-aggregator/client.cert
new file mode 100644
index 0000000..d43d88b
--- /dev/null
+++ b/examples/sse-twitter-aggregator/client.cert
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIDJTCCAuOgAwIBAgIET5ZyYjALBgcqhkjOOAQDBQAwdjELMAkGA1UEBhMCQ1oxFzAVBgNVBAgT
+DkN6ZWNoIFJlcHVibGljMQ8wDQYDVQQHEwZQcmFndWUxGzAZBgNVBAoTEk9yYWNsZSBDb3Jwb3Jh
+dGlvbjEPMA0GA1UECxMGSmVyc2V5MQ8wDQYDVQQDEwZDbGllbnQwHhcNMTIwNDI0MDkyOTA2WhcN
+MTIwNzIzMDkyOTA2WjB2MQswCQYDVQQGEwJDWjEXMBUGA1UECBMOQ3plY2ggUmVwdWJsaWMxDzAN
+BgNVBAcTBlByYWd1ZTEbMBkGA1UEChMST3JhY2xlIENvcnBvcmF0aW9uMQ8wDQYDVQQLEwZKZXJz
+ZXkxDzANBgNVBAMTBkNsaWVudDCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu
+7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSf
+n+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgB
+xwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9
+B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6
+ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GEAAKBgBmHNACDk1aw
+vUZjsRecMSBlkkCSqr/cCrYOsNwpfleQKsM6rdOofujANUVeoUFhX8e8K45FknxEqAugmhGQ9NRn
+uMenrvV+XupC0V2uGH0OciXeAzHbfeItBCbmJcvMdPW/q+I2vFchv6+ajEiNHogBrCc3qwSMhyVQ
+ug2fXHmJMAsGByqGSM44BAMFAAMvADAsAhQYznYmH0hrcLni4EqX3Ovac+pNJgIUehnEaW1V5djn
+dhYBAYUkSycETl4=
+-----END CERTIFICATE-----
diff --git a/examples/sse-twitter-aggregator/keystore_client b/examples/sse-twitter-aggregator/keystore_client
new file mode 100644
index 0000000..d016fd2
--- /dev/null
+++ b/examples/sse-twitter-aggregator/keystore_client
Binary files differ
diff --git a/examples/sse-twitter-aggregator/pom.xml b/examples/sse-twitter-aggregator/pom.xml
new file mode 100644
index 0000000..8a65d36
--- /dev/null
+++ b/examples/sse-twitter-aggregator/pom.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>sse-twitter-aggregator</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-sse-twitter-aggregator</name>
+
+    <description>Jersey SSE Twitter Message Aggregator Example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <!-- uncomment to use Grizzly client -->
+        <!--dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+            <scope>test</scope>
+        </dependency-->
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-sse</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.swinglabs</groupId>
+            <artifactId>swing-layout</artifactId>
+            <version>1.0.3</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.aggregator.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/AbstractTestAggregator.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/AbstractTestAggregator.java
new file mode 100644
index 0000000..3c4f1e9
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/AbstractTestAggregator.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.aggregator;
+
+import java.util.Random;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+
+/**
+ * Fake message aggregator used for testing purposes.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public abstract class AbstractTestAggregator implements DataAggregator {
+    private static final Logger LOGGER = Logger.getLogger(AbstractTestAggregator.class.getName());
+    private static final String[] MESSAGES = new String[] {
+            "Where do your RESTful Web Services want to go today?",
+            "Jersey RESTful Web Services framework rocks!",
+            "Jersey and JAX-RS are cool!",
+            "What are the 5 insane but true things about JAX-RS?",
+            "Wow, JAX-RS 2.0 provides asynchronous service and client APIs!",
+            "Finally! JAX-RS 2.0 adds filters and interceptors support.",
+            "Jersey 2.0 programmatic resource API looks great!",
+            "How could I live without Jersey ResourceConfig class??",
+            "Just wrote my first JAX-RS service using Jersey.",
+            "Jersey is the best RESTful framework ever.",
+            "JAX-RS rules the web services.",
+            "Jersey 2.0 is the new American idol!"
+    };
+    private static final String IMG_URI
+            = "http://files.softicons.com/download/internet-cons/halloween-avatars-icons-by-deleket/png/48/Voodoo%20Doll.png";
+
+    private final String rgbColor;
+    private volatile boolean running;
+
+    AbstractTestAggregator(String rgbColor) {
+        this.rgbColor = rgbColor;
+    }
+
+    @Override
+    public void start(final String keywords, final DataListener msgListener) {
+        msgListener.onStart();
+        running = true;
+
+        final Random rnd = new Random();
+        final String aggregatorPrefix = getPrefix();
+
+        Executors.newSingleThreadExecutor().submit(() -> {
+            final Client resourceClient = ClientBuilder.newClient();
+            resourceClient.register(new MoxyJsonFeature());
+            final WebTarget messageStreamResource = resourceClient.target(App.getApiUri()).path(getPath());
+
+            try {
+                while (running) {
+                    final Message message = new Message(
+                            aggregatorPrefix + " " + MESSAGES[rnd.nextInt(MESSAGES.length)],
+                            rgbColor,
+                            IMG_URI);
+                    msgListener.onMessage(message);
+                    final Response r = messageStreamResource.request().put(Entity.json(message));
+                    if (r.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) {
+                        LOGGER.warning("Unexpected PUT message response status code: " + r.getStatus());
+                    }
+                    Thread.sleep(rnd.nextInt(1000) + 750);
+                }
+                msgListener.onComplete();
+            } catch (Throwable t) {
+                LOGGER.log(Level.WARNING, "Waiting for a message has been interrupted.", t);
+                msgListener.onError();
+            }
+        });
+    }
+
+
+    @Override
+    public void stop() {
+        running = false;
+    }
+
+    /**
+     * Get relative path to the event stream.
+     */
+    protected abstract String getPath();
+
+    /**
+     * Get message prefix to identify the concrete aggregator.
+     *
+     * @return message prefix (aggregator qualifier)
+     */
+    protected abstract String getPrefix();
+
+}
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/App.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/App.java
new file mode 100644
index 0000000..70f928a
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/App.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.aggregator;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer;
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.http.util.HttpStatus;
+
+/**
+ * Jersey Twitter Aggregator example application.
+ */
+public class App {
+
+    private static final Properties TWITTER_PROPERTIES = loadSettings();
+    private static final String TWITTER_USER_NAME = "twitter.user.name";
+    private static final String TWITTER_USER_PASSWORD = "twitter.user.password";
+    private static final String TWITTER_PROPERTIES_FILE_NAME = "twitter-api.properties";
+
+    private static final String APP_PATH = "/aggregator/";
+    private static final String API_PATH = "/aggregator-api/";
+    static final String WEB_ROOT = "/webroot";
+    private static final int PORT = 8080;
+
+    /**
+     * Starts Grizzly HTTP server exposing static content, JAX-RS resources
+     * and web sockets defined in this application.
+     *
+     * @param webRootPath static content root path.
+     */
+    private static void startServer(String webRootPath) {
+        final HttpServer server = new HttpServer();
+        Runtime.getRuntime().addShutdownHook(new Thread(server::shutdownNow));
+
+        final NetworkListener listener = new NetworkListener("grizzly", "localhost", PORT);
+
+        server.addListener(listener);
+
+        final ServerConfiguration config = server.getServerConfiguration();
+        // add handler for serving static content
+        config.addHttpHandler(new StaticContentHandler(webRootPath),
+                APP_PATH);
+
+        // add handler for serving JAX-RS resources
+        config.addHttpHandler(RuntimeDelegate.getInstance().createEndpoint(createResourceConfig(), GrizzlyHttpContainer.class),
+                API_PATH);
+
+        try {
+            // Start the server.
+            server.start();
+        } catch (Exception ex) {
+            throw new ProcessingException("Exception thrown when trying to start grizzly server", ex);
+        }
+    }
+
+    public static void main(String[] args) {
+        MainWindow.main(args);
+
+        try {
+            System.out.println("\"SSE Twitter Message Aggregator\" Jersey Example App");
+            startServer(args.length >= 1 ? args[0] : null);
+            System.out.println(String.format("Application started.\n"
+                            + "Access it at %s\n"
+                            + "Stop the application using CTRL+C",
+                    getAppUri()));
+
+            Thread.currentThread().join();
+        } catch (InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    static String getApiUri() {
+        return String.format("http://localhost:%s%s", PORT, API_PATH);
+    }
+
+    private static String getAppUri() {
+        return String.format("http://localhost:%s%s", PORT, APP_PATH);
+    }
+
+    /**
+     * Create Jersey server-side application resource configuration.
+     *
+     * @return Jersey server-side application configuration.
+     */
+    private static ResourceConfig createResourceConfig() {
+        return new ResourceConfig()
+                .registerClasses(MessageStreamResourceJersey.class,
+                        MessageStreamResourceJaxRs.class,
+                        SseFeature.class,
+                        MoxyJsonFeature.class);
+    }
+
+    /**
+     * Get configured twitter user name.
+     *
+     * @return configured twitter user name.
+     */
+    static String getTwitterUserName() {
+        return (String) TWITTER_PROPERTIES.get(TWITTER_USER_NAME);
+    }
+
+    /**
+     * Get configured twitter user password.
+     *
+     * @return configured twitter user password.
+     */
+    static String getTwitterUserPassword() {
+        return (String) TWITTER_PROPERTIES.get(TWITTER_USER_PASSWORD);
+    }
+
+    private static Properties loadSettings() {
+        final Properties properties = new Properties();
+
+        FileInputStream st = null;
+        try {
+            String homeDir = System.getProperty("user.home");
+            st = new FileInputStream(homeDir + File.separator + TWITTER_PROPERTIES_FILE_NAME);
+            properties.load(st);
+        } catch (IOException e) {
+            // ignore
+        } finally {
+            if (st != null) {
+                try {
+                    st.close();
+                } catch (IOException ex) {
+                    // ignore
+                }
+            }
+        }
+
+        for (String name : new String[] {TWITTER_USER_NAME, TWITTER_USER_PASSWORD}) {
+            String value = System.getProperty(name);
+            if (value != null) {
+                properties.setProperty(name, value);
+            }
+        }
+
+        if (properties.getProperty(TWITTER_USER_NAME) == null
+                || properties.getProperty(TWITTER_USER_PASSWORD) == null) {
+            System.out.println(String.format(
+                    "'%s' and '%s' properties not set. "
+                            + "You need to provide them either via '$HOME/%s' file or as system properties.",
+                    TWITTER_USER_NAME, TWITTER_USER_PASSWORD, TWITTER_PROPERTIES_FILE_NAME));
+            System.exit(1);
+        }
+        return properties;
+    }
+
+    /**
+     * Simple HttpHandler for serving static content included in web root
+     * directory of this application.
+     */
+    private static class StaticContentHandler extends HttpHandler {
+
+        private static final HashMap<String, String> EXTENSION_TO_MEDIA_TYPE;
+
+        static {
+            EXTENSION_TO_MEDIA_TYPE = new HashMap<>();
+
+            EXTENSION_TO_MEDIA_TYPE.put("html", "text/html");
+            EXTENSION_TO_MEDIA_TYPE.put("js", "application/javascript");
+            EXTENSION_TO_MEDIA_TYPE.put("css", "text/css");
+            EXTENSION_TO_MEDIA_TYPE.put("png", "image/png");
+            EXTENSION_TO_MEDIA_TYPE.put("ico", "image/png");
+        }
+
+        private final String webRootPath;
+
+        StaticContentHandler(String webRootPath) {
+            this.webRootPath = webRootPath;
+        }
+
+        @Override
+        public void service(Request request, Response response) throws Exception {
+            String uri = request.getRequestURI();
+
+            int pos = uri.lastIndexOf('.');
+            String extension = uri.substring(pos + 1);
+            String mediaType = EXTENSION_TO_MEDIA_TYPE.get(extension);
+
+            if (uri.contains("..") || mediaType == null) {
+                response.sendError(HttpStatus.NOT_FOUND_404.getStatusCode());
+                return;
+            }
+
+            final String resourcesContextPath = request.getContextPath();
+            System.out.println("context: " + resourcesContextPath);
+            if (resourcesContextPath != null && !resourcesContextPath.isEmpty()) {
+                if (!uri.startsWith(resourcesContextPath)) {
+                    response.sendError(HttpStatus.NOT_FOUND_404.getStatusCode());
+                    return;
+                }
+
+                uri = uri.substring(resourcesContextPath.length());
+                System.out.println("URI: " + uri);
+            }
+
+            InputStream fileStream;
+
+            try {
+                fileStream = webRootPath == null
+                        ? App.class.getResourceAsStream(WEB_ROOT + uri)
+                        : new FileInputStream(webRootPath + uri);
+            } catch (IOException e) {
+                fileStream = null;
+            }
+
+            if (fileStream == null) {
+                response.sendError(HttpStatus.NOT_FOUND_404.getStatusCode());
+            } else {
+                response.setStatus(HttpStatus.OK_200);
+                response.setContentType(mediaType);
+                ReaderWriter.writeTo(fileStream, response.getOutputStream());
+            }
+        }
+    }
+}
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/DataAggregator.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/DataAggregator.java
new file mode 100644
index 0000000..6c677ae
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/DataAggregator.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.aggregator;
+
+/**
+ * Data aggregator for listening for events aggregated based on give keywords.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public interface DataAggregator {
+    void start(String keywords, DataListener msgListener);
+
+    void stop();
+}
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/DataListener.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/DataListener.java
new file mode 100644
index 0000000..5b513d7
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/DataListener.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.aggregator;
+
+/**
+ * Incoming data listener.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public interface DataListener {
+    /**
+     * Invoked when the connection to the data stream has been established.
+     */
+    public void onStart();
+
+    /**
+     * Invoked when the data stream has dried out (or the connection has been closed).
+     */
+    public void onComplete();
+
+    /**
+     * Invoked when there was an error while receiving streamed data.
+     */
+    public void onError();
+
+    /**
+     * Invoked when a new message data are available.
+     *
+     * @param message new message data.
+     */
+    public void onMessage(Message message);
+}
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/MainWindow.form b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/MainWindow.form
new file mode 100644
index 0000000..73beb7e
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/MainWindow.form
@@ -0,0 +1,342 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
+  <Properties>
+    <Property name="defaultCloseOperation" type="int" value="3"/>
+    <Property name="title" type="java.lang.String" value="Message aggregator"/>
+    <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+      <Dimension value="[304, 300]"/>
+    </Property>
+  </Properties>
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="2"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="jPanel1" max="32767" attributes="0"/>
+                  <Component id="jPanel4" alignment="0" max="32767" attributes="0"/>
+                  <Component id="jPanel3" alignment="0" max="32767" attributes="0"/>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jPanel2" max="32767" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="jPanel2" max="32767" attributes="0"/>
+                  <Group type="102" attributes="0">
+                      <Component id="jPanel4" min="-2" max="-2" attributes="0"/>
+                      <EmptySpace max="-2" attributes="0"/>
+                      <Component id="jPanel3" min="-2" max="-2" attributes="0"/>
+                      <EmptySpace max="-2" attributes="0"/>
+                      <Component id="jPanel1" max="32767" attributes="0"/>
+                  </Group>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="jPanel3">
+      <Properties>
+        <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+          <Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
+            <EtchetBorder/>
+          </Border>
+        </Property>
+      </Properties>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
+        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
+      </AuxValues>
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="newKeywordField" max="32767" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="addButton" min="-2" pref="72" max="-2" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="newKeywordField" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="addButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="32767" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JTextField" name="newKeywordField">
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="onKeywordEntered"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JButton" name="addButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Add"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="onAdd"/>
+          </Events>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Container class="javax.swing.JPanel" name="jPanel1">
+      <Properties>
+        <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+          <Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
+            <EtchetBorder/>
+          </Border>
+        </Property>
+      </Properties>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
+        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
+      </AuxValues>
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Group type="102" attributes="0">
+                          <Component id="jScrollPane2" pref="247" max="32767" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                      </Group>
+                      <Group type="102" attributes="0">
+                          <Component id="connectionStatusLabel" max="32767" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="startStopButton" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                  </Group>
+              </Group>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="startStopButton" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="connectionStatusLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="jScrollPane2" max="32767" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JLabel" name="connectionStatusLabel">
+        </Component>
+        <Component class="javax.swing.JButton" name="startStopButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Start"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="onStartOrStop"/>
+          </Events>
+        </Component>
+        <Container class="javax.swing.JScrollPane" name="jScrollPane2">
+          <AuxValues>
+            <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
+            <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
+            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+          </AuxValues>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JTextArea" name="messagesArea">
+              <Properties>
+                <Property name="columns" type="int" value="20"/>
+                <Property name="lineWrap" type="boolean" value="true"/>
+                <Property name="rows" type="int" value="5"/>
+                <Property name="wrapStyleWord" type="boolean" value="true"/>
+                <Property name="focusable" type="boolean" value="false"/>
+              </Properties>
+            </Component>
+          </SubComponents>
+        </Container>
+      </SubComponents>
+    </Container>
+    <Container class="javax.swing.JPanel" name="jPanel2">
+      <Properties>
+        <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+          <Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
+            <EtchetBorder/>
+          </Border>
+        </Property>
+      </Properties>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
+        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
+      </AuxValues>
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="jScrollPane1" pref="97" max="32767" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="jScrollPane1" pref="332" max="32767" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+          <AuxValues>
+            <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
+            <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
+            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+          </AuxValues>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JList" name="keywordList">
+              <Properties>
+                <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+                  <Connection code="keywordListModel" type="code"/>
+                </Property>
+              </Properties>
+            </Component>
+          </SubComponents>
+        </Container>
+      </SubComponents>
+    </Container>
+    <Container class="javax.swing.JPanel" name="jPanel4">
+      <Properties>
+        <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+          <Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
+            <EtchetBorder/>
+          </Border>
+        </Property>
+      </Properties>
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Group type="102" alignment="1" attributes="0">
+                          <Component id="twitterColorLabel" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="twitterCheckbox" max="32767" attributes="0"/>
+                      </Group>
+                      <Group type="102" alignment="1" attributes="0">
+                          <Component id="testColorLabel" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="testCheckbox" max="32767" attributes="0"/>
+                      </Group>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="twitterCheckbox" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="twitterColorLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="testCheckbox" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="testColorLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="32767" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JCheckBox" name="twitterCheckbox">
+          <Properties>
+            <Property name="selected" type="boolean" value="true"/>
+            <Property name="text" type="java.lang.String" value="Twitter aggregator"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JLabel" name="twitterColorLabel">
+          <Properties>
+            <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+              <Color blue="0" green="aa" red="55" type="rgb"/>
+            </Property>
+            <Property name="text" type="java.lang.String" value="    "/>
+            <Property name="opaque" type="boolean" value="true"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JLabel" name="testColorLabel">
+          <Properties>
+            <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+              <Color blue="aa" green="55" red="0" type="rgb"/>
+            </Property>
+            <Property name="text" type="java.lang.String" value="    "/>
+            <Property name="opaque" type="boolean" value="true"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JCheckBox" name="testCheckbox">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Test Aggregator"/>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/MainWindow.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/MainWindow.java
new file mode 100644
index 0000000..608c288
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/MainWindow.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.aggregator;
+
+import java.awt.Color;
+import java.awt.EventQueue;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.swing.ActionMap;
+import javax.swing.DefaultListModel;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.KeyStroke;
+import javax.swing.WindowConstants;
+
+/**
+ * Main data aggregator client application UI window.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class MainWindow extends javax.swing.JFrame {
+
+    private final DefaultListModel keywordListModel;
+    private final AtomicBoolean receiveMessages;
+    private final List<DataAggregator> dataAggregators;
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton addButton;
+    private javax.swing.JLabel connectionStatusLabel;
+    private javax.swing.JPanel jPanel4;
+    private javax.swing.JList keywordList;
+    private javax.swing.JTextArea messagesArea;
+    private javax.swing.JTextField newKeywordField;
+    private javax.swing.JButton startStopButton;
+    private javax.swing.JCheckBox testCheckbox;
+    private javax.swing.JLabel testColorLabel;
+    private javax.swing.JCheckBox twitterCheckbox;
+    private javax.swing.JLabel twitterColorLabel;
+    // End of variables declaration//GEN-END:variables
+
+    /**
+     * Creates new form MessageAggregator
+     */
+    public MainWindow() {
+        keywordListModel = new DefaultListModel();
+        receiveMessages = new AtomicBoolean(false);
+
+        initComponents();
+
+        // Bind "remove selected list items" action to DELETE key pressed in keyword list.
+        ActionMap actionMap = keywordList.getActionMap();
+        InputMap inputMap = keywordList.getInputMap(JComponent.WHEN_FOCUSED);
+
+        final String actionKey = "RemoveSelectedListItems";
+        actionMap.put(actionKey, new RemoveSelectedListItemsAction(keywordList, keywordListModel));
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), actionKey);
+
+        dataAggregators = new ArrayList<DataAggregator>(2);
+    }
+
+    private void initAggregators() {
+        if (twitterCheckbox.isSelected()) {
+            dataAggregators.add(new TwitterAggregator(colorToString(twitterColorLabel.getBackground())));
+        }
+        if (testCheckbox.isSelected()) {
+            dataAggregators.add(new TestAggregatorJersey(colorToString(testColorLabel.getBackground())));
+            dataAggregators.add(new TestAggregatorJaxRs(colorToString(testColorLabel.getBackground())));
+        }
+    }
+
+    private String colorToString(Color color) {
+        StringBuilder colorBuilder = new StringBuilder();
+
+        colorBuilder
+                .append(String.format("%02X", color.getRed()))
+                .append(String.format("%02X", color.getGreen()))
+                .append(String.format("%02X", color.getBlue()));
+
+        return colorBuilder.toString();
+    }
+
+    private void onKeywordEntered(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onKeywordEntered
+        final String keyword = newKeywordField.getText();
+        if (keyword == null || keyword.isEmpty() || keywordListModel.contains(keyword)) {
+            return;
+        }
+        addButton.doClick();
+    }//GEN-LAST:event_onKeywordEntered
+
+    private void onAdd(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onAdd
+        final String keyword = newKeywordField.getText();
+        if (keyword == null || keyword.isEmpty() || keywordListModel.contains(keyword)) {
+            return;
+        }
+
+        keywordListModel.addElement(keyword);
+        newKeywordField.setText("");
+    }//GEN-LAST:event_onAdd
+
+    private void onStartOrStop(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_onStartOrStop
+        if (receiveMessages.get()) {
+            // stop
+            receiveMessages.set(false);
+            startStopButton.setText("Start");
+            newKeywordField.setEnabled(true);
+            addButton.setEnabled(true);
+            stop();
+        } else {
+            // start
+            receiveMessages.set(true);
+            startStopButton.setText("Stop");
+            connectionStatusLabel.setText("Connecting...");
+            newKeywordField.setEnabled(false);
+            addButton.setEnabled(false);
+            messagesArea.setText("");
+
+            StringBuilder keywordsBuilder = new StringBuilder();
+            final Enumeration<?> elements = keywordListModel.elements();
+            while (elements.hasMoreElements()) {
+                keywordsBuilder.append(elements.nextElement());
+                if (elements.hasMoreElements()) {
+                    keywordsBuilder.append(",");
+                }
+            }
+
+            start(keywordsBuilder.toString(), new DataListener() {
+                @Override
+                public void onStart() {
+                    EventQueue.invokeLater(() -> connectionStatusLabel.setText("Connected."));
+                }
+
+                @Override
+                public void onMessage(final Message message) {
+                    EventQueue.invokeLater(() -> messagesArea.append(message.getText() + "\n\n"));
+                }
+
+                @Override
+                public void onError() {
+                    EventQueue.invokeLater(() -> {
+                        connectionStatusLabel.setText("Connection Error!");
+                        receiveMessages.set(false);
+                        startStopButton.setText("Start");
+                        newKeywordField.setEnabled(true);
+                        addButton.setEnabled(true);
+                    });
+                }
+
+                @Override
+                public void onComplete() {
+                    EventQueue.invokeLater(() -> connectionStatusLabel.setText("Disconnected."));
+                }
+            });
+        }
+
+    }//GEN-LAST:event_onStartOrStop
+
+    private void stop() {
+        for (DataAggregator dataAggregator : dataAggregators) {
+            dataAggregator.stop();
+        }
+        dataAggregators.clear();
+    }
+
+    private void start(String keywords, DataListener listener) {
+        initAggregators();
+
+        for (DataAggregator dataAggregator : dataAggregators) {
+            dataAggregator.start(keywords, listener);
+        }
+    }
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String args[]) {
+        /* Create and display the form */
+        EventQueue.invokeLater(() -> {
+            final MainWindow messageAggregator = new MainWindow();
+            messageAggregator.setLocationRelativeTo(null);
+            messageAggregator.setVisible(true);
+        });
+    }
+
+    /**
+     * This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        javax.swing.JPanel jPanel3 = new javax.swing.JPanel();
+        newKeywordField = new javax.swing.JTextField();
+        addButton = new javax.swing.JButton();
+        javax.swing.JPanel jPanel1 = new javax.swing.JPanel();
+        connectionStatusLabel = new javax.swing.JLabel();
+        startStopButton = new javax.swing.JButton();
+        javax.swing.JScrollPane jScrollPane2 = new javax.swing.JScrollPane();
+        messagesArea = new javax.swing.JTextArea();
+        javax.swing.JPanel jPanel2 = new javax.swing.JPanel();
+        javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
+        keywordList = new javax.swing.JList();
+        jPanel4 = new javax.swing.JPanel();
+        twitterCheckbox = new javax.swing.JCheckBox();
+        twitterColorLabel = new javax.swing.JLabel();
+        testColorLabel = new javax.swing.JLabel();
+        testCheckbox = new javax.swing.JCheckBox();
+
+        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+        setTitle("Message aggregator");
+        setMinimumSize(new java.awt.Dimension(304, 300));
+
+        jPanel3.setBorder(javax.swing.BorderFactory.createEtchedBorder());
+
+        newKeywordField.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                onKeywordEntered(evt);
+            }
+        });
+
+        addButton.setText("Add");
+        addButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                onAdd(evt);
+            }
+        });
+
+        org.jdesktop.layout.GroupLayout jPanel3Layout = new org.jdesktop.layout.GroupLayout(jPanel3);
+        jPanel3.setLayout(jPanel3Layout);
+        jPanel3Layout.setHorizontalGroup(
+                jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(jPanel3Layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(newKeywordField)
+                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                .add(addButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 72,
+                                        org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
+                                .addContainerGap())
+        );
+        jPanel3Layout.setVerticalGroup(
+                jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(jPanel3Layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
+                                        .add(newKeywordField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
+                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
+                                        .add(addButton))
+                                .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+        );
+
+        jPanel1.setBorder(javax.swing.BorderFactory.createEtchedBorder());
+
+        startStopButton.setText("Start");
+        startStopButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                onStartOrStop(evt);
+            }
+        });
+
+        messagesArea.setColumns(20);
+        messagesArea.setLineWrap(true);
+        messagesArea.setRows(5);
+        messagesArea.setWrapStyleWord(true);
+        messagesArea.setFocusable(false);
+        jScrollPane2.setViewportView(messagesArea);
+
+        org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1);
+        jPanel1.setLayout(jPanel1Layout);
+        jPanel1Layout.setHorizontalGroup(
+                jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(jPanel1Layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                                        .add(jPanel1Layout.createSequentialGroup()
+                                                .add(jScrollPane2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 247,
+                                                        Short.MAX_VALUE)
+                                                .addContainerGap())
+                                        .add(jPanel1Layout.createSequentialGroup()
+                                                .add(connectionStatusLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                                .add(startStopButton))))
+        );
+        jPanel1Layout.setVerticalGroup(
+                jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(jPanel1Layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
+                                        .add(startStopButton)
+                                        .add(connectionStatusLabel))
+                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                .add(jScrollPane2)
+                                .addContainerGap())
+        );
+
+        jPanel2.setBorder(javax.swing.BorderFactory.createEtchedBorder());
+
+        keywordList.setModel(keywordListModel);
+        jScrollPane1.setViewportView(keywordList);
+
+        org.jdesktop.layout.GroupLayout jPanel2Layout = new org.jdesktop.layout.GroupLayout(jPanel2);
+        jPanel2.setLayout(jPanel2Layout);
+        jPanel2Layout.setHorizontalGroup(
+                jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(jPanel2Layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 97, Short.MAX_VALUE)
+                                .addContainerGap())
+        );
+        jPanel2Layout.setVerticalGroup(
+                jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(jPanel2Layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 332, Short.MAX_VALUE)
+                                .addContainerGap())
+        );
+
+        jPanel4.setBorder(javax.swing.BorderFactory.createEtchedBorder());
+
+        twitterCheckbox.setSelected(true);
+        twitterCheckbox.setText("Twitter aggregator");
+
+        twitterColorLabel.setBackground(new java.awt.Color(85, 170, 0));
+        twitterColorLabel.setText("    ");
+        twitterColorLabel.setOpaque(true);
+
+        testColorLabel.setBackground(new java.awt.Color(0, 85, 170));
+        testColorLabel.setText("    ");
+        testColorLabel.setOpaque(true);
+
+        testCheckbox.setText("Test Aggregator");
+
+        org.jdesktop.layout.GroupLayout jPanel4Layout = new org.jdesktop.layout.GroupLayout(jPanel4);
+        jPanel4.setLayout(jPanel4Layout);
+        jPanel4Layout.setHorizontalGroup(
+                jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(jPanel4Layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                                        .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel4Layout.createSequentialGroup()
+                                                .add(twitterColorLabel)
+                                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                                .add(twitterCheckbox, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                                        .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel4Layout.createSequentialGroup()
+                                                .add(testColorLabel)
+                                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                                .add(testCheckbox, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+                                .addContainerGap())
+        );
+        jPanel4Layout.setVerticalGroup(
+                jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(jPanel4Layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
+                                        .add(twitterCheckbox)
+                                        .add(twitterColorLabel))
+                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                .add(jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
+                                        .add(testCheckbox)
+                                        .add(testColorLabel))
+                                .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+        );
+
+        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
+        getContentPane().setLayout(layout);
+        layout.setHorizontalGroup(
+                layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                                        .add(jPanel1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                        .add(jPanel4, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                        .add(jPanel3, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                .add(jPanel2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addContainerGap())
+        );
+        layout.setVerticalGroup(
+                layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                        .add(layout.createSequentialGroup()
+                                .addContainerGap()
+                                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
+                                        .add(jPanel2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                        .add(layout.createSequentialGroup()
+                                                .add(jPanel4, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
+                                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                        org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
+                                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                                .add(jPanel3, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
+                                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                        org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
+                                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
+                                                .add(jPanel1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
+                                                        org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+                                .addContainerGap())
+        );
+
+        pack();
+    }// </editor-fold>//GEN-END:initComponents
+}
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/Message.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/Message.java
new file mode 100644
index 0000000..46a93a8
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/Message.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.aggregator;
+
+import javax.xml.bind.annotation.XmlElement;
+
+import org.eclipse.persistence.oxm.annotations.XmlPath;
+
+/**
+ * Message bean.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class Message {
+
+    @XmlElement
+    private String text;
+
+    @XmlPath("user/profile_image_url/text()")
+    private String profileImg;
+
+    @XmlElement(nillable = true)
+    private String rgbColor;
+
+    public Message() {
+    }
+
+    public Message(final String text, final String rgbColor, final String profileImg) {
+        this.text = text;
+        this.rgbColor = rgbColor;
+        this.profileImg = profileImg;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public String getProfileImg() {
+        return profileImg;
+    }
+
+    public String getRgbColor() {
+        return rgbColor;
+    }
+
+    public void setRgbColor(String rgbColor) {
+        this.rgbColor = rgbColor;
+    }
+
+    @Override
+    public String toString() {
+        return "Message{"
+                + "text='" + text + '\''
+                + ", profileImg='" + profileImg + '\''
+                + ", rgpColor='" + rgbColor + '\''
+                + '}';
+    }
+}
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/MessageStreamResourceJaxRs.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/MessageStreamResourceJaxRs.java
new file mode 100644
index 0000000..94a2466
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/MessageStreamResourceJaxRs.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.aggregator;
+
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.sse.OutboundSseEvent;
+import javax.ws.rs.sse.Sse;
+import javax.ws.rs.sse.SseBroadcaster;
+import javax.ws.rs.sse.SseEventSink;
+
+import javax.inject.Singleton;
+
+/**
+ * Resource that aggregates incoming messages and broadcasts them
+ * to the registered Server-Sent Even (SSE) client streams.
+ * <p>
+ * Uses the JAX-RS 2.1 SSE API.
+ *
+ * @see MessageStreamResourceJersey
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("message/stream/jaxrs")
+@Singleton
+public final class MessageStreamResourceJaxRs {
+    private static final Logger LOGGER = Logger.getLogger(MessageStreamResourceJaxRs.class.getName());
+    private static AtomicLong nextMessageId = new AtomicLong(0);
+
+    private final Sse sse;
+    private final SseBroadcaster broadcaster;
+
+    public MessageStreamResourceJaxRs(@Context Sse sse) {
+        this.sse = sse;
+        this.broadcaster = sse.newBroadcaster();
+    }
+
+    /**
+     * Put a new message to the stream.
+     *
+     * The message will be broadcast to all registered SSE clients.
+     *
+     * @param message message to be broadcast.
+     */
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void putMessage(final Message message) {
+        LOGGER.info("--> Message received.");
+
+        final OutboundSseEvent event = sse.newEventBuilder()
+                .id(String.valueOf(nextMessageId.getAndIncrement()))
+                .mediaType(MediaType.APPLICATION_JSON_TYPE)
+                .data(Message.class, message)
+                .build();
+
+        broadcaster.broadcast(event);
+    }
+
+    /**
+     * Get the new SSE message stream channel.
+     */
+    @GET
+    @Produces(MediaType.SERVER_SENT_EVENTS)
+    public void getMessageStream(@Context SseEventSink eventSink) {
+        LOGGER.info("--> SSE connection received.");
+        broadcaster.register(eventSink);
+    }
+
+}
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/MessageStreamResourceJersey.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/MessageStreamResourceJersey.java
new file mode 100644
index 0000000..391776a
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/MessageStreamResourceJersey.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.aggregator;
+
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.media.sse.EventOutput;
+import org.glassfish.jersey.media.sse.OutboundEvent;
+import org.glassfish.jersey.media.sse.SseBroadcaster;
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.server.ChunkedOutput;
+
+/**
+ * Resource that aggregates incoming messages and broadcasts them
+ * to the registered Server-Sent Even (SSE) client streams.
+ * <p>
+ * Uses the Jersey-specific SSE API.
+ *
+ * @see MessageStreamResourceJaxRs
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("message/stream/jersey")
+public final class MessageStreamResourceJersey {
+    private static final Logger LOGGER = Logger.getLogger(MessageStreamResourceJersey.class.getName());
+
+    private static SseBroadcaster broadcaster = new SseBroadcaster() {
+        @Override
+        public void onException(final ChunkedOutput<OutboundEvent> chunkedOutput, final Exception exception) {
+            LOGGER.log(Level.SEVERE, "Error broadcasting message.", exception);
+        }
+    };
+    private static AtomicLong nextMessageId = new AtomicLong(0);
+
+    /**
+     * Put a new message to the stream.
+     *
+     * The message will be broadcast to all registered SSE clients.
+     *
+     * @param message message to be broadcast.
+     */
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void putMessage(final Message message) {
+        LOGGER.info("--> Message received.");
+
+        final OutboundEvent event = new OutboundEvent.Builder()
+                .id(String.valueOf(nextMessageId.getAndIncrement()))
+                .mediaType(MediaType.APPLICATION_JSON_TYPE)
+                .data(Message.class, message)
+                .build();
+
+        broadcaster.broadcast(event);
+    }
+
+    /**
+     * Get the new SSE message stream channel.
+     *
+     * @return new SSE message stream channel.
+     */
+    @GET
+    @Produces(SseFeature.SERVER_SENT_EVENTS)
+    public EventOutput getMessageStream() {
+        LOGGER.info("--> SSE connection received.");
+        final EventOutput eventOutput = new EventOutput();
+        broadcaster.add(eventOutput);
+        return eventOutput;
+    }
+
+}
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/RemoveSelectedListItemsAction.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/RemoveSelectedListItemsAction.java
new file mode 100644
index 0000000..34eb7d7
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/RemoveSelectedListItemsAction.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.aggregator;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.DefaultListModel;
+import javax.swing.JList;
+import javax.swing.KeyStroke;
+
+/**
+ * TODO: javadoc.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public final class RemoveSelectedListItemsAction extends AbstractAction {
+
+    private final JList list;
+    private final DefaultListModel listModel;
+
+    public RemoveSelectedListItemsAction(JList list, DefaultListModel model) {
+        if (list == null || model == null) {
+            throw new NullPointerException("Bound JList component and it's model must not be null.");
+        }
+
+        this.list = list;
+        this.listModel = model;
+
+        putValue(NAME, "Delete");
+        putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        removeSelectedListItems();
+    }
+
+    private void removeSelectedListItems() {
+        Object[] selectedValues = list.getSelectedValues();
+        boolean itemsSelected = selectedValues.length > 0;
+
+        if (itemsSelected && confirmRemove()) {
+            for (Object selectedValue : selectedValues) {
+                listModel.removeElement(selectedValue);
+            }
+        }
+    }
+
+    private boolean confirmRemove() {
+        // E.g. JOptionPane-Confirm-Dialog
+        return true;
+    }
+}
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/TestAggregatorJaxRs.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/TestAggregatorJaxRs.java
new file mode 100644
index 0000000..bc151f1
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/TestAggregatorJaxRs.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.aggregator;
+
+/**
+ * Fake message aggregator used for testing purposes pointing to SSE event stream implemented using Jersey-specific API.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class TestAggregatorJaxRs extends AbstractTestAggregator {
+
+    public TestAggregatorJaxRs(String rgbColor) {
+        super(rgbColor);
+    }
+
+    @Override
+    public String getPath() {
+        return "message/stream/jaxrs";
+    }
+
+    @Override
+    protected String getPrefix() {
+        return "JAX-RS aggregator: ";
+    }
+}
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/TestAggregatorJersey.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/TestAggregatorJersey.java
new file mode 100644
index 0000000..c5ca5dc
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/TestAggregatorJersey.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.aggregator;
+
+/**
+ * Fake message aggregator used for testing purposes pointing to SSE event stream implemented using Jersey-specific API.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class TestAggregatorJersey extends AbstractTestAggregator {
+
+    public TestAggregatorJersey(String rgbColor) {
+        super(rgbColor);
+    }
+
+    @Override
+    public String getPath() {
+        return "message/stream/jersey";
+    }
+
+    @Override
+    protected String getPrefix() {
+        return "Jersey aggregator: ";
+    }
+
+}
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/TwitterAggregator.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/TwitterAggregator.java
new file mode 100644
index 0000000..a4a0aa2
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/TwitterAggregator.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.aggregator;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.SslConfigurator;
+import org.glassfish.jersey.client.ChunkedInput;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.glassfish.jersey.message.GZipEncoder;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+
+/**
+ * Twitter message-based data aggregator implementation.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public final class TwitterAggregator implements DataAggregator {
+    private static final Logger LOGGER = Logger.getLogger(TwitterAggregator.class.getName());
+
+    private volatile boolean cancelled;
+    private final String rgbColor;
+
+    /**
+     * Create new twitter message aggregator with a specific message color.
+     *
+     * @param rgbColor message color.
+     */
+    public TwitterAggregator(String rgbColor) {
+        this.rgbColor = rgbColor;
+    }
+
+    @Override
+    public void start(final String keywords, final DataListener msgListener) {
+        cancelled = false;
+
+//        System.setProperty("http.proxyHost", "www-proxy.us.oracle.com");
+//        System.setProperty("http.proxyPort", "80");
+//        System.setProperty("https.proxyHost", "www-proxy.us.oracle.com");
+//        System.setProperty("https.proxyPort", "80");
+
+
+        final LinkedBlockingQueue<Message> messages = new LinkedBlockingQueue<Message>();
+
+        final Future<?> readerHandle = Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                SslConfigurator sslConfig = SslConfigurator.newInstance()
+                        .trustStoreFile("./truststore_client")
+                        .trustStorePassword("asdfgh")
+
+                        .keyStoreFile("./keystore_client")
+                        .keyPassword("asdfgh");
+
+                final Client client = ClientBuilder.newBuilder().sslContext(sslConfig.createSSLContext()).build();
+                client.property(ClientProperties.CONNECT_TIMEOUT, 2000)
+                        .register(new MoxyJsonFeature())
+                        .register(HttpAuthenticationFeature.basic(App.getTwitterUserName(), App.getTwitterUserPassword()))
+                        .register(GZipEncoder.class);
+
+                final Response response = client.target("https://stream.twitter.com/1.1/statuses/filter.json")
+                        .queryParam("track", keywords)
+//                .queryParam("locations", "-122.75,36.8,-121.75,37.8") // San Francisco
+                        .request(MediaType.APPLICATION_JSON_TYPE)
+                        .header(HttpHeaders.HOST, "stream.twitter.com")
+                        .header(HttpHeaders.USER_AGENT, "Jersey/2.0")
+                        .header(HttpHeaders.ACCEPT_ENCODING, "gzip")
+                        .get();
+
+                if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) {
+                    LOGGER.log(Level.WARNING, "Error connecting to Twitter Streaming API: " + response.getStatus());
+                    msgListener.onError();
+                    return;
+                }
+                msgListener.onStart();
+
+                try {
+                    final ChunkedInput<Message> chunks = response.readEntity(new GenericType<ChunkedInput<Message>>() {
+                    });
+                    try {
+                        while (!Thread.interrupted()) {
+                            Message message = chunks.read();
+                            if (message == null) {
+                                break;
+                            }
+                            try {
+                                message.setRgbColor(rgbColor);
+                                System.out.println(message.toString());
+                                messages.put(message);
+                            } catch (InterruptedException e) {
+                                break;
+                            }
+                        }
+                    } finally {
+                        if (chunks != null) {
+                            chunks.close();
+                        }
+                    }
+                } catch (Throwable t) {
+                    LOGGER.log(Level.WARNING, "Reading from the Twitter stream has failed", t);
+                    messages.offer(null);
+                    msgListener.onError();
+                }
+            }
+        });
+
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                final Client resourceClient = ClientBuilder.newClient();
+                resourceClient.register(new MoxyJsonFeature());
+                final WebTarget messageStreamResource = resourceClient.target(App.getApiUri()).path("message/stream");
+
+                Message message = null;
+                try {
+                    while (!cancelled && (message = messages.take()) != null) {
+                        msgListener.onMessage(message);
+
+                        final Response r = messageStreamResource.request().put(Entity.json(message));
+                        if (r.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) {
+                            LOGGER.warning("Unexpected PUT message response status code: " + r.getStatus());
+                        }
+                    }
+
+                    if (message == null) {
+                        LOGGER.info("Timed out while waiting for a message.");
+                    }
+                } catch (InterruptedException ex) {
+                    LOGGER.log(Level.WARNING, "Waiting for a message has been interrupted.", ex);
+                } finally {
+                    readerHandle.cancel(true);
+                    msgListener.onComplete();
+                }
+            }
+        });
+    }
+
+    @Override
+    public void stop() {
+        cancelled = true;
+    }
+}
diff --git a/examples/sse-twitter-aggregator/src/main/resources/webroot/css/main.css b/examples/sse-twitter-aggregator/src/main/resources/webroot/css/main.css
new file mode 100644
index 0000000..4813510
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/resources/webroot/css/main.css
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+div.message {
+    border: thick solid;
+    border-radius: 10px;
+    margin: 10px;
+    width: 300px;
+    padding: 10px;
+    box-shadow: 5px 5px 5px #888888;
+    font-family: Helvetica, serif;
+    color: #444444;
+}
diff --git a/examples/sse-twitter-aggregator/src/main/resources/webroot/index.html b/examples/sse-twitter-aggregator/src/main/resources/webroot/index.html
new file mode 100644
index 0000000..5ebfceb
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/resources/webroot/index.html
@@ -0,0 +1,22 @@
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <title>Jersey Message Streaming Example</title>
+    <link rel="stylesheet" href="css/main.css" />
+    <script src="js/sse-client.js" type="text/javascript"></script>
+</head>
+<body>
+</body>
+</html>
diff --git a/examples/sse-twitter-aggregator/src/main/resources/webroot/js/sse-client.js b/examples/sse-twitter-aggregator/src/main/resources/webroot/js/sse-client.js
new file mode 100644
index 0000000..b8af823
--- /dev/null
+++ b/examples/sse-twitter-aggregator/src/main/resources/webroot/js/sse-client.js
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+"use strict";
+
+function receiveMessages() {
+    if (typeof(EventSource) !== "undefined") {
+        // Yes! Server-sent events support!
+        var sourceJersey = new EventSource('/aggregator-api/message/stream/jersey');
+        var sourceJaxRs = new EventSource('/aggregator-api/message/stream/jaxrs');
+        var eventHandler =function (event) {
+            var data = JSON.parse(event.data);
+            console.log(data);
+
+            var newEntry =
+                '<div class="message">'
+                    + '<img src="' + data.user.profile_image_url + '" />'
+                    + '<span style="color: #' + data.rgbColor + '">'
+                    + data.text + '</span></div>';
+
+            document.body.innerHTML = newEntry + document.body.innerHTML;
+        };
+        sourceJersey.onmessage = eventHandler;
+        sourceJaxRs.onmessage = eventHandler;
+
+        sourceJersey.onopen = function (event) {
+            // Connection was opened.
+            console.log('Jresey stream opened.')
+        };
+
+        sourceJaxRs.onopen = function (event) {
+            console.log('JAX-RS stream opened.')
+        }
+
+        sourceJersey.onclose = function (event) {
+            // Connection was closed.
+            console.log('Jersey connection closed')
+        };
+
+        sourceJaxRs.onclose = function (event) {
+            // Connection was closed.
+            console.log('JAX-RS connection closed')
+        };
+    } else {
+        // Sorry! No server-sent events support..
+        console.log('SSE not supported by browser.')
+    }
+}
+
+window.onload = receiveMessages ;
diff --git a/examples/sse-twitter-aggregator/truststore_client b/examples/sse-twitter-aggregator/truststore_client
new file mode 100644
index 0000000..74784fb
--- /dev/null
+++ b/examples/sse-twitter-aggregator/truststore_client
Binary files differ
diff --git a/examples/sse-twitter-aggregator/twitter-api.properties b/examples/sse-twitter-aggregator/twitter-api.properties
new file mode 100644
index 0000000..1d419bc
--- /dev/null
+++ b/examples/sse-twitter-aggregator/twitter-api.properties
@@ -0,0 +1,12 @@
+#
+# Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Distribution License v. 1.0, which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+twitter.user.name=<enter_your_twitter_account_name_here>
+twitter.user.password=<enter_your_twitter_account_password_here>
diff --git a/examples/system-properties-example/README.MD b/examples/system-properties-example/README.MD
new file mode 100644
index 0000000..a196bdf
--- /dev/null
+++ b/examples/system-properties-example/README.MD
@@ -0,0 +1,36 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Helloworld webapp Example
+=========================
+
+This example demonstrates how to manage system properties using REST API. System Properties Example
+is able to expose all registered system properties, find out its values and even change a given
+property value.
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path                        | Resource class          | HTTP methods
+------------------------------- | ----------------------- | --------------
+**_/properties_**               | PropertyNamesResource   | GET
+**_/properties/{prop_name}_**   | PropertyResource        | GET
+**_/properties/{prop_name}_**   | PropertyResource        | PUT
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container. You can access the application at:
+
+-   <http://localhost:8080/properties>
\ No newline at end of file
diff --git a/examples/system-properties-example/pom.xml b/examples/system-properties-example/pom.xml
new file mode 100644
index 0000000..c3657a2
--- /dev/null
+++ b/examples/system-properties-example/pom.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>system-properties-example</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-system-properties</name>
+
+    <description>Jersey system properties example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-proxy-client</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.sysprops.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/App.java b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/App.java
new file mode 100644
index 0000000..65ae843
--- /dev/null
+++ b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/App.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sysprops;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.examples.sysprops.impl.PropertyNamesResourceImpl;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * @author Martin Matula
+ */
+public class App {
+    private static final URI BASE_URI = URI.create("http://localhost:8080/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("System Properties Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, createApp(), false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(
+                    String.format("Application started.%n"
+                            + "Try out %s%n"
+                            + "Stop the application using CTRL+C",
+                            BASE_URI + "/properties"));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+
+    public static ResourceConfig createApp() {
+        return new ResourceConfig(
+                PropertyNamesResourceImpl.class,
+                PropertiesWriter.class
+        );
+    }
+}
diff --git a/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/PropertiesReader.java b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/PropertiesReader.java
new file mode 100644
index 0000000..35dd392
--- /dev/null
+++ b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/PropertiesReader.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sysprops;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+
+import org.glassfish.jersey.message.MessageUtils;
+
+/**
+ * @author Martin Matula
+ */
+@Consumes(MediaType.TEXT_PLAIN)
+public class PropertiesReader implements MessageBodyReader<Set<String>> {
+
+    @Override
+    public boolean isReadable(final Class<?> type,
+                              final Type genericType,
+                              final Annotation[] annotations,
+                              final MediaType mediaType) {
+        return Set.class.isAssignableFrom(type);
+    }
+
+    @Override
+    public Set<String> readFrom(final Class<Set<String>> type,
+                                final Type genericType,
+                                final Annotation[] annotations,
+                                final MediaType mediaType,
+                                final MultivaluedMap<String, String> httpHeaders,
+                                final InputStream entityStream) throws IOException, WebApplicationException {
+        final BufferedReader br = new BufferedReader(new InputStreamReader(entityStream, MessageUtils.getCharset(mediaType)));
+        final Set<String> result = new HashSet<>();
+        String line;
+        while ((line = br.readLine()) != null) {
+            result.add(line);
+        }
+        return result;
+    }
+}
diff --git a/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/PropertiesWriter.java b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/PropertiesWriter.java
new file mode 100644
index 0000000..99b8472
--- /dev/null
+++ b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/PropertiesWriter.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sysprops;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.util.Set;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import org.glassfish.jersey.message.MessageUtils;
+
+/**
+ * @author Martin Matula
+ */
+@Produces(MediaType.TEXT_PLAIN)
+public class PropertiesWriter implements MessageBodyWriter<Set<String>> {
+    @Override
+    public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return Set.class.isAssignableFrom(type);
+    }
+
+    @Override
+    public long getSize(final Set<String> s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                        final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final Set<String> s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                        final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders,
+                        final OutputStream entityStream) throws IOException, WebApplicationException {
+        final PrintStream ps = new PrintStream(entityStream, true, MessageUtils.getCharset(mediaType).name());
+
+        for (final String item : s) {
+            ps.println(item);
+        }
+    }
+}
diff --git a/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/PropertyNamesResource.java b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/PropertyNamesResource.java
new file mode 100644
index 0000000..037e0a9
--- /dev/null
+++ b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/PropertyNamesResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sysprops;
+
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Martin Matula
+ */
+@Path("properties")
+@Produces(MediaType.TEXT_PLAIN)
+@Consumes(MediaType.TEXT_PLAIN)
+public interface PropertyNamesResource {
+    @GET
+    Set<String> getPropertyNames();
+
+    @Path("{name}")
+    public PropertyResource getProperty(@PathParam("name") String name);
+}
diff --git a/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/PropertyResource.java b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/PropertyResource.java
new file mode 100644
index 0000000..33e8954
--- /dev/null
+++ b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/PropertyResource.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sysprops;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Martin Matula
+ */
+@Produces(MediaType.TEXT_PLAIN)
+@Consumes(MediaType.TEXT_PLAIN)
+public interface PropertyResource {
+    @GET
+    String get();
+
+    @PUT
+    String set(String value);
+}
diff --git a/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/impl/PropertyNamesResourceImpl.java b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/impl/PropertyNamesResourceImpl.java
new file mode 100644
index 0000000..d5d3eb8
--- /dev/null
+++ b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/impl/PropertyNamesResourceImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sysprops.impl;
+
+import java.util.Set;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.examples.sysprops.PropertyNamesResource;
+import org.glassfish.jersey.examples.sysprops.PropertyResource;
+
+/**
+ * @author Martin Matula
+ */
+public class PropertyNamesResourceImpl implements PropertyNamesResource {
+    @Context
+    private UriInfo uriInfo;
+
+    @Override
+    public Set<String> getPropertyNames() {
+        return System.getProperties().stringPropertyNames();
+    }
+
+    @Override
+    public PropertyResource getProperty(String name) {
+        return new PropertyResourceImpl(name, uriInfo);
+    }
+}
diff --git a/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/impl/PropertyResourceImpl.java b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/impl/PropertyResourceImpl.java
new file mode 100644
index 0000000..d479417
--- /dev/null
+++ b/examples/system-properties-example/src/main/java/org/glassfish/jersey/examples/sysprops/impl/PropertyResourceImpl.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sysprops.impl;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.examples.sysprops.PropertyResource;
+
+/**
+ * @author Martin Matula
+ */
+public class PropertyResourceImpl implements PropertyResource {
+    private final String name;
+    private final UriInfo uriInfo;
+
+    public PropertyResourceImpl(String name, UriInfo uriInfo) {
+        this.name = name;
+        this.uriInfo = uriInfo;
+    }
+
+    @Override
+    public String get() {
+        String value = System.getProperty(name);
+        if (value == null) {
+            throw new WebApplicationException(404);
+        }
+        return value;
+    }
+
+    @Override
+    public String set(String value) {
+        if (System.setProperty(name, value) == null) {
+            throw new WebApplicationException(Response.created(uriInfo.getRequestUri()).entity(value).build());
+        }
+        return value;
+    }
+}
diff --git a/examples/system-properties-example/src/test/java/org/glassfish/jersey/examples/sysprops/SysPropsTest.java b/examples/system-properties-example/src/test/java/org/glassfish/jersey/examples/sysprops/SysPropsTest.java
new file mode 100644
index 0000000..21887fc
--- /dev/null
+++ b/examples/system-properties-example/src/test/java/org/glassfish/jersey/examples/sysprops/SysPropsTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.sysprops;
+
+import java.util.Set;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.proxy.WebResourceFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * System properties example unit test.
+ *
+ * @author Martin Matula
+ */
+public class SysPropsTest extends JerseyTest {
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return App.createApp();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(PropertiesReader.class);
+    }
+
+    @Test
+    public void testGetPropertyNames() {
+        PropertyNamesResource propertyNamesResource = WebResourceFactory.newResource(PropertyNamesResource.class, target());
+        Set<String> propertyNames = propertyNamesResource.getPropertyNames();
+        assertEquals(System.getProperties().stringPropertyNames(), propertyNames);
+    }
+
+    @Test
+    public void testGetProperty() {
+        PropertyNamesResource pnr = WebResourceFactory.newResource(PropertyNamesResource.class, target());
+
+        assertEquals(System.getProperty("java.home"), pnr.getProperty("java.home").get());
+    }
+
+    @Test
+    public void testSetProperty() {
+        PropertyNamesResource pnr = WebResourceFactory.newResource(PropertyNamesResource.class, target());
+
+        pnr.getProperty("test").set("this is a test");
+        assertEquals(System.getProperty("test"), pnr.getProperty("test").get());
+    }
+}
diff --git a/examples/webapp-example-parent/pom.xml b/examples/webapp-example-parent/pom.xml
new file mode 100644
index 0000000..aba706e
--- /dev/null
+++ b/examples/webapp-example-parent/pom.xml
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>webapp-example-parent</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-examples-webapp-parent-pom</name>
+
+    <description>Jersey Web Application (Servlet) examples parent POM.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>xml-maven-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <id>assemble-gf-src-zip</id>
+                            <phase>prepare-package</phase>
+                            <goals>
+                                <goal>transform</goal>
+                            </goals>
+                            <configuration>
+                                <transformationSets>
+                                    <transformationSet>
+                                        <dir>${project.basedir}</dir>
+                                        <includes>
+                                            <include>pom.xml</include>
+                                        </includes>
+                                        <stylesheet>../etc/gf-project-src-pom.xsl</stylesheet>
+                                        <outputDir>${project.build.directory}/gf-pom-file</outputDir>
+                                    </transformationSet>
+                                </transformationSets>
+                            </configuration>
+                        </execution>
+                        <execution>
+                            <id>assemble-wls-src-zip</id>
+                            <phase>prepare-package</phase>
+                            <goals>
+                                <goal>transform</goal>
+                            </goals>
+                            <configuration>
+                                <transformationSets>
+                                    <transformationSet>
+                                        <dir>${project.basedir}</dir>
+                                        <includes>
+                                            <include>pom.xml</include>
+                                        </includes>
+                                        <stylesheet>../etc/wls-project-src-pom.xsl</stylesheet>
+                                        <outputDir>${project.build.directory}/wls-pom-file</outputDir>
+                                    </transformationSet>
+                                    <transformationSet>
+                                        <dir>${project.basedir}</dir>
+                                        <includes>
+                                            <include>**/web.xml</include>
+                                        </includes>
+                                        <stylesheet>../etc/wls-project-src-web.xsl</stylesheet>
+                                        <outputDir>${project.build.directory}/wls-web-xml-file</outputDir>
+                                    </transformationSet>
+                                </transformationSets>
+                            </configuration>
+                        </execution>
+                        <execution>
+                            <id>assemble-wls1213-src-zip</id>
+                            <phase>prepare-package</phase>
+                            <goals>
+                                <goal>transform</goal>
+                            </goals>
+                            <configuration>
+                                <transformationSets>
+                                    <transformationSet>
+                                        <dir>${project.basedir}</dir>
+                                        <includes>
+                                            <include>pom.xml</include>
+                                        </includes>
+                                        <stylesheet>../etc/wls1213-project-src-pom.xsl</stylesheet>
+                                        <outputDir>${project.build.directory}/wls1213-pom-file</outputDir>
+                                    </transformationSet>
+                                    <transformationSet>
+                                        <dir>${project.basedir}</dir>
+                                        <includes>
+                                            <include>**/web.xml</include>
+                                        </includes>
+                                        <stylesheet>../etc/wls-project-src-web.xsl</stylesheet>
+                                        <outputDir>${project.build.directory}/wls1213-web-xml-file</outputDir>
+                                    </transformationSet>
+                                </transformationSets>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-assembly-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <id>assemble-gf-src-zip</id>
+                            <phase>package</phase>
+                            <goals>
+                                <goal>single</goal>
+                            </goals>
+                            <configuration>
+                                <descriptorRefs>
+                                    <!-- Reference to a descriptor in org.glassfish.jersey.examples:assemblies module -->
+                                    <descriptorRef>glassfish-src-zip</descriptorRef>
+                                </descriptorRefs>
+                            </configuration>
+                        </execution>
+                        <execution>
+                            <id>assemble-wls-src-zip</id>
+                            <phase>package</phase>
+                            <goals>
+                                <goal>single</goal>
+                            </goals>
+                            <configuration>
+                                <descriptorRefs>
+                                    <!-- Reference to a descriptor in org.glassfish.jersey.examples:assemblies module -->
+                                    <descriptorRef>weblogic-src-zip</descriptorRef>
+                                </descriptorRefs>
+                            </configuration>
+                        </execution>
+                        <execution>
+                            <id>assemble-wls1213-src-zip</id>
+                            <phase>package</phase>
+                            <goals>
+                                <goal>single</goal>
+                            </goals>
+                            <configuration>
+                                <descriptorRefs>
+                                    <!-- Reference to a descriptor in org.glassfish.jersey.examples:assemblies module -->
+                                    <descriptorRef>weblogic1213-src-zip</descriptorRef>
+                                </descriptorRefs>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.mortbay.jetty</groupId>
+                    <artifactId>maven-jetty-plugin</artifactId>
+                    <configuration>
+                        <webApp>${project.build.directory}/${project.artifactId}.war</webApp>
+                        <contextPath>${project.artifactId}</contextPath>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+</project>
diff --git a/examples/xml-moxy/README.MD b/examples/xml-moxy/README.MD
new file mode 100644
index 0000000..8c67e04
--- /dev/null
+++ b/examples/xml-moxy/README.MD
@@ -0,0 +1,117 @@
+[//]: # " Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+MOXy Example
+============
+
+What is MOXy
+------------
+
+This example demonstrates that you can utilize MOXy extensions when
+dealing with XML representation when developing a Jersey based RESTful
+Web application
+
+MOXy is EclipseLink's Object to XML Mapping services. MOXy allows for a
+POJO object model to be mapped to an XML schema. The Java Architecture
+for XML Binding (JAXB) provides a Java standard for object XML mapping
+(OXM). MOXy supports JAXB, as well as providing its' own native API and
+integration with Web Services.
+
+MOXy XML Path Mapping Extension
+-------------------------------
+
+The MOXy extension shown in this example is described on the Eclipse
+Wiki site at
+<http://wiki.eclipse.org/EclipseLink/Examples/MOXy/GettingStarted/MOXyExtensions>.
+It allows you to specify path based mapping via @XmlPath annotation.
+
+If the MOXy extension was not used, the XML represenation of the
+customer data would look like follows:
+
+```xml
+<customer>
+  <name>Jane Doe</name>
+  <address>
+    <city>My Town</city>
+    <street>123 Any Street</street>
+  </address>
+  <phoneNumbers type="work">613-555-1111</phoneNumbers>
+  <phoneNumbers type="cell">613-555-2222</phoneNumbers>
+</customer>
+```
+
+By adding `@org.eclipse.persistence.oxm.annotations.XmlPath` annotation
+to the bean definition classes, you will get the following XML
+representation instead:
+
+```xml
+<customer>
+  <personal-info>
+    <name>Jane Doe</name>
+  </personal-info>
+  <contact-info>
+    <address>
+      <city>My Town</city>
+      <street>123 Any Street</street>
+    </address>
+    <phone-number type="work">613-555-1111</phone-number>
+    <phone-number type="cell">613-555-2222</phone-number>
+  </contact-info>
+</customer>
+```
+
+XML Path expressions used are:
+
+-   personal-info/name/text()
+-   contact-info/address
+-   contact-info/phone-number
+
+Please check out the source code and the wiki page linked above for the
+detailed information on the XML Path mapping feature.
+
+Replacing Implicit JAXB Runtime With MOXy
+-----------------------------------------
+
+Since MOXy is a JAXB implementation, the example still utilizes the
+standard Jersey JAXB message body reader/writer providers. To make
+Jersey use MOXy runtime, you just need to put a `jaxb.properties` file
+into the Java package containing your JAXB beans. The file should have
+the following content:
+
+    javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
+
+Concrete file used in this example is placed under
+
+    src/main/resources/com/sun/jersey/samples/moxy/beans/jaxb.properties
+
+Contents
+--------
+
+The example consists of a single REST Resource represented by the
+following Java class:
+
+    org.glassfish.jersey.examples.xmlmoxy.CustomerResource
+
+A resource class that maintains a single customer data.
+
+The mapping of the URI path space is presented in the following table:
+
+URI path         | Resource class     | HTTP methods
+---------------- | ------------------ | --------------
+**_/customer_**  | CustomerResource   | GET, PUT
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container. You can access the application at:
+
+-   [http://localhost:8080/moxy/customer](http://localhost:8080/xml-moxy/customer)
diff --git a/examples/xml-moxy/pom.xml b/examples/xml-moxy/pom.xml
new file mode 100644
index 0000000..f6ffdef
--- /dev/null
+++ b/examples/xml-moxy/pom.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>xml-moxy</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-moxy</name>
+
+    <description>Jersey XML MOXy example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.xmlmoxy.App</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/App.java b/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/App.java
new file mode 100644
index 0000000..d6cd52c
--- /dev/null
+++ b/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/App.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.xmlmoxy;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.moxy.xml.MoxyXmlFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/xml-moxy/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("XML with MOXy Jersey Example App");
+
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, createApp());
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            System.out.println(String.format("Application started.%nTry out %s%nStop the application using CTRL+C",
+                    BASE_URI + "/customer"));
+
+            Thread.currentThread().join();
+        } catch (IOException | InterruptedException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    public static ResourceConfig createApp() {
+        return new ResourceConfig().packages("org.glassfish.jersey.examples.xmlmoxy").register(new MoxyXmlFeature());
+    }
+}
diff --git a/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/CustomerResource.java b/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/CustomerResource.java
new file mode 100644
index 0000000..7fb5ee0
--- /dev/null
+++ b/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/CustomerResource.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.xmlmoxy;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.examples.xmlmoxy.beans.Address;
+import org.glassfish.jersey.examples.xmlmoxy.beans.Customer;
+import org.glassfish.jersey.examples.xmlmoxy.beans.PhoneNumber;
+
+@Path("/customer")
+public class CustomerResource {
+
+    private static Customer customer = createInitialCustomer();
+
+    @GET
+    @Produces(MediaType.APPLICATION_XML)
+    public Customer getCustomer() {
+        return customer;
+    }
+
+    @PUT
+    @Consumes(MediaType.APPLICATION_XML)
+    public void setCustomer(final Customer c) {
+        setCustomerToStatic(c);
+    }
+
+    private static Customer createInitialCustomer() {
+        final Customer result = new Customer();
+
+        result.setName("Jane Doe");
+        result.setAddress(new Address("123 Any Street", "My Town"));
+        result.getPhoneNumbers().add(new PhoneNumber("work", "613-555-1111"));
+        result.getPhoneNumbers().add(new PhoneNumber("cell", "613-555-2222"));
+
+        return result;
+    }
+
+    private static void setCustomerToStatic(final Customer customer) {
+        CustomerResource.customer = customer;
+    }
+}
diff --git a/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/beans/Address.java b/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/beans/Address.java
new file mode 100644
index 0000000..26238f1
--- /dev/null
+++ b/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/beans/Address.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.xmlmoxy.beans;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class Address {
+
+    private String street;
+    private String city;
+
+    public Address(){}
+
+    public Address(String street, String city) {
+        this.street = street;
+        this.city = city;
+    }
+
+    public String getStreet() {
+        return street;
+    }
+
+    public void setStreet(String street) {
+        this.street = street;
+    }
+
+    public String getCity() {
+        return city;
+    }
+
+    public void setCity(String city) {
+        this.city = city;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Address other = (Address) obj;
+        if ((this.street == null) ? (other.street != null) : !this.street.equals(other.street)) {
+            return false;
+        }
+        if ((this.city == null) ? (other.city != null) : !this.city.equals(other.city)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 29 * hash + (this.street != null ? this.street.hashCode() : 0);
+        hash = 29 * hash + (this.city != null ? this.city.hashCode() : 0);
+        return hash;
+    }
+}
diff --git a/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/beans/Customer.java b/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/beans/Customer.java
new file mode 100644
index 0000000..f1f6b33
--- /dev/null
+++ b/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/beans/Customer.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.xmlmoxy.beans;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import org.eclipse.persistence.oxm.annotations.XmlPath;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@XmlRootElement
+@XmlType(propOrder = {"name", "address", "phoneNumbers"})
+public class Customer {
+
+    private String name;
+    private Address address;
+    private List<PhoneNumber> phoneNumbers;
+
+    public Customer() {
+        phoneNumbers = new ArrayList<PhoneNumber>();
+    }
+
+    @XmlPath("personal-info/name/text()")
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @XmlPath("contact-info/address")
+    public Address getAddress() {
+        return address;
+    }
+
+    public void setAddress(Address address) {
+        this.address = address;
+    }
+
+    @XmlPath("contact-info/phone-number")
+    public List<PhoneNumber> getPhoneNumbers() {
+        return phoneNumbers;
+    }
+
+    public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
+        this.phoneNumbers = phoneNumbers;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Customer other = (Customer) obj;
+        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
+            return false;
+        }
+        if (this.address != other.address && (this.address == null || !this.address.equals(other.address))) {
+            return false;
+        }
+        if (this.phoneNumbers != other.phoneNumbers && (this.phoneNumbers == null || !this.phoneNumbers
+                .equals(other.phoneNumbers))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 97 * hash + (this.name != null ? this.name.hashCode() : 0);
+        hash = 97 * hash + (this.address != null ? this.address.hashCode() : 0);
+        hash = 97 * hash + (this.phoneNumbers != null ? this.phoneNumbers.hashCode() : 0);
+        return hash;
+    }
+}
diff --git a/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/beans/PhoneNumber.java b/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/beans/PhoneNumber.java
new file mode 100644
index 0000000..5df8b0e
--- /dev/null
+++ b/examples/xml-moxy/src/main/java/org/glassfish/jersey/examples/xmlmoxy/beans/PhoneNumber.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.xmlmoxy.beans;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlValue;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class PhoneNumber {
+
+    private String type;
+    private String value;
+
+    public PhoneNumber() {}
+
+    public PhoneNumber(String type, String value) {
+        this.type = type;
+        this.value = value;
+    }
+
+    @XmlAttribute
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    @XmlValue
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final PhoneNumber other = (PhoneNumber) obj;
+        if ((this.type == null) ? (other.type != null) : !this.type.equals(other.type)) {
+            return false;
+        }
+        if ((this.value == null) ? (other.value != null) : !this.value.equals(other.value)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 59 * hash + (this.type != null ? this.type.hashCode() : 0);
+        hash = 59 * hash + (this.value != null ? this.value.hashCode() : 0);
+        return hash;
+    }
+}
diff --git a/examples/xml-moxy/src/test/java/org/glassfish/jersey/examples/xmlmoxy/MoxyAppTest.java b/examples/xml-moxy/src/test/java/org/glassfish/jersey/examples/xmlmoxy/MoxyAppTest.java
new file mode 100644
index 0000000..4d207a0
--- /dev/null
+++ b/examples/xml-moxy/src/test/java/org/glassfish/jersey/examples/xmlmoxy/MoxyAppTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.xmlmoxy;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.examples.xmlmoxy.beans.Customer;
+import org.glassfish.jersey.moxy.xml.MoxyXmlFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class MoxyAppTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        return App.createApp();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(new MoxyXmlFeature());
+    }
+
+    /**
+     * Test that the expected response is sent back.
+     * @throws java.lang.Exception
+     */
+    @Test
+    public void testCustomer() throws Exception {
+        final WebTarget webTarget = target().path("customer");
+
+        Customer customer = webTarget.request(MediaType.APPLICATION_XML).get(Customer.class);
+        customer.setName("Tom Dooley");
+        webTarget.request(MediaType.APPLICATION_XML).put(Entity.entity(customer, MediaType.APPLICATION_XML));
+
+        Customer updatedCustomer = webTarget.request(MediaType.APPLICATION_XML).get(Customer.class);
+        assertEquals(customer, updatedCustomer);
+    }
+}