This is an automated email from the ASF dual-hosted git repository.
dgriffon pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/master by this push:
new 7dfcb08 [UNOMI-449] : forward existing endpoints to jax-rs endpoints
(#273)
7dfcb08 is described below
commit 7dfcb0807d113aada8cb9c3e5be51d08576161ad
Author: David Griffon <[email protected]>
AuthorDate: Fri Apr 2 14:34:10 2021 +0200
[UNOMI-449] : forward existing endpoints to jax-rs endpoints (#273)
* UNOMI-449 : forward existing endpoints to jax-rs endpoints
* UNOMI-449 : fix following code review
* UNOMI-449 : fix iTests, restore context.js
---
.../java/org/apache/unomi/itests/TestUtils.java | 4 +-
.../httpclient/HttpClientThatWaitsForUnomi.java | 52 +++
.../java/org/apache/unomi/rest/ClientEndpoint.java | 50 +-
.../org/apache/unomi/rest/ContextJsonEndpoint.java | 141 +++---
.../apache/unomi/rest/EventsCollectorEndpoint.java | 60 +--
.../impl/DefaultRestAuthenticationConfig.java | 40 +-
.../main/java/org/apache/unomi/web/Changes.java | 53 ---
.../java/org/apache/unomi/web/ClientServlet.java | 157 +------
.../java/org/apache/unomi/web/ContextServlet.java | 502 +--------------------
.../apache/unomi/web/EventsCollectorServlet.java | 196 +-------
.../web/HttpServletRequestForwardWrapper.java | 86 ++++
.../main/java/org/apache/unomi/web/HttpUtils.java | 119 -----
.../java/org/apache/unomi/web/ServletCommon.java | 115 -----
.../resources/OSGI-INF/blueprint/blueprint.xml | 14 +-
14 files changed, 318 insertions(+), 1271 deletions(-)
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 97feed0..1045d12 100644
--- a/itests/src/test/java/org/apache/unomi/itests/TestUtils.java
+++ b/itests/src/test/java/org/apache/unomi/itests/TestUtils.java
@@ -23,7 +23,6 @@ import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
-import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.unomi.api.ContextResponse;
import org.apache.unomi.api.Event;
@@ -31,6 +30,7 @@ import org.apache.unomi.api.Profile;
import org.apache.unomi.api.Session;
import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.services.DefinitionsService;
+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;
@@ -64,7 +64,7 @@ public class TestUtils {
}
public static RequestResponse executeContextJSONRequest(HttpPost
request, String sessionId) throws IOException {
- try (CloseableHttpResponse response =
HttpClientBuilder.create().build().execute(request)) {
+ try (CloseableHttpResponse response =
HttpClientThatWaitsForUnomi.doPost(request)) {
// validate mimeType
HttpEntity entity = response.getEntity();
String mimeType =
ContentType.getOrDefault(entity).getMimeType();
diff --git
a/itests/src/test/java/org/apache/unomi/itests/tools/httpclient/HttpClientThatWaitsForUnomi.java
b/itests/src/test/java/org/apache/unomi/itests/tools/httpclient/HttpClientThatWaitsForUnomi.java
new file mode 100644
index 0000000..9685c01
--- /dev/null
+++
b/itests/src/test/java/org/apache/unomi/itests/tools/httpclient/HttpClientThatWaitsForUnomi.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.unomi.itests.tools.httpclient;
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.eclipse.jetty.http.HttpStatus;
+
+import java.io.IOException;
+
+public class HttpClientThatWaitsForUnomi {
+
+ private static final long TIMER = 1000L;
+ private static final int MAX_TRIES = 10;
+
+ public static CloseableHttpResponse doPost(HttpPost request) throws
IOException {
+ int count = 0;
+ while (true) {
+ CloseableHttpResponse response =
HttpClientBuilder.create().build().execute(request);
+ final int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode < HttpStatus.BAD_REQUEST_400) {
+ return response;
+ }
+ if (count++ > MAX_TRIES || statusCode >
HttpStatus.INTERNAL_SERVER_ERROR_500) {
+ throw new RuntimeException(String.format("connecting to the
server failed %s times with status %s %s", MAX_TRIES, statusCode,
response.getStatusLine().getReasonPhrase()));
+ }
+ try {
+ // We should find another way to avoid that sleep here.
+ Thread.sleep(TIMER);
+ } catch (InterruptedException e) {
+ // exit .. it should not happen
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/rest/src/main/java/org/apache/unomi/rest/ClientEndpoint.java
b/rest/src/main/java/org/apache/unomi/rest/ClientEndpoint.java
index fb7f046..6d0f689 100644
--- a/rest/src/main/java/org/apache/unomi/rest/ClientEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/ClientEndpoint.java
@@ -25,7 +25,6 @@ import
org.apache.cxf.rs.security.cors.CrossOriginResourceSharing;
import org.apache.unomi.api.Profile;
import org.apache.unomi.api.services.ConfigSharingService;
import org.apache.unomi.api.services.ProfileService;
-import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
@@ -37,9 +36,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import java.io.OutputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
@@ -51,8 +48,6 @@ import java.util.Set;
* A servlet filter to serve a context-specific Javascript containing the
current request context object.
*/
@WebService
-@Produces
-@Consumes(MediaType.TEXT_PLAIN)
@CrossOriginResourceSharing(
allowAllOrigins = true,
allowCredentials = true
@@ -62,30 +57,40 @@ import java.util.Set;
public class ClientEndpoint {
private static final Logger logger =
LoggerFactory.getLogger(ClientEndpoint.class.getName());
- private static final long serialVersionUID = 2928875960103325238L;
+ private static final String CONTENT_DISPOSITION_HEADER_KEY =
"Content-Disposition";
+
+ private static final String FILE_NAME_WO_EXT = "my-profile";
+
+ private static String getContentDispostionHeader(String extension) {
+ return String.format("attachment; filename=\"%s.%s\"",
FILE_NAME_WO_EXT, extension);
+ }
@Reference
private ProfileService profileService;
@Reference
private ConfigSharingService configSharingService;
- private final String FILE_NAME_WO_EXT = "my-profile";
@Context
HttpServletRequest request;
@Context
HttpServletResponse response;
+ @OPTIONS
+ @Path("/client/{operation}/{param}")
+ public Response options() {
+ return Response.status(Response.Status.NO_CONTENT).build();
+ }
+
@GET
@Path("/client/{operation}/{param}")
public Response getClient(@PathParam("operation") String operation,
@PathParam("param") String param) throws JsonProcessingException {
- switch (operation) {
- case "myprofile":
- if (((String)
configSharingService.getProperty("allowedProfileDownloadFormats")).contains(param))
{
- return donwloadCurrentProfile(param);
- } else {
- throw new InternalServerErrorException(String.format("%s
is not an allowed param", param));
- }
+ if ("myprofile".equals(operation)) {
+ if (((String)
configSharingService.getProperty("allowedProfileDownloadFormats")).contains(param))
{
+ return donwloadCurrentProfile(param);
+ } else {
+ throw new InternalServerErrorException(String.format("%s is
not an allowed param", param));
+ }
}
throw new NotFoundException();
}
@@ -112,8 +117,9 @@ public class ClientEndpoint {
return prepareCsvFileToDownload(currentProfile,
request.getParameter("vertical") != null);
case "text":
return prepareYamlFileToDownload(currentProfile, true);
+ default:
+ throw new NotFoundException();
}
-
}
}
throw new NotFoundException();
@@ -121,7 +127,8 @@ public class ClientEndpoint {
private Response prepareCsvFileToDownload(Profile currentProfile, boolean
vertical) {
response.setContentType("text/csv");
- response.setHeader("Content-Disposition", "attachment; filename=\"" +
FILE_NAME_WO_EXT + ".csv\"");
+
+ response.setHeader(CONTENT_DISPOSITION_HEADER_KEY,
getContentDispostionHeader("csv"));
StringWriter writer = new StringWriter();
//using custom delimiter and quote character
CSVWriter csvWriter = new CSVWriter(writer);
@@ -132,12 +139,12 @@ public class ClientEndpoint {
}
} else {
Set<String> keySet = currentProfile.getProperties().keySet();
- List<String> values = new ArrayList();
+ List<String> values = new ArrayList<>();
for (Object value : currentProfile.getProperties().values()) {
values.add(value.toString().trim().replace("\n", ""));
}
- csvWriter.writeNext(keySet.toArray(new String[keySet.size()]));
- csvWriter.writeNext(values.toArray(new String[values.size()]));
+ csvWriter.writeNext(keySet.toArray(new String[0]));
+ csvWriter.writeNext(values.toArray(new String[0]));
}
Response.ResponseBuilder responseBuilder =
Response.ok(writer.toString());
return responseBuilder.build();
@@ -145,7 +152,8 @@ public class ClientEndpoint {
private Response prepareJsonFileToDownload(Profile currentProfile) throws
JsonProcessingException {
response.setContentType("text/json");
- response.setHeader("Content-Disposition", "attachment; filename=\"" +
FILE_NAME_WO_EXT + ".json\"");
+ response.setHeader(CONTENT_DISPOSITION_HEADER_KEY,
getContentDispostionHeader("json"));
+
ObjectMapper mapper = new ObjectMapper();
String jsonContent =
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(currentProfile.getProperties());
return Response.ok(jsonContent).build();
@@ -153,7 +161,7 @@ public class ClientEndpoint {
private Response prepareYamlFileToDownload(Profile currentProfile, boolean
asTextFile) throws JsonProcessingException {
response.setContentType("text/" + (asTextFile ? "plain" : "yaml"));
- response.setHeader("Content-Disposition", "attachment; filename=\"" +
FILE_NAME_WO_EXT + (asTextFile ? ".txt" : ".yml") + "\"");
+ response.setHeader(CONTENT_DISPOSITION_HEADER_KEY,
getContentDispostionHeader((asTextFile ? "txt" : "yml")));
YAMLFactory yf = new YAMLFactory();
ObjectMapper mapper = new ObjectMapper(yf);
String yamlContent =
mapper.writeValueAsString(currentProfile.getProperties());
diff --git a/rest/src/main/java/org/apache/unomi/rest/ContextJsonEndpoint.java
b/rest/src/main/java/org/apache/unomi/rest/ContextJsonEndpoint.java
index 9a205ca..45f9f62 100644
--- a/rest/src/main/java/org/apache/unomi/rest/ContextJsonEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/ContextJsonEndpoint.java
@@ -17,10 +17,8 @@
package org.apache.unomi.rest;
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.commons.lang3.StringUtils;
-import org.apache.cxf.jaxrs.utils.ExceptionUtils;
import org.apache.cxf.rs.security.cors.CrossOriginResourceSharing;
import org.apache.unomi.api.*;
import org.apache.unomi.api.conditions.Condition;
@@ -47,8 +45,7 @@ import javax.ws.rs.core.Response;
import java.util.*;
@WebService
-@Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-@Consumes(MediaType.TEXT_PLAIN)
+@Consumes(MediaType.APPLICATION_JSON)
@CrossOriginResourceSharing(
allowAllOrigins = true,
allowCredentials = true
@@ -82,9 +79,68 @@ public class ContextJsonEndpoint {
@Reference
private ConfigSharingService configSharingService;
+ @OPTIONS
+ @Path("/context.json")
+ public Response options() {
+ return
Response.status(Response.Status.NO_CONTENT).header("Access-Control-Allow-Origin",
"*").build();
+ }
+
@POST
+ @Produces(MediaType.TEXT_PLAIN)
+ @Path("/context.js")
+ public Response contextJSAsPost(ContextRequest contextRequest,
+ @QueryParam("personaId") String personaId,
+ @QueryParam("sessionId") String sessionId,
+ @QueryParam("timestamp") Long
timestampAsLong,
+ @QueryParam("invalidateProfile") boolean
invalidateProfile,
+ @QueryParam("invalidateSession") boolean
invalidateSession) throws JsonProcessingException {
+ return contextJSAsGet(contextRequest, personaId, sessionId,
timestampAsLong, invalidateProfile, invalidateSession);
+ }
+
+
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ @Path("/context.js")
+ public Response contextJSAsGet(ContextRequest contextRequest,
+ @QueryParam("personaId") String personaId,
+ @QueryParam("sessionId") String sessionId,
+ @QueryParam("timestamp") Long
timestampAsLong,
+ @QueryParam("invalidateProfile") boolean
invalidateProfile,
+ @QueryParam("invalidateSession") boolean
invalidateSession) throws JsonProcessingException {
+ ContextResponse contextResponse = contextJSONAsPost(contextRequest,
personaId, sessionId, timestampAsLong, invalidateProfile, invalidateSession);
+ String contextAsJSONString =
CustomObjectMapper.getObjectMapper().writeValueAsString(contextResponse);
+ StringBuilder responseAsString = new StringBuilder();
+ responseAsString.append("window.digitalData = window.digitalData ||
{};\n")
+ .append("var cxs = ")
+ .append(contextAsJSONString)
+ .append(";\n");
+ return Response.ok(responseAsString.toString()).build();
+
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
@Path("/context.json")
- public ContextResponse getContextJSON(String contextRequestAsString,
@QueryParam("timestamp") Long timestampAsLong,
@CookieParam("context-profile-id") String cookieProfileId) {
+ public ContextResponse contextJSONAsGet(ContextRequest contextRequest,
+ @QueryParam("personaId") String
personaId,
+ @QueryParam("sessionId") String
sessionId,
+ @QueryParam("timestamp") Long
timestampAsLong,
+ @QueryParam("invalidateProfile")
boolean invalidateProfile,
+ @QueryParam("invalidateSession")
boolean invalidateSession) {
+ return contextJSONAsPost(contextRequest, personaId, sessionId,
timestampAsLong, invalidateProfile, invalidateSession);
+ }
+
+ @POST
+ @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
+ @Path("/context.json")
+ public ContextResponse contextJSONAsPost(
+ ContextRequest contextRequest,
+ @QueryParam("personaId") String personaId,
+ @QueryParam("sessionId") String sessionId,
+ @QueryParam("timestamp") Long timestampAsLong,
+ @QueryParam("invalidateProfile") boolean invalidateProfile,
+ @QueryParam("invalidateSession") boolean invalidateSession
+ ) {
Date timestamp = new Date();
if (timestampAsLong != null) {
timestamp = new Date(timestampAsLong);
@@ -93,7 +149,7 @@ public class ContextJsonEndpoint {
// Handle persona
Profile profile = null;
Session session = null;
- String personaId = request.getParameter("personaId");
+ String profileId = null;
if (personaId != null) {
PersonaWithSessions personaWithSessions =
profileService.loadPersonaWithSessions(personaId);
if (personaWithSessions == null) {
@@ -105,32 +161,18 @@ public class ContextJsonEndpoint {
}
}
- // Extract payload
- ContextRequest contextRequest = null;
String scope = null;
- String sessionId = null;
- String profileId = null;
- ObjectMapper mapper = CustomObjectMapper.getObjectMapper();
- JsonFactory factory = mapper.getFactory();
- try {
- contextRequest =
mapper.readValue(factory.createParser(contextRequestAsString),
ContextRequest.class);
- } catch (Exception e) {
- logger.error("Cannot deserialize the context request payload. See
debug level for more information");
- if (logger.isDebugEnabled()) {
- logger.debug("Cannot deserialize the context request payload
because of {}", e.getMessage(), e);
+ if (contextRequest != null) {
+ if (contextRequest.getSource() != null) {
+ scope = contextRequest.getSource().getScope();
}
- throw ExceptionUtils.toHttpException(e, null);
- }
- if (contextRequest.getSource() != null) {
- scope = contextRequest.getSource().getScope();
- }
- sessionId = contextRequest.getSessionId();
- profileId = contextRequest.getProfileId();
- if (sessionId == null) {
- sessionId = request.getParameter("sessionId");
- }
+ if (contextRequest.getSessionId() != null) {
+ sessionId = contextRequest.getSessionId();
+ }
+ profileId = contextRequest.getProfileId();
+ }
if (profileId == null) {
// Get profile id from the cookie
profileId = ServletCommon.getProfileIdCookieValue(request,
(String) configSharingService.getProperty("profileIdCookieName"));
@@ -141,7 +183,7 @@ public class ContextJsonEndpoint {
if (logger.isDebugEnabled()) {
logger.debug("Request dump: {}",
HttpUtils.dumpRequestInfo(request));
}
- throw new InternalServerErrorException("Couldn't find profileId,
sessionId or personaId in incoming request!");
+ throw new BadRequestException("Couldn't find profileId, sessionId
or personaId in incoming request!");
}
int changes = EventService.NO_CHANGE;
@@ -149,18 +191,16 @@ public class ContextJsonEndpoint {
// Not a persona, resolve profile now
boolean profileCreated = false;
- boolean invalidateProfile =
request.getParameter("invalidateProfile") != null ?
- new Boolean(request.getParameter("invalidateProfile")) :
false;
if (profileId == null || invalidateProfile) {
// no profileId cookie was found or the profile has to be
invalidated, we generate a new one and create the profile in the profile service
- profile = createNewProfile(null, response, timestamp);
+ profile = createNewProfile(null, timestamp);
profileCreated = true;
} else {
profile = profileService.load(profileId);
if (profile == null) {
// this can happen if we have an old cookie but have reset
the server,
// or if we merged the profiles and somehow this cookie
didn't get updated.
- profile = createNewProfile(profileId, response, timestamp);
+ profile = createNewProfile(profileId, timestamp);
profileCreated = true;
} else {
Changes changesObject = checkMergedProfile(profile,
session);
@@ -170,8 +210,6 @@ public class ContextJsonEndpoint {
}
Profile sessionProfile;
- boolean invalidateSession =
request.getParameter("invalidateSession") != null ?
- new Boolean(request.getParameter("invalidateSession")) :
false;
if (StringUtils.isNotBlank(sessionId) && !invalidateSession) {
session = profileService.loadSession(sessionId, timestamp);
if (session != null) {
@@ -296,7 +334,6 @@ public class ContextJsonEndpoint {
}
} else {
logger.warn("Couldn't find merged profile {}, falling back to
profile {}", masterProfileId, currentProfile.getItemId());
- profile = currentProfile;
profile.setMergedWith(null);
changes = EventService.PROFILE_UPDATED;
}
@@ -379,25 +416,23 @@ public class ContextJsonEndpoint {
* @param session
*/
private void processOverrides(ContextRequest contextRequest, Profile
profile, Session session) {
- if (profile instanceof Persona) {
- if (contextRequest.getProfileOverrides() != null) {
- if (contextRequest.getProfileOverrides().getScores() != null) {
-
profile.setScores(contextRequest.getProfileOverrides().getScores());
- }
- if (contextRequest.getProfileOverrides().getSegments() !=
null) {
-
profile.setSegments(contextRequest.getProfileOverrides().getSegments());
- }
- if (contextRequest.getProfileOverrides().getProperties() !=
null) {
-
profile.setProperties(contextRequest.getProfileOverrides().getProperties());
- }
- if (contextRequest.getSessionPropertiesOverrides() != null &&
session != null) {
-
session.setProperties(contextRequest.getSessionPropertiesOverrides());
- }
+ if (profile instanceof Persona && contextRequest.getProfileOverrides()
!= null) {
+ if (contextRequest.getProfileOverrides().getScores() != null) {
+
profile.setScores(contextRequest.getProfileOverrides().getScores());
+ }
+ if (contextRequest.getProfileOverrides().getSegments() != null) {
+
profile.setSegments(contextRequest.getProfileOverrides().getSegments());
+ }
+ if (contextRequest.getProfileOverrides().getProperties() != null) {
+
profile.setProperties(contextRequest.getProfileOverrides().getProperties());
+ }
+ if (contextRequest.getSessionPropertiesOverrides() != null &&
session != null) {
+
session.setProperties(contextRequest.getSessionPropertiesOverrides());
}
}
}
- private Profile createNewProfile(String existingProfileId, ServletResponse
response, Date timestamp) {
+ private Profile createNewProfile(String existingProfileId, Date timestamp)
{
Profile profile;
String profileId = existingProfileId;
if (profileId == null) {
@@ -443,7 +478,7 @@ public class ContextJsonEndpoint {
List<PersonalizationService.PersonalizationRequest> result = new
ArrayList<>();
for (PersonalizationService.PersonalizationRequest
personalizationRequest : personalizations) {
List<PersonalizationService.PersonalizedContent>
personalizedContents =
sanitizePersonalizedContentObjects(personalizationRequest.getContents());
- if (personalizedContents != null && personalizedContents.size() >
0) {
+ if (personalizedContents != null &&
!personalizedContents.isEmpty()) {
result.add(personalizationRequest);
}
}
diff --git
a/rest/src/main/java/org/apache/unomi/rest/EventsCollectorEndpoint.java
b/rest/src/main/java/org/apache/unomi/rest/EventsCollectorEndpoint.java
index a673fd3..cf1bb0c 100644
--- a/rest/src/main/java/org/apache/unomi/rest/EventsCollectorEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/EventsCollectorEndpoint.java
@@ -17,43 +17,33 @@
package org.apache.unomi.rest;
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.rs.security.cors.CrossOriginResourceSharing;
-import org.apache.unomi.api.Event;
-import org.apache.unomi.api.EventsCollectorRequest;
-import org.apache.unomi.api.Persona;
-import org.apache.unomi.api.Profile;
-import org.apache.unomi.api.Session;
+import org.apache.unomi.api.*;
import org.apache.unomi.api.services.ConfigSharingService;
import org.apache.unomi.api.services.EventService;
import org.apache.unomi.api.services.PrivacyService;
import org.apache.unomi.api.services.ProfileService;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.apache.unomi.utils.Changes;
import org.apache.unomi.utils.ServletCommon;
-import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jws.WebService;
-import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
-import java.io.IOException;
-import java.rmi.RemoteException;
+import javax.ws.rs.core.Response;
import java.util.Date;
import java.util.UUID;
@WebService
@Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-@Consumes(MediaType.TEXT_PLAIN)
+@Consumes(MediaType.APPLICATION_JSON)
@CrossOriginResourceSharing(
allowAllOrigins = true,
allowCredentials = true
@@ -77,38 +67,32 @@ public class EventsCollectorEndpoint {
@Context
HttpServletResponse response;
+ @OPTIONS
+ @Path("/eventcollector")
+ public Response options() {
+ return Response.status(Response.Status.NO_CONTENT).build();
+ }
+
@GET
@Path("/eventcollector")
- public EventCollectorResponse get(String eventsCollectorRequestAsString,
@QueryParam("timestamp") Long timestampAsString) throws IOException {
- return doEvent(eventsCollectorRequestAsString, timestampAsString);
+ public EventCollectorResponse collectAsGet(EventsCollectorRequest
eventsCollectorRequest, @QueryParam("timestamp") Long timestampAsString) {
+ return doEvent(eventsCollectorRequest, timestampAsString);
}
@POST
@Path("/eventcollector")
- public EventCollectorResponse post(String eventsCollectorRequestAsString,
@QueryParam("timestamp") Long timestampAsLong) throws IOException {
- return doEvent(eventsCollectorRequestAsString, timestampAsLong);
+ public EventCollectorResponse collectAsPost(EventsCollectorRequest
eventsCollectorRequest, @QueryParam("timestamp") Long timestampAsLong) {
+ return doEvent(eventsCollectorRequest, timestampAsLong);
}
- private EventCollectorResponse doEvent(String
eventsCollectorRequestAsString, Long timestampAsLong) throws IOException {
+ private EventCollectorResponse doEvent(EventsCollectorRequest
eventsCollectorRequest, Long timestampAsLong) {
Date timestamp = new Date();
if (timestampAsLong != null) {
timestamp = new Date(timestampAsLong);
}
-
- ObjectMapper mapper = CustomObjectMapper.getObjectMapper();
- JsonFactory factory = mapper.getFactory();
- EventsCollectorRequest eventsCollectorRequest;
- try {
- eventsCollectorRequest =
mapper.readValue(factory.createParser(eventsCollectorRequestAsString),
EventsCollectorRequest.class);
- } catch (Exception e) {
- if (logger.isDebugEnabled()) {
- logger.debug("Cannot parse eventsCollectorRequest: {}",
eventsCollectorRequestAsString, e);
- }
- throw new RemoteException("Cannot read payload. See debug level
for more information");
- }
if (eventsCollectorRequest == null ||
eventsCollectorRequest.getEvents() == null) {
- throw new InternalServerErrorException("No events found");
+ throw new BadRequestException("No events found");
}
String sessionId = eventsCollectorRequest.getSessionId();
@@ -125,8 +109,8 @@ public class EventsCollectorEndpoint {
// Get the first available scope that is not equal to systemscope
to create the session otherwise systemscope will be used
for (Event event : eventsCollectorRequest.getEvents()) {
if (StringUtils.isNotBlank(event.getEventType())) {
- if (StringUtils.isNotBlank(event.getScope()) &&
!event.getScope().equals("systemscope")) {
- scope = event.getScope();
+ if (StringUtils.isNotBlank(event.getSourceId()) &&
!event.getSourceId().equals("systemscope")) {
+ scope = event.getSourceId();
break;
} else if (event.getSource() != null &&
StringUtils.isNotBlank(event.getSource().getScope()) &&
!event.getSource().getScope().equals("systemscope")) {
scope = event.getSource().getScope();
@@ -134,6 +118,7 @@ public class EventsCollectorEndpoint {
}
}
}
+ logger.debug("scope is now {}", scope);
String cookieProfileId =
ServletCommon.getProfileIdCookieValue(request, (String)
configSharingService.getProperty("profileIdCookieName"));
if (StringUtils.isNotBlank(cookieProfileId)) {
profile = profileService.load(cookieProfileId);
@@ -154,11 +139,13 @@ public class EventsCollectorEndpoint {
*/
} else {
Profile sessionProfile = session.getProfile();
+ final String errorMessage = String.format("No valid profile found
or persona found for profileId=%s, aborting request !", session.getProfileId());
if (sessionProfile.getItemId() != null) {
// Reload up-to-date profile
profile = profileService.load(sessionProfile.getItemId());
if (profile == null || profile instanceof Persona) {
- throw new InternalServerErrorException(String.format("No
valid profile found or persona found for profileId=%s, aborting request !",
session.getProfileId()));
+ logger.error(errorMessage);
+ throw new BadRequestException(errorMessage);
}
} else {
// Session uses anonymous profile, try to find profile from
cookie
@@ -168,7 +155,8 @@ public class EventsCollectorEndpoint {
}
if (profile == null) {
- throw new InternalServerErrorException(String.format("No
valid profile found or persona found for profileId=%s, aborting request !",
session.getProfileId()));
+ logger.error(errorMessage);
+ throw new BadRequestException(errorMessage);
}
}
}
@@ -186,7 +174,7 @@ public class EventsCollectorEndpoint {
}
if ((changes & EventService.ERROR) == EventService.ERROR) {
String errorMessage = "Error processing events. Total number of
processed events: " + changesObject.getProcessedItems() + "/" +
eventsCollectorRequest.getEvents().size();
- throw new InternalServerErrorException(errorMessage);
+ throw new BadRequestException(errorMessage);
}
return new EventCollectorResponse(changes);
diff --git
a/rest/src/main/java/org/apache/unomi/rest/authentication/impl/DefaultRestAuthenticationConfig.java
b/rest/src/main/java/org/apache/unomi/rest/authentication/impl/DefaultRestAuthenticationConfig.java
index bc41bb9..d6b06be 100644
---
a/rest/src/main/java/org/apache/unomi/rest/authentication/impl/DefaultRestAuthenticationConfig.java
+++
b/rest/src/main/java/org/apache/unomi/rest/authentication/impl/DefaultRestAuthenticationConfig.java
@@ -31,26 +31,38 @@ public class DefaultRestAuthenticationConfig implements
RestAuthenticationConfig
private static final String GUEST_ROLES = "ROLE_UNOMI_PUBLIC";
private static final String ADMIN_ROLES = "ROLE_UNOMI_ADMIN";
+ private static final List<Pattern> PUBLIC_PATH_PATTERNS = Arrays.asList(
+ Pattern.compile("(GET|POST|OPTIONS) context\\.js(on|)"),
+ Pattern.compile("(GET|POST|OPTIONS) eventcollector"),
+ Pattern.compile("(GET|OPTIONS) client/.*")
+ );
+
+
+ private static final Map<String, String> ROLES_MAPPING;
+
+ static {
+ Map<String, String> roles = new HashMap<>();
+ roles.put("org.apache.unomi.rest.ContextJsonEndpoint.contextJSAsGet",
GUEST_ROLES);
+ roles.put("org.apache.unomi.rest.ContextJsonEndpoint.contextJSAsPost",
GUEST_ROLES);
+
roles.put("org.apache.unomi.rest.ContextJsonEndpoint.contextJSONAsGet",
GUEST_ROLES);
+
roles.put("org.apache.unomi.rest.ContextJsonEndpoint.contextJSONAsPost",
GUEST_ROLES);
+ roles.put("org.apache.unomi.rest.ContextJsonEndpoint.options",
GUEST_ROLES);
+
roles.put("org.apache.unomi.rest.EventsCollectorEndpoint.collectAsGet",
GUEST_ROLES);
+
roles.put("org.apache.unomi.rest.EventsCollectorEndpoint.collectAsPost",
GUEST_ROLES);
+ roles.put("org.apache.unomi.rest.EventsCollectorEndpoint.options",
GUEST_ROLES);
+ roles.put("org.apache.unomi.rest.ClientEndpoint.getClient",
GUEST_ROLES);
+ roles.put("org.apache.unomi.rest.ClientEndpoint.options", GUEST_ROLES);
+ ROLES_MAPPING = Collections.unmodifiableMap(roles);
+ }
+
@Override
public List<Pattern> getPublicPathPatterns() {
- List<Pattern> publicPaths = new ArrayList<>();
- publicPaths.add(Pattern.compile("(GET|POST|OPTIONS) context\\.json"));
- publicPaths.add(Pattern.compile("(GET|POST|OPTIONS) eventcollector"));
- publicPaths.add(Pattern.compile("GET client/.*"));
- return publicPaths;
+ return PUBLIC_PATH_PATTERNS;
}
@Override
public Map<String, String> getMethodRolesMap() {
- Map<String, String> roleMappings = new HashMap<>();
-
roleMappings.put("org.apache.unomi.rest.ContextJsonEndpoint.contextJSONAsGet",
GUEST_ROLES);
-
roleMappings.put("org.apache.unomi.rest.ContextJsonEndpoint.contextJSONAsPost",
GUEST_ROLES);
- roleMappings.put("org.apache.unomi.rest.ContextJsonEndpoint.options",
GUEST_ROLES);
-
roleMappings.put("org.apache.unomi.rest.EventsCollectorEndpoint.collectAsGet",
GUEST_ROLES);
-
roleMappings.put("org.apache.unomi.rest.EventsCollectorEndpoint.collectAsPost",
GUEST_ROLES);
-
roleMappings.put("org.apache.unomi.rest.EventsCollectorEndpoint.options",
GUEST_ROLES);
- roleMappings.put("org.apache.unomi.rest.ClientEndpoint.getClient",
GUEST_ROLES);
- return roleMappings;
+ return ROLES_MAPPING;
}
@Override
diff --git a/wab/src/main/java/org/apache/unomi/web/Changes.java
b/wab/src/main/java/org/apache/unomi/web/Changes.java
deleted file mode 100644
index 1f1043c..0000000
--- a/wab/src/main/java/org/apache/unomi/web/Changes.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.unomi.web;
-
-import org.apache.unomi.api.Profile;
-
-/**
- * This class is a simple object to get the updated profile without the need
of reloading it
- *
- * @author dgaillard
- */
-public class Changes {
- private int changeType;
- private int processedItems;
- private Profile profile;
-
- public Changes(int changeType, Profile profile) {
- this(changeType,0,profile);
- }
-
- public Changes(int changeType, int processedItems, Profile profile) {
- this.changeType = changeType;
- this.processedItems = processedItems;
- this.profile = profile;
- }
-
- public int getChangeType() {
- return changeType;
- }
-
- public int getProcessedItems() {
- return processedItems;
- }
-
- public Profile getProfile() {
- return profile;
- }
-}
diff --git a/wab/src/main/java/org/apache/unomi/web/ClientServlet.java
b/wab/src/main/java/org/apache/unomi/web/ClientServlet.java
index 7f3e923..c105302 100644
--- a/wab/src/main/java/org/apache/unomi/web/ClientServlet.java
+++ b/wab/src/main/java/org/apache/unomi/web/ClientServlet.java
@@ -17,28 +17,16 @@
package org.apache.unomi.web;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
-import com.opencsv.CSVWriter;
-import org.apache.unomi.api.Profile;
import org.apache.unomi.api.services.ConfigSharingService;
-import org.apache.unomi.api.services.ProfileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
-import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
-import java.io.OutputStream;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
/**
* A servlet filter to serve a context-specific Javascript containing the
current request context object.
@@ -47,15 +35,11 @@ public class ClientServlet extends HttpServlet {
private static final Logger logger =
LoggerFactory.getLogger(ClientServlet.class.getName());
private static final long serialVersionUID = 2928875960103325238L;
- private ProfileService profileService;
- private String profileIdCookieName = "context-profile-id";
private String allowedProfileDownloadFormats;
private ConfigSharingService configSharingService;
- private final String FILE_NAME_WO_EXT = "my-profile";
-
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
@@ -71,148 +55,15 @@ public class ClientServlet extends HttpServlet {
}
@Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
- try {
- String[] pathInfo = req.getPathInfo().substring(1).split("\\.");
- if (pathInfo != null && pathInfo.length > 0) {
- String operation = pathInfo[0];
- String param = pathInfo[1];
- switch (operation) {
- case "myprofile":
- if (allowedProfileDownloadFormats.contains(param)) {
- donwloadCurrentProfile(req, resp, param);
- } else {
- resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
- }
- break;
- default:
- resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
-
- }
- } else {
- resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
- }
- } catch (Throwable t) { // Here in order to return generic message
instead of the whole stack trace in case of not caught exception
- logger.error("ClientServlet failed to execute get", t);
- resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Internal server error");
- }
+ protected void service(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
+ HttpServletRequestForwardWrapper.forward(request, response);
}
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
- }
-
- public void donwloadCurrentProfile(HttpServletRequest request,
HttpServletResponse response, String downloadFileType) throws ServletException,
IOException {
- String cookieProfileId = null;
- Cookie[] cookies = request.getCookies();
- if (cookies != null) {
- for (Cookie cookie : cookies) {
- if (profileIdCookieName.equals(cookie.getName())) {
- cookieProfileId = cookie.getValue();
- }
- }
- }
- if (cookieProfileId != null) {
- Profile currentProfile = profileService.load(cookieProfileId);
- if (currentProfile != null) {
- switch (downloadFileType) {
- case "yaml":
- prepareYamlFileToDownload(response, currentProfile,
false);
- break;
- case "json":
- prepareJsonFileToDownload(response, currentProfile);
- break;
- case "csv":
- prepareCsvFileToDownload(response, currentProfile,
request.getParameter("vertical") != null);
- break;
- case "text":
- prepareYamlFileToDownload(response, currentProfile,
true);
- break;
- default:
- return;
-
- }
-
- }
- }
- }
-
- private void prepareCsvFileToDownload(HttpServletResponse response,
Profile currentProfile, boolean vertical) {
- response.setContentType("text/csv");
- response.setHeader("Content-Disposition", "attachment; filename=\"" +
FILE_NAME_WO_EXT + ".csv\"");
- try {
- StringWriter writer = new StringWriter();
-
- //using custom delimiter and quote character
- CSVWriter csvWriter = new CSVWriter(writer);
- OutputStream outputStream = response.getOutputStream();
- if (vertical) {
- csvWriter.writeNext(new String[]{"name", "value"});
- for (Map.Entry<String, Object> entry :
currentProfile.getProperties().entrySet()) {
- csvWriter.writeNext(new String[]{entry.getKey(),
entry.getValue().toString().trim().replace("\n", "")});
- }
- } else {
- Set<String> keySet = currentProfile.getProperties().keySet();
- List<String> values = new ArrayList();
- for (Object value : currentProfile.getProperties().values()) {
- values.add(value.toString().trim().replace("\n", ""));
- }
- csvWriter.writeNext(keySet.toArray(new String[keySet.size()]));
- csvWriter.writeNext(values.toArray(new String[values.size()]));
- }
- outputStream.write(writer.toString().getBytes());
- outputStream.flush();
- outputStream.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private void prepareJsonFileToDownload(HttpServletResponse response,
Profile currentProfile) {
- response.setContentType("text/json");
- response.setHeader("Content-Disposition", "attachment; filename=\"" +
FILE_NAME_WO_EXT + ".json\"");
- try {
- ObjectMapper mapper = new ObjectMapper();
- String jsonContent =
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(currentProfile.getProperties());
- OutputStream outputStream = response.getOutputStream();
- outputStream.write(jsonContent.getBytes());
- outputStream.flush();
- outputStream.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private void prepareYamlFileToDownload(HttpServletResponse response,
Profile currentProfile, boolean asTextFile) {
- response.setContentType("text/" + (asTextFile ? "plain" : "yaml"));
- response.setHeader("Content-Disposition", "attachment; filename=\"" +
FILE_NAME_WO_EXT + (asTextFile ? ".txt" : ".yml") + "\"");
- try {
- YAMLFactory yf = new YAMLFactory();
- ObjectMapper mapper = new ObjectMapper(yf);
- String yamlContent =
mapper.writeValueAsString(currentProfile.getProperties());
- OutputStream outputStream = response.getOutputStream();
- outputStream.write(yamlContent.getBytes());
- outputStream.flush();
- outputStream.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public void setProfileService(ProfileService profileService) {
- this.profileService = profileService;
+ public void setConfigSharingService(ConfigSharingService
configSharingService) {
+ this.configSharingService = configSharingService;
}
public void setAllowedProfileDownloadFormats(String
allowedProfileDownloadFormats) {
this.allowedProfileDownloadFormats = allowedProfileDownloadFormats;
}
-
- public void setProfileIdCookieName(String profileIdCookieName) {
- this.profileIdCookieName = profileIdCookieName;
- }
-
- public void setConfigSharingService(ConfigSharingService
configSharingService) {
- this.configSharingService = configSharingService;
- }
-
}
diff --git a/wab/src/main/java/org/apache/unomi/web/ContextServlet.java
b/wab/src/main/java/org/apache/unomi/web/ContextServlet.java
index 2c37611..ee93a2c 100644
--- a/wab/src/main/java/org/apache/unomi/web/ContextServlet.java
+++ b/wab/src/main/java/org/apache/unomi/web/ContextServlet.java
@@ -17,27 +17,16 @@
package org.apache.unomi.web;
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.unomi.api.*;
-import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.api.services.*;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
+import org.apache.unomi.api.services.ConfigSharingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
-import java.io.Writer;
-import java.util.*;
/**
* A servlet filter to serve a context-specific Javascript containing the
current request context object.
@@ -52,15 +41,8 @@ public class ContextServlet extends HttpServlet {
private String profileIdCookieDomain;
private int profileIdCookieMaxAgeInSeconds = MAX_COOKIE_AGE_IN_SECONDS;
- private ProfileService profileService;
- private EventService eventService;
- private RulesService rulesService;
- private PrivacyService privacyService;
- private PersonalizationService personalizationService;
private ConfigSharingService configSharingService;
- private boolean sanitizeConditions =
Boolean.parseBoolean(System.getProperty("org.apache.unomi.security.personalization.sanitizeConditions",
"true"));
-
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
@@ -71,396 +53,15 @@ public class ContextServlet extends HttpServlet {
}
@Override
- public void service(HttpServletRequest request, HttpServletResponse
response) throws IOException {
- try {
- final Date timestamp = new Date();
- if (request.getParameter("timestamp") != null) {
- try {
-
timestamp.setTime(Long.parseLong(request.getParameter("timestamp")));
- } catch (NumberFormatException e) {
- // catch to avoid logging the error with the timestamp
value to avoid potential log injection
- logger.error("Invalid timestamp parameter");
- return;
- }
- }
-
- // set up CORS headers as soon as possible so that errors are not
misconstrued on the client for CORS errors
- HttpUtils.setupCORSHeaders(request, response);
-
- // Handle OPTIONS request
- String httpMethod = request.getMethod();
- if ("options".equals(httpMethod.toLowerCase())) {
- response.flushBuffer();
- if (logger.isDebugEnabled()) {
- logger.debug("OPTIONS request received. No context will be
returned.");
- }
- return;
- }
-
- // Handle persona
- Profile profile = null;
- Session session = null;
- String personaId = request.getParameter("personaId");
- if (personaId != null) {
- PersonaWithSessions personaWithSessions =
profileService.loadPersonaWithSessions(personaId);
- if (personaWithSessions == null) {
- logger.error("Couldn't find persona, please check your
personaId parameter");
- profile = null;
- } else {
- profile = personaWithSessions.getPersona();
- session = personaWithSessions.getLastSession();
- }
- }
-
- // Extract payload
- ContextRequest contextRequest = null;
- String scope = null;
- String sessionId = null;
- String profileId = null;
- String stringPayload = HttpUtils.getPayload(request);
- if (stringPayload != null) {
- ObjectMapper mapper = CustomObjectMapper.getObjectMapper();
- JsonFactory factory = mapper.getFactory();
- try {
- contextRequest =
mapper.readValue(factory.createParser(stringPayload), ContextRequest.class);
- } catch (Exception e) {
-
((HttpServletResponse)response).sendError(HttpServletResponse.SC_BAD_REQUEST,
"Check logs for more details");
- logger.error("Cannot deserialize the context request
payload. See debug level for more information");
- if (logger.isDebugEnabled()) {
- logger.debug("Cannot deserialize the context request
payload: {}", stringPayload, e);
- }
- return;
- }
- if (contextRequest.getSource() != null) {
- scope = contextRequest.getSource().getScope();
- }
- sessionId = contextRequest.getSessionId();
- profileId = contextRequest.getProfileId();
- }
-
- if (sessionId == null) {
- sessionId = request.getParameter("sessionId");
- }
-
- if (profileId == null) {
- // Get profile id from the cookie
- profileId = ServletCommon.getProfileIdCookieValue(request,
profileIdCookieName);
- }
-
- if (profileId == null && sessionId == null && personaId == null) {
-
((HttpServletResponse)response).sendError(HttpServletResponse.SC_BAD_REQUEST,
"Check logs for more details");
- logger.error("Couldn't find profileId, sessionId or personaId
in incoming request! Stopped processing request. See debug level for more
information");
- if (logger.isDebugEnabled()) {
- logger.debug("Request dump: {}",
HttpUtils.dumpRequestInfo(request));
- }
- return;
- }
-
- int changes = EventService.NO_CHANGE;
- if (profile == null) {
- // Not a persona, resolve profile now
- boolean profileCreated = false;
-
- boolean invalidateProfile =
request.getParameter("invalidateProfile") != null ?
- new Boolean(request.getParameter("invalidateProfile"))
: false;
- if (profileId == null || invalidateProfile) {
- // no profileId cookie was found or the profile has to be
invalidated, we generate a new one and create the profile in the profile service
- profile = createNewProfile(null, response, timestamp);
- profileCreated = true;
- } else {
- profile = profileService.load(profileId);
- if (profile == null) {
- // this can happen if we have an old cookie but have
reset the server,
- // or if we merged the profiles and somehow this
cookie didn't get updated.
- profile = createNewProfile(profileId, response,
timestamp);
- profileCreated = true;
- } else {
- Changes changesObject = checkMergedProfile(response,
profile, session);
- changes |= changesObject.getChangeType();
- profile = changesObject.getProfile();
- }
- }
-
- Profile sessionProfile;
- boolean invalidateSession =
request.getParameter("invalidateSession") != null ?
- new Boolean(request.getParameter("invalidateSession"))
: false;
- if (StringUtils.isNotBlank(sessionId) && !invalidateSession) {
- session = profileService.loadSession(sessionId, timestamp);
- if (session != null) {
- sessionProfile = session.getProfile();
-
- boolean anonymousSessionProfile =
sessionProfile.isAnonymousProfile();
- if (!profile.isAnonymousProfile() &&
!anonymousSessionProfile &&
!profile.getItemId().equals(sessionProfile.getItemId())) {
- // Session user has been switched, profile id in
cookie is not up to date
- // We must reload the profile with the session ID
as some properties could be missing from the session profile
- // #personalIdentifier
- profile =
profileService.load(sessionProfile.getItemId());
- if (profile != null) {
- HttpUtils.sendProfileCookie(profile, response,
profileIdCookieName, profileIdCookieDomain, profileIdCookieMaxAgeInSeconds);
- } else {
- logger.warn("Couldn't load profile {}
referenced in session {}", sessionProfile.getItemId(), session.getItemId());
- }
- }
-
- // Handle anonymous situation
- Boolean requireAnonymousBrowsing =
privacyService.isRequireAnonymousBrowsing(profile);
- if (requireAnonymousBrowsing &&
anonymousSessionProfile) {
- // User wants to browse anonymously, anonymous
profile is already set.
- } else if (requireAnonymousBrowsing &&
!anonymousSessionProfile) {
- // User wants to browse anonymously, update the
sessionProfile to anonymous profile
- sessionProfile =
privacyService.getAnonymousProfile(profile);
- session.setProfile(sessionProfile);
- changes |= EventService.SESSION_UPDATED;
- } else if (!requireAnonymousBrowsing &&
anonymousSessionProfile) {
- // User does not want to browse anonymously
anymore, update the sessionProfile to real profile
- sessionProfile = profile;
- session.setProfile(sessionProfile);
- changes |= EventService.SESSION_UPDATED;
- } else if (!requireAnonymousBrowsing &&
!anonymousSessionProfile) {
- // User does not want to browse anonymously, use
the real profile. Check that session contains the current profile.
- sessionProfile = profile;
- if
(!session.getProfileId().equals(sessionProfile.getItemId())) {
- changes |= EventService.SESSION_UPDATED;
- }
- session.setProfile(sessionProfile);
- }
- }
- }
-
- if (session == null || invalidateSession) {
- sessionProfile =
privacyService.isRequireAnonymousBrowsing(profile) ?
privacyService.getAnonymousProfile(profile) : profile;
-
- if (StringUtils.isNotBlank(sessionId)) {
- // Only save session and send event if a session id
was provided, otherwise keep transient session
- session = new Session(sessionId, sessionProfile,
timestamp, scope);
- changes |= EventService.SESSION_UPDATED;
- Event event = new Event("sessionCreated", session,
profile, scope, null, session, timestamp);
- if (sessionProfile.isAnonymousProfile()) {
- // Do not keep track of profile in event
- event.setProfileId(null);
- }
-
event.getAttributes().put(Event.HTTP_REQUEST_ATTRIBUTE, request);
-
event.getAttributes().put(Event.HTTP_RESPONSE_ATTRIBUTE, response);
- if (logger.isDebugEnabled()) {
- logger.debug("Received event {} for profile={}
session={} target={} timestamp={}",
- event.getEventType(), profile.getItemId(),
session.getItemId(), event.getTarget(), timestamp);
- }
- changes |= eventService.send(event);
- }
- }
-
- if (profileCreated) {
- changes |= EventService.PROFILE_UPDATED;
-
- Event profileUpdated = new Event("profileUpdated",
session, profile, scope, null, profile, timestamp);
- profileUpdated.setPersistent(false);
-
profileUpdated.getAttributes().put(Event.HTTP_REQUEST_ATTRIBUTE, request);
-
profileUpdated.getAttributes().put(Event.HTTP_RESPONSE_ATTRIBUTE, response);
-
- if (logger.isDebugEnabled()) {
- logger.debug("Received event {} for profile={} {}
target={} timestamp={}", profileUpdated.getEventType(), profile.getItemId(),
- " session=" + (session != null ?
session.getItemId() : null), profileUpdated.getTarget(), timestamp);
- }
- changes |= eventService.send(profileUpdated);
- }
- }
-
- ContextResponse contextResponse = new ContextResponse();
- contextResponse.setProfileId(profile.getItemId());
- if (session != null) {
- contextResponse.setSessionId(session.getItemId());
- } else if (sessionId != null) {
- contextResponse.setSessionId(sessionId);
- }
-
- if (contextRequest != null) {
- Changes changesObject = handleRequest(contextRequest, session,
profile, contextResponse, request, response, timestamp);
- changes |= changesObject.getChangeType();
- profile = changesObject.getProfile();
- }
-
- if ((changes & EventService.PROFILE_UPDATED) ==
EventService.PROFILE_UPDATED) {
- profileService.save(profile);
- contextResponse.setProfileId(profile.getItemId());
- }
- if ((changes & EventService.SESSION_UPDATED) ==
EventService.SESSION_UPDATED && session != null) {
- profileService.saveSession(session);
- contextResponse.setSessionId(session.getItemId());
- }
-
- if ((changes & EventService.ERROR) == EventService.ERROR) {
-
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- }
-
- String extension =
request.getRequestURI().substring(request.getRequestURI().lastIndexOf(".") + 1);
- boolean noScript = "json".equals(extension);
- String contextAsJSONString =
CustomObjectMapper.getObjectMapper().writeValueAsString(contextResponse);
- Writer responseWriter;
- response.setCharacterEncoding("UTF-8");
- if (noScript) {
- responseWriter = response.getWriter();
- response.setContentType("application/json");
- IOUtils.write(contextAsJSONString, responseWriter);
- } else {
- responseWriter = response.getWriter();
- responseWriter.append("window.digitalData = window.digitalData
|| {};\n")
- .append("var cxs = ")
- .append(contextAsJSONString)
- .append(";\n");
- }
-
- responseWriter.flush();
- } catch (Throwable t) { // Here in order to return generic message
instead of the whole stack trace in case of not caught exception
- logger.error("ContextServlet failed to execute request", t);
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Internal server error");
- }
+ public void service(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
+ HttpServletRequestForwardWrapper.forward(request, response);
}
- private Changes checkMergedProfile(ServletResponse response, Profile
profile, Session session) {
- int changes = EventService.NO_CHANGE;
- if (profile.getMergedWith() != null &&
!privacyService.isRequireAnonymousBrowsing(profile) &&
!profile.isAnonymousProfile()) {
- Profile currentProfile = profile;
- String masterProfileId = profile.getMergedWith();
- Profile masterProfile = profileService.load(masterProfileId);
- if (masterProfile != null) {
- logger.info("Current profile {} was merged with profile {},
replacing profile in session", currentProfile.getItemId(), masterProfileId);
- profile = masterProfile;
- if (session != null) {
- session.setProfile(profile);
- changes = EventService.SESSION_UPDATED;
- }
- HttpUtils.sendProfileCookie(profile, response,
profileIdCookieName, profileIdCookieDomain, profileIdCookieMaxAgeInSeconds);
- } else {
- logger.warn("Couldn't find merged profile {}, falling back to
profile {}", masterProfileId, currentProfile.getItemId());
- profile = currentProfile;
- profile.setMergedWith(null);
- changes = EventService.PROFILE_UPDATED;
- }
- }
-
- return new Changes(changes, profile);
- }
-
- private Changes handleRequest(ContextRequest contextRequest, Session
session, Profile profile, ContextResponse data,
- ServletRequest request, ServletResponse
response, Date timestamp) {
- Changes changes =
ServletCommon.handleEvents(contextRequest.getEvents(), session, profile,
request, response, timestamp,
- privacyService, eventService);
- data.setProcessedEvents(changes.getProcessedItems());
-
- profile = changes.getProfile();
-
- if (contextRequest.isRequireSegments()) {
- data.setProfileSegments(profile.getSegments());
- }
-
- if (contextRequest.getRequiredProfileProperties() != null) {
- Map<String, Object> profileProperties = new
HashMap<>(profile.getProperties());
- if (!contextRequest.getRequiredProfileProperties().contains("*")) {
-
profileProperties.keySet().retainAll(contextRequest.getRequiredProfileProperties());
- }
- data.setProfileProperties(profileProperties);
- }
-
- if (session != null) {
- data.setSessionId(session.getItemId());
- if (contextRequest.getRequiredSessionProperties() != null) {
- Map<String, Object> sessionProperties = new
HashMap<>(session.getProperties());
- if
(!contextRequest.getRequiredSessionProperties().contains("*")) {
-
sessionProperties.keySet().retainAll(contextRequest.getRequiredSessionProperties());
- }
- data.setSessionProperties(sessionProperties);
- }
- }
-
- processOverrides(contextRequest, profile, session);
-
- List<PersonalizationService.PersonalizedContent> filterNodes =
contextRequest.getFilters();
- if (filterNodes != null) {
- data.setFilteringResults(new HashMap<>());
- for (PersonalizationService.PersonalizedContent
personalizedContent : sanitizePersonalizedContentObjects(filterNodes)) {
- data.getFilteringResults().put(personalizedContent.getId(),
personalizationService.filter(profile,
- session, personalizedContent));
- }
- }
-
- List<PersonalizationService.PersonalizationRequest> personalizations =
contextRequest.getPersonalizations();
- if (personalizations != null) {
- data.setPersonalizations(new HashMap<>());
- for (PersonalizationService.PersonalizationRequest personalization
: sanitizePersonalizations(personalizations)) {
- data.getPersonalizations().put(personalization.getId(),
personalizationService.personalizeList(profile,
- session, personalization));
- }
- }
-
- if (!(profile instanceof Persona)) {
-
data.setTrackedConditions(rulesService.getTrackedConditions(contextRequest.getSource()));
- } else {
- data.setTrackedConditions(Collections.emptySet());
- }
-
-
data.setAnonymousBrowsing(privacyService.isRequireAnonymousBrowsing(profile));
- data.setConsents(profile.getConsents());
-
- return changes;
- }
-
- /**
- * This function will update the profile if it is from Persona instance.
- * The profile will be updated using the overrides attributes :
- * - profileOverrides for profile properties, segments and scores
- * - sessionPropertiesOverrides for session properties
- * @param contextRequest
- * @param profile
- * @param session
- */
- private void processOverrides(ContextRequest contextRequest, Profile
profile, Session session) {
- if (profile instanceof Persona) {
- if (contextRequest.getProfileOverrides() != null) {
- if (contextRequest.getProfileOverrides().getScores()!=null) {
-
profile.setScores(contextRequest.getProfileOverrides().getScores());
- }
- if (contextRequest.getProfileOverrides().getSegments()!=null) {
-
profile.setSegments(contextRequest.getProfileOverrides().getSegments());
- }
- if
(contextRequest.getProfileOverrides().getProperties()!=null) {
-
profile.setProperties(contextRequest.getProfileOverrides().getProperties());
- }
- if (contextRequest.getSessionPropertiesOverrides()!=null &&
session != null) {
-
session.setProperties(contextRequest.getSessionPropertiesOverrides());
- }
- }
- }
- }
-
- private Profile createNewProfile(String existingProfileId, ServletResponse
response, Date timestamp) {
- Profile profile;
- String profileId = existingProfileId;
- if (profileId == null) {
- profileId = UUID.randomUUID().toString();
- }
- profile = new Profile(profileId);
- profile.setProperty("firstVisit", timestamp);
- HttpUtils.sendProfileCookie(profile, response, profileIdCookieName,
profileIdCookieDomain, profileIdCookieMaxAgeInSeconds);
- return profile;
- }
-
-
+ @Override
public void destroy() {
logger.info("Context servlet shutdown.");
}
- public void setProfileService(ProfileService profileService) {
- this.profileService = profileService;
- }
-
- public void setEventService(EventService eventService) {
- this.eventService = eventService;
- }
-
- public void setRulesService(RulesService rulesService) {
- this.rulesService = rulesService;
- }
-
public void setProfileIdCookieDomain(String profileIdCookieDomain) {
this.profileIdCookieDomain = profileIdCookieDomain;
}
@@ -473,103 +74,8 @@ public class ContextServlet extends HttpServlet {
this.profileIdCookieMaxAgeInSeconds = profileIdCookieMaxAgeInSeconds;
}
- public void setPrivacyService(PrivacyService privacyService) {
- this.privacyService = privacyService;
- }
-
- public void setPersonalizationService(PersonalizationService
personalizationService) {
- this.personalizationService = personalizationService;
- }
-
public void setConfigSharingService(ConfigSharingService
configSharingService) {
this.configSharingService = configSharingService;
}
- private List<PersonalizationService.PersonalizedContent>
sanitizePersonalizedContentObjects(List<PersonalizationService.PersonalizedContent>
personalizedContentObjects) {
- if (!sanitizeConditions) {
- return personalizedContentObjects;
- }
- List<PersonalizationService.PersonalizedContent> result = new
ArrayList<>();
- for (PersonalizationService.PersonalizedContent
personalizedContentObject : personalizedContentObjects) {
- boolean foundInvalidCondition = false;
- if (personalizedContentObject.getFilters() != null) {
- for (PersonalizationService.Filter filter :
personalizedContentObject.getFilters()) {
- if (sanitizeCondition(filter.getCondition()) == null) {
- foundInvalidCondition = true;
- break;
- }
- }
- }
- if (!foundInvalidCondition) {
- result.add(personalizedContentObject);
- }
- }
-
- return result;
- }
-
- private List<PersonalizationService.PersonalizationRequest>
sanitizePersonalizations(List<PersonalizationService.PersonalizationRequest>
personalizations) {
- if (!sanitizeConditions) {
- return personalizations;
- }
- List<PersonalizationService.PersonalizationRequest> result = new
ArrayList<>();
- for (PersonalizationService.PersonalizationRequest
personalizationRequest : personalizations) {
- List<PersonalizationService.PersonalizedContent>
personalizedContents =
sanitizePersonalizedContentObjects(personalizationRequest.getContents());
- if (personalizedContents != null && personalizedContents.size() >
0) {
- result.add(personalizationRequest);
- }
- }
- return result;
- }
-
- private Condition sanitizeCondition(Condition condition) {
- Map<String,Object> newParameterValues = new LinkedHashMap<>();
- for (Map.Entry<String,Object> parameterEntry :
condition.getParameterValues().entrySet()) {
- Object sanitizedValue = sanitizeValue(parameterEntry.getValue());
- if (sanitizedValue != null) {
- newParameterValues.put(parameterEntry.getKey(),
parameterEntry.getValue());
- } else {
- return null;
- }
- }
- return condition;
- }
-
- private Object sanitizeValue(Object value) {
- if (value instanceof String) {
- String stringValue = (String) value;
- if (stringValue.startsWith("script::") ||
stringValue.startsWith("parameter::")) {
- logger.warn("Scripting detected in context request, filtering
out. See debug level for more information");
- if (logger.isDebugEnabled()) {
- logger.debug("Scripting detected in context request with
value {}, filtering out...", value);
- }
- return null;
- } else {
- return stringValue;
- }
- } else if (value instanceof List) {
- List values = (List) value;
- List newValues = new ArrayList();
- for (Object listObject : values) {
- Object newObject = sanitizeValue(listObject);
- if (newObject != null) {
- newValues.add(newObject);
- }
- }
- return values;
- } else if (value instanceof Map) {
- Map<Object,Object> newMap = new LinkedHashMap<>();
- ((Map<?, ?>) value).forEach((key, value1) -> {
- Object newObject = sanitizeValue(value1);
- if (newObject != null) {
- newMap.put(key, newObject);
- }
- });
- return newMap;
- } else if (value instanceof Condition) {
- return sanitizeCondition((Condition) value);
- } else {
- return value;
- }
- }
}
diff --git a/wab/src/main/java/org/apache/unomi/web/EventsCollectorServlet.java
b/wab/src/main/java/org/apache/unomi/web/EventsCollectorServlet.java
index 5bca92d..8268b76 100644
--- a/wab/src/main/java/org/apache/unomi/web/EventsCollectorServlet.java
+++ b/wab/src/main/java/org/apache/unomi/web/EventsCollectorServlet.java
@@ -17,14 +17,6 @@
package org.apache.unomi.web;
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.unomi.api.*;
-import org.apache.unomi.api.services.EventService;
-import org.apache.unomi.api.services.PrivacyService;
-import org.apache.unomi.api.services.ProfileService;
-import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,20 +26,11 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Date;
-import java.util.UUID;
public class EventsCollectorServlet extends HttpServlet {
private static final long serialVersionUID = 2008054804885122957L;
private static final Logger logger =
LoggerFactory.getLogger(EventsCollectorServlet.class.getName());
- private String profileIdCookieName = "context-profile-id";
-
- private EventService eventService;
- private ProfileService profileService;
- private PrivacyService privacyService;
-
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
@@ -61,182 +44,7 @@ public class EventsCollectorServlet extends HttpServlet {
}
@Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
- try {
- doEvent(req, resp);
- } catch (Throwable t) { // Here in order to return generic message
instead of the whole stack trace in case of not caught exception
- logger.error("EventsCollectorServlet failed to execute get", t);
- resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Internal server error");
- }
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
- try {
- doEvent(req, resp);
- } catch (Throwable t) { // Here in order to return generic message
instead of the whole stack trace in case of not caught exception
- logger.error("EventsCollectorServlet failed to execute post", t);
- resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Internal server error");
- }
- }
-
- @Override
- protected void doOptions(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
-// logger.debug(HttpUtils.dumpRequestInfo(request));
- try {
- HttpUtils.setupCORSHeaders(request, response);
- response.flushBuffer();
- }
- catch (Throwable t) { // Here in order to return generic message
instead of the whole stack trace in case of not caught exception
- logger.error("EventsCollectorServlet failed to execute doOptions
request", t);
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Internal server error");
- }
- }
-
- private void doEvent(HttpServletRequest request, HttpServletResponse
response) throws IOException {
- Date timestamp = new Date();
- if (request.getParameter("timestamp") != null) {
- try {
-
timestamp.setTime(Long.parseLong(request.getParameter("timestamp")));
- } catch (NumberFormatException e) {
- // catch to avoid logging the error with the timestamp value
to avoid potential log injection
- logger.error("Invalid timestamp parameter");
- return;
- }
- }
-
- HttpUtils.setupCORSHeaders(request, response);
-
- String payload = HttpUtils.getPayload(request);
- if (payload == null) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Check logs
for more details");
- logger.error("No event payload found for request, aborting !");
- return;
- }
-
- ObjectMapper mapper = CustomObjectMapper.getObjectMapper();
- JsonFactory factory = mapper.getFactory();
- EventsCollectorRequest eventsCollectorRequest;
- try {
- eventsCollectorRequest =
mapper.readValue(factory.createParser(payload), EventsCollectorRequest.class);
- } catch (Exception e) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Check logs
for more details");
- logger.error("Cannot read payload. See debug level for more
information");
- if (logger.isDebugEnabled()) {
- logger.debug("Cannot read payload: {}", payload, e);
- }
- return;
- }
- if (eventsCollectorRequest == null ||
eventsCollectorRequest.getEvents() == null) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Check logs
for more details");
- logger.error("No events found in payload");
- return;
- }
-
- String sessionId = eventsCollectorRequest.getSessionId();
- if (sessionId == null) {
- sessionId = request.getParameter("sessionId");
- }
- Session session = null;
- if (sessionId != null) {
- session = profileService.loadSession(sessionId, timestamp);
- }
- Profile profile = null;
- if (session == null) {
- String scope = "systemscope";
- // Get the first available scope that is not equal to systemscope
to create the session otherwise systemscope will be used
- for (Event event : eventsCollectorRequest.getEvents()) {
- if (StringUtils.isNotBlank(event.getEventType())) {
- if (StringUtils.isNotBlank(event.getSourceId()) &&
!event.getSourceId().equals("systemscope")) {
- scope = event.getSourceId();
- break;
- } else if (event.getSource() != null &&
StringUtils.isNotBlank(event.getSource().getScope()) &&
!event.getSource().getScope().equals("systemscope")) {
- scope = event.getSource().getScope();
- break;
- }
- }
- }
- String cookieProfileId =
ServletCommon.getProfileIdCookieValue(request, profileIdCookieName);
- if (StringUtils.isNotBlank(cookieProfileId)) {
- profile = profileService.load(cookieProfileId);
- }
- if (profile == null) {
- // Create non persisted profile to create the session
- profile = new Profile("temp_" + UUID.randomUUID().toString());
- profile.setProperty("firstVisit", timestamp);
- }
- /*
- // Create anonymous profile so we don't keep track of the temp
profile anywhere
- Profile anonymousProfile =
privacyService.getAnonymousProfile(profile);
- // Create new session which should not be persisted as well as the
temp profile
- session = new Session(sessionId, anonymousProfile, timestamp,
scope);
- if (logger.isDebugEnabled()) {
- logger.debug("No session found for sessionId={}, creating new
session!", sessionId);
- }
- */
- } else {
- Profile sessionProfile = session.getProfile();
- if (sessionProfile.getItemId() != null) {
- // Reload up-to-date profile
- profile = profileService.load(sessionProfile.getItemId());
- if (profile == null || profile instanceof Persona) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST,
"Check logs for more details");
- logger.error("No valid profile found or persona found for
profileId={}, aborting request !", session.getProfileId());
- return;
- }
- } else {
- // Session uses anonymous profile, try to find profile from
cookie
- String cookieProfileId =
ServletCommon.getProfileIdCookieValue(request, profileIdCookieName);
- if (StringUtils.isNotBlank(cookieProfileId)) {
- profile = profileService.load(cookieProfileId);
- }
-
- if (profile == null) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST,
"Check logs for more details");
- logger.error("No valid profile found or persona found for
profileId={}, aborting request !", session.getProfileId());
- return;
- }
- }
- }
-
- Changes changesObject =
ServletCommon.handleEvents(eventsCollectorRequest.getEvents(), session,
profile, request, response,
- timestamp, privacyService, eventService);
- int changes = changesObject.getChangeType();
- profile = changesObject.getProfile();
-
- if ((changes & EventService.PROFILE_UPDATED) ==
EventService.PROFILE_UPDATED) {
- profileService.save(profile);
- }
- if ((changes & EventService.SESSION_UPDATED) ==
EventService.SESSION_UPDATED) {
- if (session != null) {
- profileService.saveSession(session);
- }
- }
- if ((changes & EventService.ERROR) == EventService.ERROR) {
- String errorMessage = "Error processing events. Total number of
processed events: " + changesObject.getProcessedItems() + "/" +
eventsCollectorRequest.getEvents().size();
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
errorMessage);
- return;
- }
-
- response.setContentType("application/json");
- PrintWriter responseWriter = response.getWriter();
- responseWriter.append("{\"updated\":" + changes + "}");
- responseWriter.flush();
- }
-
- public void setEventService(EventService eventService) {
- this.eventService = eventService;
- }
-
- public void setProfileService(ProfileService profileService) {
- this.profileService = profileService;
- }
-
- public void setPrivacyService(PrivacyService privacyService) {
- this.privacyService = privacyService;
- }
-
- public void setProfileIdCookieName(String profileIdCookieName) {
- this.profileIdCookieName = profileIdCookieName;
+ protected void service(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
+ HttpServletRequestForwardWrapper.forward(request, response);
}
}
diff --git
a/wab/src/main/java/org/apache/unomi/web/HttpServletRequestForwardWrapper.java
b/wab/src/main/java/org/apache/unomi/web/HttpServletRequestForwardWrapper.java
new file mode 100644
index 0000000..717df09
--- /dev/null
+++
b/wab/src/main/java/org/apache/unomi/web/HttpServletRequestForwardWrapper.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.web;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Enumeration;
+
+/**
+ * Http wrapper that force the content type to be "application/json"
+ */
+class HttpServletRequestForwardWrapper extends HttpServletRequestWrapper {
+
+ private static final Logger logger =
LoggerFactory.getLogger(HttpServletRequestForwardWrapper.class.getName());
+ private static final String JSON_CONTENT_TYPE = "application/json";
+
+ public HttpServletRequestForwardWrapper(HttpServletRequest request) {
+ super(request);
+ }
+
+ /**
+ * Forward servlet request to jax-rs endpoints. For a given path, forward
to /cxs + path.
+ *
+ * @param request initial request
+ * @param response initial response
+ */
+ public static void forward(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
+ try {
+ HttpServletRequest requestWrapper = new
HttpServletRequestForwardWrapper(request);
+ requestWrapper.getServletContext()
+ .getContext("/cxs")
+ .getRequestDispatcher("/cxs" + request.getRequestURI())
+ .forward(requestWrapper, response);
+ } catch (Throwable t) { // Here in order to return generic message
instead of the whole stack trace in case of not caught exception
+ logger.error("HttpServletRequestForwardWrapper failed to forward
the request", t);
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Internal server error");
+ }
+ }
+
+ @Override
+ public String getHeader(String name) {
+ if ("Content-Type".equals(name) || "content-type".equals(name)) {
+ return JSON_CONTENT_TYPE;
+ }
+ return super.getHeader(name);
+ }
+
+ @Override
+ public Enumeration<String> getHeaders(String name) {
+ if ("Content-Type".equals(name) || "content-type".equals(name)) {
+ return
Collections.enumeration(Collections.singleton(JSON_CONTENT_TYPE));
+ }
+ return super.getHeaders(name);
+ }
+
+ @Override
+ public String getContentType() {
+ return JSON_CONTENT_TYPE;
+ }
+
+ @Override
+ public String getCharacterEncoding() {
+ return "UTF-8";
+ }
+}
diff --git a/wab/src/main/java/org/apache/unomi/web/HttpUtils.java
b/wab/src/main/java/org/apache/unomi/web/HttpUtils.java
index a6c34a3..dbe7eab 100644
--- a/wab/src/main/java/org/apache/unomi/web/HttpUtils.java
+++ b/wab/src/main/java/org/apache/unomi/web/HttpUtils.java
@@ -17,19 +17,10 @@
package org.apache.unomi.web;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.unomi.api.Persona;
-import org.apache.unomi.api.Profile;
-
import javax.servlet.ServletResponse;
-import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.util.Enumeration;
-import java.util.LinkedHashMap;
-import java.util.Map;
public class HttpUtils {
@@ -50,116 +41,6 @@ public class HttpUtils {
httpServletResponse.setHeader("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept");
httpServletResponse.setHeader("Access-Control-Allow-Credentials",
"true");
httpServletResponse.setHeader("Access-Control-Allow-Methods",
"OPTIONS, POST, GET");
- // httpServletResponse.setHeader("Access-Control-Max-Age", "600");
- //
httpServletResponse.setHeader("Access-Control-Expose-Headers","Access-Control-Allow-Origin");
- // httpServletResponse.flushBuffer();
- }
- }
-
- public static String dumpRequestInfo(HttpServletRequest
httpServletRequest) {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append("\n");
-
stringBuilder.append("======================================================================================\n");
- stringBuilder.append(dumpBasicRequestInfo(httpServletRequest));
- stringBuilder.append(dumpRequestHeaders(httpServletRequest));
-
stringBuilder.append(dumpRequestCookies(httpServletRequest.getCookies()));
-
stringBuilder.append("======================================================================================\n");
- return stringBuilder.toString();
- }
-
- public static String dumpBasicRequestInfo(HttpServletRequest
httpServletRequest) {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append(httpServletRequest.getMethod()).append("
").append(httpServletRequest.getRequestURI());
- if (httpServletRequest.getQueryString() != null) {
-
stringBuilder.append("?").append(httpServletRequest.getQueryString());
- }
- stringBuilder.append("
serverName=").append(httpServletRequest.getServerName()).append("
serverPort=").append(httpServletRequest.getServerPort()).append("
remoteAddr=").append(httpServletRequest.getRemoteAddr()).append("
remotePort=").append(httpServletRequest.getRemotePort()).append("\n");
- return stringBuilder.toString();
- }
-
-
- public static String dumpRequestCookies(Cookie[] cookies) {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append("Cookies:\n");
- if (cookies == null) {
- stringBuilder.append(" none");
- } else {
- for (Cookie cookie : cookies) {
- stringBuilder.append("
").append(cookie.getName()).append("=").append(cookie.getValue()).append("
domain=").append(cookie.getDomain()).append("
path=").append(cookie.getPath()).append("
maxAge=").append(cookie.getMaxAge()).append("
httpOnly=").append(cookie.isHttpOnly()).append("
secure=").append(cookie.getSecure()).append("
version=").append(cookie.getVersion()).append("
comment=").append(cookie.getComment()).append("\n");
- }
- }
- return stringBuilder.toString();
- }
-
- public static String dumpRequestHeaders(HttpServletRequest
httpServletRequest) {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append("Headers:\n");
- Enumeration<String> headerNameEnum =
httpServletRequest.getHeaderNames();
- while (headerNameEnum.hasMoreElements()) {
- String headerName = headerNameEnum.nextElement();
- stringBuilder.append(" ").append(headerName).append(":
").append(httpServletRequest.getHeader(headerName)).append("\n");
- }
- return stringBuilder.toString();
- }
-
- public static String getBaseRequestURL(HttpServletRequest
httpServletRequest) {
- String baseRequestURL;
- baseRequestURL = httpServletRequest.getScheme() + "://" +
httpServletRequest.getServerName();
- if (("http".equals(httpServletRequest.getScheme()) &&
(httpServletRequest.getServerPort() == 80)) ||
- ("https".equals(httpServletRequest.getScheme()) &&
(httpServletRequest.getServerPort() == 443))) {
- // normal case, don't add the port
- } else {
- baseRequestURL += ":" + httpServletRequest.getServerPort();
- }
- return baseRequestURL;
- }
-
- public static void sendProfileCookie(Profile profile, ServletResponse
response, String profileIdCookieName, String profileIdCookieDomain, int
profileIdCookieMaxAgeInSeconds) {
- if (response instanceof HttpServletResponse) {
- HttpServletResponse httpServletResponse = (HttpServletResponse)
response;
- if (!(profile instanceof Persona)) {
- httpServletResponse.addHeader("Set-Cookie",
- profileIdCookieName + "=" + profile.getItemId() +
- "; Path=/" +
- "; Max-Age=" + profileIdCookieMaxAgeInSeconds +
- (StringUtils.isNotBlank(profileIdCookieDomain)
? ("; Domain=" + profileIdCookieDomain) : "") +
- "; SameSite=Lax");
- }
- }
- }
-
- public static void clearCookie(ServletResponse response, String
cookieName) {
- if (response instanceof HttpServletResponse) {
- HttpServletResponse httpServletResponse = (HttpServletResponse)
response;
- Cookie cookie = new Cookie(cookieName, "");
- cookie.setPath("/");
- cookie.setMaxAge(0);
- httpServletResponse.addCookie(cookie);
- }
- }
-
- public static Map<String, Cookie> getCookieMap(Cookie[] cookieArray) {
- Map<String, Cookie> cookieMap = new LinkedHashMap<String, Cookie>();
- for (Cookie cookie : cookieArray) {
- cookieMap.put(cookie.getName(), cookie);
- }
- return cookieMap;
- }
-
- public static String getPayload(HttpServletRequest request) throws
IOException {
- if ("post".equals(request.getMethod().toLowerCase())) {
- StringBuilder buffer = new StringBuilder();
- String line;
- BufferedReader reader = request.getReader();
- while ((line = reader.readLine()) != null) {
- buffer.append(line);
- }
- if (buffer.length() > 0) {
- return buffer.toString();
- }
- } else if ("get".equals(request.getMethod().toLowerCase()) &&
request.getParameter("payload") != null) {
- return request.getParameter("payload");
}
- return null;
}
}
diff --git a/wab/src/main/java/org/apache/unomi/web/ServletCommon.java
b/wab/src/main/java/org/apache/unomi/web/ServletCommon.java
deleted file mode 100644
index e8722c3..0000000
--- a/wab/src/main/java/org/apache/unomi/web/ServletCommon.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.unomi.web;
-
-import org.apache.unomi.api.Event;
-import org.apache.unomi.api.Persona;
-import org.apache.unomi.api.Profile;
-import org.apache.unomi.api.Session;
-import org.apache.unomi.api.services.EventService;
-import org.apache.unomi.api.services.PrivacyService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import java.util.Date;
-import java.util.List;
-
-/**
- * @author dgaillard
- */
-public class ServletCommon {
- private static final Logger logger =
LoggerFactory.getLogger(ServletCommon.class.getName());
-
- public static String getProfileIdCookieValue(HttpServletRequest
httpServletRequest, String profileIdCookieName) {
- String cookieProfileId = null;
-
- Cookie[] cookies = httpServletRequest.getCookies();
- if (cookies != null) {
- for (Cookie cookie : cookies) {
- if (profileIdCookieName.equals(cookie.getName())) {
- cookieProfileId = cookie.getValue();
- }
- }
- }
-
- return cookieProfileId;
- }
-
- public static Changes handleEvents(List<Event> events, Session session,
Profile profile,
- ServletRequest request, ServletResponse
response, Date timestamp,
- PrivacyService privacyService,
EventService eventService) {
- List<String> filteredEventTypes =
privacyService.getFilteredEventTypes(profile);
-
- String thirdPartyId =
eventService.authenticateThirdPartyServer(((HttpServletRequest)
request).getHeader("X-Unomi-Peer"),
- request.getRemoteAddr());
-
- int changes = EventService.NO_CHANGE;
- // execute provided events if any
- int processedEventsCnt = 0;
- if (events != null && !(profile instanceof Persona)) {
- for (Event event : events) {
- processedEventsCnt++;
- if (event.getEventType() != null) {
- if (!eventService.isEventValid(event)) {
- logger.warn("Event is not valid : {}",
event.getEventType());
- continue;
- }
-
- Event eventToSend = new Event(event.getEventType(),
session, profile, event.getSourceId(), event.getSource(),
- event.getTarget(), event.getProperties(),
timestamp, event.isPersistent());
- if (!eventService.isEventAllowed(event, thirdPartyId)) {
- logger.warn("Event is not allowed : {}",
event.getEventType());
- continue;
- }
- if (thirdPartyId != null && event.getItemId() != null) {
- eventToSend = new Event(event.getItemId(),
event.getEventType(), session, profile, event.getSourceId(), event.getSource(),
event.getTarget(), event.getProperties(), timestamp, event.isPersistent());
- }
- if (filteredEventTypes != null &&
filteredEventTypes.contains(event.getEventType())) {
- logger.debug("Profile is filtering event type {}",
event.getEventType());
- continue;
- }
- if (profile.isAnonymousProfile()) {
- // Do not keep track of profile in event
- eventToSend.setProfileId(null);
- }
-
-
eventToSend.getAttributes().put(Event.HTTP_REQUEST_ATTRIBUTE, request);
-
eventToSend.getAttributes().put(Event.HTTP_RESPONSE_ATTRIBUTE, response);
- logger.debug("Received event " + event.getEventType() + "
for profile=" + profile.getItemId() + " session="
- + (session != null ? session.getItemId() : null) +
" target=" + event.getTarget() + " timestamp=" + timestamp);
- changes |= eventService.send(eventToSend);
- // If the event execution changes the profile we need to
update it so the next event use the right profile
- if ((changes & EventService.PROFILE_UPDATED) ==
EventService.PROFILE_UPDATED) {
- profile = eventToSend.getProfile();
- }
- if ((changes & EventService.ERROR) == EventService.ERROR) {
- //Don't count the event that failed
- processedEventsCnt--;
- logger.error("Error processing events. Total number of
processed events: {}/{}", processedEventsCnt, events.size());
- break;
- }
- }
- }
- }
- return new Changes(changes, processedEventsCnt, profile);
- }
-}
diff --git a/wab/src/main/resources/OSGI-INF/blueprint/blueprint.xml
b/wab/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index ccdd8ed..b4ea919 100644
--- a/wab/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/wab/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -41,11 +41,6 @@
<bean id="contextServlet" class="org.apache.unomi.web.ContextServlet">
- <property name="profileService" ref="profileService"/>
- <property name="eventService" ref="eventService"/>
- <property name="rulesService" ref="rulesService"/>
- <property name="privacyService" ref="privacyService" />
- <property name="personalizationService" ref="personalizationService"/>
<property name="configSharingService" ref="configSharingService"/>
<property name="profileIdCookieDomain"
value="${web.contextserver.domain}" />
<property name="profileIdCookieName"
value="${web.contextserver.profileIdCookieName}"/>
@@ -68,12 +63,7 @@
</service-properties>
</service>
- <bean id="eventsCollectorServlet"
class="org.apache.unomi.web.EventsCollectorServlet">
- <property name="profileService" ref="profileService"/>
- <property name="eventService" ref="eventService"/>
- <property name="privacyService" ref="privacyService" />
- <property name="profileIdCookieName"
value="${web.contextserver.profileIdCookieName}"/>
- </bean>
+ <bean id="eventsCollectorServlet"
class="org.apache.unomi.web.EventsCollectorServlet"/>
<service id="eventsCollectorServletService" ref="eventsCollectorServlet">
<interfaces>
@@ -87,10 +77,8 @@
</service>
<bean id="clientServlet" class="org.apache.unomi.web.ClientServlet">
- <property name="profileService" ref="profileService"/>
<property name="configSharingService" ref="configSharingService"/>
<property name="allowedProfileDownloadFormats"
value="${web.allowed.profile.download.formats}" />
- <property name="profileIdCookieName"
value="${web.contextserver.profileIdCookieName}"/>
</bean>
<service id="clientServletService" ref="clientServlet">