diff --git a/eclipse/bundles/org.openntf.xsp.cdi/src/org/openntf/xsp/cdi/util/ContainerUtil.java b/eclipse/bundles/org.openntf.xsp.cdi/src/org/openntf/xsp/cdi/util/ContainerUtil.java
index cc0764103..aa672c321 100644
--- a/eclipse/bundles/org.openntf.xsp.cdi/src/org/openntf/xsp/cdi/util/ContainerUtil.java
+++ b/eclipse/bundles/org.openntf.xsp.cdi/src/org/openntf/xsp/cdi/util/ContainerUtil.java
@@ -56,12 +56,11 @@
import org.openntf.xsp.jakartaee.util.ModuleUtil;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
-import org.osgi.framework.BundleReference;
import org.osgi.framework.wiring.BundleWiring;
import com.ibm.commons.util.StringUtil;
import com.ibm.designer.runtime.domino.adapter.ComponentModule;
-import com.ibm.domino.xsp.adapter.osgi.OSGIModule;
+import com.ibm.domino.xsp.adapter.osgi.AbstractOSGIModule;
import jakarta.el.ELResolver;
import jakarta.el.ExpressionFactory;
@@ -143,7 +142,7 @@ public static CDI
*
* @param bundle a {@link Bundle} instance to query
+ * @param nonExported {@code true} to include classes not marked as exported from the bundle
* @return a {@link Stream} of discovered exported classes
* @throws BundleException if there is a problem parsing the bundle manifest
*/
- public static Stream findCandidateBeanClassNames(Bundle bundle) throws BundleException {
+ public static Stream findCandidateBeanClassNames(Bundle bundle, boolean nonExported) throws BundleException {
ScanType scanType = determineScanType(bundle);
if(scanType == ScanType.ALL || scanType == ScanType.ANNOTATED) {
String exportPackages = bundle.getHeaders().get("Export-Package"); //$NON-NLS-1$
- if(StringUtil.isNotEmpty(exportPackages)) {
+ if(StringUtil.isNotEmpty(exportPackages) || nonExported) {
// Restrict to exported packages for sanity's sake
- ManifestElement[] elements = ManifestElement.parseHeader("Export-Package", exportPackages); //$NON-NLS-1$
- Set packages = Arrays.stream(elements)
- .map(ManifestElement::getValue)
- .filter(StringUtil::isNotEmpty)
- .collect(Collectors.toSet());
+ Set packages = null;
+ if(!nonExported) {
+ ManifestElement[] elements = ManifestElement.parseHeader("Export-Package", exportPackages); //$NON-NLS-1$
+ packages = Arrays.stream(elements)
+ .map(ManifestElement::getValue)
+ .filter(StringUtil::isNotEmpty)
+ .collect(Collectors.toSet());
+ }
URL jandexUrl = bundle.getResource("/META-INF/jandex.idx"); //$NON-NLS-1$
if(jandexUrl != null) {
@@ -99,24 +109,32 @@ public static Stream findCandidateBeanClassNames(Bundle bundle) throws B
throw new UncheckedIOException(MessageFormat.format("Encountered exception reading jandex.idx for {0}", bundle.getSymbolicName()), e);
}
- return packages.stream()
- .map(p -> jandex.getClassesInPackage(p))
- .flatMap(Collection::stream)
- .map(c -> c.name().toString())
- .distinct();
+ if(packages != null) {
+ return packages.stream()
+ .map(p -> jandex.getClassesInPackage(p))
+ .flatMap(Collection::stream)
+ .map(c -> c.name().toString())
+ .distinct();
+ } else {
+ return jandex.getKnownClasses()
+ .stream()
+ .map(c -> c.name().toString())
+ .distinct();
+ }
} else {
// Otherwise, do a manual crawl for class names
String baseUrl = bundle.getEntry("/").toString(); //$NON-NLS-1$
List entries = Collections.list(bundle.findEntries("/", "*.class", true)); //$NON-NLS-1$ //$NON-NLS-2$
Set classNames = new HashSet<>();
+ Set fpackages = packages;
return entries.stream()
.parallel()
.map(String::valueOf)
.map(url -> url.substring(baseUrl.length()))
.map(LibraryUtil::toClassName)
.filter(StringUtil::isNotEmpty)
- .filter(className -> packages.contains(className.substring(0, className.lastIndexOf('.'))))
+ .filter(className -> fpackages == null || fpackages.contains(className.substring(0, className.lastIndexOf('.'))))
.filter(className -> !classNames.contains(className))
.peek(classNames::add)
.sequential();
@@ -135,15 +153,15 @@ public static Stream findCandidateBeanClassNames(Bundle bundle) throws B
* the bundle's {@code Export-Package} listing.
*
* @param bundle a {@link Bundle} instance to query
- * @param force include classes even when there's no beans.xml
+ * @param nonExported {@code true} to include classes not marked as exported from the bundle
* @return a {@link Stream} of discovered exported classes
* @throws BundleException if there is a problem parsing the bundle manifest
* @since 2.3.0
*/
- public static Stream> findBeanClasses(Bundle bundle) throws BundleException {
+ public static Stream> findBeanClasses(Bundle bundle, boolean nonExported) throws BundleException {
ScanType scanType = determineScanType(bundle);
- return findCandidateBeanClassNames(bundle)
+ return findCandidateBeanClassNames(bundle, nonExported)
.map(className -> {
try {
return bundle.loadClass(className);
@@ -162,6 +180,30 @@ public static Stream> findBeanClasses(Bundle bundle) throws BundleExcep
.map(c -> (Class>)c);
}
+ public static Optional getBundleForClassLoader(ClassLoader cl) {
+ // Equinox Servlets
+ if(cl instanceof BundleReference) {
+ return Optional.of(((BundleReference) cl).getBundle());
+ }
+
+ // Bundle webapps
+ if("com.ibm.pvc.internal.webcontainer.webapp.BundleWebAppClassLoader".equals(cl.getClass().getName())) { //$NON-NLS-1$
+ return AccessController.doPrivileged((PrivilegedAction>)() -> {
+ try {
+ if(WEBAPP_BUNDLE_FIELD == null) {
+ WEBAPP_BUNDLE_FIELD = cl.getClass().getDeclaredField("bundle"); //$NON-NLS-1$
+ WEBAPP_BUNDLE_FIELD.setAccessible(true);
+ }
+ return Optional.ofNullable((Bundle)WEBAPP_BUNDLE_FIELD.get(cl));
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ return Optional.empty();
+ }
+
private static ScanType determineScanType(Bundle bundle) {
URL beansXml = bundle.getResource("/META-INF/beans.xml"); //$NON-NLS-1$
if(beansXml == null) {
diff --git a/eclipse/bundles/org.openntf.xsp.jakartaee.commons/src/org/openntf/xsp/jakartaee/discovery/impl/ComponentModuleComponentEnabledLocator.java b/eclipse/bundles/org.openntf.xsp.jakartaee.commons/src/org/openntf/xsp/jakartaee/discovery/impl/ComponentModuleComponentEnabledLocator.java
index e27dff0b5..2ba766e95 100644
--- a/eclipse/bundles/org.openntf.xsp.jakartaee.commons/src/org/openntf/xsp/jakartaee/discovery/impl/ComponentModuleComponentEnabledLocator.java
+++ b/eclipse/bundles/org.openntf.xsp.jakartaee.commons/src/org/openntf/xsp/jakartaee/discovery/impl/ComponentModuleComponentEnabledLocator.java
@@ -37,7 +37,6 @@ public boolean isActive() {
@Override
public boolean isComponentEnabled(String componentId) {
- // TODO consider using an NSFComponentModule surrounding an OSGIModule
ComponentModule module = ComponentModuleLocator.getDefault().get().getActiveModule();
if(module != null) {
return LibraryUtil.usesLibrary(componentId, module);
diff --git a/eclipse/bundles/org.openntf.xsp.microprofile.health/src/org/openntf/xsp/microprofile/health/HealthBeanContributor.java b/eclipse/bundles/org.openntf.xsp.microprofile.health/src/org/openntf/xsp/microprofile/health/HealthBeanContributor.java
index 788e598b5..0f4bd91fd 100644
--- a/eclipse/bundles/org.openntf.xsp.microprofile.health/src/org/openntf/xsp/microprofile/health/HealthBeanContributor.java
+++ b/eclipse/bundles/org.openntf.xsp.microprofile.health/src/org/openntf/xsp/microprofile/health/HealthBeanContributor.java
@@ -41,8 +41,7 @@ public Collection> getBeanClasses() {
// Look for annotated beans in io.smallrye.health
Bundle bundle = FrameworkUtil.getBundle(ResponseProvider.class);
try {
- return DiscoveryUtil.findBeanClasses(bundle)
- // TODO filter to only annotated
+ return DiscoveryUtil.findBeanClasses(bundle, false)
.collect(Collectors.toList());
} catch (BundleException e) {
throw new RuntimeException(e);
diff --git a/eclipse/bundles/org.openntf.xsp.nosql/src/org/openntf/xsp/nosql/NoSQLBeanContributor.java b/eclipse/bundles/org.openntf.xsp.nosql/src/org/openntf/xsp/nosql/NoSQLBeanContributor.java
index 8d649ebbe..e50d1b758 100644
--- a/eclipse/bundles/org.openntf.xsp.nosql/src/org/openntf/xsp/nosql/NoSQLBeanContributor.java
+++ b/eclipse/bundles/org.openntf.xsp.nosql/src/org/openntf/xsp/nosql/NoSQLBeanContributor.java
@@ -53,7 +53,7 @@ public Collection> getBeanClasses() {
.map(FrameworkUtil::getBundle)
.flatMap(t -> {
try {
- return DiscoveryUtil.findBeanClasses(t);
+ return DiscoveryUtil.findBeanClasses(t, false);
} catch (BundleException e) {
throw new RuntimeException(e);
}
diff --git a/eclipse/tests/it-xsp-jakartaee/src/test/java/it/org/openntf/xsp/jakartaee/webapp/cdi/TestWebappBeanResource.java b/eclipse/tests/it-xsp-jakartaee/src/test/java/it/org/openntf/xsp/jakartaee/webapp/cdi/TestWebappBeanResource.java
new file mode 100644
index 000000000..1792e1d6b
--- /dev/null
+++ b/eclipse/tests/it-xsp-jakartaee/src/test/java/it/org/openntf/xsp/jakartaee/webapp/cdi/TestWebappBeanResource.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2018-2023 Contributors to the XPages Jakarta EE Support Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package it.org.openntf.xsp.jakartaee.webapp.cdi;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.StringReader;
+
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Response;
+
+import org.junit.jupiter.api.Test;
+
+import it.org.openntf.xsp.jakartaee.AbstractWebClientTest;
+import it.org.openntf.xsp.jakartaee.TestDatabase;
+
+@SuppressWarnings({ "nls" })
+public class TestWebappBeanResource extends AbstractWebClientTest {
+
+ @Test
+ public void testBundleBean() {
+ int expectedIdentity = 0;
+ // First NSF - uses .cdibundle
+ {
+ JsonObject obj = getBean(getRootUrl(null, TestDatabase.OSGI_WEBAPP) + "/cdiServlet");
+ assertEquals("Hello from webappBean", obj.getString("hello"));
+ expectedIdentity = obj.getInt("identity");
+ assertNotEquals(0, expectedIdentity);
+ }
+ // Call this again to ensure that it uses the same bean
+ {
+ JsonObject obj = getBean(getRootUrl(null, TestDatabase.OSGI_WEBAPP) + "/cdiServlet");
+ assertEquals("Hello from webappBean", obj.getString("hello"));
+ int identity = obj.getInt("identity");
+ assertEquals(expectedIdentity, identity);
+ }
+ }
+
+ private JsonObject getBean(String base) {
+ Client client = getAnonymousClient();
+ WebTarget target = client.target(base);
+ Response response = target.request().get();
+
+ checkResponse(200, response);
+ String output = response.readEntity(String.class);
+ try {
+ return Json.createReader(new StringReader(output)).readObject();
+ } catch(Exception e) {
+ fail("Exception parsing JSON: " + output, e);
+ return null;
+ }
+ }
+}
diff --git a/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/META-INF/MANIFEST.MF b/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/META-INF/MANIFEST.MF
index 8fc77255f..cee66da84 100644
--- a/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/META-INF/MANIFEST.MF
+++ b/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/META-INF/MANIFEST.MF
@@ -3,8 +3,18 @@ Bundle-ManifestVersion: 2
Bundle-Name: Example Jakarta EE Webapp
Bundle-SymbolicName: org.openntf.xsp.jakarta.example.webapp;singleton:=true
Automatic-Module-Name: org.openntf.xsp.jakarta.example.webapp
+Bundle-ActivationPolicy: lazy
+Bundle-Activator: org.openntf.xsp.jakarta.example.webapp.WebappActivator
Bundle-Version: 2.13.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: org.openntf.xsp.jsp;bundle-version="2.8.0",
org.openntf.xsp.jakarta.servlet;bundle-version="2.8.0"
-Import-Package: com.ibm.designer.runtime.domino.adapter
+Import-Package: com.ibm.designer.runtime.domino.adapter,
+ jakarta.enterprise.context;version="3.0.0",
+ jakarta.enterprise.inject;version="3.0.0",
+ jakarta.enterprise.inject.spi;version="3.0.0",
+ jakarta.inject;version="2.0.0",
+ jakarta.json.bind;version="2.0.0",
+ org.openntf.xsp.cdi.provider;version="2.13.0",
+ org.osgi.framework;version="1.8.0"
+DynamicImport-Package: *
diff --git a/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/META-INF/beans.xml b/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/META-INF/beans.xml
new file mode 100644
index 000000000..a64451de7
--- /dev/null
+++ b/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/META-INF/beans.xml
@@ -0,0 +1,23 @@
+
+
+
\ No newline at end of file
diff --git a/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/WebContent/WEB-INF/web.xml b/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/WebContent/WEB-INF/web.xml
index a587fac4d..57ac381df 100644
--- a/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/WebContent/WEB-INF/web.xml
+++ b/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/WebContent/WEB-INF/web.xml
@@ -59,4 +59,18 @@
LocatorTestServlet
/locatorTestServlet
+
+
+ CDIServlet
+ org.openntf.xsp.jakarta.servlet.webapp.JakartaServletFacade
+
+
+ org.openntf.xsp.jakarta.servlet.class
+ org.openntf.xsp.jakarta.example.webapp.CDIServlet
+
+
+
+ CDIServlet
+ /cdiServlet
+
\ No newline at end of file
diff --git a/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/src/org/openntf/xsp/jakarta/example/webapp/CDIServlet.java b/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/src/org/openntf/xsp/jakarta/example/webapp/CDIServlet.java
new file mode 100644
index 000000000..baebf3970
--- /dev/null
+++ b/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/src/org/openntf/xsp/jakarta/example/webapp/CDIServlet.java
@@ -0,0 +1,24 @@
+package org.openntf.xsp.jakarta.example.webapp;
+
+import java.io.IOException;
+
+import org.openntf.xsp.jakarta.example.webapp.beans.WebappBean;
+
+import jakarta.enterprise.inject.spi.CDI;
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+public class CDIServlet extends HttpServlet {
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ WebappBean bean = CDI.current().select(WebappBean.class).get();
+ ServletOutputStream out = resp.getOutputStream();
+ Jsonb jsonb = JsonbBuilder.create();
+ jsonb.toJson(bean, out);
+ }
+}
diff --git a/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/src/org/openntf/xsp/jakarta/example/webapp/WebappActivator.java b/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/src/org/openntf/xsp/jakarta/example/webapp/WebappActivator.java
new file mode 100644
index 000000000..ec00fb5b0
--- /dev/null
+++ b/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/src/org/openntf/xsp/jakarta/example/webapp/WebappActivator.java
@@ -0,0 +1,21 @@
+package org.openntf.xsp.jakarta.example.webapp;
+
+import org.openntf.xsp.cdi.provider.DominoCDIProvider;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+import jakarta.enterprise.inject.spi.CDI;
+
+public class WebappActivator implements BundleActivator {
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ CDI.setCDIProvider(new DominoCDIProvider());
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+
+ }
+
+}
diff --git a/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/src/org/openntf/xsp/jakarta/example/webapp/beans/WebappBean.java b/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/src/org/openntf/xsp/jakarta/example/webapp/beans/WebappBean.java
new file mode 100644
index 000000000..228434e93
--- /dev/null
+++ b/eclipse/tests/org.openntf.xsp.jakarta.example.webapp/src/org/openntf/xsp/jakarta/example/webapp/beans/WebappBean.java
@@ -0,0 +1,14 @@
+package org.openntf.xsp.jakarta.example.webapp.beans;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+@ApplicationScoped
+public class WebappBean {
+ public String getHello() {
+ return "Hello from webappBean";
+ }
+
+ public int getIdentity() {
+ return System.identityHashCode(this);
+ }
+}