diff --git a/docker/README.md b/docker/README.md
index ae1e4f93b..8cfa6c048 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -46,25 +46,25 @@ If you want to run it without docker-compose you should then make sure you setup
For ElasticSearch:
```bash
-docker pull docker.elastic.co/elasticsearch/elasticsearch:9.2.1
+docker pull docker.elastic.co/elasticsearch/elasticsearch:9.4.3
docker network create unomi
docker run -d --name elasticsearch --net unomi -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e "xpack.security.enabled=false" \
-e cluster.name=contextElasticSearch \
- docker.elastic.co/elasticsearch/elasticsearch:9.2.1
+ docker.elastic.co/elasticsearch/elasticsearch:9.4.3
```
For OpenSearch:
```bash
-docker pull opensearchproject/opensearch:3.0.0
+docker pull opensearchproject/opensearch:3.7.0
docker network create unomi
export OPENSEARCH_ADMIN_PASSWORD=enter_your_custom_admin_password_here
docker run -d --name opensearch --net unomi -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD} \
- opensearchproject/opensearch:3.0.0
+ opensearchproject/opensearch:3.7.0
```
For Unomi (with ElasticSearch):
diff --git a/docker/src/main/docker/docker-compose-build-es.yml b/docker/src/main/docker/docker-compose-build-es.yml
index 0aa221209..03f265a7b 100644
--- a/docker/src/main/docker/docker-compose-build-es.yml
+++ b/docker/src/main/docker/docker-compose-build-es.yml
@@ -17,7 +17,7 @@
version: '2.4'
services:
elasticsearch:
- image: docker.elastic.co/elasticsearch/elasticsearch:9.2.1
+ image: docker.elastic.co/elasticsearch/elasticsearch:9.4.3
volumes:
- unomi-3-elasticsearch-data:/usr/share/elasticsearch/data
environment:
diff --git a/docker/src/main/docker/docker-compose-build-os.yml b/docker/src/main/docker/docker-compose-build-os.yml
index fb2d27d27..97d38cfe4 100644
--- a/docker/src/main/docker/docker-compose-build-os.yml
+++ b/docker/src/main/docker/docker-compose-build-os.yml
@@ -23,7 +23,7 @@ networks:
services:
opensearch-node1:
- image: opensearchproject/opensearch:3.0.0
+ image: opensearchproject/opensearch:3.7.0
container_name: opensearch-node1
environment:
- cluster.name=opensearch-cluster
@@ -51,7 +51,7 @@ services:
- opensearch-node1
opensearch-node2:
- image: opensearchproject/opensearch:3.0.0
+ image: opensearchproject/opensearch:3.7.0
container_name: opensearch-node2
environment:
- cluster.name=opensearch-cluster
@@ -76,7 +76,7 @@ services:
- opensearch-node2
opensearch-dashboards:
- image: opensearchproject/opensearch-dashboards:3.0.0
+ image: opensearchproject/opensearch-dashboards:3.7.0
container_name: opensearch-dashboards
ports:
- 5601:5601
diff --git a/docker/src/main/docker/docker-compose-cluster.yml b/docker/src/main/docker/docker-compose-cluster.yml
index a088fc082..75aed4628 100644
--- a/docker/src/main/docker/docker-compose-cluster.yml
+++ b/docker/src/main/docker/docker-compose-cluster.yml
@@ -17,7 +17,7 @@
version: '2.4'
services:
elasticsearch:
- image: docker.elastic.co/elasticsearch/elasticsearch:9.2.1
+ image: docker.elastic.co/elasticsearch/elasticsearch:9.4.3
volumes:
- unomi-3-elasticsearch-data:/usr/share/elasticsearch/data
environment:
diff --git a/docker/src/main/docker/docker-compose-es.yml b/docker/src/main/docker/docker-compose-es.yml
index db97b6d64..edfada591 100644
--- a/docker/src/main/docker/docker-compose-es.yml
+++ b/docker/src/main/docker/docker-compose-es.yml
@@ -23,7 +23,7 @@ networks:
services:
elasticsearch:
- image: docker.elastic.co/elasticsearch/elasticsearch:9.2.1
+ image: docker.elastic.co/elasticsearch/elasticsearch:9.4.3
container_name: elasticsearch
volumes:
- unomi-3-elasticsearch-data:/usr/share/elasticsearch/data
diff --git a/docker/src/main/docker/docker-compose-os.yml b/docker/src/main/docker/docker-compose-os.yml
index 03abe5263..579d3e2bc 100644
--- a/docker/src/main/docker/docker-compose-os.yml
+++ b/docker/src/main/docker/docker-compose-os.yml
@@ -23,7 +23,7 @@ networks:
services:
opensearch-node1:
- image: opensearchproject/opensearch:3.0.0
+ image: opensearchproject/opensearch:3.7.0
container_name: opensearch-node1
environment:
- cluster.name=opensearch-cluster
@@ -51,7 +51,7 @@ services:
- opensearch-node1
opensearch-node2:
- image: opensearchproject/opensearch:3.0.0
+ image: opensearchproject/opensearch:3.7.0
container_name: opensearch-node2
environment:
- cluster.name=opensearch-cluster
@@ -103,7 +103,7 @@ services:
- unomi
opensearch-dashboards:
- image: opensearchproject/opensearch-dashboards:3.0.0
+ image: opensearchproject/opensearch-dashboards:3.7.0
container_name: opensearch-dashboards
ports:
- 5601:5601
diff --git a/itests/docker-compose-snapshot-analysis.yml b/itests/docker-compose-snapshot-analysis.yml
index 7e9f955e8..af517f1fc 100644
--- a/itests/docker-compose-snapshot-analysis.yml
+++ b/itests/docker-compose-snapshot-analysis.yml
@@ -16,7 +16,7 @@
################################################################################
services:
elasticsearch:
- image: docker.elastic.co/elasticsearch/elasticsearch:9.1.3
+ image: docker.elastic.co/elasticsearch/elasticsearch:9.4.3
container_name: es-snapshot-analysis
environment:
- discovery.type=single-node
diff --git a/itests/pom.xml b/itests/pom.xml
index 3e8c79ee0..d0051bb63 100644
--- a/itests/pom.xml
+++ b/itests/pom.xml
@@ -261,6 +261,17 @@
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ INFO
+
+
+
@@ -293,6 +304,7 @@
elasticsearch
${elasticsearch.port}
${karaf.heap}
+ INFO
@@ -414,6 +426,7 @@
foo
opensearch
localhost:${opensearch.port}
+ INFO
diff --git a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
index 479cc110b..e5d9e76a4 100644
--- a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
@@ -17,11 +17,8 @@
package org.apache.unomi.itests;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
import org.apache.camel.CamelContext;
import org.apache.camel.Route;
import org.apache.camel.ServiceStatus;
@@ -143,15 +140,15 @@ public abstract class BaseIT extends KarafTestSupport {
protected static final String ENABLE_LOG_CHECKING_PROPERTY = "it.unomi.log.checking.enabled";
protected static final String CAMEL_DEBUG_PROPERTY = "it.unomi.camel.debug";
- protected final static ObjectMapper objectMapper;
protected static boolean unomiStarted = false;
protected static String searchEngine = SEARCH_ENGINE_ELASTICSEARCH;
- static {
- objectMapper = new ObjectMapper();
- objectMapper.registerModule(new JaxbAnnotationModule());
- objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
- objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ /**
+ * JSON mapper for IT HTTP/JSON helpers. Initialized on first use (after Unomi features are up),
+ * delegating to the same mapper as the running server.
+ */
+ protected ObjectMapper getObjectMapper() {
+ return CustomObjectMapper.getCustomInstance();
}
protected PersistenceService persistenceService;
@@ -193,6 +190,9 @@ public abstract class BaseIT extends KarafTestSupport {
protected LogChecker logChecker;
private String currentTestName;
+
+
+
public enum AuthType {
NONE, // No authentication
PUBLIC_KEY, // X-Unomi-Api-Key header with public key
@@ -207,6 +207,20 @@ public enum AuthType {
* This method should be called early, before any test setup, to ensure
* the correct search engine is detected and any necessary fixes are applied.
*/
+
+
+ protected TestUtils.RequestResponse executeContextJSONRequest(org.apache.http.client.methods.HttpUriRequest request, String sessionId) throws IOException {
+ return TestUtils.executeContextJSONRequest(request, sessionId, getObjectMapper());
+ }
+
+ protected TestUtils.RequestResponse executeContextJSONRequest(org.apache.http.client.methods.HttpUriRequest request, String sessionId, int expectedStatusCode, boolean withAuth) throws IOException {
+ return TestUtils.executeContextJSONRequest(request, sessionId, expectedStatusCode, withAuth, getObjectMapper());
+ }
+
+ protected TestUtils.RequestResponse executeContextJSONRequest(org.apache.http.client.methods.HttpPost request) throws IOException {
+ return TestUtils.executeContextJSONRequest(request, null, getObjectMapper());
+ }
+
protected void checkSearchEngine() {
searchEngine = System.getProperty(SEARCH_ENGINE_PROPERTY, SEARCH_ENGINE_ELASTICSEARCH);
}
@@ -893,8 +907,8 @@ protected String getValidatedBundleJSON(final String resourcePath, Map T get(final String url, Class clazz) {
final HttpGet httpGet = new HttpGet(getFullUrl(url));
response = executeHttpRequest(httpGet);
if (response.getStatusLine().getStatusCode() == 200) {
- return objectMapper.readValue(response.getEntity().getContent(), clazz);
+ return getObjectMapper().readValue(response.getEntity().getContent(), clazz);
} else {
return null;
}
@@ -1248,8 +1262,8 @@ protected CloseableHttpResponse executeHttpRequest(HttpUriRequest request, AuthT
protected String resourceAsString(final String resource) {
final java.net.URL url = bundleContext.getBundle().getResource(resource);
try (InputStream stream = url.openStream()) {
- JsonNode node = objectMapper.readTree(stream);
- String value = objectMapper.writeValueAsString(node);
+ JsonNode node = getObjectMapper().readTree(stream);
+ String value = getObjectMapper().writeValueAsString(node);
return value;
} catch (final Exception e) {
throw new RuntimeException(e);
diff --git a/itests/src/test/java/org/apache/unomi/itests/BasicIT.java b/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
index cd42fcd1b..9d0408352 100644
--- a/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
@@ -32,7 +32,6 @@
import org.apache.unomi.api.conditions.ConditionType;
import org.apache.unomi.api.rules.Rule;
import org.apache.unomi.itests.tools.httpclient.HttpClientThatWaitsForUnomi;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -132,7 +131,7 @@ public void testContextJSONWithUrlParameter() throws Exception {
LOGGER.info("Start test testContextJSONWithUrlParameter");
ContextRequest contextRequest = new ContextRequest();
HttpPost request = new HttpPost(getFullUrl("/cxs/context.json?sessionId=" + SESSION_ID_1));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.create("application/json")));
executeContextJSONRequest(request, SESSION_ID_1);
LOGGER.info("End test testContextJSONWithUrlParameter");
@@ -144,7 +143,7 @@ public void testContextJSON() throws Exception {
ContextRequest contextRequest = new ContextRequest();
contextRequest.setSessionId(SESSION_ID_2);
HttpPost request = new HttpPost(getFullUrl("/cxs/context.json"));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.create("application/json")));
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.create("application/json")));
executeContextJSONRequest(request, SESSION_ID_2);
LOGGER.info("End test testContextJSON");
@@ -155,7 +154,7 @@ public void testMultipleLoginOnSameBrowser() throws Exception {
LOGGER.info("Start test testMultipleLoginOnSameBrowser");
// Add login event condition
- ConditionType conditionType = CustomObjectMapper.getObjectMapper().readValue(
+ ConditionType conditionType = getObjectMapper().readValue(
new File("data/tmp/testLoginEventCondition.json").toURI().toURL(), ConditionType.class);
definitionsService.setConditionType(conditionType);
@@ -171,7 +170,7 @@ public void testMultipleLoginOnSameBrowser() throws Exception {
);
// Add login rule
- Rule rule = CustomObjectMapper.getObjectMapper().readValue(new File("data/tmp/testLogin.json").toURI().toURL(),
+ Rule rule = getObjectMapper().readValue(new File("data/tmp/testLogin.json").toURI().toURL(),
Rule.class);
createAndWaitForRule(rule);
@@ -181,7 +180,7 @@ public void testMultipleLoginOnSameBrowser() throws Exception {
// First page view with the first visitor aka VISITOR_1 and SESSION_ID_3
ContextRequest contextRequestPageViewSession1 = getContextRequestWithPageViewEvent(sourceSite, SESSION_ID_3);
HttpPost requestPageView1 = new HttpPost(getFullUrl("/cxs/context.json"));
- requestPageView1.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequestPageViewSession1),
+ requestPageView1.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequestPageViewSession1),
ContentType.create("application/json")));
TestUtils.RequestResponse requestResponsePageView1 = executeContextJSONRequest(requestPageView1, SESSION_ID_3);
String profileIdVisitor1 = requestResponsePageView1.getContextResponse().getProfileId();
@@ -201,7 +200,7 @@ public void testMultipleLoginOnSameBrowser() throws Exception {
HttpPost requestLoginVisitor1 = new HttpPost(getFullUrl("/cxs/context.json"));
requestLoginVisitor1.addHeader("Cookie", requestResponsePageView1.getCookieHeaderValue());
requestLoginVisitor1.addHeader("X-Unomi-Api-Key", testPublicKey.getKey());
- requestLoginVisitor1.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequestLoginVisitor1),
+ requestLoginVisitor1.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequestLoginVisitor1),
ContentType.create("application/json")));
TestUtils.RequestResponse requestResponseLoginVisitor1 = executeContextJSONRequest(requestLoginVisitor1, SESSION_ID_3);
Assert.assertEquals("Context profile id should be the same", profileIdVisitor1,
@@ -214,7 +213,7 @@ public void testMultipleLoginOnSameBrowser() throws Exception {
// Lets add a page view with VISITOR_1 to simulate reloading the page after login and be able to check the profile properties
HttpPost requestPageView2 = new HttpPost(getFullUrl("/cxs/context.json"));
requestPageView2.addHeader("Cookie", requestResponsePageView1.getCookieHeaderValue());
- requestPageView2.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequestPageViewSession1),
+ requestPageView2.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequestPageViewSession1),
ContentType.create("application/json")));
TestUtils.RequestResponse requestResponsePageView2 = executeContextJSONRequest(requestPageView2, SESSION_ID_3);
Assert.assertEquals("Context profile id should be the same", profileIdVisitor1,
@@ -229,7 +228,7 @@ public void testMultipleLoginOnSameBrowser() throws Exception {
ContextRequest contextRequestPageViewSession2 = getContextRequestWithPageViewEvent(sourceSite, SESSION_ID_4);
HttpPost requestPageView3 = new HttpPost(getFullUrl("/cxs/context.json"));
requestPageView3.addHeader("Cookie", requestResponsePageView1.getCookieHeaderValue());
- requestPageView3.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequestPageViewSession2),
+ requestPageView3.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequestPageViewSession2),
ContentType.create("application/json")));
TestUtils.RequestResponse requestResponsePageView3 = executeContextJSONRequest(requestPageView3, SESSION_ID_4);
Assert.assertEquals("Context profile id should be the same", profileIdVisitor1,
@@ -255,7 +254,7 @@ public void testMultipleLoginOnSameBrowser() throws Exception {
HttpPost requestLoginVisitor2 = new HttpPost(getFullUrl("/cxs/context.json"));
requestLoginVisitor2.addHeader("Cookie", requestResponsePageView1.getCookieHeaderValue());
requestLoginVisitor2.addHeader("X-Unomi-Api-Key", testPublicKey.getKey());
- requestLoginVisitor2.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequestLoginVisitor2),
+ requestLoginVisitor2.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequestLoginVisitor2),
ContentType.create("application/json")));
TestUtils.RequestResponse requestResponseLoginVisitor2 = executeContextJSONRequest(requestLoginVisitor2, SESSION_ID_4);
// We should have a new profile id so the session should have been moved from VISITOR_1 to VISITOR_2
@@ -268,7 +267,7 @@ public void testMultipleLoginOnSameBrowser() throws Exception {
// Lets add a page view with VISITOR_2 to simulate reloading the page after login
HttpPost requestPageView4 = new HttpPost(getFullUrl("/cxs/context.json"));
requestPageView4.addHeader("Cookie", requestResponseLoginVisitor2.getCookieHeaderValue());
- requestPageView4.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequestPageViewSession2),
+ requestPageView4.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequestPageViewSession2),
ContentType.create("application/json")));
TestUtils.RequestResponse requestResponsePageView4 = executeContextJSONRequest(requestPageView4, SESSION_ID_4);
Assert.assertEquals("Context profile id should be the same", profileIdVisitor2,
@@ -341,9 +340,6 @@ private ContextRequest getContextRequestWithPageViewEvent(CustomItem sourceSite,
return contextRequest;
}
- private TestUtils.RequestResponse executeContextJSONRequest(HttpPost request, String sessionId) throws IOException {
- return TestUtils.executeContextJSONRequest(request, sessionId);
- }
private void checkVisitor1ResponseProperties(Map profileProperties) {
checkVisitorResponseProperties(profileProperties, FIRST_NAME_VISITOR_1, LAST_NAME_VISITOR_1, EMAIL_VISITOR_1);
diff --git a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
index 03a314a7d..0c8beb2eb 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
@@ -39,7 +39,6 @@
import org.apache.unomi.api.tenants.ApiKey;
import org.apache.unomi.api.tenants.Tenant;
import org.apache.unomi.itests.TestUtils.RequestResponse;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -172,8 +171,8 @@ public void testUpdateEventFromContextAuthorizedThirdParty_Success() throws Exce
contextRequest.setEvents(Arrays.asList(event));
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
addPrivateTenantAuth(request, testTenant, testPrivateKey);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.executeContextJSONRequest(request, sessionId, -1, false);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ executeContextJSONRequest(request, sessionId, -1, false);
event = keepTrying("Event " + eventId + " not updated in the required time", () -> eventService.getEvent(eventId),
savedEvent -> Objects.nonNull(savedEvent) && TEST_EVENT_TYPE.equals(savedEvent.getEventType()), DEFAULT_TRYING_TIMEOUT,
@@ -204,8 +203,8 @@ public void testCallingContextWithSessionCreation() throws Exception {
contextRequest.setEvents(Collections.singletonList(event));
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
addPublicTenantAuth(request);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.executeContextJSONRequest(request, sessionId);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ executeContextJSONRequest(request, sessionId);
Session session = keepTrying("Session with the id " + sessionId + " not saved in the required time",
() -> profileService.loadSession(sessionId), Objects::nonNull, DEFAULT_TRYING_TIMEOUT,
@@ -244,8 +243,8 @@ public void testUpdateEventFromContextUnAuthorizedThirdParty_Fail() throws Excep
contextRequest.setSessionId(session.getItemId());
contextRequest.setEvents(Arrays.asList(event));
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.executeContextJSONRequest(request, sessionId);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ executeContextJSONRequest(request, sessionId);
// Check event type did not changed
event = shouldBeTrueUntilEnd("Event type should not have changed", () -> eventService.getEvent(eventId),
@@ -276,8 +275,8 @@ public void testUpdateEventFromContextAuthorizedThirdPartyNoItemID_Fail() throws
contextRequest.setSessionId(session.getItemId());
contextRequest.setEvents(Arrays.asList(event));
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.executeContextJSONRequest(request, sessionId);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ executeContextJSONRequest(request, sessionId);
// Check event type did not changed
event = shouldBeTrueUntilEnd("Event type should not have changed", () -> eventService.getEvent(eventId),
@@ -301,8 +300,8 @@ public void testCreateEventsWithNoTimestampParam_profileAddedToSegment() throws
contextRequest.setRequireSegments(true);
contextRequest.setEvents(Arrays.asList(event));
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- String cookieHeaderValue = TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ String cookieHeaderValue = executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
refreshPersistence(Event.class);
@@ -316,8 +315,8 @@ public void testCreateEventsWithNoTimestampParam_profileAddedToSegment() throws
secondContextRequest.setEvents(Arrays.asList(secondEvent));
HttpPost secondRequest = new HttpPost(getFullUrl(CONTEXT_URL));
secondRequest.addHeader("Cookie", cookieHeaderValue);
- secondRequest.setEntity(new StringEntity(objectMapper.writeValueAsString(secondContextRequest), ContentType.APPLICATION_JSON));
- TestUtils.executeContextJSONRequest(secondRequest, sessionId);
+ secondRequest.setEntity(new StringEntity(getObjectMapper().writeValueAsString(secondContextRequest), ContentType.APPLICATION_JSON));
+ executeContextJSONRequest(secondRequest, sessionId);
// Wait for profile to be saved with updated past event counts and segments
// The SetEventOccurenceCountAction updates pastEvents, then EvaluateProfileSegmentsAction
@@ -334,8 +333,8 @@ public void testCreateEventsWithNoTimestampParam_profileAddedToSegment() throws
ContextRequest retryContextRequest = new ContextRequest();
retryContextRequest.setSessionId(sessionId);
retryContextRequest.setRequireSegments(true);
- retryRequest.setEntity(new StringEntity(objectMapper.writeValueAsString(retryContextRequest), ContentType.APPLICATION_JSON));
- ContextResponse response = (TestUtils.executeContextJSONRequest(retryRequest, sessionId)).getContextResponse();
+ retryRequest.setEntity(new StringEntity(getObjectMapper().writeValueAsString(retryContextRequest), ContentType.APPLICATION_JSON));
+ ContextResponse response = (executeContextJSONRequest(retryRequest, sessionId)).getContextResponse();
// Also refresh to ensure profile is loaded from persistence
refreshPersistence(Profile.class);
return response;
@@ -372,13 +371,13 @@ public void testCreateEventWithTimestampParam_pastEvent_profileIsNotAddedToSegme
contextRequest.setRequireSegments(true);
contextRequest.setEvents(Arrays.asList(event));
HttpPost request = new HttpPost(regularURI);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
//The first event is with a default timestamp (now)
- String cookieHeaderValue = TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
+ String cookieHeaderValue = executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
//The second event is with a customized timestamp
request.setURI(URI.create(customTimestampURI));
request.addHeader("Cookie", cookieHeaderValue);
- ContextResponse response = (TestUtils.executeContextJSONRequest(request, sessionId)).getContextResponse(); //second event
+ ContextResponse response = (executeContextJSONRequest(request, sessionId)).getContextResponse(); //second event
shouldBeTrueUntilEnd("Profile " + response.getProfileId() + " not found in the required time",
() -> profileService.load(response.getProfileId()),
@@ -404,14 +403,14 @@ public void testCreateEventWithTimestampParam_futureEvent_profileIsNotAddedToSeg
contextRequest.setRequireSegments(true);
contextRequest.setEvents(Arrays.asList(event));
HttpPost request = new HttpPost(regularURI);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
//The first event is with a default timestamp (now)
- String cookieHeaderValue = TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
+ String cookieHeaderValue = executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
//The second event is with a customized timestamp
request.setURI(URI.create(customTimestampURI));
request.addHeader("Cookie", cookieHeaderValue);
- ContextResponse response = TestUtils.executeContextJSONRequest(request, sessionId).getContextResponse(); //second event
+ ContextResponse response = executeContextJSONRequest(request, sessionId).getContextResponse(); //second event
shouldBeTrueUntilEnd("Profile " + response.getProfileId() + " not found in the required time",
() -> profileService.load(response.getProfileId()),
@@ -435,8 +434,8 @@ public void testCreateEventWithProfileId_Success() throws Exception {
//Act
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
addPublicTenantAuth(request);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.executeContextJSONRequest(request);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ executeContextJSONRequest(request);
keepTrying("Profile " + TEST_PROFILE_ID + " not found in the required time", () -> profileService.load(TEST_PROFILE_ID),
Objects::nonNull, DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
@@ -462,8 +461,8 @@ public void testCreateEventWithPropertiesValidation_Success() throws Exception {
//Act
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
addPrivateTenantAuth(request, testTenant, testPrivateKey);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.executeContextJSONRequest(request, null, -1, false);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ executeContextJSONRequest(request, null, -1, false);
//Assert
event = keepTrying("Event not found", () -> eventService.getEvent(eventId), Objects::nonNull, DEFAULT_TRYING_TIMEOUT,
@@ -492,8 +491,8 @@ public void testCreateEventWithPropertyValueValidation_Failure() throws Exceptio
//Act
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
addPrivateTenantAuth(request, testTenant, testPrivateKey);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.executeContextJSONRequest(request);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ executeContextJSONRequest(request);
//Assert
shouldBeTrueUntilEnd("Event should be null", () -> eventService.getEvent(eventId), Objects::isNull, DEFAULT_TRYING_TIMEOUT,
@@ -519,8 +518,8 @@ public void testCreateEventWithPropertyNameValidation_Failure() throws Exception
//Act
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
addPrivateTenantAuth(request, testTenant, testPrivateKey);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.executeContextJSONRequest(request);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ executeContextJSONRequest(request);
//Assert
shouldBeTrueUntilEnd("Event should be null", () -> eventService.getEvent(eventId), Objects::isNull, DEFAULT_TRYING_TIMEOUT,
@@ -542,7 +541,7 @@ public void testMVELVulnerability() throws Exception {
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
request.setEntity(
new StringEntity(getValidatedBundleJSON("security/mvel-payload-1.json", parameters), ContentType.APPLICATION_JSON));
- RequestResponse response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ RequestResponse response = executeContextJSONRequest(request, TEST_SESSION_ID);
shouldBeTrueUntilEnd("Vulnerability successfully executed ! File created at " + vulnFileCanonicalPath, vulnFile::exists,
exists -> exists == Boolean.FALSE, DEFAULT_TRYING_TIMEOUT, DEFAULT_SHOULDBETRUE_TRIES);
@@ -562,7 +561,7 @@ public void testMVELVulnerabilityWithListPropertyValues() throws Exception {
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
request.setEntity(
new StringEntity(getValidatedBundleJSON("security/mvel-payload-list.json", parameters), ContentType.APPLICATION_JSON));
- TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ executeContextJSONRequest(request, TEST_SESSION_ID);
shouldBeTrueUntilEnd("Vulnerability successfully executed ! File created at " + vulnFileCanonicalPath, vulnFile::exists,
exists -> exists == Boolean.FALSE, DEFAULT_TRYING_TIMEOUT, DEFAULT_SHOULDBETRUE_TRIES);
@@ -574,7 +573,7 @@ public void testPersonalization() throws Exception {
Map parameters = new HashMap<>();
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
request.setEntity(new StringEntity(getValidatedBundleJSON("personalization.json", parameters), ContentType.APPLICATION_JSON));
- RequestResponse response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ RequestResponse response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("Invalid response code", 200, response.getStatusCode());
}
@@ -583,7 +582,7 @@ public void testScorePersonalizationStrategy_Interests() throws Exception {
// Test request before adding interests to current profile.
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
request.setEntity(new StringEntity(getValidatedBundleJSON("personalization-score-interests.json", null), ContentType.APPLICATION_JSON));
- TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request);
+ TestUtils.RequestResponse response = executeContextJSONRequest(request);
ContextResponse contextResponse = response.getContextResponse();
List variants = contextResponse.getPersonalizations().get("perso-by-interest");
assertEquals("Invalid response code", 200, response.getStatusCode());
@@ -601,7 +600,7 @@ public void testScorePersonalizationStrategy_Interests() throws Exception {
// check results of the perso now
request = new HttpPost(getFullUrl(CONTEXT_URL));
request.setEntity(new StringEntity(getValidatedBundleJSON("personalization-score-interests.json", null), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request);
+ response = executeContextJSONRequest(request);
contextResponse = response.getContextResponse();
variants = contextResponse.getPersonalizations().get("perso-by-interest");
assertEquals("Invalid response code", 200, response.getStatusCode());
@@ -638,7 +637,7 @@ public void testScorePersonalizationStrategy_Interests() throws Exception {
// re test now that profiles has interests
request = new HttpPost(getFullUrl(CONTEXT_URL));
request.setEntity(new StringEntity(getValidatedBundleJSON("personalization-score-interests.json", null), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request);
+ response = executeContextJSONRequest(request);
contextResponse = response.getContextResponse();
variants = contextResponse.getPersonalizations().get("perso-by-interest");
assertEquals("Invalid response code", 200, response.getStatusCode());
@@ -666,7 +665,7 @@ public void testRequireScoring() throws Exception {
Map parameters = new HashMap<>();
String scoringSource = getValidatedBundleJSON("score1.json", parameters);
- Scoring scoring = CustomObjectMapper.getObjectMapper().readValue(scoringSource, Scoring.class);
+ Scoring scoring = getObjectMapper().readValue(scoringSource, Scoring.class);
segmentService.setScoringDefinition(scoring);
keepTrying("Profile does not contains scores in the required time", () -> profileService.load(TEST_PROFILE_ID), storedProfile ->
@@ -676,7 +675,7 @@ public void testRequireScoring() throws Exception {
parameters = new HashMap<>();
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
request.setEntity(new StringEntity(getValidatedBundleJSON("withoutRequireScores.json", parameters), ContentType.APPLICATION_JSON));
- TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request);
+ TestUtils.RequestResponse response = executeContextJSONRequest(request);
assertEquals("Invalid response code", 200, response.getStatusCode());
assertNotNull("Context response should not be null", response.getContextResponse());
@@ -687,7 +686,7 @@ public void testRequireScoring() throws Exception {
parameters = new HashMap<>();
request = new HttpPost(getFullUrl(CONTEXT_URL));
request.setEntity(new StringEntity(getValidatedBundleJSON("withRequireScores.json", parameters), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request);
+ response = executeContextJSONRequest(request);
assertEquals("Invalid response code", 200, response.getStatusCode());
assertNotNull("Context response should not be null", response.getContextResponse());
@@ -868,8 +867,8 @@ public void testContextEndpointAuthentication() throws Exception {
contextRequest.setSessionId(TEST_SESSION_ID);
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID, 401, false);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ TestUtils.RequestResponse response = executeContextJSONRequest(request, TEST_SESSION_ID, 401, false);
Assert.assertEquals("Unauthenticated request should be rejected", 401, response.getStatusCode());
// Test with JAAS authentication (should succeed)
@@ -887,7 +886,7 @@ public void testContextEndpointAuthentication() throws Exception {
.build();
request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
// We need to specify which tenant we want to access since we are using the system administrator.
request.addHeader(UNOMI_TENANT_ID_HEADER, TEST_TENANT_ID);
CloseableHttpResponse jaasResponse = adminClient.execute(request);
@@ -896,15 +895,15 @@ public void testContextEndpointAuthentication() throws Exception {
// Test with public API key (should succeed)
contextRequest.setPublicApiKey(publicKey.getKey());
request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ response = executeContextJSONRequest(request, TEST_SESSION_ID);
Assert.assertEquals("Public API key request should succeed", 200, response.getStatusCode());
// Test with private API key (should fail for public endpoint)
request = new HttpPost(getFullUrl(CONTEXT_URL));
addPrivateTenantAuth(request, tenant, privateKey);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ response = executeContextJSONRequest(request, TEST_SESSION_ID);
Assert.assertEquals("Private API key should be accepted for public endpoint to be able to update events and send restricted events", 200, response.getStatusCode());
// Cleanup
@@ -928,7 +927,7 @@ private void performPersonalizationWithControlGroup(Map controlG
request.setEntity(new StringEntity(getValidatedBundleJSON("personalization-no-control-group.json", null), ContentType.APPLICATION_JSON));
}
- TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request);
+ TestUtils.RequestResponse response = executeContextJSONRequest(request);
ContextResponse contextResponse = response.getContextResponse();
// Check variants
@@ -978,23 +977,23 @@ public void testConcealedProperties() throws Exception {
contextRequest.setProfileId(profile.getItemId());
contextRequest.setSessionId(sessionId);
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- assertEquals(TestUtils.executeContextJSONRequest(request, sessionId).getContextResponse().getProfileProperties().get("customProperty"), ("concealedValue"));
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ assertEquals(executeContextJSONRequest(request, sessionId).getContextResponse().getProfileProperties().get("customProperty"), ("concealedValue"));
// set the property as concealed
customPropertyType.getMetadata().getSystemTags().add("concealed");
profileService.deletePropertyType(customPropertyType.getItemId());
profileService.setPropertyType(customPropertyType);
// Not in all properties
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- assertNull(TestUtils.executeContextJSONRequest(request, sessionId).getContextResponse().getProfileProperties().get("customProperty"));
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ assertNull(executeContextJSONRequest(request, sessionId).getContextResponse().getProfileProperties().get("customProperty"));
// Got it explicitly
contextRequest.setRequiredProfileProperties(Arrays.asList("customProperty"));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- assertEquals(TestUtils.executeContextJSONRequest(request, sessionId).getContextResponse().getProfileProperties().get("customProperty"), ("concealedValue"));
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ assertEquals(executeContextJSONRequest(request, sessionId).getContextResponse().getProfileProperties().get("customProperty"), ("concealedValue"));
// Got it with all
contextRequest.setRequiredProfileProperties(Arrays.asList("*", "customProperty"));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- assertEquals(TestUtils.executeContextJSONRequest(request, sessionId).getContextResponse().getProfileProperties().get("customProperty"), ("concealedValue"));
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ assertEquals(executeContextJSONRequest(request, sessionId).getContextResponse().getProfileProperties().get("customProperty"), ("concealedValue"));
// remove the concealed tag on the property type
customPropertyType.getMetadata().getSystemTags().remove("concealed");
@@ -1003,8 +1002,8 @@ public void testConcealedProperties() throws Exception {
// Got it from all properties
contextRequest.setRequiredProfileProperties(Arrays.asList("*"));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- assertEquals(TestUtils.executeContextJSONRequest(request, sessionId).getContextResponse().getProfileProperties().get("customProperty"), ("concealedValue"));
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ assertEquals(executeContextJSONRequest(request, sessionId).getContextResponse().getProfileProperties().get("customProperty"), ("concealedValue"));
}
@Test
@@ -1020,8 +1019,8 @@ public void testContextRequestWithPublicApiKey() throws Exception {
// Send request
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ TestUtils.RequestResponse response = executeContextJSONRequest(request, TEST_SESSION_ID);
// Verify response
ContextResponse contextResponse = response.getContextResponse();
@@ -1031,8 +1030,8 @@ public void testContextRequestWithPublicApiKey() throws Exception {
request = new HttpPost(getFullUrl(CONTEXT_URL));
contextRequest.setPublicApiKey("invalid-key");
request.addHeader(UNOMI_API_KEY_HTTP_HEADER_KEY, "invalid-key");
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID, 401, false);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ response = executeContextJSONRequest(request, TEST_SESSION_ID, 401, false);
// Verify error response for invalid key
assertEquals("Should receive unauthorized response", 401, response.getStatusCode());
diff --git a/itests/src/test/java/org/apache/unomi/itests/CopyPropertiesActionIT.java b/itests/src/test/java/org/apache/unomi/itests/CopyPropertiesActionIT.java
index 571b0ae94..5205ffc94 100644
--- a/itests/src/test/java/org/apache/unomi/itests/CopyPropertiesActionIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/CopyPropertiesActionIT.java
@@ -24,7 +24,6 @@
import org.apache.unomi.api.rules.Rule;
import org.apache.unomi.itests.tools.LogChecker;
import org.apache.unomi.api.services.EventService;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -163,7 +162,7 @@ private void initializePropertyTypeWithDifferentSystemTag() throws InterruptedEx
}
private void createRule(String filename) throws IOException, InterruptedException {
- Rule rule = CustomObjectMapper.getObjectMapper().readValue(new File(filename).toURI().toURL(), Rule.class);
+ Rule rule = getObjectMapper().readValue(new File(filename).toURI().toURL(), Rule.class);
createAndWaitForRule(rule);
}
diff --git a/itests/src/test/java/org/apache/unomi/itests/EventsCollectorIT.java b/itests/src/test/java/org/apache/unomi/itests/EventsCollectorIT.java
index 79971ef05..2553c7c59 100644
--- a/itests/src/test/java/org/apache/unomi/itests/EventsCollectorIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/EventsCollectorIT.java
@@ -104,13 +104,13 @@ public void testEventsCollectorWithPublicApiKey() throws Exception {
// Send request with public API key
HttpPost request = new HttpPost(getFullUrl(EVENTS_URL));
request.addHeader("Content-Type", "application/json");
- String requestBody = objectMapper.writeValueAsString(eventsCollectorRequest);
+ String requestBody = getObjectMapper().writeValueAsString(eventsCollectorRequest);
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
// Execute request and verify response
try (CloseableHttpResponse response = HttpClientThatWaitsForUnomi.doRequest(request, 200)) {
String responseContent = EntityUtils.toString(response.getEntity());
- EventCollectorResponse eventResponse = objectMapper.readValue(responseContent, EventCollectorResponse.class);
+ EventCollectorResponse eventResponse = getObjectMapper().readValue(responseContent, EventCollectorResponse.class);
Assert.assertNotNull("Event collector response should not be null", eventResponse);
// Check that the response indicates the session and profile were updated
diff --git a/itests/src/test/java/org/apache/unomi/itests/GroovyActionsServiceIT.java b/itests/src/test/java/org/apache/unomi/itests/GroovyActionsServiceIT.java
index cd889fbd4..7731fa74d 100644
--- a/itests/src/test/java/org/apache/unomi/itests/GroovyActionsServiceIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/GroovyActionsServiceIT.java
@@ -23,7 +23,6 @@
import org.apache.unomi.api.Profile;
import org.apache.unomi.api.actions.ActionType;
import org.apache.unomi.api.rules.Rule;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -74,7 +73,7 @@ private String loadGroovyAction(String pathname) throws IOException {
}
private void createRule(String filename) throws IOException, InterruptedException {
- Rule rule = CustomObjectMapper.getObjectMapper().readValue(new File(filename).toURI().toURL(), Rule.class);
+ Rule rule = getObjectMapper().readValue(new File(filename).toURI().toURL(), Rule.class);
createAndWaitForRule(rule);
}
diff --git a/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java b/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java
index fd15aada7..9fe11829d 100644
--- a/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java
@@ -115,7 +115,7 @@ protected T get(final String url, TypeReference typeReference) {
final HttpGet httpGet = new HttpGet(getFullUrl(url));
response = executeHttpRequest(httpGet, AuthType.CUSTOM_BASIC, HEALTHCHECK_AUTH_USER_NAME, HEALTHCHECK_AUTH_PASSWORD);
if (response.getStatusLine().getStatusCode() == 200 || response.getStatusLine().getStatusCode() == 206) {
- return objectMapper.readValue(response.getEntity().getContent(), typeReference);
+ return getObjectMapper().readValue(response.getEntity().getContent(), typeReference);
} else {
return null;
}
diff --git a/itests/src/test/java/org/apache/unomi/itests/InputValidationIT.java b/itests/src/test/java/org/apache/unomi/itests/InputValidationIT.java
index dd9ac69a7..ab7ea7269 100644
--- a/itests/src/test/java/org/apache/unomi/itests/InputValidationIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/InputValidationIT.java
@@ -332,7 +332,7 @@ private void assertInvalidClientDataResponse(String responseBody) throws IOExcep
if (ERROR_MESSAGE_INVALID_DATA_RECEIVED.equals(responseBody)) {
return;
}
- JsonNode json = objectMapper.readTree(responseBody);
+ JsonNode json = getObjectMapper().readTree(responseBody);
JsonNode errorNode = json.get("errorMessage");
assertNotNull("Response JSON missing 'errorMessage' field. Body: " + responseBody, errorNode);
assertEquals("badRequest", errorNode.asText());
diff --git a/itests/src/test/java/org/apache/unomi/itests/LegacyQueryBuilderMappingIT.java b/itests/src/test/java/org/apache/unomi/itests/LegacyQueryBuilderMappingIT.java
index 14c001277..7a7597712 100644
--- a/itests/src/test/java/org/apache/unomi/itests/LegacyQueryBuilderMappingIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/LegacyQueryBuilderMappingIT.java
@@ -22,7 +22,6 @@
import org.apache.unomi.api.Profile;
import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.conditions.ConditionType;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -141,7 +140,7 @@ public void testPropertyConditionLegacyMapping() throws IOException {
* @throws IOException if the JSON file cannot be read
*/
private void testLegacyMapping(String jsonFilePath, Map parameters) throws IOException {
- ConditionType customConditionType = CustomObjectMapper.getObjectMapper().readValue(
+ ConditionType customConditionType = getObjectMapper().readValue(
new File(jsonFilePath).toURI().toURL(), ConditionType.class);
definitionsService.setConditionType(customConditionType);
diff --git a/itests/src/test/java/org/apache/unomi/itests/PatchIT.java b/itests/src/test/java/org/apache/unomi/itests/PatchIT.java
index 5a068ea4a..71795f0ea 100644
--- a/itests/src/test/java/org/apache/unomi/itests/PatchIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/PatchIT.java
@@ -20,7 +20,6 @@
import org.apache.unomi.api.PropertyType;
import org.apache.unomi.api.actions.ActionType;
import org.apache.unomi.api.conditions.ConditionType;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import java.util.Objects;
import org.junit.Assert;
import org.junit.Test;
@@ -43,7 +42,7 @@ public void testPatch() throws IOException, InterruptedException {
PropertyType company = profileService.getPropertyType("company");
try {
- Patch patch = CustomObjectMapper.getObjectMapper().readValue(bundleContext.getBundle().getResource("patch1.json"), Patch.class);
+ Patch patch = getObjectMapper().readValue(bundleContext.getBundle().getResource("patch1.json"), Patch.class);
PropertyType newCompany = (PropertyType) patchService.patch(patch);
Assert.assertEquals("foo", newCompany.getDefaultValue());
@@ -65,7 +64,7 @@ public void testOverride() throws IOException, InterruptedException {
PropertyType gender = profileService.getPropertyType("gender");
try {
- Patch patch = CustomObjectMapper.getObjectMapper().readValue(bundleContext.getBundle().getResource("patch2.json"), Patch.class);
+ Patch patch = getObjectMapper().readValue(bundleContext.getBundle().getResource("patch2.json"), Patch.class);
PropertyType newGender = (PropertyType) patchService.patch(patch);
Assert.assertEquals("foo", newGender.getDefaultValue());
@@ -91,7 +90,7 @@ public void testRemove() throws IOException, InterruptedException {
executionContextManager.executeAsSystem(() -> {
Patch patch = null;
try {
- patch = CustomObjectMapper.getObjectMapper().readValue(bundleContext.getBundle().getResource("patch3.json"), Patch.class);
+ patch = getObjectMapper().readValue(bundleContext.getBundle().getResource("patch3.json"), Patch.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -130,7 +129,7 @@ public void testPatchOnConditionType() throws IOException, InterruptedException
Assert.assertTrue(formCondition.getMetadata().getSystemTags().contains("profileTags"));
try {
- Patch patch = CustomObjectMapper.getObjectMapper().readValue(bundleContext.getBundle().getResource("patch4.json"), Patch.class);
+ Patch patch = getObjectMapper().readValue(bundleContext.getBundle().getResource("patch4.json"), Patch.class);
patchService.patch(patch);
@@ -157,7 +156,7 @@ public void testPatchOnActionType() throws IOException, InterruptedException {
Assert.assertTrue(mailAction.getMetadata().getSystemTags().contains("availableToEndUser"));
try {
- Patch patch = CustomObjectMapper.getObjectMapper().readValue(bundleContext.getBundle().getResource("patch5.json"), Patch.class);
+ Patch patch = getObjectMapper().readValue(bundleContext.getBundle().getResource("patch5.json"), Patch.class);
patchService.patch(patch);
diff --git a/itests/src/test/java/org/apache/unomi/itests/PersonaIT.java b/itests/src/test/java/org/apache/unomi/itests/PersonaIT.java
index a5754bcbe..80d32f630 100644
--- a/itests/src/test/java/org/apache/unomi/itests/PersonaIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/PersonaIT.java
@@ -25,7 +25,6 @@
import org.apache.unomi.api.PartialList;
import org.apache.unomi.api.PersonaSession;
import org.apache.unomi.api.PersonaWithSessions;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -101,7 +100,7 @@ public void testSavePersonaWithSessionsAndRetrieveSessions() throws Exception {
Assert.assertEquals("Persona creation should return 200 OK", 200, statusCode);
String responseBody = EntityUtils.toString(createResponse.getEntity());
- createdPersona = CustomObjectMapper.getObjectMapper().readValue(responseBody, PersonaWithSessions.class);
+ createdPersona = getObjectMapper().readValue(responseBody, PersonaWithSessions.class);
}
Assert.assertNotNull("Created persona should not be null", createdPersona);
@@ -120,7 +119,7 @@ public void testSavePersonaWithSessionsAndRetrieveSessions() throws Exception {
try (CloseableHttpResponse response = executeHttpRequest(new HttpGet(getFullUrl(sessionsUrl)), AuthType.JAAS_ADMIN)) {
if (response.getStatusLine().getStatusCode() == 200) {
String responseBody = EntityUtils.toString(response.getEntity());
- PartialList result = CustomObjectMapper.getObjectMapper().readValue(
+ PartialList result = getObjectMapper().readValue(
responseBody, new TypeReference>() {});
// Check if the test session is present
if (result != null && result.getList() != null && !result.getList().isEmpty()) {
diff --git a/itests/src/test/java/org/apache/unomi/itests/PropertiesUpdateActionIT.java b/itests/src/test/java/org/apache/unomi/itests/PropertiesUpdateActionIT.java
index 70a732e15..ad2206a6b 100644
--- a/itests/src/test/java/org/apache/unomi/itests/PropertiesUpdateActionIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/PropertiesUpdateActionIT.java
@@ -20,7 +20,6 @@
import org.apache.unomi.api.Event;
import org.apache.unomi.api.Profile;
import org.apache.unomi.api.rules.Rule;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.apache.unomi.plugins.baseplugin.actions.UpdatePropertiesAction;
import org.junit.Assert;
import org.junit.Before;
@@ -317,7 +316,7 @@ public void testSetPropertyActionDates() throws InterruptedException, IOExceptio
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
// register test rule
- Rule rule = CustomObjectMapper.getObjectMapper()
+ Rule rule = getObjectMapper()
.readValue(getValidatedBundleJSON("testSetPropertyActionRule.json", new HashMap<>()), Rule.class);
createAndWaitForRule(rule);
diff --git a/itests/src/test/java/org/apache/unomi/itests/RestCreateValidationIT.java b/itests/src/test/java/org/apache/unomi/itests/RestCreateValidationIT.java
index cff021b3a..e40ac5970 100644
--- a/itests/src/test/java/org/apache/unomi/itests/RestCreateValidationIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/RestCreateValidationIT.java
@@ -126,7 +126,7 @@ private void assertBadRequestResponse(String url, String responseBody) throws IO
assertNotNull("Expected a response body for 400 on POST " + url, responseBody);
JsonNode json;
try {
- json = objectMapper.readTree(responseBody);
+ json = getObjectMapper().readTree(responseBody);
} catch (Exception e) {
fail("Expected JSON in 400 response for POST " + url + " but got: " + responseBody);
return;
diff --git a/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java b/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
index f59afe50e..5187810a1 100644
--- a/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
@@ -24,7 +24,6 @@
import org.apache.unomi.api.services.EventService;
import org.apache.unomi.api.services.RulesService;
import org.apache.unomi.api.utils.ConditionBuilder;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -296,7 +295,7 @@ private Event generateViewEvent(Session session, Profile profile) {
public void testGetTrackedConditions() throws InterruptedException, IOException {
// Add custom condition with parameter
try {
- ConditionType conditionType = CustomObjectMapper.getObjectMapper().readValue(
+ ConditionType conditionType = getObjectMapper().readValue(
new File("data/tmp/testClickEventCondition.json").toURI().toURL(), ConditionType.class);
definitionsService.setConditionType(conditionType);
refreshPersistence(Rule.class);
diff --git a/itests/src/test/java/org/apache/unomi/itests/SchedulerIT.java b/itests/src/test/java/org/apache/unomi/itests/SchedulerIT.java
index f5d98681b..9d0a96973 100644
--- a/itests/src/test/java/org/apache/unomi/itests/SchedulerIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/SchedulerIT.java
@@ -137,7 +137,7 @@ public void testRetryTask() throws Exception {
try (CloseableHttpResponse response = post("/cxs/tasks/" + testTaskId + "/retry?resetFailureCount=true", null)) {
assertEquals("Response should be OK", 200, response.getStatusLine().getStatusCode());
String responseBody = EntityUtils.toString(response.getEntity());
- ScheduledTask retried = objectMapper.readValue(responseBody, ScheduledTask.class);
+ ScheduledTask retried = getObjectMapper().readValue(responseBody, ScheduledTask.class);
assertNotNull("Task should not be null", retried);
assertEquals("Task should be scheduled", ScheduledTask.TaskStatus.SCHEDULED, retried.getStatus());
assertEquals("Failure count should be reset", 0, retried.getFailureCount());
diff --git a/itests/src/test/java/org/apache/unomi/itests/TenantIT.java b/itests/src/test/java/org/apache/unomi/itests/TenantIT.java
index bfdf693f1..88df8b429 100644
--- a/itests/src/test/java/org/apache/unomi/itests/TenantIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/TenantIT.java
@@ -33,7 +33,6 @@
import org.apache.unomi.api.tenants.ApiKey;
import org.apache.unomi.api.tenants.ResourceQuota;
import org.apache.unomi.api.tenants.Tenant;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -51,12 +50,9 @@
public class TenantIT extends BaseIT {
private static final String REST_ENDPOINT = "/cxs/tenants";
- private CustomObjectMapper objectMapper;
@Before
public void setUp() throws InterruptedException {
- objectMapper = new CustomObjectMapper();
-
// Wait for tenant REST endpoint to be available
keepTrying("Couldn't find tenant endpoint", () -> {
try (CloseableHttpResponse response = executeHttpRequest(new HttpGet(getFullUrl(REST_ENDPOINT)), AuthType.JAAS_ADMIN)) {
@@ -78,13 +74,13 @@ public void testRestEndpoint() throws Exception {
requestBody.put("properties", properties);
HttpPost createRequest = new HttpPost(getFullUrl(REST_ENDPOINT));
- createRequest.setEntity(new StringEntity(objectMapper.writeValueAsString(requestBody), ContentType.APPLICATION_JSON));
+ createRequest.setEntity(new StringEntity(getObjectMapper().writeValueAsString(requestBody), ContentType.APPLICATION_JSON));
String createResponse;
Tenant createdTenant;
try (CloseableHttpResponse response = executeHttpRequest(createRequest, AuthType.JAAS_ADMIN)) {
createResponse = EntityUtils.toString(response.getEntity());
- createdTenant = objectMapper.readValue(createResponse, Tenant.class);
+ createdTenant = getObjectMapper().readValue(createResponse, Tenant.class);
}
Assert.assertNotNull("Created tenant should not be null", createdTenant);
@@ -99,7 +95,7 @@ public void testRestEndpoint() throws Exception {
Tenant retrievedTenant;
try (CloseableHttpResponse response = executeHttpRequest(new HttpGet(getFullUrl(REST_ENDPOINT + "/" + createdTenant.getItemId())), AuthType.JAAS_ADMIN)) {
getResponse = EntityUtils.toString(response.getEntity());
- retrievedTenant = objectMapper.readValue(getResponse, Tenant.class);
+ retrievedTenant = getObjectMapper().readValue(getResponse, Tenant.class);
}
Assert.assertEquals("Retrieved tenant should match created tenant", createdTenant.getItemId(), retrievedTenant.getItemId());
@@ -112,13 +108,13 @@ public void testRestEndpoint() throws Exception {
retrievedTenant.setResourceQuota(quota);
HttpPut updateRequest = new HttpPut(getFullUrl(REST_ENDPOINT + "/" + retrievedTenant.getItemId()));
- updateRequest.setEntity(new StringEntity(objectMapper.writeValueAsString(retrievedTenant), ContentType.APPLICATION_JSON));
+ updateRequest.setEntity(new StringEntity(getObjectMapper().writeValueAsString(retrievedTenant), ContentType.APPLICATION_JSON));
String updateResponse;
Tenant updatedTenant;
try (CloseableHttpResponse response = executeHttpRequest(updateRequest, AuthType.JAAS_ADMIN)) {
updateResponse = EntityUtils.toString(response.getEntity());
- updatedTenant = objectMapper.readValue(updateResponse, Tenant.class);
+ updatedTenant = getObjectMapper().readValue(updateResponse, Tenant.class);
}
Assert.assertEquals("Tenant name should be updated", "Updated Rest Test Tenant", updatedTenant.getName());
@@ -133,7 +129,7 @@ public void testRestEndpoint() throws Exception {
ApiKey newApiKey;
try (CloseableHttpResponse response = executeHttpRequest(generateKeyRequest, AuthType.JAAS_ADMIN)) {
generateKeyResponse = EntityUtils.toString(response.getEntity());
- newApiKey = objectMapper.readValue(generateKeyResponse, ApiKey.class);
+ newApiKey = getObjectMapper().readValue(generateKeyResponse, ApiKey.class);
}
Assert.assertNotNull("New API key should not be null", newApiKey);
@@ -197,13 +193,13 @@ public void testTenantEndpointAuthentication() throws Exception {
requestBody.put("properties", Collections.emptyMap());
HttpPost createRequest = new HttpPost(getFullUrl(REST_ENDPOINT));
- createRequest.setEntity(new StringEntity(objectMapper.writeValueAsString(requestBody), ContentType.APPLICATION_JSON));
+ createRequest.setEntity(new StringEntity(getObjectMapper().writeValueAsString(requestBody), ContentType.APPLICATION_JSON));
String createResponse;
Tenant tenant;
try (CloseableHttpResponse response = adminClient.execute(createRequest)) {
createResponse = EntityUtils.toString(response.getEntity());
- tenant = objectMapper.readValue(createResponse, Tenant.class);
+ tenant = getObjectMapper().readValue(createResponse, Tenant.class);
}
try {
diff --git a/itests/src/test/java/org/apache/unomi/itests/TestUtils.java b/itests/src/test/java/org/apache/unomi/itests/TestUtils.java
index 5b2aa5c01..71b4300c0 100644
--- a/itests/src/test/java/org/apache/unomi/itests/TestUtils.java
+++ b/itests/src/test/java/org/apache/unomi/itests/TestUtils.java
@@ -34,7 +34,6 @@
import org.apache.unomi.api.tenants.Tenant;
import org.apache.unomi.api.tenants.TenantService;
import org.apache.unomi.itests.tools.httpclient.HttpClientThatWaitsForUnomi;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.apache.unomi.persistence.spi.PersistenceService;
import org.junit.Assert;
import org.slf4j.Logger;
@@ -61,7 +60,7 @@ public class TestUtils {
* @return The deserialized resource object, or null if the response or entity is null
* @throws IOException if there is an error reading or parsing the response
*/
- public static T retrieveResourceFromResponse(HttpResponse response, Class clazz) throws IOException {
+ public static T retrieveResourceFromResponse(HttpResponse response, Class clazz, ObjectMapper objectMapper) throws IOException {
if (response == null) {
return null;
}
@@ -69,7 +68,7 @@ public static T retrieveResourceFromResponse(HttpResponse response, Class
return null;
}
String jsonFromResponse = EntityUtils.toString(response.getEntity());
- ObjectMapper mapper = CustomObjectMapper.getObjectMapper();
+ ObjectMapper mapper = objectMapper;
try {
T value = mapper.readValue(jsonFromResponse, clazz);
return value;
@@ -89,8 +88,8 @@ public static T retrieveResourceFromResponse(HttpResponse response, Class
* @return A RequestResponse object containing the response details
* @throws IOException if there is an error executing the request or processing the response
*/
- public static RequestResponse executeContextJSONRequest(HttpUriRequest request, String sessionId) throws IOException {
- return executeContextJSONRequest(request, sessionId, -1, true);
+ public static RequestResponse executeContextJSONRequest(HttpUriRequest request, String sessionId, ObjectMapper objectMapper) throws IOException {
+ return executeContextJSONRequest(request, sessionId, -1, true, objectMapper);
}
/**
@@ -101,10 +100,11 @@ public static RequestResponse executeContextJSONRequest(HttpUriRequest request,
* @param sessionId The session ID to use for cookie handling, or null if not needed
* @param expectedStatusCode The expected status code of the response, or -1 if not needed
* @param withAuth Whether to include authentication headers in the request
+ * @param objectMapper JSON mapper (use {@link BaseIT#getObjectMapper()} from integration tests)
* @return A RequestResponse object containing the response details
* @throws IOException if there is an error executing the request or processing the response
*/
- public static RequestResponse executeContextJSONRequest(HttpUriRequest request, String sessionId, int expectedStatusCode, boolean withAuth) throws IOException {
+ public static RequestResponse executeContextJSONRequest(HttpUriRequest request, String sessionId, int expectedStatusCode, boolean withAuth, ObjectMapper objectMapper) throws IOException {
try (CloseableHttpResponse response = HttpClientThatWaitsForUnomi.doRequest(request, expectedStatusCode, withAuth, false)) {
// validate mimeType
HttpEntity entity = response.getEntity();
@@ -131,7 +131,7 @@ public static RequestResponse executeContextJSONRequest(HttpUriRequest request,
ContextResponse contextResponse = null;
if (responseCode == 200) {
- contextResponse = CustomObjectMapper.getObjectMapper().readValue(responseContent, ContextResponse.class);
+ contextResponse = objectMapper.readValue(responseContent, ContextResponse.class);
}
return new RequestResponse(cookieHeader, responseCode, contextResponse);
@@ -146,8 +146,8 @@ public static RequestResponse executeContextJSONRequest(HttpUriRequest request,
* @return A RequestResponse object containing the response details
* @throws IOException if there is an error executing the request or processing the response
*/
- public static RequestResponse executeContextJSONRequest(HttpPost request) throws IOException {
- return executeContextJSONRequest(request, null);
+ public static RequestResponse executeContextJSONRequest(HttpPost request, String sessionId, ObjectMapper objectMapper) throws IOException {
+ return executeContextJSONRequest(request, sessionId, -1, true, objectMapper);
}
private static boolean removeAllItems(DefinitionsService definitionsService, PersistenceService persistenceService,
diff --git a/itests/src/test/java/org/apache/unomi/itests/V2CompatibilityModeIT.java b/itests/src/test/java/org/apache/unomi/itests/V2CompatibilityModeIT.java
index 691514eff..04b41414d 100644
--- a/itests/src/test/java/org/apache/unomi/itests/V2CompatibilityModeIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/V2CompatibilityModeIT.java
@@ -201,28 +201,28 @@ private void testV3ModeBehavior() throws Exception {
contextRequest.setSessionId(TEST_SESSION_ID);
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID, 401, false);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ TestUtils.RequestResponse response = executeContextJSONRequest(request, TEST_SESSION_ID, 401, false);
assertEquals("V2-style request should be rejected in V3 mode", 401, response.getStatusCode());
// Test V3-style request with public API key - should work
request = new HttpPost(getFullUrl(CONTEXT_URL));
request.addHeader(UNOMI_API_KEY_HEADER, testPublicKey.getKey());
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("V3-style request with public API key should work in V3 mode", 200, response.getStatusCode());
// Test V3-style request with private API key - should work
request = new HttpPost(getFullUrl(CONTEXT_URL));
addPrivateTenantAuth(request, testTenant, testPrivateKey);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("V3-style request with private API key should work in V3 mode", 200, response.getStatusCode());
// Test V3-style request with JAAS authentication - should work
request = new HttpPost(getFullUrl(CONTEXT_URL));
request.addHeader(UNOMI_TENANT_ID_HEADER, testTenant.getItemId());
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("karaf", "karaf"));
@@ -252,30 +252,30 @@ private void testV2ModeBehavior() throws Exception {
contextRequest.setSessionId(TEST_SESSION_ID);
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ TestUtils.RequestResponse response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("V2-style request should work in V2 compatibility mode", 200, response.getStatusCode());
// Test V2-style request with X-Unomi-Peer header (V2 third-party auth) - should work
request = new HttpPost(getFullUrl(CONTEXT_URL));
request.addHeader(UNOMI_PEER_HEADER, "670c26d1cc413346c3b2fd9ce65dab41");
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("V2-style request with X-Unomi-Peer should work in V2 compatibility mode", 200, response.getStatusCode());
// Test V3-style request with public API key - in V2 mode, V3 API keys are ignored (request succeeds but no events processed)
request = new HttpPost(getFullUrl(CONTEXT_URL));
request.addHeader(UNOMI_API_KEY_HEADER, testPublicKey.getKey());
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("V3-style request with public API key should return 200 in V2 compatibility mode", 200, response.getStatusCode());
assertEquals("V3-style request with public API key should have 0 processed events in V2 mode", 0, response.getContextResponse().getProcessedEvents());
// Test V3-style request with private API key - in V2 mode, V3 API keys are ignored (request succeeds but no events processed)
request = new HttpPost(getFullUrl(CONTEXT_URL));
addPrivateTenantAuth(request, testTenant, testPrivateKey);
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("V3-style request with private API key should return 200 in V2 compatibility mode", 200, response.getStatusCode());
assertEquals("V3-style request with private API key should have 0 processed events in V2 mode", 0, response.getContextResponse().getProcessedEvents());
@@ -323,24 +323,24 @@ public void testV2CompatibilityModeWithProtectedEvents() throws Exception {
contextRequest.setEvents(Arrays.asList(loginEvent));
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID, 200, false);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ TestUtils.RequestResponse response = executeContextJSONRequest(request, TEST_SESSION_ID, 200, false);
assertEquals("Protected event without V2 auth should return 200", 200, response.getStatusCode());
assertEquals("Protected event without V2 auth should have 0 processed events", 0, response.getContextResponse().getProcessedEvents());
// Test protected event with V2 third-party authentication - should work
request = new HttpPost(getFullUrl(CONTEXT_URL));
request.addHeader(UNOMI_PEER_HEADER, "670c26d1cc413346c3b2fd9ce65dab41");
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("Protected event with V2 auth should work", 200, response.getStatusCode());
assertEquals("Protected event with V2 auth should have 1 processed event", 1, response.getContextResponse().getProcessedEvents());
// Test protected event with empty X-Unomi-Peer header - should be rejected
request = new HttpPost(getFullUrl(CONTEXT_URL));
request.addHeader(UNOMI_PEER_HEADER, "");
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("Protected event with empty X-Unomi-Peer should return 200", 200, response.getStatusCode());
assertEquals("Protected event with empty X-Unomi-Peer should have 0 processed events", 0, response.getContextResponse().getProcessedEvents());
@@ -354,7 +354,7 @@ public void testV2CompatibilityModeWithProtectedEvents() throws Exception {
request = new HttpPost(getFullUrl(CONTEXT_URL));
request.setEntity(new StringEntity(contextRequestJson, ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("Non-protected event without auth should work in V2 mode", 200, response.getStatusCode());
assertEquals("Non-protected event without auth should have 1 processed event", 1, response.getContextResponse().getProcessedEvents());
}
@@ -385,8 +385,8 @@ public void testV2CompatibilityModeDefaultTenant() throws Exception {
contextRequest.setSessionId(TEST_SESSION_ID);
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ TestUtils.RequestResponse response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("V2-style request should work with BaseIT tenant as default", 200, response.getStatusCode());
}
@@ -420,8 +420,8 @@ public void testV2CompatibilityModeConfigurationPersistence() throws Exception {
contextRequest.setSessionId(TEST_SESSION_ID);
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
- request.setEntity(new StringEntity(objectMapper.writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
- TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ request.setEntity(new StringEntity(getObjectMapper().writeValueAsString(contextRequest), ContentType.APPLICATION_JSON));
+ TestUtils.RequestResponse response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("V2-style request should still work after service update", 200, response.getStatusCode());
}
@@ -440,13 +440,13 @@ public void testV2CompatibilityProtectedEventNegativeCases() throws Exception {
ContextRequest contextRequest = new ContextRequest();
contextRequest.setSessionId(TEST_SESSION_ID);
contextRequest.setEvents(Arrays.asList(loginEvent));
- String requestBody = objectMapper.writeValueAsString(contextRequest);
+ String requestBody = getObjectMapper().writeValueAsString(contextRequest);
// Case 1: protected event with an unknown provider key → rejected (0 processed events)
HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
request.addHeader(UNOMI_PEER_HEADER, "unknownkey000000000000000000000000");
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
- TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ TestUtils.RequestResponse response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("Protected event with unknown provider key should return 200", 200, response.getStatusCode());
assertEquals("Protected event with unknown provider key should have 0 processed events", 0, response.getContextResponse().getProcessedEvents());
@@ -467,7 +467,7 @@ public void testV2CompatibilityProtectedEventNegativeCases() throws Exception {
request = new HttpPost(getFullUrl(CONTEXT_URL));
request.addHeader(UNOMI_PEER_HEADER, testProviderKey);
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("Protected event with valid key but wrong source IP should return 200", 200, response.getStatusCode());
assertEquals("Protected event with valid key but wrong source IP should have 0 processed events", 0, response.getContextResponse().getProcessedEvents());
@@ -486,7 +486,7 @@ public void testV2CompatibilityProtectedEventNegativeCases() throws Exception {
request = new HttpPost(getFullUrl(CONTEXT_URL));
request.addHeader(UNOMI_PEER_HEADER, limitedKey);
request.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
- response = TestUtils.executeContextJSONRequest(request, TEST_SESSION_ID);
+ response = executeContextJSONRequest(request, TEST_SESSION_ID);
assertEquals("Protected login event with key that only allows updateProperties should return 200", 200, response.getStatusCode());
assertEquals("Protected login event with key that only allows updateProperties should have 0 processed events", 0, response.getContextResponse().getProcessedEvents());
} finally {
diff --git a/itests/src/test/java/org/apache/unomi/itests/migration/Migrate16xToCurrentVersionIT.java b/itests/src/test/java/org/apache/unomi/itests/migration/Migrate16xToCurrentVersionIT.java
index 3f7a84361..402c3f549 100644
--- a/itests/src/test/java/org/apache/unomi/itests/migration/Migrate16xToCurrentVersionIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/migration/Migrate16xToCurrentVersionIT.java
@@ -498,7 +498,7 @@ private void countNumberOfSessionIndices() {
}
private void getScopeFromEvents(CloseableHttpClient httpClient, String eventIndex) throws IOException {
String requestBody = resourceAsString(RESOURCE_MATCH_ALL_LOGIN_EVENT);
- JsonNode jsonNode = objectMapper.readTree(HttpUtils.executePostRequest(httpClient, getEsBaseUrl() + "/" + eventIndex + "/_search", requestBody, null));
+ JsonNode jsonNode = getObjectMapper().readTree(HttpUtils.executePostRequest(httpClient, getEsBaseUrl() + "/" + eventIndex + "/_search", requestBody, null));
if (jsonNode.has("hits") && jsonNode.get("hits").has("hits") && !jsonNode.get("hits").get("hits").isEmpty()) {
jsonNode.get("hits").get("hits").forEach(doc -> {
JsonNode event = doc.get("_source");
@@ -522,7 +522,7 @@ private int countItems(CloseableHttpClient httpClient, String index, String requ
if (requestBody == null) {
requestBody = resourceAsString(RESOURCE_MUST_NOT_MATCH_EVENTTYPE);
}
- JsonNode jsonNode = objectMapper.readTree(HttpUtils.executePostRequest(httpClient, getEsBaseUrl() + "/" + index + "/_count", requestBody, null));
+ JsonNode jsonNode = getObjectMapper().readTree(HttpUtils.executePostRequest(httpClient, getEsBaseUrl() + "/" + index + "/_count", requestBody, null));
return jsonNode.get("count").asInt();
}
@@ -577,7 +577,7 @@ private void checkTenantIdsApplied() throws IOException {
*/
private void checkDocumentsInIndex(String indexName, String expectedTenantId, boolean isSystemIndex) throws IOException {
String query = HttpUtils.executeGetRequest(httpClient, getEsBaseUrl() + "/" + indexName + "/_search?size=10", null);
- JsonNode jsonNode = objectMapper.readTree(query);
+ JsonNode jsonNode = getObjectMapper().readTree(query);
if (jsonNode.has("hits") && jsonNode.get("hits").has("hits") && !jsonNode.get("hits").get("hits").isEmpty()) {
for (JsonNode hit : jsonNode.get("hits").get("hits")) {
JsonNode source = hit.get("_source");
@@ -667,7 +667,7 @@ private void checkDefaultTenantCreated() throws Exception {
if (defaultTenant == null) {
// Check if tenant exists in Elasticsearch directly
String query = HttpUtils.executeGetRequest(httpClient, getEsBaseUrl() + "/" + INDEX_PREFIX_CONTEXT + "tenant/_search?q=itemId:" + tenantId, null);
- JsonNode jsonNode = objectMapper.readTree(query);
+ JsonNode jsonNode = getObjectMapper().readTree(query);
if (jsonNode.has("hits") && jsonNode.get("hits").has("hits") && !jsonNode.get("hits").get("hits").isEmpty()) {
JsonNode tenantDoc = jsonNode.get("hits").get("hits").get(0).get("_source");
Assert.assertEquals("Default tenant should have correct itemId", tenantId, tenantDoc.get("itemId").asText());
@@ -722,8 +722,8 @@ private void checkDefinitionsServiceObjects(String itemType, String itemTypeDesc
query.set("query", queryWrapper);
query.put("size", 1000);
- String response = HttpUtils.executePostRequest(httpClient, getEsBaseUrl() + "/" + INDEX_SYSTEMITEMS + "/_search", objectMapper.writeValueAsString(query), null);
- JsonNode jsonNode = objectMapper.readTree(response);
+ String response = HttpUtils.executePostRequest(httpClient, getEsBaseUrl() + "/" + INDEX_SYSTEMITEMS + "/_search", getObjectMapper().writeValueAsString(query), null);
+ JsonNode jsonNode = getObjectMapper().readTree(response);
Set itemIds = new HashSet<>();
if (jsonNode.has("hits") && jsonNode.get("hits").has("hits")) {
@@ -839,8 +839,8 @@ private void checkLegacyQueryBuilderMigration() throws Exception {
query.set("query", queryWrapper);
query.put("size", 1000);
- String response = HttpUtils.executePostRequest(httpClient, getEsBaseUrl() + "/" + INDEX_SYSTEMITEMS + "/_search", objectMapper.writeValueAsString(query), null);
- JsonNode jsonNode = objectMapper.readTree(response);
+ String response = HttpUtils.executePostRequest(httpClient, getEsBaseUrl() + "/" + INDEX_SYSTEMITEMS + "/_search", getObjectMapper().writeValueAsString(query), null);
+ JsonNode jsonNode = getObjectMapper().readTree(response);
int conditionTypesChecked = 0;
int conditionTypesWithLegacyIds = 0;
@@ -928,8 +928,8 @@ private void checkRulesAndSegmentsForEmbeddedConditionTypes() throws Exception {
query.put("_source", "condition");
String response = HttpUtils.executePostRequest(httpClient, getEsBaseUrl() + "/" + rulesIndex + "/_search",
- objectMapper.writeValueAsString(query), null);
- JsonNode jsonNode = objectMapper.readTree(response);
+ getObjectMapper().writeValueAsString(query), null);
+ JsonNode jsonNode = getObjectMapper().readTree(response);
int rulesChecked = 0;
int rulesWithEmbeddedConditionTypes = 0;
@@ -977,8 +977,8 @@ private void checkRulesAndSegmentsForEmbeddedConditionTypes() throws Exception {
query.put("_source", "condition");
String response = HttpUtils.executePostRequest(httpClient, getEsBaseUrl() + "/" + segmentsIndex + "/_search",
- objectMapper.writeValueAsString(query), null);
- JsonNode jsonNode = objectMapper.readTree(response);
+ getObjectMapper().writeValueAsString(query), null);
+ JsonNode jsonNode = getObjectMapper().readTree(response);
int segmentsChecked = 0;
int segmentsWithEmbeddedConditionTypes = 0;
diff --git a/itests/src/test/java/org/apache/unomi/itests/shell/ShellCommandsBaseIT.java b/itests/src/test/java/org/apache/unomi/itests/shell/ShellCommandsBaseIT.java
index 9f2288157..78d36ce17 100644
--- a/itests/src/test/java/org/apache/unomi/itests/shell/ShellCommandsBaseIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/shell/ShellCommandsBaseIT.java
@@ -16,9 +16,7 @@
*/
package org.apache.unomi.itests.shell;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.unomi.itests.BaseIT;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,18 +38,6 @@ public abstract class ShellCommandsBaseIT extends BaseIT {
protected static final Logger LOGGER = LoggerFactory.getLogger(ShellCommandsBaseIT.class);
- /**
- * Get ObjectMapper for JSON parsing.
- * Uses CustomObjectMapper for consistency with Unomi's JSON handling.
- * This ensures proper deserialization of Unomi Item types and maintains
- * the same date formatting and configuration as the rest of the system.
- *
- * Note: This is lazy-initialized to avoid class loading issues before OSGi is ready.
- */
- protected ObjectMapper getJsonMapper() {
- return CustomObjectMapper.getObjectMapper();
- }
-
/**
* Execute a shell command and capture its output as a string.
*
@@ -93,10 +79,10 @@ protected Map parseJsonOutput(String output) {
int jsonEnd = output.lastIndexOf('}');
if (jsonStart >= 0 && jsonEnd > jsonStart) {
String jsonStr = output.substring(jsonStart, jsonEnd + 1);
- return (Map) getJsonMapper().readValue(jsonStr, Map.class);
+ return (Map) getObjectMapper().readValue(jsonStr, Map.class);
}
// If no JSON found, try parsing the whole output
- return (Map) getJsonMapper().readValue(output, Map.class);
+ return (Map) getObjectMapper().readValue(output, Map.class);
} catch (Exception e) {
// Don't log here - any logging can be captured by command output stream causing StackOverflow
// Just throw exception without logging
diff --git a/kar/src/main/feature/feature.xml b/kar/src/main/feature/feature.xml
index 9d59b3022..9165caf52 100644
--- a/kar/src/main/feature/feature.xml
+++ b/kar/src/main/feature/feature.xml
@@ -39,8 +39,8 @@
feature
spifly
shell-compat
- jackson
- jackson-jaxrs
+ jackson
+ jackson-jaxrs
mvn:commons-collections/commons-collections/${commons-collections.version}
mvn:org.apache.commons/commons-lang3/${commons-lang3.version}
mvn:commons-beanutils/commons-beanutils/${commons-beanutils.version}
diff --git a/lifecycle-watcher/pom.xml b/lifecycle-watcher/pom.xml
index 8b608383a..92b28c314 100644
--- a/lifecycle-watcher/pom.xml
+++ b/lifecycle-watcher/pom.xml
@@ -61,6 +61,11 @@
commons-lang3
provided
+
+ org.slf4j
+ slf4j-api
+ provided
+
diff --git a/manual/src/main/asciidoc/index.adoc b/manual/src/main/asciidoc/index.adoc
index 09a9d8843..f5cfa4a44 100644
--- a/manual/src/main/asciidoc/index.adoc
+++ b/manual/src/main/asciidoc/index.adoc
@@ -147,6 +147,8 @@ include::connectors/salesforce-connector.adoc[]
include::building-and-deploying.adoc[]
+include::upgrades/platform-upgrades.adoc[]
+
include::shell-commands.adoc[]
include::writing-plugins.adoc[]
diff --git a/manual/src/main/asciidoc/upgrades/platform-upgrades.adoc b/manual/src/main/asciidoc/upgrades/platform-upgrades.adoc
new file mode 100644
index 000000000..1430bc391
--- /dev/null
+++ b/manual/src/main/asciidoc/upgrades/platform-upgrades.adoc
@@ -0,0 +1,40 @@
+//
+// 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.
+//
+
+[[_platform_upgrades]]
+=== Platform upgrades
+
+This section is for Apache Unomi *maintainers* upgrading runtime platform components: Apache Karaf, JDK, CXF/Jackson/Jetty, Elasticsearch and OpenSearch clients, Log4j, integration tests, and application dependency hygiene.
+
+Procedures are derived from git history (UNOMI-216, 491, 876, 899, 901, 828, 921, 875) and Unomi 3.1 platform upgrade work, cross-checked against Apache JIRA (see <<_upgrade_jira_reference,JIRA reference>>).
+
+include::upgrade-overview.adoc[]
+
+include::upgrade-jira-reference.adoc[]
+
+include::upgrade-karaf.adoc[]
+
+include::upgrade-java.adoc[]
+
+include::upgrade-cxf-jackson-jetty.adoc[]
+
+include::upgrade-elasticsearch.adoc[]
+
+include::upgrade-opensearch.adoc[]
+
+include::upgrade-log4j.adoc[]
+
+include::upgrade-integration-tests.adoc[]
+
+include::upgrade-dependency-hygiene.adoc[]
diff --git a/manual/src/main/asciidoc/upgrades/upgrade-cxf-jackson-jetty.adoc b/manual/src/main/asciidoc/upgrades/upgrade-cxf-jackson-jetty.adoc
new file mode 100644
index 000000000..85f1d6d6b
--- /dev/null
+++ b/manual/src/main/asciidoc/upgrades/upgrade-cxf-jackson-jetty.adoc
@@ -0,0 +1,181 @@
+//
+// 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.
+//
+
+[[_upgrade_cxf_jackson_jetty]]
+==== CXF, Jackson, and Jetty (Karaf cascade libraries)
+
+Karaf upgrades change the *runtime* versions of several frameworks even when you only bump `${karaf.version}`. Unomi pins some of these in root `pom.xml` and `kar/src/main/feature/feature.xml` so compile-time, feature verify, and OSGi resolution stay aligned.
+
+This guide covers libraries that *cascade from Karaf* but are *owned by Unomi* for REST, WAB, GraphQL, and ITs.
+
+===== Version pins (Unomi 3.1 baseline)
+
+[cols="1,1,2", options="header"]
+|===
+| Property | Value | Primary consumers
+
+| `cxf.version`
+| 3.6.8
+| `kar/.../feature.xml`, `rest/`, `bom/pom.xml`, OpenAPI/Swagger
+
+| `jackson.version`
+| 2.18.3
+| `bom/pom.xml`, `kar/.../feature.xml` (pinned features + extra modules)
+
+| `jetty.version`
+| 9.4.58.v20250814
+| `graphql/karaf-feature/pom.xml`, `itests/pom.xml` (compile/test only)
+|===
+
+IMPORTANT: `${jetty.version}` must match the Jetty bundles shipped by the Pax Web version bundled in your `${karaf.version}`. Inspect `pax-web-features-*-features.xml` on Maven Central — do not guess from release notes alone.
+
+===== How Karaf supplies vs how Unomi overrides
+
+[cols="1,2,2", options="header"]
+|===
+| Library | Karaf source | Unomi override
+
+| CXF
+| `standard` features reference `apache-cxf/RELEASE`
+| Explicit repo + versioned feature in `feature.xml` (UNOMI-876, UNOMI-899)
+
+| Jackson
+| `specs` features (`jackson`, `jackson-jaxrs`) — multiple versions on 4.4.11+
+| Pin `` — never unversioned (PR3 lesson)
+
+| Jetty
+| Pax Web bundles (runtime)
+| `${jetty.version}` for Maven compile/test deps only
+
+| Log4j (runtime)
+| pax-logging-log4j2 bundle
+| See <<_upgrade_log4j,Log4j upgrade>> — `${log4j.version}` is compile-time for fragments
+|===
+
+
+===== Karaf supply vs Unomi override (diagram)
+
+[plantuml]
+----
+@startuml
+skinparam componentStyle uml2
+
+package "Runtime (OSGi inside Karaf)" {
+ [Pax Web Jetty bundles]
+ [Karaf jackson features]
+ [CXF JAX-RS from apache-cxf repo]
+ [pax-logging-log4j2]
+}
+
+package "Build / verify (Maven)" {
+ [(jetty.version) compile deps]
+ [(jackson.version) in feature.xml]
+ [(cxf.version) feature + bom]
+ [(log4j.version) fragment compile]
+}
+
+note right of [Karaf jackson features]
+ 4.4.11+ may expose multiple
+ jackson feature versions —
+ always pin version attribute
+end note
+
+note right of [CXF JAX-RS from apache-cxf repo]
+ CXF 4.x has no OSGi support —
+ stay on CXF 3.6.x (UNOMI-876)
+end note
+
+[(jetty.version) compile deps] ..> [Pax Web Jetty bundles] : must match bundle version
+[(jackson.version) in feature.xml] --> [Karaf jackson features]
+[(cxf.version) feature + bom] --> [CXF JAX-RS from apache-cxf repo]
+@enduml
+----
+
+===== CXF upgrade (UNOMI-899, `8cff171ef`)
+
+*When:* REST/OpenAPI breakage, Karaf major jump (4.2 → 4.4), or explicit CXF security release.
+
+*Steps:*
+
+. Bump `` in root `pom.xml` (managed via `bom/pom.xml`).
+. Confirm `kar/src/main/feature/feature.xml` still has:
+
+[source,xml]
+----
+mvn:org.apache.cxf.karaf/apache-cxf/${cxf.version}/xml/features
+...
+cxf-jaxrs
+----
+
+. Rebuild REST modules and run feature verify (`mvn -pl package -DskipTests package`).
+. Regenerate or smoke-test OpenAPI/Swagger UI (`unomi-rest-ui` feature) if JAX-RS annotations changed.
+
+*History:* UNOMI-876 moved CXF 3.3.11 → 3.6.5 with Karaf 4.4.8. UNOMI-899 bumped 3.6.5 → 3.6.8 independently and switched Jackson delivery to Karaf features.
+
+===== Jackson upgrade
+
+*Risk:* Karaf 4.4.11+ ships *multiple* `jackson` feature versions (e.g. 2.18.3 and 2.21.2). An unversioned `jackson` may resolve to the newest, while Unomi still installs datatype/jaxb modules at `${jackson.version}` — causing mixed Jackson on the OSGi classpath.
+
+*Required pattern* in `kar/src/main/feature/feature.xml`:
+
+[source,xml]
+----
+jackson
+jackson-jaxrs
+mvn:com.fasterxml.jackson.datatype/jackson-datatype-jsr310/${jackson.version}
+mvn:com.fasterxml.jackson.module/jackson-module-jaxb-annotations/${jackson.version}
+----
+
+*Procedure:*
+
+. Check which `jackson` feature versions exist for your `${karaf.version}`:
+
+[source,bash]
+----
+curl -sL "https://repo1.maven.org/maven2/org/apache/karaf/features/specs/${KARAF_VERSION}/specs-${KARAF_VERSION}-features.xml" \
+ | rg 'feature name="jackson"'
+----
+
+. Set `${jackson.version}` to a version that exists in Karaf *specs* (not necessarily the highest).
+. Bump `bom/pom.xml` managed Jackson artifacts if needed.
+. Do **not** embed Jackson in WABs or persistence bundles — `scope=provided` (UNOMI-491, UNOMI-876).
+
+*Major Karaf migration note:* UNOMI-876 initially listed every Jackson JAR in `feature.xml`; UNOMI-899 moved to Karaf features *with* version pins. Do not revert to unversioned features.
+
+===== Jetty alignment after Karaf bump
+
+Pax Web version is tied to `${karaf.version}`. Example: Karaf 4.4.11 → Pax Web 8.0.35 → Jetty 9.4.58.v20250814.
+
+. Inspect Pax Web features for your Karaf version:
+
+[source,bash]
+----
+curl -sL "https://repo1.maven.org/maven2/org/apache/karaf/features/standard/${KARAF_VERSION}/standard-${KARAF_VERSION}-features.xml" \
+ | rg 'pax-web-features'
+# then open that pax-web version's features XML and rg 'jetty-server'
+----
+
+. Update `${jetty.version}` in root `pom.xml` to match Pax Web's Jetty bundle version.
+. Modules using `${jetty.version}`: `graphql/karaf-feature/pom.xml`, `itests/pom.xml`.
+
+Runtime Jetty comes from Pax Web OSGi bundles — no Jetty bundles in Unomi `feature.xml`.
+
+===== Verification
+
+[source,bash]
+----
+rg 'cxf\.version|jackson\.version|jetty\.version' pom.xml kar/src/main/feature/feature.xml
+mvn -pl kar,package -DskipTests package
+./build.sh --ci
+----
diff --git a/manual/src/main/asciidoc/upgrades/upgrade-dependency-hygiene.adoc b/manual/src/main/asciidoc/upgrades/upgrade-dependency-hygiene.adoc
new file mode 100644
index 000000000..f362ea1bc
--- /dev/null
+++ b/manual/src/main/asciidoc/upgrades/upgrade-dependency-hygiene.adoc
@@ -0,0 +1,115 @@
+//
+// 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.
+//
+
+[[_upgrade_dependency_hygiene]]
+==== Dependency hygiene (non-platform bumps)
+
+Not every version bump is a Karaf or persistence migration. PR2-style hygiene (UNOMI-875 `#795`, `ef10dfe15`) covers *application* dependencies that must *not* break OSGi rules established in platform upgrades.
+
+The bulk dependency pass https://issues.apache.org/jira/browse/UNOMI-829[UNOMI-829] defines the maintenance strategy: 30-minute timebox per library, maintenance-branch fallback, and separate tickets for Karaf/CXF/GraphQL — see <<_upgrade_jira_reference,JIRA reference>>.
+
+===== Scope
+
+[cols="1,2,2", options="header"]
+|===
+| Track | Examples | OSGi impact
+
+| Frontend / WAB npm
+| `wab/`, `graphql-ui/` yarn lockfiles
+| Build-time only — verify WAB packaging still works
+
+| Messaging
+| `kafka-clients` in `plugins/kafka-injector`
+| Usually embed or declare imports explicitly in bundle
+
+| Commons / utilities
+| `commons-lang3`, `commons-io`, Guava
+| Prefer `provided` if Karaf feature supplies them
+
+| Logging
+| `${log4j.version}`
+| **High** — never without persistence embed exclusion
+|===
+
+===== Recommended order (platform stack)
+
+
+
+===== Hygiene vs platform upgrade scope
+
+[plantuml]
+----
+@startuml
+skinparam activityBackgroundColor #F8F8F8
+
+start
+:Library bump requested;
+if (Touches Karaf features,
+CXF, persistence embed,
+or log4j OSGi?) then (yes)
+ :Stop — use platform upgrade guides
+not bulk hygiene (UNOMI-829);
+ stop
+else (no)
+endif
+:Try upgrade + mvn compile
+(30 min timebox);
+if (Build green?) then (yes)
+ :Commit in hygiene PR slice;
+ stop
+else (no)
+ :File follow-up ticket
+or use maintenance branch version;
+ stop
+endif
+@enduml
+----
+. Karaf (+ JDK if major) — <<_upgrade_karaf,Karaf>>
+. CXF / Jackson / Jetty alignment — <<_upgrade_cxf_jackson_jetty,CXF/Jackson/Jetty>>
+. Elasticsearch + OpenSearch clients — <<_upgrade_elasticsearch,Elasticsearch>>, <<_upgrade_opensearch,OpenSearch>>
+. Log4j — <<_upgrade_log4j,Log4j>> (last among platform pins)
+. Application hygiene (npm, kafka, commons) — this section
+. `./build.sh --ci`
+
+===== npm / frontend (Track E1)
+
+. Bump lockfiles in `wab/` and `graphql-ui/` with `yarn upgrade` or targeted version edits.
+. Rebuild WAB modules: `mvn -pl wab,graphql-ui -am -DskipTests package`
+. Smoke-test tracker and GraphQL UI in a running Karaf if UI behavior changed.
+
+===== Java libraries (Track E2)
+
+. Bump property in root `pom.xml`; confirm `bom/pom.xml` manages the artifact.
+. Check consuming bundle `Import-Package` / embed instructions — do not pull platform libs into compile scope accidentally.
+. Run unit tests for affected modules before full CI.
+
+===== Log4j hygiene trap (PR2 lesson)
+
+Bumping `${log4j.version}` alone caused Elasticsearch core to embed `log4j-core` transitively → Felix duplicate activator → feature verify failure. Always pair log4j bumps with:
+
+[source,xml]
+----
+!groupId=org.apache.logging.log4j;artifactId=*,
+----
+
+in persistence ES/OS `Embed-Dependency` (see <<_upgrade_log4j,Log4j upgrade>>).
+
+===== Verification
+
+[source,bash]
+----
+mvn -DskipTests compile
+mvn -pl package -DskipTests package
+./build.sh --ci
+----
diff --git a/manual/src/main/asciidoc/upgrades/upgrade-elasticsearch.adoc b/manual/src/main/asciidoc/upgrades/upgrade-elasticsearch.adoc
new file mode 100644
index 000000000..8673ee107
--- /dev/null
+++ b/manual/src/main/asciidoc/upgrades/upgrade-elasticsearch.adoc
@@ -0,0 +1,183 @@
+//
+// 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.
+//
+
+[[_upgrade_elasticsearch]]
+==== Upgrading the Elasticsearch client
+
+Unomi does *not* embed the Elasticsearch server. It embeds the *Java API client* and REST transport inside the `unomi-persistence-elasticsearch-core` OSGi bundle, then exports query DSL types for the conditions bundle.
+
+For *data migration* between Elasticsearch major versions, see also <<_migrate_from_elasticsearch_7_to_elasticsearch_9,ES7 to ES9 migration>> (distinct from https://issues.apache.org/jira/browse/UNOMI-884[UNOMI-884] V3 data migration scripts).
+
+Client migration is tracked in https://issues.apache.org/jira/browse/UNOMI-901[UNOMI-901] (supersedes https://issues.apache.org/jira/browse/UNOMI-851[UNOMI-851]). JIRA notes an interim ES 8 client on Java 8 for 2.x branches only — Unomi 3 pairs ES 9 with Karaf 4.4 + JDK 17; see <<_upgrade_jira_reference,JIRA reference>>.
+
+===== Version pins
+
+[cols="1,2", options="header"]
+|===
+| Property | Files
+
+| `elasticsearch.version`
+| Root `pom.xml`, `bom/pom.xml`
+
+| `elasticsearch.test.version`
+| Root `pom.xml`, IT Docker image in `itests/pom.xml`
+|===
+
+Update in the same pull request: `docker/README.md`, `docker/src/main/docker/docker-compose-*.yml`, `itests/docker-compose-snapshot-analysis.yml`.
+
+Image tag example: `docker.elastic.co/elasticsearch/elasticsearch:9.4.3`.
+
+===== Bundle architecture
+
+[cols="1,2", options="header"]
+|===
+| Module | Role
+
+| `persistence-elasticsearch/core`
+| Embeds ES client; exports `co.elastic.clients.*`; provides `PersistenceService`
+
+| `persistence-elasticsearch/conditions`
+| Query builders; imports exported ES types; does not re-embed client
+|===
+
+
+[plantuml]
+----
+@startuml
+skinparam componentStyle uml2
+skinparam component {
+ BackgroundColor<> #E8F4E8
+ BackgroundColor<