This is an automated email from the ASF dual-hosted git repository.
enapps-enorman pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-factory-configuration.git
The following commit(s) were added to refs/heads/master by this push:
new 0939378 SLING-13165 migrate to jakarta Servlet (#23)
0939378 is described below
commit 093937814409e2a669bf113dc999aeed8414a9ed
Author: Eric Norman <[email protected]>
AuthorDate: Mon Apr 20 14:42:54 2026 -0700
SLING-13165 migrate to jakarta Servlet (#23)
---
bnd.bnd | 4 +-
pom.xml | 45 +++-
.../ConfigurationSerializerWebConsolePlugin.java | 95 ++++----
...onfigurationSerializerWebConsolePluginTest.java | 239 +++++++++++++++++++++
.../configuration/impl/ReflectionTools.java | 71 ++++++
5 files changed, 400 insertions(+), 54 deletions(-)
diff --git a/bnd.bnd b/bnd.bnd
index e94d050..2cc25f3 100644
--- a/bnd.bnd
+++ b/bnd.bnd
@@ -1,7 +1,7 @@
Import-Package: !org.osgi.service.metatype, \
!org.apache.felix.webconsole.spi,\
- javax.servlet;resolution:=optional,\
+ jakarta.servlet;resolution:=optional,\
*
DynamicImport-Package: org.osgi.service.metatype, \
org.apache.felix.webconsole.spi, \
- javax.servlet
+ jakarta.servlet
diff --git a/pom.xml b/pom.xml
index 1c148e5..0c20cc5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,7 +27,7 @@
</parent>
<artifactId>org.apache.sling.installer.factory.configuration</artifactId>
- <version>1.4.11-SNAPSHOT</version>
+ <version>2.0.0-SNAPSHOT</version>
<name>Apache Sling Installer Configuration Factory</name>
<description>Provides support for configurations to the Apache Sling OSGi
installer</description>
@@ -36,7 +36,7 @@
<connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-factory-configuration.git</connection>
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-factory-configuration.git</developerConnection>
<tag>HEAD</tag>
-
<url>https://github.com/apache/sling-org-apache-sling-installer-factory-configuration.git</url>
+
<url>https://github.com/apache/sling-org-apache-sling-installer-factory-configuration/tree/${project.scm.tag}</url>
</scm>
<properties>
@@ -45,6 +45,12 @@
</properties>
<dependencies>
+ <!-- JetBrains annotations for null-analysis (SLING-7798) -->
+ <dependency>
+ <groupId>org.jetbrains</groupId>
+ <artifactId>annotations</artifactId>
+ <scope>provided</scope>
+ </dependency>
<!-- OSGi annotations (OSGi R7) -->
<!--
https://osgi.org/javadoc/osgi.annotation/7.0.0/org/osgi/annotation/versioning/package-frame.html
-->
<dependency>
@@ -99,7 +105,7 @@
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.webconsole</artifactId>
- <version>4.7.0</version>
+ <version>5.0.10</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -123,9 +129,10 @@
<artifactId>org.osgi.util.function</artifactId>
<scope>provided</scope>
</dependency>
+ <!-- for Web Console plugin -->
<dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>javax.servlet-api</artifactId>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
@@ -138,11 +145,39 @@
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-params</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.sling-mock.junit5</artifactId>
+ <version>4.0.4</version>
+ <scope>test</scope>
+ </dependency>
+ <!-- JSON Configurations -->
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.cm.json</artifactId>
+ <version>1.0.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.util.promise</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePlugin.java
b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePlugin.java
index e2f56a2..68bfedb 100644
---
a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePlugin.java
+++
b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePlugin.java
@@ -18,10 +18,6 @@
*/
package org.apache.sling.installer.factories.configuration.impl;
-import javax.servlet.GenericServlet;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -37,9 +33,14 @@ import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
+import jakarta.servlet.GenericServlet;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
import org.apache.sling.installer.api.info.InfoProvider;
import
org.apache.sling.installer.api.serializer.ConfigurationSerializerFactory;
import
org.apache.sling.installer.api.serializer.ConfigurationSerializerFactory.Format;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.osgi.framework.Constants;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
@@ -51,7 +52,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(
- service = javax.servlet.Servlet.class,
+ service = jakarta.servlet.Servlet.class,
property = {
Constants.SERVICE_VENDOR + "=The Apache Software Foundation",
Constants.SERVICE_DESCRIPTION + "=Apache Sling OSGi Installer
Configuration Serializer Web Console Plugin",
@@ -69,16 +70,16 @@ public class ConfigurationSerializerWebConsolePlugin
extends GenericServlet {
private static final String PARAMETER_HIDE_REDUNDANT_PROPERTIES =
"hideRedundantProperties";
/** The logger */
- private final Logger LOGGER =
LoggerFactory.getLogger(ConfigurationSerializerWebConsolePlugin.class);
+ private final transient Logger logger =
LoggerFactory.getLogger(ConfigurationSerializerWebConsolePlugin.class);
@Reference
- ConfigurationAdmin configurationAdmin;
+ transient ConfigurationAdmin configurationAdmin;
@Reference
- private InfoProvider infoProvider;
+ private transient InfoProvider infoProvider;
@Reference
- private ServiceComponentRuntime scr;
+ private transient ServiceComponentRuntime scr;
@Override
public void service(final ServletRequest request, final ServletResponse
response) throws IOException {
@@ -95,7 +96,7 @@ public class ConfigurationSerializerWebConsolePlugin extends
GenericServlet {
try {
serializationFormat =
ConfigurationSerializerFactory.Format.valueOf(format);
} catch (IllegalArgumentException e) {
- LOGGER.warn("Illegal parameter 'format' given, falling back to
default '{}'", serializationFormat, e);
+ logger.warn("Illegal parameter 'format' given, falling back to
default '{}'", serializationFormat, e);
}
}
final boolean hideRedundantProperties;
@@ -108,10 +109,10 @@ public class ConfigurationSerializerWebConsolePlugin
extends GenericServlet {
}
private void dumpConfiguration(
- Configuration configuration,
- ConfigurationSerializerFactory.Format serializationFormat,
+ @Nullable Configuration configuration,
+ @NotNull ConfigurationSerializerFactory.Format serializationFormat,
boolean hideRedundantProperties,
- PrintWriter pw) {
+ @NotNull PrintWriter pw) {
// map with key = configuration pid and value =
Set<ComponentDescriptionDTO>
Map<String, Set<ComponentDescriptionDTO>> allComponentDescriptions =
new HashMap<>();
String pid = configuration != null ? configuration.getPid() : "";
@@ -183,7 +184,7 @@ public class ConfigurationSerializerWebConsolePlugin
extends GenericServlet {
Set<ComponentDescriptionDTO> componentDescriptions =
allComponentDescriptions.get(pidReferencedFromComponentDescription);
if (componentDescriptions != null) {
- if (sb.length() > 0) {
+ if (!sb.isEmpty()) {
sb.append(" and ");
}
sb.append("from component description(s) of ");
@@ -200,7 +201,7 @@ public class ConfigurationSerializerWebConsolePlugin
extends GenericServlet {
} else {
pidReferencedFromComponentDescription = "";
}
- if (sb.length() == 0) {
+ if (sb.isEmpty()) {
sb.append("no fallback sources found");
}
pw.append(sb.toString()).append(")</label>").println();
@@ -259,7 +260,7 @@ public class ConfigurationSerializerWebConsolePlugin
extends GenericServlet {
pw.print("<p class='ui-state-error-text'>");
pw.print("Error serializing pid '" + escapeXml(pid) + "':
" + e.getMessage());
pw.println("</p>");
- LOGGER.warn("Error serializing pid '{}'", pid, e);
+ logger.warn("Error serializing pid '{}'", pid, e);
}
}
closeTd(pw);
@@ -269,29 +270,33 @@ public class ConfigurationSerializerWebConsolePlugin
extends GenericServlet {
pw.print("</form>");
}
- private void tdContent(final PrintWriter pw) {
+ private void tdContent(@NotNull final PrintWriter pw) {
pw.print("<td class='content' colspan='2'>");
}
- private void closeTd(final PrintWriter pw) {
+ private void closeTd(@NotNull final PrintWriter pw) {
pw.print("</td>");
}
- private void closeTr(final PrintWriter pw) {
+ private void closeTr(@NotNull final PrintWriter pw) {
pw.println("</tr>");
}
- private void tdLabel(final PrintWriter pw, final String label) {
+ private void tdLabel(@NotNull final PrintWriter pw, @NotNull final String
label) {
pw.print("<td class='content'>");
pw.print(label);
pw.println("</td>");
}
- private void tr(final PrintWriter pw) {
+ private void tr(@NotNull final PrintWriter pw) {
pw.println("<tr class='content'>");
}
- private void option(final PrintWriter pw, String value, String label,
String selectedValue) {
+ private void option(
+ @NotNull final PrintWriter pw,
+ @NotNull String value,
+ @NotNull String label,
+ @NotNull String selectedValue) {
pw.print("<option value='");
pw.print(value);
pw.print("'");
@@ -303,20 +308,19 @@ public class ConfigurationSerializerWebConsolePlugin
extends GenericServlet {
pw.println("</option>");
}
- private void titleHtml(final PrintWriter pw, final String title, final
String description) {
+ private void titleHtml(
+ @NotNull final PrintWriter pw, @NotNull final String title,
@NotNull final String description) {
tr(pw);
pw.print("<th colspan='3' class='content container'>");
pw.print(escapeXml(title));
pw.println("</th>");
closeTr(pw);
- if (description != null) {
- tr(pw);
- pw.print("<td colspan='3' class='content'>");
- pw.print(description);
- pw.println("</th>");
- closeTr(pw);
- }
+ tr(pw);
+ pw.print("<td colspan='3' class='content'>");
+ pw.print(description);
+ pw.println("</th>");
+ closeTr(pw);
}
/**
@@ -325,7 +329,7 @@ public class ConfigurationSerializerWebConsolePlugin
extends GenericServlet {
* @param input The input text
* @return The escaped text
*/
- protected String escapeXml(final String input) {
+ protected String escapeXml(@Nullable final String input) {
if (input == null) {
return null;
}
@@ -359,18 +363,17 @@ public class ConfigurationSerializerWebConsolePlugin
extends GenericServlet {
* @param mergedProperties the merged/inherited properties from some other
OSGi installer resource
*/
private void removeComponentDefaultProperties(
- final Map<String, Set<ComponentDescriptionDTO>>
allComponentDescriptions,
- final String pidReferencedFromComponentDescription,
- final Dictionary<String, Object> properties,
- final Dictionary<String, Object> mergedProperties) {
+ @NotNull final Map<String, Set<ComponentDescriptionDTO>>
allComponentDescriptions,
+ @NotNull final String pidReferencedFromComponentDescription,
+ @NotNull final Dictionary<String, Object> properties,
+ @NotNull final Dictionary<String, Object> mergedProperties) {
Set<ComponentDescriptionDTO> componentDescriptions =
allComponentDescriptions.get(pidReferencedFromComponentDescription);
-
- final Enumeration<String> e = properties.keys();
- while (e.hasMoreElements()) {
- final String key = e.nextElement();
- final Object newValue = properties.get(key);
- if (componentDescriptions != null) {
+ if (componentDescriptions != null) {
+ final Enumeration<String> e = properties.keys();
+ while (e.hasMoreElements()) {
+ final String key = e.nextElement();
+ final Object newValue = properties.get(key);
// check all bound component descriptions
Set<Object> defaultValues = componentDescriptions.stream()
.map(dto -> dto.properties.get(key))
@@ -388,6 +391,7 @@ public class ConfigurationSerializerWebConsolePlugin
extends GenericServlet {
}
}
+ @NotNull
String getRelativeResourcePrefix() {
return RES_LOC;
}
@@ -396,14 +400,11 @@ public class ConfigurationSerializerWebConsolePlugin
extends GenericServlet {
* Method to retrieve static resources from this bundle.
*/
@SuppressWarnings("unused")
- private URL getResource(final String path) {
+ private @Nullable URL getResource(@NotNull final String path) {
if (path.startsWith("/" + getRelativeResourcePrefix())) {
// strip label
- int index = path.indexOf('/', 1);
- if (index <= 0) {
- throw new IllegalStateException("The relativeResourcePrefix
must contain at least one '/'");
- }
- return this.getClass().getResource(path.substring(index));
+ String resPath = path.substring(LABEL.length() + 1);
+ return this.getClass().getResource(resPath);
}
return null;
}
diff --git
a/src/test/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePluginTest.java
b/src/test/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePluginTest.java
new file mode 100644
index 0000000..905d455
--- /dev/null
+++
b/src/test/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePluginTest.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.installer.factories.configuration.impl;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import org.apache.sling.installer.api.info.InfoProvider;
+import org.apache.sling.installer.api.info.InstallationState;
+import org.apache.sling.installer.api.info.Resource;
+import org.apache.sling.installer.api.info.ResourceGroup;
+import
org.apache.sling.installer.api.serializer.ConfigurationSerializerFactory;
+import org.apache.sling.testing.mock.sling.junit5.SlingContext;
+import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
+import
org.apache.sling.testing.mock.sling.servlet.MockSlingJakartaHttpServletRequest;
+import
org.apache.sling.testing.mock.sling.servlet.MockSlingJakartaHttpServletResponse;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.osgi.framework.dto.BundleDTO;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.component.runtime.ServiceComponentRuntime;
+import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+
+/**
+ *
+ */
+@ExtendWith(SlingContextExtension.class)
+class ConfigurationSerializerWebConsolePluginTest {
+
+ public final SlingContext context = new SlingContext();
+
+ private ConfigurationSerializerWebConsolePlugin plugin;
+
+ private InfoProvider mockInfoProvider;
+ private ServiceComponentRuntime mockScr;
+
+ @BeforeEach
+ void beforeEach() {
+ mockInfoProvider = context.registerService(InfoProvider.class,
Mockito.mock(InfoProvider.class));
+ mockScr = context.registerService(ServiceComponentRuntime.class,
Mockito.mock(ServiceComponentRuntime.class));
+
+ plugin =
context.registerInjectActivateService(ConfigurationSerializerWebConsolePlugin.class);
+ }
+
+ protected static Stream<Arguments> testServiceArgs() {
+ return Stream.of(
+ Arguments.of(Map.of()),
+ Arguments.of(Map.of("pid", "")),
+ Arguments.of(Map.of("pid", "test1")),
+ Arguments.of(Map.of("pid", "test1", "format", "")),
+ Arguments.of(Map.of("pid", "test1", "format", "JSON")),
+ Arguments.of(Map.of("pid", "test2", "format", "JSON")),
+ Arguments.of(Map.of("pid", "test1", "format", "INVALID")));
+ }
+
+ /**
+ * Test method for {@link
org.apache.sling.installer.factories.configuration.impl.ConfigurationSerializerWebConsolePlugin#service(jakarta.servlet.ServletRequest,
jakarta.servlet.ServletResponse)}.
+ */
+ @ParameterizedTest
+ @MethodSource("testServiceArgs")
+ void testService(Map<String, Object> reqParams) throws IOException {
+ // mock InfoProvider
+ mockInstallationState();
+
+ // mock existing test1 component configuration
+ mockExistingConfiguration("test1", Map.of("key1", "value1"));
+
+ // mock DTO for test1 component
+ mockComponentDescriptionDTO("test1", Map.of());
+
+ final @NotNull MockSlingJakartaHttpServletRequest req =
context.jakartaRequest();
+ req.setParameterMap(reqParams);
+ final @NotNull MockSlingJakartaHttpServletResponse resp =
context.jakartaResponse();
+ plugin.service(req, resp);
+ final String outputAsString = resp.getOutputAsString();
+ assertNotNull(outputAsString);
+ }
+
+ @Test
+ void testServiceWithSerializerException() throws Exception {
+ // mock existing test1 component configuration
+ mockExistingConfiguration("test1", Map.of("key1", "value1"));
+
+ final @NotNull MockSlingJakartaHttpServletRequest req =
context.jakartaRequest();
+ req.setParameterMap(Map.of("pid", "test1"));
+ final @NotNull MockSlingJakartaHttpServletResponse resp =
context.jakartaResponse();
+ try (MockedStatic<ConfigurationSerializerFactory> factoryMock =
+ Mockito.mockStatic(ConfigurationSerializerFactory.class); ) {
+ factoryMock
+ .when(() ->
ConfigurationSerializerFactory.create(any(ConfigurationSerializerFactory.Format.class)))
+ .thenThrow(UnsupportedOperationException.class);
+
+ plugin.service(req, resp);
+ final String outputAsString = resp.getOutputAsString();
+ assertNotNull(outputAsString);
+ assertTrue(outputAsString.contains("Error serializing pid"));
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void testServiceWithMergeScheme(boolean createDTO) throws IOException {
+ Activator.MERGE_SCHEMES = List.of("launchpad");
+
+ // mock InfoProvider
+ final InstallationState mockInstallationState =
mockInstallationState();
+ ResourceGroup mockResourceGroup = Mockito.mock(ResourceGroup.class);
+ Resource mockInstalledResource = Mockito.mock(Resource.class);
+
Mockito.doReturn("config:factory1~test1").when(mockInstalledResource).getEntityId();
+ Mockito.doReturn("launchpad").when(mockInstalledResource).getScheme();
+ Mockito.doReturn(new Hashtable<>(Map.of("key1", "value1", "key2",
"value2")))
+ .when(mockInstalledResource)
+ .getDictionary();
+
Mockito.doReturn(List.of(mockInstalledResource)).when(mockResourceGroup).getResources();
+
Mockito.doReturn(List.of(mockResourceGroup)).when(mockInstallationState).getInstalledResources();
+
+ // mock existing test1 component configuration
+ mockExistingFactoryConfiguration(
+ "factory1",
+ "test1",
+ Map.of("key1", "value1", "key2", "value2changed", "key3",
"value3", "key4", "value4"));
+
+ if (createDTO) {
+ // mock DTO for test1 component
+ mockComponentDescriptionDTO(
+ "factory1", Map.of("key1", "value1", "key3", "value3",
"key4", "value4changed"));
+ }
+
+ final @NotNull MockSlingJakartaHttpServletRequest req =
context.jakartaRequest();
+ req.setParameterMap(Map.of("pid", "factory1~test1",
"hideRedundantProperties", "true"));
+ final @NotNull MockSlingJakartaHttpServletResponse resp =
context.jakartaResponse();
+ plugin.service(req, resp);
+ final String outputAsString = resp.getOutputAsString();
+ assertNotNull(outputAsString);
+ }
+
+ private void mockComponentDescriptionDTO(String configPid, Map<String,
Object> properties) {
+ ComponentDescriptionDTO mockDTO =
Mockito.mock(ComponentDescriptionDTO.class);
+ mockDTO.bundle = Mockito.mock(BundleDTO.class);
+ mockDTO.bundle.id = 1;
+ mockDTO.name = "name1";
+ mockDTO.configurationPid = new String[] {configPid};
+ mockDTO.properties = properties;
+
Mockito.doReturn(List.of(mockDTO)).when(mockScr).getComponentDescriptionDTOs();
+ }
+
+ private void mockExistingConfiguration(String pid, Map<String, Object>
props) throws IOException {
+ ConfigurationAdmin configAdmin =
context.getService(ConfigurationAdmin.class);
+ final Configuration test1Configuration =
configAdmin.getConfiguration(pid);
+ test1Configuration.update(new Hashtable<>(props));
+ }
+
+ private void mockExistingFactoryConfiguration(String factoryPid, String
name, Map<String, Object> props)
+ throws IOException {
+ ConfigurationAdmin configAdmin =
context.getService(ConfigurationAdmin.class);
+ final Configuration test1Configuration =
configAdmin.getFactoryConfiguration(factoryPid, name);
+ test1Configuration.update(new Hashtable<>(props));
+ }
+
+ private InstallationState mockInstallationState() {
+ InstallationState mockInstallationState =
Mockito.mock(InstallationState.class);
+
Mockito.doReturn(mockInstallationState).when(mockInfoProvider).getInstallationState();
+ return mockInstallationState;
+ }
+
+ /**
+ * Test method for {@link
org.apache.sling.installer.factories.configuration.impl.ConfigurationSerializerWebConsolePlugin#escapeXml(java.lang.String)}.
+ */
+ @Test
+ void testEscapeXml() {
+ assertNull(plugin.escapeXml(null));
+ assertEquals("", plugin.escapeXml(""));
+ assertEquals("<hello/>", plugin.escapeXml("<hello/>"));
+ assertEquals(""dog's & cat"",
plugin.escapeXml("\"dog's & cat\""));
+ }
+
+ /**
+ * Test method for {@link
org.apache.sling.installer.factories.configuration.impl.ConfigurationSerializerWebConsolePlugin#getRelativeResourcePrefix()}.
+ */
+ @Test
+ void testGetRelativeResourcePrefix() {
+ assertNotNull(plugin.getRelativeResourcePrefix());
+ }
+
+ /**
+ * Test method for {@link
+ *
org.apache.sling.installer.factories.configuration.impl.ConfigurationSerializerWebConsolePlugin#getResource(java.lang.String)}.
+ */
+ @Test
+ void testGetResource() throws SecurityException, IllegalArgumentException {
+ final URL value1 = ReflectionTools.invokeMethodWithReflection(
+ plugin, "getResource", new Class[] {String.class}, URL.class,
new Object[] {"/invalid"});
+ assertNull(value1);
+
+ final URL value2 = ReflectionTools.invokeMethodWithReflection(
+ plugin, "getResource", new Class[] {String.class}, URL.class,
new Object[] {
+ "/osgi-installer-config-printer/res/ui/clipboard.js"
+ });
+ assertNotNull(value2);
+
+ final URL value3 = ReflectionTools.invokeMethodWithReflection(
+ plugin, "getResource", new Class[] {String.class}, URL.class,
new Object[] {
+ "/osgi-installer-config-printer/res/ui/invalid.css"
+ });
+ assertNull(value3);
+ }
+}
diff --git
a/src/test/java/org/apache/sling/installer/factories/configuration/impl/ReflectionTools.java
b/src/test/java/org/apache/sling/installer/factories/configuration/impl/ReflectionTools.java
new file mode 100644
index 0000000..4af9453
--- /dev/null
+++
b/src/test/java/org/apache/sling/installer/factories/configuration/impl/ReflectionTools.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.installer.factories.configuration.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Reflection utilities to facilitate testing
+ */
+public class ReflectionTools {
+
+ private ReflectionTools() {
+ // hide the public constructor
+ }
+
+ public static <T> T invokeMethodWithReflection(
+ Object obj, String methodName, Class<?>[] parameterTypes, Class<T>
expectedType, Object[] methodArgs) {
+ Object result = null;
+ try {
+ Class<?> clazz = obj.getClass();
+ Method method = null;
+ do {
+ try { // NOSONAR
+ method = clazz.getDeclaredMethod(methodName,
parameterTypes);
+ } catch (NoSuchMethodException nsfe) {
+ clazz = clazz.getSuperclass();
+ }
+ } while (method == null && clazz != null);
+ if (method != null) {
+ if (Modifier.isStatic(method.getModifiers())) {
+ if (!method.canAccess(null)) {
+ method.setAccessible(true);
+ }
+ result = method.invoke(null, methodArgs);
+ } else {
+ if (!method.canAccess(obj)) {
+ method.setAccessible(true);
+ }
+ result = method.invoke(obj, methodArgs);
+ }
+ } else {
+ fail("Failed to find method via reflection: " + methodName);
+ }
+ } catch (IllegalArgumentException | IllegalAccessException |
SecurityException e) {
+ fail("Failed to get method via reflection. Reason: " +
e.getMessage());
+ } catch (InvocationTargetException e) {
+ fail("Failed to invoke method via reflection. Reason: " +
e.getMessage());
+ }
+ return expectedType == null ? null : expectedType.cast(result);
+ }
+}