Skip to content

Commit

Permalink
Merge pull request #491 from OpenNTF/feature/issue-469-context-params
Browse files Browse the repository at this point in the history
Add support for specifying context-param in web.xml in an NSF (fixes #469)
  • Loading branch information
jesse-gallagher authored Oct 27, 2023
2 parents 0b19c06 + 0f68f83 commit e9d1700
Show file tree
Hide file tree
Showing 14 changed files with 230 additions and 2 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,26 @@ These Servlets participate in the XPages lifecycle and have programmatic access

Note, however, that other Servlet artifacts such as `@WebFilter` and `@WebListener` are not yet supported.

#### web.xml

It also adds preliminary support for web.xml, currently in the form of only supporting context parameters. For example, this file can be stored in the NSF in WebContext/WEB-INF/web.xml:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0" metadata-complete="false">

<context-param>
<param-name>org.openntf.example.param</param-name>
<param-value>I am the param value</param-value>
</context-param>
</web-app>
```

When present, this will cause the "org.openntf.example.param" parameter to be set and available for all Jakarta users of `ServletContext`, such as Servlets, REST, Pages, and Faces.

## RESTful Web Services

The [RESTful Web Services](https://jakarta.ee/specifications/restful-ws/3.0/) specification is the standard way to provide web services in Java EE applications. A version of it has been included for a long time in Domino by way of the Extension Library. However, this version is also out of date, with Apache Wink implementing JAX-RS 1.1.1.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ public void init(ServletConfig config) throws ServletException {
super.init(config);
this.config = config;

// Look for a web.xml file and populate init params
ServletUtil.populateWebXmlParams(module, config.getServletContext());

// Look for registered ServletContainerInitializers and emulate the behavior
List<ServletContainerInitializer> initializers = LibraryUtil.findExtensions(ServletContainerInitializer.class);
for(ServletContainerInitializer initializer : initializers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -233,7 +234,11 @@ public String getInitParameter(String name) {

@Override
public Enumeration<String> getInitParameterNames() {
return delegate.getInitParameterNames();
Map<String, String> params = getExtraInitParameters();
Set<String> paramNames = new HashSet<>();
paramNames.addAll(Collections.list(delegate.getInitParameterNames()));
paramNames.addAll(params.keySet());
return Collections.enumeration(paramNames);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,25 @@
package org.openntf.xsp.jakartaee.servlet;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.openntf.xsp.jakartaee.util.LibraryUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.ibm.commons.xml.DOMUtil;
import com.ibm.commons.xml.XMLException;
import com.ibm.commons.xml.XResult;
import com.ibm.designer.runtime.domino.adapter.ComponentModule;
import com.ibm.designer.runtime.domino.adapter.servlet.LCDAdapterHttpServletResponse;
import com.ibm.designer.runtime.domino.bootstrap.adapter.HttpServletResponseAdapter;
import com.ibm.domino.xsp.bridge.http.servlet.XspCmdHttpServletResponse;
Expand All @@ -42,6 +56,8 @@
public enum ServletUtil {
;

private static final Logger log = Logger.getLogger(ServletUtil.class.getName());

public static javax.servlet.Servlet newToOld(jakarta.servlet.Servlet servlet) {
if(servlet == null) {
return null;
Expand Down Expand Up @@ -481,4 +497,74 @@ public static boolean isClosedConnection(Throwable t) {

return false;
}

/**
* Processes init-param values from any WEB-INF/web.xml and META-INF/web-fragment.xml files
* inside the module.
*
* @param module the module to load resources from
* @param context the context to populate with init parameters
* @since 2.15.0
*/
public static void populateWebXmlParams(ComponentModule module, jakarta.servlet.ServletContext context) {
try {
if(module != null) {
InputStream is = module.getResourceAsStream("/WEB-INF/web.xml"); //$NON-NLS-1$
if(is != null) {
if(log.isLoggable(Level.FINE)) {
log.fine(MessageFormat.format("Processing web.xml in {0}", module.getModuleName()));
}
processWebXmlPart(module, context, is);
}

// Look for META-INF/web-fragment.xml files
ClassLoader cl = module.getModuleClassLoader();
if(cl != null) {
Enumeration<URL> fragments = cl.getResources("META-INF/web-fragment.xml"); //$NON-NLS-1$
for(URL url : Collections.list(fragments)) {
if(log.isLoggable(Level.FINE)) {
log.fine(MessageFormat.format("Processing web-fragment.xml {0} in {1}", url, module.getModuleName()));
}
InputStream fis = url.openStream();
if(fis != null) {
processWebXmlPart(module, context, fis);
}
}
}
}
} catch(Exception e) {
if(log.isLoggable(Level.WARNING)) {
log.log(Level.WARNING, MessageFormat.format("Encountered exception processing web.xml in {0}", module.getModuleName()), e);
}
}
}

private static void processWebXmlPart(ComponentModule module, jakarta.servlet.ServletContext context, InputStream is) throws XMLException {
Document webXml = DOMUtil.createDocument(is);

// Process context-param values
NodeList contextParams = webXml.getDocumentElement().getElementsByTagName("context-param"); //$NON-NLS-1$
for(int i = 0; i < contextParams.getLength(); i++) {
Element contextParam = (Element)contextParams.item(i);
NodeList nameNodes = contextParam.getElementsByTagName("param-name"); //$NON-NLS-1$
if(nameNodes.getLength() < 1) {
if(log.isLoggable(Level.FINE)) {
log.fine(MessageFormat.format("Encountered context-param with missing param-name in {0}", module.getModuleName()));
}
continue;
}
NodeList valueNodes = contextParam.getElementsByTagName("param-value"); //$NON-NLS-1$
if(valueNodes.getLength() < 1) {
if(log.isLoggable(Level.FINE)) {
log.fine(MessageFormat.format("Encountered context-param with missing param-value in {0}", module.getModuleName()));
}
continue;
}

String name = nameNodes.item(0).getTextContent();
String value = valueNodes.item(0).getTextContent();

context.setInitParameter(name, value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ public void doInit(HttpServletRequest req, ServletConfig config) throws ServletE
Properties props = LibraryUtil.getXspProperties(module);
String projectStage = props.getProperty(ProjectStage.PROJECT_STAGE_PARAM_NAME, ""); //$NON-NLS-1$
context.setInitParameter(ProjectStage.PROJECT_STAGE_PARAM_NAME, projectStage);

// Look for a web.xml file and populate init params
ServletUtil.populateWebXmlParams(module, config.getServletContext());

Bundle b = FrameworkUtil.getBundle(FacesServlet.class);
Bundle b2 = FrameworkUtil.getBundle(MyFacesContainerInitializer.class);
Expand Down
3 changes: 3 additions & 0 deletions eclipse/nsfs/nsf-jakartaee-example/odp/Code/Jars/fragment.jar
Git LFS file not shown
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<note class="form" xmlns="http://www.lotus.com/dxl">
<item name="$TITLE">
<text>fragment.jar</text>
</item>
<item name="$Flags">
<text>34567Cg~,</text>
</item>
<item name="$FileNames" sign="true">
<text>fragment.jar</text>
</item>
</note>
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package rest;

import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import jakarta.inject.Inject;
import jakarta.servlet.ServletContext;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
Expand All @@ -14,9 +19,24 @@ public class JaxRsConfigResource {
@Context
private Configuration configuration;

@Inject
private ServletContext servletContext;

@GET
@Produces(MediaType.APPLICATION_JSON)
public Map<String, Object> get() {
return configuration.getProperties();
}

@Path("servlet")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Map<String, Object> getServletConfig() {
return Collections.list(servletContext.getInitParameterNames())
.stream()
.collect(Collectors.toMap(
Function.identity(),
name -> servletContext.getInitParameter(name)
));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<facelet-taglib>
<namespace>http://openntf.org/facelets</namespace>
<tag>
<tag-name>exampleFacelet</tag-name>
<source>facelets/exampleFacelet.xhtml</source>
</tag>
</facelet-taglib>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<body>
<ui:composition>
<div class="example-facelet">I am the example facelet</div>
</ui:composition>
</body>
</html>
16 changes: 16 additions & 0 deletions eclipse/nsfs/nsf-jakartaee-example/odp/WebContent/WEB-INF/web.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0" metadata-complete="false">

<context-param>
<param-name>org.openntf.example.param</param-name>
<param-value>I am the param value</param-value>
</context-param>

<context-param>
<param-name>jakarta.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/exampleFacelets.taglib.xml</param-value>
</context-param>
</web-app>
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
<f:view xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:comp="http://java.sun.com/jsf/composite/comps">
xmlns:comp="http://java.sun.com/jsf/composite/comps"
xmlns:openntf="http://openntf.org/facelets">

<h:head>
<title>JSF 3.0 Hello World</title>
Expand Down Expand Up @@ -64,6 +65,9 @@

<dt>database</dt>
<dd><h:outputText value="#{database}"/></dd>

<dt>initParam</dt>
<dd><h:outputText value="#{initParam}"/></dd>
</dl>

<fieldset>
Expand All @@ -77,5 +81,7 @@
<p><h:outputText id="formOutput" value="#{applicationGuy.beanProperty}"/></p>
</h:form>
</fieldset>

<openntf:exampleFacelet/>
</h:body>
</f:view>
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,28 @@ public void testContributedProperty() {
fail("Received unexpected JSON: " + json, e);
}
}

/**
* Tests that an in-NSF web.xml file can contribute context parameters that will be picked up
* by JAX-RS
*
* @see <a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/issues/469">Issue #469</a>
*/
@Test
public void testWebXmlProperty() {
Client client = getAnonymousClient();
WebTarget target = client.target(getRestUrl(null, TestDatabase.MAIN) + "/jaxrsConfig/servlet");
Response response = target.request().get();

String json = response.readEntity(String.class);
try {
JsonObject config = Json.createReader(new StringReader(json)).readObject();
assertNotNull(config);

assertEquals("I am the param value", config.getString("org.openntf.example.param", null), () -> "Received unexpected JSON: " + json);
assertEquals("I am the param value from a fragment in a JAR", config.getString("org.openntf.example.fragment.param", null), () -> "Received unexpected JSON: " + json);
} catch(Exception e) {
fail("Received unexpected JSON: " + json, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ public void testHelloPage(WebDriver driver) {
assertEquals("dev/jakartaee.nsf", dd.getText());
}

// Make sure the init param from web.xml made it
{
WebElement dd = driver.findElement(By.xpath("//dt[text()=\"initParam\"]/following-sibling::dd[1]"));
assertTrue(dd.getText().contains("org.openntf.example.param=I am the param value"), () -> "initParam value should have contained the example param: " + dd.getText());

// And the one from the web-fragment.xml
assertTrue(dd.getText().contains("org.openntf.example.fragment.param=I am the param value from a fragment in a JAR"), () -> "initParam value should have contained the example param: " + dd.getText());
}

WebElement input = form.findElement(By.xpath("input[1]"));
assertTrue(input.getAttribute("id").endsWith(":appGuyProperty"), () -> input.getAttribute("id"));
// May be set by previous test
Expand All @@ -112,6 +121,12 @@ public void testHelloPage(WebDriver driver) {
assertEquals(expected, span.getText());
}

// Check for the NSF-defined Facelet library
{
WebElement div = driver.findElement(By.cssSelector("div.example-facelet"));
assertEquals("I am the example facelet", div.getText());
}

// While here, test the phase listeners
{
WebElement dd = driver.findElement(By.xpath("//dt[text()=\"Faces Phase Listener Output\"]/following-sibling::dd[1]"));
Expand Down

0 comments on commit e9d1700

Please sign in to comment.