Adopted EjbComponentProvider and company from jersey

- it is an adapter between GlassFish EJB container and Jersey
- old version limited osgi dependency to GlassFish versions 4-6
diff --git a/appserver/extras/embedded/all/pom.xml b/appserver/extras/embedded/all/pom.xml
index 95affaa..c994b31 100644
--- a/appserver/extras/embedded/all/pom.xml
+++ b/appserver/extras/embedded/all/pom.xml
@@ -1041,6 +1041,12 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.main.web</groupId>
+            <artifactId>jersey-ejb-component-provider</artifactId>
+            <version>${project.version}</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.main.web</groupId>
             <artifactId>jersey-mvc-connector</artifactId>
             <version>${project.version}</version>
             <optional>true</optional>
@@ -1179,11 +1185,6 @@
             <optional>true</optional>
         </dependency>
         <dependency>
-            <groupId>org.glassfish.jersey.containers.glassfish</groupId>
-            <artifactId>jersey-gf-ejb</artifactId>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
             <artifactId>jersey-container-servlet</artifactId>
             <optional>true</optional>
diff --git a/appserver/extras/embedded/shell/glassfish-embedded-static-shell/pom.xml b/appserver/extras/embedded/shell/glassfish-embedded-static-shell/pom.xml
index d74b78b..9c8a9e8 100755
--- a/appserver/extras/embedded/shell/glassfish-embedded-static-shell/pom.xml
+++ b/appserver/extras/embedded/shell/glassfish-embedded-static-shell/pom.xml
@@ -949,6 +949,12 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.main.web</groupId>
+            <artifactId>jersey-ejb-component-provider</artifactId>
+            <version>${project.version}</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.main.web</groupId>
             <artifactId>jersey-mvc-connector</artifactId>
             <version>${project.version}</version>
             <optional>true</optional>
@@ -1133,11 +1139,6 @@
             <optional>true</optional>
         </dependency>
         <dependency>
-            <groupId>org.glassfish.jersey.containers.glassfish</groupId>
-            <artifactId>jersey-gf-ejb</artifactId>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
             <artifactId>jersey-container-servlet</artifactId>
             <optional>true</optional>
diff --git a/appserver/extras/embedded/web/pom.xml b/appserver/extras/embedded/web/pom.xml
index 945dd8c..c3d418e 100644
--- a/appserver/extras/embedded/web/pom.xml
+++ b/appserver/extras/embedded/web/pom.xml
@@ -893,6 +893,12 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.main.web</groupId>
+            <artifactId>jersey-ejb-component-provider</artifactId>
+            <version>${project.version}</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.main.web</groupId>
             <artifactId>jersey-mvc-connector</artifactId>
             <version>${project.version}</version>
             <optional>true</optional>
@@ -1130,11 +1136,6 @@
             <optional>true</optional>
         </dependency>
         <dependency>
-            <groupId>org.glassfish.jersey.containers.glassfish</groupId>
-            <artifactId>jersey-gf-ejb</artifactId>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
             <artifactId>jersey-container-servlet</artifactId>
             <optional>true</optional>
diff --git a/appserver/featuresets/web/pom.xml b/appserver/featuresets/web/pom.xml
index 14772a3..cbea6ee 100644
--- a/appserver/featuresets/web/pom.xml
+++ b/appserver/featuresets/web/pom.xml
@@ -205,16 +205,6 @@
             </exclusions>
         </dependency>
         <dependency>
-            <groupId>org.glassfish.jersey.containers.glassfish</groupId>
-            <artifactId>jersey-gf-ejb</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>*</groupId>
-                    <artifactId>*</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
             <artifactId>jersey-container-servlet</artifactId>
             <exclusions>
@@ -971,6 +961,17 @@
             </exclusions>
         </dependency>
         <dependency>
+            <groupId>org.glassfish.main.web</groupId>
+            <artifactId>jersey-ejb-component-provider</artifactId>
+            <version>${project.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>*</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
             <groupId>org.glassfish.main.common</groupId>
             <artifactId>stats77</artifactId>
             <version>${project.version}</version>
diff --git a/appserver/web/jersey-ejb-component-provider/pom.xml b/appserver/web/jersey-ejb-component-provider/pom.xml
new file mode 100644
index 0000000..16ae6aa
--- /dev/null
+++ b/appserver/web/jersey-ejb-component-provider/pom.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<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.main.web</groupId>
+        <artifactId>web</artifactId>
+        <version>6.2.4-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-ejb-component-provider</artifactId>
+    <packaging>glassfish-jar</packaging>
+    <name>Jersey EJB Component Provider Module</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.ejb</groupId>
+            <artifactId>jakarta.ejb-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.interceptor</groupId>
+            <artifactId>jakarta.interceptor-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.inject</groupId>
+            <artifactId>jakarta.inject-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.main.ejb</groupId>
+            <artifactId>ejb-container</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.main.admin</groupId>
+            <artifactId>config-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Import-Package>
+                            jakarta.annotation.*;version="${jakarta.annotation-api.version}",
+                            org.glassfish.hk2.*;version="${hk2.version}",
+                            *
+                        </Import-Package>
+                        <Export-Package>
+                            org.glassfish.jersey.gf.ejb.internal,
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/appserver/web/jersey-ejb-component-provider/src/main/java/org/glassfish/jersey/gf/ejb/internal/EjbComponentInterceptor.java b/appserver/web/jersey-ejb-component-provider/src/main/java/org/glassfish/jersey/gf/ejb/internal/EjbComponentInterceptor.java
new file mode 100644
index 0000000..662d5d6
--- /dev/null
+++ b/appserver/web/jersey-ejb-component-provider/src/main/java/org/glassfish/jersey/gf/ejb/internal/EjbComponentInterceptor.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 Contributors to the Eclipse Foundation
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.gf.ejb.internal;
+
+import jakarta.annotation.PostConstruct;
+import jakarta.interceptor.InvocationContext;
+
+import org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+
+/**
+ * EJB interceptor to inject Jersey specific stuff into EJB beans.
+ *
+ * @author Jakub Podlesak
+ */
+public final class EjbComponentInterceptor {
+
+    private final InjectionManager injectionManager;
+
+    /**
+     * Create new EJB component injection manager.
+     *
+     * @param injectionManager injection manager.
+     */
+    public EjbComponentInterceptor(final InjectionManager injectionManager) {
+        this.injectionManager = injectionManager;
+    }
+
+    @PostConstruct
+    private void inject(final InvocationContext context) throws Exception {
+
+        final Object beanInstance = context.getTarget();
+        injectionManager.inject(beanInstance, CdiComponentProvider.CDI_CLASS_ANALYZER);
+
+        // Invoke next interceptor in chain
+        context.proceed();
+    }
+}
diff --git a/appserver/web/jersey-ejb-component-provider/src/main/java/org/glassfish/jersey/gf/ejb/internal/EjbComponentProvider.java b/appserver/web/jersey-ejb-component-provider/src/main/java/org/glassfish/jersey/gf/ejb/internal/EjbComponentProvider.java
new file mode 100644
index 0000000..f81a8f0
--- /dev/null
+++ b/appserver/web/jersey-ejb-component-provider/src/main/java/org/glassfish/jersey/gf/ejb/internal/EjbComponentProvider.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019 Payara Foundation and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 Contributors to the Eclipse Foundation
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.gf.ejb.internal;
+
+import java.io.Externalizable;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Supplier;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jakarta.ws.rs.ext.ExceptionMapper;
+
+import jakarta.annotation.Priority;
+import jakarta.ejb.Local;
+import jakarta.ejb.Remote;
+import jakarta.ejb.Stateless;
+import jakarta.inject.Singleton;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.Binding;
+import org.glassfish.jersey.internal.inject.Bindings;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.InstanceBinding;
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.model.Invocable;
+import org.glassfish.jersey.server.spi.ComponentProvider;
+import org.glassfish.jersey.server.spi.internal.ResourceMethodInvocationHandlerProvider;
+
+import org.glassfish.ejb.deployment.descriptor.EjbBundleDescriptorImpl;
+import org.glassfish.ejb.deployment.descriptor.EjbDescriptor;
+import org.glassfish.internal.data.ApplicationInfo;
+import org.glassfish.internal.data.ApplicationRegistry;
+import org.glassfish.internal.data.ModuleInfo;
+
+import com.sun.ejb.containers.BaseContainer;
+import com.sun.ejb.containers.EjbContainerUtil;
+import com.sun.ejb.containers.EjbContainerUtilImpl;
+import com.sun.enterprise.config.serverbeans.Application;
+import com.sun.enterprise.config.serverbeans.Applications;
+
+/**
+ * EJB component provider.
+ *
+ * @author Paul Sandoz
+ * @author Jakub Podlesak
+ */
+@Priority(300)
+public final class EjbComponentProvider implements ComponentProvider, ResourceMethodInvocationHandlerProvider {
+
+    private static final Logger LOGGER = Logger.getLogger(EjbComponentProvider.class.getName());
+
+    private InitialContext initialContext;
+    private final List<String> libNames = new CopyOnWriteArrayList<>();
+
+    private boolean ejbInterceptorRegistered = false;
+
+    /**
+     * HK2 factory to provide EJB components obtained via JNDI lookup.
+     */
+    private static class EjbFactory<T> implements Supplier<T> {
+
+        final InitialContext ctx;
+        final Class<T> clazz;
+        final String beanName;
+        final EjbComponentProvider ejbProvider;
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public T get() {
+            try {
+                return (T) lookup(ctx, clazz, beanName, ejbProvider);
+            } catch (NamingException ex) {
+                Logger.getLogger(ApplicationHandler.class.getName()).log(Level.SEVERE, null, ex);
+                return null;
+            }
+        }
+
+        private static <T> String getBeanName(final Class<T> clazz) {
+            final Stateless stateless = clazz.getAnnotation(Stateless.class);
+            if (stateless != null) {
+                if (stateless.name().isEmpty()) {
+                    return clazz.getSimpleName();
+                }
+                return stateless.name();
+            }
+            final jakarta.ejb.Singleton singleton = clazz.getAnnotation(jakarta.ejb.Singleton.class);
+            if (singleton != null) {
+                if (singleton.name().isEmpty()) {
+                    return clazz.getSimpleName();
+                }
+                return singleton.name();
+            }
+            return clazz.getSimpleName();
+        }
+
+        public EjbFactory(Class<T> rawType, InitialContext ctx, EjbComponentProvider ejbProvider) {
+            this.clazz = rawType;
+            this.ctx = ctx;
+            this.ejbProvider = ejbProvider;
+            this.beanName = getBeanName(rawType);
+        }
+    }
+
+    /**
+     * Annotations to determine EJB components.
+     */
+    private static final Set<String> EjbComponentAnnotations = Collections.unmodifiableSet(new HashSet<String>() {{
+        add("jakarta.ejb.Stateful");
+        add("jakarta.ejb.Stateless");
+        add("jakarta.ejb.Singleton");
+    }});
+
+    private InjectionManager injectionManager = null;
+
+    // ComponentProvider
+    @Override
+    public void initialize(final InjectionManager injectionManager) {
+        this.injectionManager = injectionManager;
+
+        InstanceBinding<EjbComponentProvider> descriptor = Bindings.service(EjbComponentProvider.this)
+                .to(ResourceMethodInvocationHandlerProvider.class);
+        this.injectionManager.register(descriptor);
+    }
+
+    private ApplicationInfo getApplicationInfo(EjbContainerUtil ejbUtil) throws NamingException {
+        ApplicationRegistry appRegistry = ejbUtil.getServices().getService(ApplicationRegistry.class);
+        Applications applications = ejbUtil.getServices().getService(Applications.class);
+        String appNamePrefix = (String) initialContext.lookup("java:app/AppName");
+        Set<String> appNames = appRegistry.getAllApplicationNames();
+        Set<String> disabledApps = new TreeSet<>();
+        for (String appName : appNames) {
+            if (appName.startsWith(appNamePrefix)) {
+                Application appDesc = applications.getApplication(appName);
+                if (appDesc != null && !ejbUtil.getDeployment().isAppEnabled(appDesc)) {
+                    // skip disabled version of the app
+                    disabledApps.add(appName);
+                } else {
+                    return ejbUtil.getDeployment().get(appName);
+                }
+            }
+        }
+
+        // grab the latest one, there is no way to make
+        // sure which one the user is actually enabling,
+        // so use the best case, i.e. upgrade
+        Iterator<String> it = disabledApps.iterator();
+        String lastDisabledApp = null;
+        while (it.hasNext()) {
+            lastDisabledApp = it.next();
+        }
+        if (lastDisabledApp != null) {
+            return ejbUtil.getDeployment().get(lastDisabledApp);
+        }
+
+        throw new NamingException("Application Information Not Found");
+    }
+
+    private void registerEjbInterceptor(Class<?> component) {
+        try {
+            final Object interceptor = new EjbComponentInterceptor(injectionManager);
+            initialContext = getInitialContext();
+            final EjbContainerUtil ejbUtil = EjbContainerUtilImpl.getInstance();
+            final ApplicationInfo appInfo = getApplicationInfo(ejbUtil);
+            for (ModuleInfo moduleInfo : appInfo.getModuleInfos()) {
+                final String jarName = moduleInfo.getName();
+                if (jarName.endsWith(".jar") || jarName.endsWith(".war")) {
+                    final String moduleName = jarName.substring(0, jarName.length() - 4);
+                    final Object bundleDescriptor = moduleInfo.getMetaData(EjbBundleDescriptorImpl.class.getName());
+                    if (bundleDescriptor instanceof EjbBundleDescriptorImpl) {
+                        final Collection<EjbDescriptor> ejbs = ((EjbBundleDescriptorImpl) bundleDescriptor).getEjbs();
+
+                        for (final EjbDescriptor ejb : ejbs) {
+                            final BaseContainer ejbContainer = EjbContainerUtilImpl.getInstance().getContainer(ejb.getUniqueId());
+                            if (ejbContainer.getEJBClass() != component) {
+                                continue;
+                            }
+                            libNames.add(moduleName);
+                            try {
+                                AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                                    @Override
+                                    public Object run() throws Exception {
+                                        final Method registerInterceptorMethod =
+                                                BaseContainer.class
+                                                        .getDeclaredMethod("registerSystemInterceptor", java.lang.Object.class);
+                                        registerInterceptorMethod.setAccessible(true);
+
+                                        registerInterceptorMethod.invoke(ejbContainer, interceptor);
+                                        return null;
+                                    }
+                                });
+                            } catch (PrivilegedActionException pae) {
+                                final Throwable cause = pae.getCause();
+                                LOGGER.log(Level.WARNING,
+                                    "Could not bind EJB intercetor for class " + ejb.getEjbClassName() + ".", cause);
+                            }
+                        }
+                    }
+                }
+            }
+
+            final Object interceptorBinder = initialContext.lookup("java:org.glassfish.ejb.container.interceptor_binding_spi");
+            // Some implementations of InitialContext return null instead of
+            // throwing NamingException if there is no Object associated with
+            // the name
+            if (interceptorBinder == null) {
+                throw new IllegalStateException(
+                    "The EJB interceptor binding API is not available. JAX-RS EJB integration can not be supported.");
+            }
+
+            try {
+                AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                    @Override
+                    public Object run() throws Exception {
+                        Method interceptorBinderMethod = interceptorBinder.getClass()
+                                .getMethod("registerInterceptor", java.lang.Object.class);
+
+                        interceptorBinderMethod.invoke(interceptorBinder, interceptor);
+                        EjbComponentProvider.this.ejbInterceptorRegistered = true;
+                        LOGGER.log(Level.CONFIG,
+                            "The Jersey EJB interceptor is bound. JAX-RS EJB integration support is enabled.");
+                        return null;
+                    }
+                });
+            } catch (PrivilegedActionException pae) {
+                throw new IllegalStateException(
+                    "Error when configuring to use the EJB interceptor binding API. JAX-RS EJB integration can not be supported.",
+                    pae.getCause());
+            }
+
+        } catch (NamingException ex) {
+            throw new IllegalStateException(
+                "The EJB interceptor binding API is not available. JAX-RS EJB integration can not be supported.", ex);
+        } catch (LinkageError ex) {
+            throw new IllegalStateException(
+                "Linkage error when configuring to use the EJB interceptor binding API. JAX-RS EJB integration can not be supported.",
+                ex);
+        }
+    }
+
+    // ComponentProvider
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean bind(Class<?> component, Set<Class<?>> providerContracts) {
+        LOGGER.log(Level.FINE, "Class, {0}, is being checked with Jersey EJB component provider.", component);
+        if (injectionManager == null) {
+            throw new IllegalStateException("EJB component provider has not been initialized properly.");
+        }
+
+        if (!isEjbComponent(component)) {
+            return false;
+        }
+
+        if (!ejbInterceptorRegistered) {
+            registerEjbInterceptor(component);
+        }
+
+        Binding binding = Bindings.supplier(new EjbFactory(component, initialContext, EjbComponentProvider.this))
+                .to(component)
+                .to(providerContracts);
+        injectionManager.register(binding);
+
+        LOGGER.log(Level.CONFIG, "Class, {0}, has been bound by Jersey EJB component provider.", component);
+        return true;
+    }
+
+    @Override
+    public void done() {
+        registerEjbExceptionMapper();
+    }
+
+    private void registerEjbExceptionMapper() {
+        injectionManager.register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(EjbExceptionMapper.class).to(ExceptionMapper.class).in(Singleton.class);
+            }
+        });
+    }
+
+    private boolean isEjbComponent(Class<?> component) {
+        for (Annotation a : component.getAnnotations()) {
+            if (EjbComponentAnnotations.contains(a.annotationType().getName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public InvocationHandler create(Invocable method) {
+
+        final Class<?> resourceClass = method.getHandler().getHandlerClass();
+
+        if (resourceClass == null || !isEjbComponent(resourceClass)) {
+            return null;
+        }
+
+        final Method handlingMethod = method.getDefinitionMethod();
+
+        for (Class iFace : remoteAndLocalIfaces(resourceClass)) {
+            try {
+                final Method iFaceMethod = iFace.getDeclaredMethod(handlingMethod.getName(), handlingMethod.getParameterTypes());
+                if (iFaceMethod != null) {
+                    return new InvocationHandler() {
+                        @Override
+                        public Object invoke(Object target, Method ignored, Object[] args)
+                                throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+                            return iFaceMethod.invoke(target, args);
+                        }
+                    };
+                }
+            } catch (NoSuchMethodException | SecurityException ex) {
+                logLookupException(handlingMethod, resourceClass, iFace, ex);
+            }
+        }
+        return null;
+    }
+
+    private void logLookupException(final Method method, final Class<?> component, Class<?> iFace, Exception ex) {
+        LOGGER.log(Level.WARNING, MessageFormat.format(
+            "Exception thrown when trying to lookup actual handling method, {0}, for EJB type, {1}, using interface {2}.",
+            method, component, iFace), ex);
+    }
+
+    private static List<Class> remoteAndLocalIfaces(final Class<?> resourceClass) {
+        final List<Class> allLocalOrRemoteIfaces = new LinkedList<>();
+        if (resourceClass.isAnnotationPresent(Remote.class)) {
+            allLocalOrRemoteIfaces.addAll(Arrays.asList(resourceClass.getAnnotation(Remote.class).value()));
+        }
+        if (resourceClass.isAnnotationPresent(Local.class)) {
+            allLocalOrRemoteIfaces.addAll(Arrays.asList(resourceClass.getAnnotation(Local.class).value()));
+        }
+        for (Class<?> i : resourceClass.getInterfaces()) {
+            if (i.isAnnotationPresent(Remote.class) || i.isAnnotationPresent(Local.class)) {
+                allLocalOrRemoteIfaces.add(i);
+            }
+        }
+        if (allLocalOrRemoteIfaces.isEmpty()) {
+            for (Class<?> i : resourceClass.getInterfaces()) {
+                if (isAcceptableLocalInterface(i)) {
+                    allLocalOrRemoteIfaces.add(i);
+                }
+            }
+        }
+        return allLocalOrRemoteIfaces;
+    }
+
+    private static boolean isAcceptableLocalInterface(final Class<?> iface) {
+        if ("jakarta.ejb".equals(iface.getPackage().getName())) {
+            return false;
+        }
+        return !Serializable.class.equals(iface) && !Externalizable.class.equals(iface);
+    }
+
+    private static InitialContext getInitialContext() {
+        try {
+            return new InitialContext();
+        } catch (Exception ex) {
+            throw new IllegalStateException("InitialContext not found. JAX-RS EJB support is not available.", ex);
+        }
+    }
+
+    private static Object lookup(InitialContext ic, Class<?> rawType, String name, EjbComponentProvider provider)
+            throws NamingException {
+        try {
+            return lookupSimpleForm(ic, rawType, name, provider);
+        } catch (NamingException ex) {
+            LOGGER.log(Level.WARNING, "An instance of EJB class, " + rawType.getName()
+                + ", could not be looked up using simple form name. Attempting to look up using the fully-qualified form name.",
+                ex);
+            return lookupFullyQualifiedForm(ic, rawType, name, provider);
+        }
+    }
+
+    private static Object lookupSimpleForm(
+            InitialContext ic,
+            Class<?> rawType,
+            String name,
+            EjbComponentProvider provider) throws NamingException {
+        if (provider.libNames.isEmpty()) {
+            String jndiName = "java:module/" + name;
+            return ic.lookup(jndiName);
+        } else {
+            NamingException ne = null;
+            for (String moduleName : provider.libNames) {
+                String jndiName = "java:app/" + moduleName + "/" + name;
+                Object result;
+                try {
+                    result = ic.lookup(jndiName);
+                    if (result != null && isLookupInstanceValid(rawType, result)) {
+                        return result;
+                    }
+                } catch (NamingException e) {
+                    ne = e;
+                }
+            }
+            throw (ne != null) ? ne : new NamingException();
+        }
+    }
+
+    private static Object lookupFullyQualifiedForm(
+            InitialContext ic,
+            Class<?> rawType,
+            String name,
+            EjbComponentProvider provider) throws NamingException {
+        if (provider.libNames.isEmpty()) {
+            String jndiName = "java:module/" + name + "!" + rawType.getName();
+            return ic.lookup(jndiName);
+        } else {
+            NamingException ne = null;
+            for (String moduleName : provider.libNames) {
+                String jndiName = "java:app/" + moduleName + "/" + name + "!" + rawType.getName();
+                Object result;
+                try {
+                    result = ic.lookup(jndiName);
+                    if (result != null && isLookupInstanceValid(rawType, result)) {
+                        return result;
+                    }
+                } catch (NamingException e) {
+                    ne = e;
+                }
+            }
+            throw (ne != null) ? ne : new NamingException();
+        }
+    }
+
+    private static boolean isLookupInstanceValid(Class<?> rawType, Object result){
+        return rawType.isInstance(result)
+                                || remoteAndLocalIfaces(rawType)
+                                        .stream()
+                                        .filter(iface -> iface.isInstance(result))
+                                        .findAny()
+                                        .isPresent();
+    }
+}
diff --git a/appserver/web/jersey-ejb-component-provider/src/main/java/org/glassfish/jersey/gf/ejb/internal/EjbExceptionMapper.java b/appserver/web/jersey-ejb-component-provider/src/main/java/org/glassfish/jersey/gf/ejb/internal/EjbExceptionMapper.java
new file mode 100644
index 0000000..4e628ca
--- /dev/null
+++ b/appserver/web/jersey-ejb-component-provider/src/main/java/org/glassfish/jersey/gf/ejb/internal/EjbExceptionMapper.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 Contributors to the Eclipse Foundation
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.gf.ejb.internal;
+
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ExceptionMapper;
+
+import jakarta.ejb.EJBException;
+import jakarta.inject.Inject;
+import jakarta.inject.Provider;
+
+import org.glassfish.jersey.spi.ExceptionMappers;
+import org.glassfish.jersey.spi.ExtendedExceptionMapper;
+
+/**
+ * Helper class to handle exceptions wrapped by the EJB container with EJBException.
+ * If this mapper was not registered, no {@link WebApplicationException}
+ * would end up mapped to the corresponding response.
+ *
+ * @author Paul Sandoz
+ * @author Jakub Podlesak
+ */
+public class EjbExceptionMapper implements ExtendedExceptionMapper<EJBException> {
+
+    private final Provider<ExceptionMappers> mappers;
+
+    /**
+     * Create new EJB exception mapper.
+     *
+     * @param mappers utility to find mapper delegate.
+     */
+    @Inject
+    public EjbExceptionMapper(Provider<ExceptionMappers> mappers) {
+        this.mappers = mappers;
+    }
+
+    @Override
+    public Response toResponse(EJBException exception) {
+        return causeToResponse(exception);
+    }
+
+    @Override
+    public boolean isMappable(EJBException exception) {
+        try {
+            return (causeToResponse(exception) != null);
+        } catch (Throwable ignored) {
+            return false;
+        }
+    }
+
+    private Response causeToResponse(EJBException exception) {
+
+        final Exception cause = exception.getCausedByException();
+
+        if (cause != null) {
+
+            final ExceptionMapper mapper = mappers.get().findMapping(cause);
+            if (mapper != null && mapper != this) {
+
+                return mapper.toResponse(cause);
+
+            } else if (cause instanceof WebApplicationException) {
+
+                return ((WebApplicationException) cause).getResponse();
+            }
+        }
+        return null;
+    }
+}
diff --git a/appserver/web/jersey-ejb-component-provider/src/main/java/org/glassfish/jersey/gf/ejb/internal/package-info.java b/appserver/web/jersey-ejb-component-provider/src/main/java/org/glassfish/jersey/gf/ejb/internal/package-info.java
new file mode 100644
index 0000000..700cfe7
--- /dev/null
+++ b/appserver/web/jersey-ejb-component-provider/src/main/java/org/glassfish/jersey/gf/ejb/internal/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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 Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Jersey internal package supporting Jersey EJB injections in Glassfish 4 environment.
+ */
+package org.glassfish.jersey.gf.ejb.internal;
diff --git a/appserver/web/jersey-ejb-component-provider/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ComponentProvider b/appserver/web/jersey-ejb-component-provider/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ComponentProvider
new file mode 100644
index 0000000..2db13b7
--- /dev/null
+++ b/appserver/web/jersey-ejb-component-provider/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ComponentProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.gf.ejb.internal.EjbComponentProvider
diff --git a/appserver/web/pom.xml b/appserver/web/pom.xml
index 454ce55..df92ce3 100755
--- a/appserver/web/pom.xml
+++ b/appserver/web/pom.xml
@@ -39,6 +39,7 @@
         <module>war-util</module>
         <module>jstl-connector</module>
         <module>jersey-mvc-connector</module>
+        <module>jersey-ejb-component-provider</module>
         <module>jsf-connector</module>
         <module>jspcaching-connector</module>
         <module>gui-plugin-common</module>
diff --git a/nucleus/admin/rest/rest-service/osgi.bundle b/nucleus/admin/rest/rest-service/osgi.bundle
index 4060ab9..092955f 100644
--- a/nucleus/admin/rest/rest-service/osgi.bundle
+++ b/nucleus/admin/rest/rest-service/osgi.bundle
@@ -15,21 +15,26 @@
 #
 
 -exportcontents: \
-                        org.glassfish.admin.rest, \
-                        org.glassfish.admin.rest.adapter, \
-                        org.glassfish.admin.rest.model, \
-						org.glassfish.admin.rest.utils, \
-						org.glassfish.admin.rest.composite, \
-						org.glassfish.admin.rest.composite.metadata, \
-						org.glassfish.admin.rest.resources.composite, \
-						org.glassfish.admin.rest.wadl, \
-						org.glassfish.admin.rest.utils.xml
-DynamicImport-Package: \
-                        org.glassfish.flashlight, \
-                        org.glassfish.flashlight.datatree
+    org.glassfish.admin.rest, \
+    org.glassfish.admin.rest.adapter, \
+    org.glassfish.admin.rest.model, \
+    org.glassfish.admin.rest.utils, \
+    org.glassfish.admin.rest.composite, \
+    org.glassfish.admin.rest.composite.metadata, \
+    org.glassfish.admin.rest.resources.composite, \
+    org.glassfish.admin.rest.wadl, \
+    org.glassfish.admin.rest.utils.xml
 
-# logging packages are not always available in every distribution, 
+DynamicImport-Package: \
+    org.glassfish.flashlight, \
+    org.glassfish.flashlight.datatree
+
+# logging packages are not always available in every distribution,
 # e.g., in some embedded setup, we may disable logging altogether.
 # So, we need to mark it as an optional dependency. We can remove this
 # when admin team moves logging rest resources out of this module to logging module.
-Import-Package: !org.glassfish.admin.rest.resources.generatedASM, com.sun.enterprise.server.logging.*; resolution:=optional, *
+Import-Package: \
+    !org.glassfish.admin.rest.resources.generatedASM, \
+    org.glassfish.jersey.gf.ejb.internal, \
+    com.sun.enterprise.server.logging.*; resolution:=optional, \
+    *
diff --git a/nucleus/admin/rest/rest-service/pom.xml b/nucleus/admin/rest/rest-service/pom.xml
index fb54458..05d4597 100644
--- a/nucleus/admin/rest/rest-service/pom.xml
+++ b/nucleus/admin/rest/rest-service/pom.xml
@@ -56,6 +56,11 @@
         </dependency>
 
         <dependency>
+            <groupId>org.glassfish.main.web</groupId>
+            <artifactId>jersey-ejb-component-provider</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
             <groupId>org.glassfish.main.admin</groupId>
             <artifactId>gf-restadmin-connector</artifactId>
             <version>${project.version}</version>