This is an automated email from the ASF dual-hosted git repository.

shuber 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 c344c4f  UNOMI-353 Integrate event type registry with ContextServlet 
and EventCollector servlets (#194)
c344c4f is described below

commit c344c4f9292a4b5c033f2930fd45de60c76225ef
Author: Pavel Milkevich <[email protected]>
AuthorDate: Tue Sep 22 11:51:00 2020 +0300

    UNOMI-353 Integrate event type registry with ContextServlet and 
EventCollector servlets (#194)
---
 .../apache/unomi/api/services/EventService.java    |  15 +
 .../unomi/api/services/EventTypeRegistry.java      |  12 +
 .../org/apache/unomi/itests/ContextServletIT.java  | 803 ++++++++++++---------
 .../services/impl/events/EventServiceImpl.java     |   8 +
 .../impl/events/EventTypeRegistryImpl.java         |  77 ++
 .../java/org/apache/unomi/web/ServletCommon.java   |  13 +-
 6 files changed, 596 insertions(+), 332 deletions(-)

diff --git a/api/src/main/java/org/apache/unomi/api/services/EventService.java 
b/api/src/main/java/org/apache/unomi/api/services/EventService.java
index 2b827e6..c6136a3 100644
--- a/api/src/main/java/org/apache/unomi/api/services/EventService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/EventService.java
@@ -70,6 +70,15 @@ public interface EventService {
      */
     boolean isEventAllowed(Event event, String thirdPartyId);
 
+
+    /**
+     * Check if event fields complies with corresponding {@link EventType} 
definition
+     *
+     * @param event        event to test
+     * @return true if the event is valid
+     */
+    boolean isEventValid(Event event);
+
     /**
      * Get the third party server name, if the request is originated from a 
known peer
      *
@@ -95,6 +104,12 @@ public interface EventService {
     EventType getEventType(String typeName);
 
     /**
+     * Registers event type
+     * @param eventType event type to register
+     */
+    void registerEventType(EventType eventType);
+
+    /**
      * Retrieves the set of known event type identifiers.
      *
      * @return the set of known event type identifiers.
diff --git 
a/api/src/main/java/org/apache/unomi/api/services/EventTypeRegistry.java 
b/api/src/main/java/org/apache/unomi/api/services/EventTypeRegistry.java
index 121405b..c81668c 100644
--- a/api/src/main/java/org/apache/unomi/api/services/EventTypeRegistry.java
+++ b/api/src/main/java/org/apache/unomi/api/services/EventTypeRegistry.java
@@ -17,6 +17,7 @@
 
 package org.apache.unomi.api.services;
 
+import org.apache.unomi.api.Event;
 import org.apache.unomi.api.EventType;
 
 import java.util.Collection;
@@ -28,6 +29,7 @@ public interface EventTypeRegistry {
 
     /**
      * Retrieve event type definition
+     *
      * @param typeName name of the event type
      * @return {@link EventType} definition
      */
@@ -35,12 +37,22 @@ public interface EventTypeRegistry {
 
     /**
      * Adds event type definition to registry
+     *
      * @param eventType {@link EventType} definition
      */
     void register(EventType eventType);
 
     /**
+     * Checks if event complies with {@link EventType} definition
+     *
+     * @param event the event to validate
+     * @return result of validation
+     */
+    boolean isValid(Event event);
+
+    /**
      * List all known event types
+     *
      * @return Lists all known {@link EventType}s
      */
     Collection<EventType> getAll();
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 a6dff7b..45cc2e0 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
@@ -21,7 +21,14 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.entity.ContentType;
 import org.apache.http.entity.StringEntity;
-import org.apache.unomi.api.*;
+import org.apache.unomi.api.ContextRequest;
+import org.apache.unomi.api.ContextResponse;
+import org.apache.unomi.api.Event;
+import org.apache.unomi.api.EventType;
+import org.apache.unomi.api.Metadata;
+import org.apache.unomi.api.Profile;
+import org.apache.unomi.api.PropertyType;
+import org.apache.unomi.api.Session;
 import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.api.segments.Segment;
 import org.apache.unomi.api.services.DefinitionsService;
@@ -48,10 +55,15 @@ import java.time.ZoneOffset;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import static org.hamcrest.core.IsCollectionContaining.hasItem;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
 
 
 /**
@@ -61,330 +73,465 @@ import static org.junit.Assert.*;
 @RunWith(PaxExam.class)
 @ExamReactorStrategy(PerSuite.class)
 public class ContextServletIT extends BaseIT {
-       private final static String CONTEXT_URL = "/context.json";
-       private final static String THIRD_PARTY_HEADER_NAME = "X-Unomi-Peer";
-       private final static String SEGMENT_EVENT_TYPE = "test-event-type";
-       private final static String SEGMENT_ID = "test-segment-id";
-       private final static int SEGMENT_NUMBER_OF_DAYS = 30;
-
-       private ObjectMapper objectMapper = new ObjectMapper();
-
-       @Inject
-       @Filter(timeout = 600000)
-       protected EventService eventService;
-
-       @Inject
-       @Filter(timeout = 600000)
-       protected PersistenceService persistenceService;
-
-       @Inject
-       @Filter(timeout = 600000)
-       protected ProfileService profileService;
-
-       @Inject
-       @Filter(timeout = 600000)
-       protected DefinitionsService definitionsService;
-
-       @Inject
-       @Filter(timeout = 600000)
-       protected SegmentService segmentService;
-
-       private Profile profile;
-
-       @Before
-       public void setUp() throws InterruptedException {
-               //Create a past-event segment
-               Metadata segmentMetadata = new Metadata(SEGMENT_ID);
-               Segment segment = new Segment(segmentMetadata);
-               Condition segmentCondition = new 
Condition(definitionsService.getConditionType("pastEventCondition"));
-               segmentCondition.setParameter("minimumEventCount",2);
-               
segmentCondition.setParameter("numberOfDays",SEGMENT_NUMBER_OF_DAYS);
-               Condition pastEventEventCondition = new 
Condition(definitionsService.getConditionType("eventTypeCondition"));
-               
pastEventEventCondition.setParameter("eventTypeId",SEGMENT_EVENT_TYPE);
-               
segmentCondition.setParameter("eventCondition",pastEventEventCondition);
-               segment.setCondition(segmentCondition);
-               segmentService.setSegmentDefinition(segment);
-
-               String profileId = "test-profile-id";
-               profile = new Profile(profileId);
-               profileService.save(profile);
-
-               refreshPersistence();
-       }
-
-       @After
-       public void tearDown() {
-               TestUtils.removeAllEvents(definitionsService, 
persistenceService);
-               TestUtils.removeAllSessions(definitionsService, 
persistenceService);
-               TestUtils.removeAllProfiles(definitionsService, 
persistenceService);
-               profileService.delete(profile.getItemId(), false);
-               segmentService.removeSegmentDefinition(SEGMENT_ID,false);
-       }
-
-       @Test
-       public void testUpdateEventFromContextAuthorizedThirdParty_Success() 
throws IOException, InterruptedException {
-               //Arrange
-               String eventId = "test-event-id-" + System.currentTimeMillis();
-               String profileId = "test-profile-id";
-               String sessionId = "test-session-id";
-               String scope = "test-scope";
-               String eventTypeOriginal = "test-event-type-original";
-               String eventTypeUpdated = "test-event-type-updated";
-               Profile profile = new Profile(profileId);
-               Session session = new Session(sessionId, profile, new Date(), 
scope);
-               Event event = new Event(eventId, eventTypeOriginal, session, 
profile, scope, null, null, new Date());
-               profileService.save(profile);
-               this.eventService.send(event);
-               refreshPersistence();
-               Thread.sleep(2000);
-               event.setEventType(eventTypeUpdated); //change the event so we 
can see the update effect
-
-               //Act
-               ContextRequest contextRequest = new ContextRequest();
-               contextRequest.setSessionId(session.getItemId());
-               contextRequest.setEvents(Arrays.asList(event));
-               HttpPost request = new HttpPost(URL + CONTEXT_URL);
-               request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
-               request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
-               TestUtils.executeContextJSONRequest(request, sessionId);
-               refreshPersistence();
-               Thread.sleep(2000); //Making sure event is updated in DB
-
-               //Assert
-               event = this.eventService.getEvent(eventId);
-               assertEquals(2, event.getVersion().longValue());
-               assertEquals(eventTypeUpdated,event.getEventType());
-       }
-
-       @Test
-       public void testUpdateEventFromContextUnAuthorizedThirdParty_Fail() 
throws IOException, InterruptedException {
-               //Arrange
-               String eventId = "test-event-id-" + System.currentTimeMillis();
-               String profileId = "test-profile-id";
-               String sessionId = "test-session-id";
-               String scope = "test-scope";
-               String eventTypeOriginal = "test-event-type-original";
-               String eventTypeUpdated = "test-event-type-updated";
-               Profile profile = new Profile(profileId);
-               Session session = new Session(sessionId, profile, new Date(), 
scope);
-               Event event = new Event(eventId, eventTypeOriginal, session, 
profile, scope, null, null, new Date());
-               profileService.save(profile);
-               this.eventService.send(event);
-               refreshPersistence();
-               Thread.sleep(2000);
-               event.setEventType(eventTypeUpdated); //change the event so we 
can see the update effect
-
-               //Act
-               ContextRequest contextRequest = new ContextRequest();
-               contextRequest.setSessionId(session.getItemId());
-               contextRequest.setEvents(Arrays.asList(event));
-               HttpPost request = new HttpPost(URL + CONTEXT_URL);
-               request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
-               TestUtils.executeContextJSONRequest(request, sessionId);
-               refreshPersistence();
-               Thread.sleep(2000); //Making sure event is updated in DB
-
-               //Assert
-               event = this.eventService.getEvent(eventId);
-               assertEquals(1, event.getVersion().longValue());
-               assertEquals(eventTypeOriginal,event.getEventType());
-       }
-
-
-       @Test
-       public void 
testUpdateEventFromContextAuthorizedThirdPartyNoItemID_Fail() throws 
IOException, InterruptedException {
-               //Arrange
-               String eventId = "test-event-id-" + System.currentTimeMillis();
-               String sessionId = "test-session-id";
-               String scope = "test-scope";
-               String eventTypeOriginal = "test-event-type-original";
-               String eventTypeUpdated = "test-event-type-updated";
-               Session session = new Session(sessionId, profile, new Date(), 
scope);
-               Event event = new Event(eventId, eventTypeOriginal, session, 
profile, scope, null, null, new Date());
-               this.eventService.send(event);
-               refreshPersistence();
-               Thread.sleep(2000);
-               event.setEventType(eventTypeUpdated); //change the event so we 
can see the update effect
-
-               //Act
-               ContextRequest contextRequest = new ContextRequest();
-               contextRequest.setSessionId(session.getItemId());
-               contextRequest.setEvents(Arrays.asList(event));
-               HttpPost request = new HttpPost(URL + CONTEXT_URL);
-               request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
-               TestUtils.executeContextJSONRequest(request, sessionId);
-               refreshPersistence();
-               Thread.sleep(2000); //Making sure event is updated in DB
-
-               //Assert
-               event = this.eventService.getEvent(eventId);
-               assertEquals(1, event.getVersion().longValue());
-               assertEquals(eventTypeOriginal,event.getEventType());
-       }
-
-       @Test
-       public void 
testCreateEventsWithNoTimestampParam_profileAddedToSegment() throws 
IOException, InterruptedException {
-               //Arrange
-               String sessionId = "test-session-id";
-               String scope = "test-scope";
-               Event event = new Event();
-               event.setEventType(SEGMENT_EVENT_TYPE);
-               event.setScope(scope);
-
-               //Act
-               ContextRequest contextRequest = new ContextRequest();
-               contextRequest.setSessionId(sessionId);
-               contextRequest.setRequireSegments(true);
-               contextRequest.setEvents(Arrays.asList(event));
-               HttpPost request = new HttpPost(URL + CONTEXT_URL);
-               request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
-               String cookieHeaderValue = 
TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
-               refreshPersistence();
-               Thread.sleep(1000); //Making sure DB is updated
-
-               //Add the context-profile-id cookie to the second event
-               request.addHeader("Cookie", cookieHeaderValue);
-               ContextResponse response = 
(TestUtils.executeContextJSONRequest(request, sessionId)).getContextResponse(); 
//second event
-
-               refreshPersistence();
-
-               //Assert
-               assertEquals(1, response.getProfileSegments().size());
-               assertThat(response.getProfileSegments(),hasItem(SEGMENT_ID));
-       }
-
-       @Test
-       public void 
testCreateEventWithTimestampParam_pastEvent_profileIsNotAddedToSegment() throws 
IOException, InterruptedException {
-               //Arrange
-               String sessionId = "test-session-id";
-               String scope = "test-scope";
-               Event event = new Event();
-               event.setEventType(SEGMENT_EVENT_TYPE);
-               event.setScope(scope);
-               String regularURI = URL + CONTEXT_URL;
-               long oldTimestamp = 
LocalDateTime.now(ZoneId.of("UTC")).minusDays(SEGMENT_NUMBER_OF_DAYS + 
1).toInstant(ZoneOffset.UTC).toEpochMilli();
-               String customTimestampURI = regularURI + "?timestamp=" + 
oldTimestamp;
-
-               //Act
-               ContextRequest contextRequest = new ContextRequest();
-               contextRequest.setSessionId(sessionId);
-               contextRequest.setRequireSegments(true);
-               contextRequest.setEvents(Arrays.asList(event));
-               HttpPost request = new HttpPost(regularURI);
-               request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
-               //The first event is with a default timestamp (now)
-               String cookieHeaderValue = 
TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
-               refreshPersistence();
-               //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
-               refreshPersistence();
-
-               //Assert
-               assertEquals(0,response.getProfileSegments().size());
-       }
-
-       @Test
-       public void 
testCreateEventWithTimestampParam_futureEvent_profileIsNotAddedToSegment() 
throws IOException, InterruptedException {
-               //Arrange
-               String sessionId = "test-session-id";
-               String scope = "test-scope";
-               Event event = new Event();
-               event.setEventType(SEGMENT_EVENT_TYPE);
-               event.setScope(scope);
-               String regularURI = URL + CONTEXT_URL;
-               long futureTimestamp = 
LocalDateTime.now(ZoneId.of("UTC")).plusDays(1).toInstant(ZoneOffset.UTC).toEpochMilli();
-               String customTimestampURI = regularURI + "?timestamp=" + 
futureTimestamp;
-
-               //Act
-               ContextRequest contextRequest = new ContextRequest();
-               contextRequest.setSessionId(sessionId);
-               contextRequest.setRequireSegments(true);
-               contextRequest.setEvents(Arrays.asList(event));
-               HttpPost request = new HttpPost(regularURI);
-               request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
-               //The first event is with a default timestamp (now)
-               String cookieHeaderValue = 
TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
-               refreshPersistence();
-               //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
-               refreshPersistence();
-
-               //Assert
-               assertEquals(0,response.getProfileSegments().size());
-       }
-
-       @Test
-       public void testCreateEventWithProfileId_Success() throws IOException, 
InterruptedException {
-               //Arrange
-               String eventId = "test-event-id-" + System.currentTimeMillis();
-               String profileId = "test-profile-id";
-               String eventType = "test-event-type";
-               Event event = new Event();
-               event.setEventType(eventType);
-               event.setItemId(eventId);
-
-               ContextRequest contextRequest = new ContextRequest();
-               contextRequest.setProfileId(profileId);
-               contextRequest.setEvents(Arrays.asList(event));
-
-               //Act
-               HttpPost request = new HttpPost(URL + CONTEXT_URL);
-               request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
-               request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
-               TestUtils.executeContextJSONRequest(request);
-               refreshPersistence();
-               Thread.sleep(2000); //Making sure event is updated in DB
-
-               //Assert
-               Profile profile =  this.profileService.load(profileId);
-               assertEquals(profileId, profile.getItemId());
-       }
-
-       @Test
-       public void testOGNLVulnerability() throws IOException, 
InterruptedException {
-
-               File vulnFile = new File("target/vuln-file.txt");
-               if (vulnFile.exists()) {
-                       vulnFile.delete();
-               }
-               String vulnFileCanonicalPath = vulnFile.getCanonicalPath();
-               vulnFileCanonicalPath = vulnFileCanonicalPath.replace("\\", 
"\\\\"); // this is required for Windows support
-
-               Map<String,String> parameters = new HashMap<>();
-               parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
-               HttpPost request = new HttpPost(URL + CONTEXT_URL);
-               request.setEntity(new 
StringEntity(getValidatedBundleJSON("security/ognl-payload-1.json", 
parameters), ContentType.create("application/json")));
-               TestUtils.executeContextJSONRequest(request);
-               refreshPersistence();
-               Thread.sleep(2000); //Making sure event is updated in DB
-
-               assertFalse("Vulnerability successfully executed ! File created 
at " + vulnFileCanonicalPath, vulnFile.exists());
-
-       }
-
-       @Test
-       public void testMVELVulnerability() throws IOException, 
InterruptedException {
-
-               File vulnFile = new File("target/vuln-file.txt");
-               if (vulnFile.exists()) {
-                       vulnFile.delete();
-               }
-               String vulnFileCanonicalPath = vulnFile.getCanonicalPath();
-               vulnFileCanonicalPath = vulnFileCanonicalPath.replace("\\", 
"\\\\"); // this is required for Windows support
-
-               Map<String,String> parameters = new HashMap<>();
-               parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
-               HttpPost request = new HttpPost(URL + CONTEXT_URL);
-               request.setEntity(new 
StringEntity(getValidatedBundleJSON("security/mvel-payload-1.json", 
parameters), ContentType.create("application/json")));
-               TestUtils.executeContextJSONRequest(request);
-               refreshPersistence();
-               Thread.sleep(2000); //Making sure event is updated in DB
-
-               assertFalse("Vulnerability successfully executed ! File created 
at " + vulnFileCanonicalPath, vulnFile.exists());
-
-       }
+    private final static String CONTEXT_URL = "/context.json";
+    private final static String THIRD_PARTY_HEADER_NAME = "X-Unomi-Peer";
+    private final static String SEGMENT_EVENT_TYPE = "test-event-type";
+    private final static String SEGMENT_ID = "test-segment-id";
+    private final static int SEGMENT_NUMBER_OF_DAYS = 30;
+
+    private ObjectMapper objectMapper = new ObjectMapper();
+
+    @Inject
+    @Filter(timeout = 600000)
+    protected EventService eventService;
+
+    @Inject
+    @Filter(timeout = 600000)
+    protected PersistenceService persistenceService;
+
+    @Inject
+    @Filter(timeout = 600000)
+    protected ProfileService profileService;
+
+    @Inject
+    @Filter(timeout = 600000)
+    protected DefinitionsService definitionsService;
+
+    @Inject
+    @Filter(timeout = 600000)
+    protected SegmentService segmentService;
+
+    private Profile profile;
+
+    @Before
+    public void setUp() throws InterruptedException {
+        this.registerEventType(SEGMENT_EVENT_TYPE);
+
+        //Create a past-event segment
+        Metadata segmentMetadata = new Metadata(SEGMENT_ID);
+        Segment segment = new Segment(segmentMetadata);
+        Condition segmentCondition = new 
Condition(definitionsService.getConditionType("pastEventCondition"));
+        segmentCondition.setParameter("minimumEventCount", 2);
+        segmentCondition.setParameter("numberOfDays", SEGMENT_NUMBER_OF_DAYS);
+        Condition pastEventEventCondition = new 
Condition(definitionsService.getConditionType("eventTypeCondition"));
+        pastEventEventCondition.setParameter("eventTypeId", 
SEGMENT_EVENT_TYPE);
+        segmentCondition.setParameter("eventCondition", 
pastEventEventCondition);
+        segment.setCondition(segmentCondition);
+        segmentService.setSegmentDefinition(segment);
+
+        String profileId = "test-profile-id";
+        profile = new Profile(profileId);
+        profileService.save(profile);
+
+        refreshPersistence();
+    }
+
+    @After
+    public void tearDown() {
+        TestUtils.removeAllEvents(definitionsService, persistenceService);
+        TestUtils.removeAllSessions(definitionsService, persistenceService);
+        TestUtils.removeAllProfiles(definitionsService, persistenceService);
+        profileService.delete(profile.getItemId(), false);
+        segmentService.removeSegmentDefinition(SEGMENT_ID, false);
+    }
+
+    private void registerEventType(final String type) {
+        final Set<PropertyType> props = new HashSet<>();
+        registerEventType(type, props);
+    }
+
+    private void registerEventType(final String type, final Set<PropertyType> 
props) {
+        final EventType eventType = new EventType(type, props, 1);
+        eventService.registerEventType(eventType);
+    }
+
+    @Test
+    public void testUpdateEventFromContextAuthorizedThirdParty_Success() 
throws IOException, InterruptedException {
+        //Arrange
+        String eventId = "test-event-id-" + System.currentTimeMillis();
+        String profileId = "test-profile-id";
+        String sessionId = "test-session-id";
+        String scope = "test-scope";
+        String eventTypeOriginal = "test-event-type-original";
+        String eventTypeUpdated = "test-event-type-updated";
+        Profile profile = new Profile(profileId);
+        Session session = new Session(sessionId, profile, new Date(), scope);
+        Event event = new Event(eventId, eventTypeOriginal, session, profile, 
scope, null, null, new Date());
+        profileService.save(profile);
+        this.eventService.send(event);
+        refreshPersistence();
+        Thread.sleep(2000);
+        event.setEventType(eventTypeUpdated); //change the event so we can see 
the update effect
+
+        this.registerEventType(eventTypeUpdated);
+
+        //Act
+        ContextRequest contextRequest = new ContextRequest();
+        contextRequest.setSessionId(session.getItemId());
+        contextRequest.setEvents(Arrays.asList(event));
+        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
+        request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
+        TestUtils.executeContextJSONRequest(request, sessionId);
+        refreshPersistence();
+        Thread.sleep(2000); //Making sure event is updated in DB
+
+        //Assert
+        event = this.eventService.getEvent(eventId);
+        assertEquals(2, event.getVersion().longValue());
+        assertEquals(eventTypeUpdated, event.getEventType());
+    }
+
+    @Test
+    public void testUpdateEventFromContextUnAuthorizedThirdParty_Fail() throws 
IOException, InterruptedException {
+        //Arrange
+        String eventId = "test-event-id-" + System.currentTimeMillis();
+        String profileId = "test-profile-id";
+        String sessionId = "test-session-id";
+        String scope = "test-scope";
+        String eventTypeOriginal = "test-event-type-original";
+        String eventTypeUpdated = "test-event-type-updated";
+        Profile profile = new Profile(profileId);
+        Session session = new Session(sessionId, profile, new Date(), scope);
+        Event event = new Event(eventId, eventTypeOriginal, session, profile, 
scope, null, null, new Date());
+        profileService.save(profile);
+        this.eventService.send(event);
+        refreshPersistence();
+        Thread.sleep(2000);
+        event.setEventType(eventTypeUpdated); //change the event so we can see 
the update effect
+
+        this.registerEventType(eventTypeUpdated);
+
+        //Act
+        ContextRequest contextRequest = new ContextRequest();
+        contextRequest.setSessionId(session.getItemId());
+        contextRequest.setEvents(Arrays.asList(event));
+        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
+        TestUtils.executeContextJSONRequest(request, sessionId);
+        refreshPersistence();
+        Thread.sleep(2000); //Making sure event is updated in DB
+
+        //Assert
+        event = this.eventService.getEvent(eventId);
+        assertEquals(1, event.getVersion().longValue());
+        assertEquals(eventTypeOriginal, event.getEventType());
+    }
+
+
+    @Test
+    public void testUpdateEventFromContextAuthorizedThirdPartyNoItemID_Fail() 
throws IOException, InterruptedException {
+        //Arrange
+        String eventId = "test-event-id-" + System.currentTimeMillis();
+        String sessionId = "test-session-id";
+        String scope = "test-scope";
+        String eventTypeOriginal = "test-event-type-original";
+        String eventTypeUpdated = "test-event-type-updated";
+        Session session = new Session(sessionId, profile, new Date(), scope);
+        Event event = new Event(eventId, eventTypeOriginal, session, profile, 
scope, null, null, new Date());
+        this.eventService.send(event);
+        refreshPersistence();
+        Thread.sleep(2000);
+        event.setEventType(eventTypeUpdated); //change the event so we can see 
the update effect
+
+        this.registerEventType(eventTypeUpdated);
+
+        //Act
+        ContextRequest contextRequest = new ContextRequest();
+        contextRequest.setSessionId(session.getItemId());
+        contextRequest.setEvents(Arrays.asList(event));
+        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
+        TestUtils.executeContextJSONRequest(request, sessionId);
+        refreshPersistence();
+        Thread.sleep(2000); //Making sure event is updated in DB
+
+        //Assert
+        event = this.eventService.getEvent(eventId);
+        assertEquals(1, event.getVersion().longValue());
+        assertEquals(eventTypeOriginal, event.getEventType());
+    }
+
+    @Test
+    public void testCreateEventsWithNoTimestampParam_profileAddedToSegment() 
throws IOException, InterruptedException {
+        //Arrange
+        String sessionId = "test-session-id";
+        String scope = "test-scope";
+        Event event = new Event();
+        event.setEventType(SEGMENT_EVENT_TYPE);
+        event.setScope(scope);
+
+        //Act
+        ContextRequest contextRequest = new ContextRequest();
+        contextRequest.setSessionId(sessionId);
+        contextRequest.setRequireSegments(true);
+        contextRequest.setEvents(Arrays.asList(event));
+        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
+        String cookieHeaderValue = 
TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
+        refreshPersistence();
+        Thread.sleep(1000); //Making sure DB is updated
+
+        //Add the context-profile-id cookie to the second event
+        request.addHeader("Cookie", cookieHeaderValue);
+        ContextResponse response = 
(TestUtils.executeContextJSONRequest(request, sessionId)).getContextResponse(); 
//second event
+
+        refreshPersistence();
+
+        //Assert
+        assertEquals(1, response.getProfileSegments().size());
+        assertThat(response.getProfileSegments(), hasItem(SEGMENT_ID));
+    }
+
+    @Test
+    public void 
testCreateEventWithTimestampParam_pastEvent_profileIsNotAddedToSegment() throws 
IOException, InterruptedException {
+        //Arrange
+        String sessionId = "test-session-id";
+        String scope = "test-scope";
+        Event event = new Event();
+        event.setEventType(SEGMENT_EVENT_TYPE);
+        event.setScope(scope);
+        String regularURI = URL + CONTEXT_URL;
+        long oldTimestamp = 
LocalDateTime.now(ZoneId.of("UTC")).minusDays(SEGMENT_NUMBER_OF_DAYS + 
1).toInstant(ZoneOffset.UTC).toEpochMilli();
+        String customTimestampURI = regularURI + "?timestamp=" + oldTimestamp;
+
+        //Act
+        ContextRequest contextRequest = new ContextRequest();
+        contextRequest.setSessionId(sessionId);
+        contextRequest.setRequireSegments(true);
+        contextRequest.setEvents(Arrays.asList(event));
+        HttpPost request = new HttpPost(regularURI);
+        request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
+        //The first event is with a default timestamp (now)
+        String cookieHeaderValue = 
TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
+        refreshPersistence();
+        //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
+        refreshPersistence();
+
+        //Assert
+        assertEquals(0, response.getProfileSegments().size());
+    }
+
+    @Test
+    public void 
testCreateEventWithTimestampParam_futureEvent_profileIsNotAddedToSegment() 
throws IOException, InterruptedException {
+        //Arrange
+        String sessionId = "test-session-id";
+        String scope = "test-scope";
+        Event event = new Event();
+        event.setEventType(SEGMENT_EVENT_TYPE);
+        event.setScope(scope);
+        String regularURI = URL + CONTEXT_URL;
+        long futureTimestamp = 
LocalDateTime.now(ZoneId.of("UTC")).plusDays(1).toInstant(ZoneOffset.UTC).toEpochMilli();
+        String customTimestampURI = regularURI + "?timestamp=" + 
futureTimestamp;
+
+        //Act
+        ContextRequest contextRequest = new ContextRequest();
+        contextRequest.setSessionId(sessionId);
+        contextRequest.setRequireSegments(true);
+        contextRequest.setEvents(Arrays.asList(event));
+        HttpPost request = new HttpPost(regularURI);
+        request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
+        //The first event is with a default timestamp (now)
+        String cookieHeaderValue = 
TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
+        refreshPersistence();
+        //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
+        refreshPersistence();
+
+        //Assert
+        assertEquals(0, response.getProfileSegments().size());
+    }
+
+    @Test
+    public void testCreateEventWithProfileId_Success() throws IOException, 
InterruptedException {
+        //Arrange
+        String eventId = "test-event-id-" + System.currentTimeMillis();
+        String profileId = "test-profile-id";
+        String eventType = "test-event-type";
+        Event event = new Event();
+        event.setEventType(eventType);
+        event.setItemId(eventId);
+
+        ContextRequest contextRequest = new ContextRequest();
+        contextRequest.setProfileId(profileId);
+        contextRequest.setEvents(Arrays.asList(event));
+
+        this.registerEventType(eventType);
+
+        //Act
+        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
+        request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
+        TestUtils.executeContextJSONRequest(request);
+        refreshPersistence();
+        Thread.sleep(2000); //Making sure event is updated in DB
+
+        //Assert
+        Profile profile = this.profileService.load(profileId);
+        assertEquals(profileId, profile.getItemId());
+    }
+
+    @Test
+    public void testCreateEventWithPropertiesValidation_Success() throws 
IOException, InterruptedException {
+        //Arrange
+        String eventId = "valid-event-id-" + System.currentTimeMillis();
+        String profileId = "valid-profile-id";
+        String eventType = "valid-event-type";
+        Event event = new Event();
+        event.setEventType(eventType);
+        event.setItemId(eventId);
+        Map<String, Object> props = new HashMap<>();
+        props.put("floatProperty", 3.14159);
+        event.setProperties(props);
+
+        ContextRequest contextRequest = new ContextRequest();
+        contextRequest.setProfileId(profileId);
+        contextRequest.setEvents(Arrays.asList(event));
+
+        final Set<PropertyType> typeProps = new HashSet<>();
+        PropertyType floatProp = new PropertyType();
+        floatProp.setItemId("floatProperty");
+        floatProp.setValueTypeId("float");
+        typeProps.add(floatProp);
+        this.registerEventType(eventType, typeProps);
+
+        //Act
+        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
+        request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
+        TestUtils.executeContextJSONRequest(request);
+        refreshPersistence();
+        Thread.sleep(2000); //Making sure event is updated in DB
+
+        //Assert
+        event = this.eventService.getEvent(eventId);
+        assertEquals(eventType, event.getEventType());
+        assertEquals(3.14159, event.getProperty("floatProperty"));
+    }
+
+    @Test
+    public void testCreateEventWithPropertyValueValidation_Failure() throws 
IOException, InterruptedException {
+        //Arrange
+        String eventId = "invalid-event-value-id-" + 
System.currentTimeMillis();
+        String profileId = "invalid-profile-id";
+        String eventType = "invalid-event-value-type";
+        Event event = new Event();
+        event.setEventType(eventType);
+        event.setItemId(eventId);
+        Map<String, Object> props = new HashMap<>();
+        props.put("floatProperty", "Invalid value");
+        event.setProperties(props);
+
+        ContextRequest contextRequest = new ContextRequest();
+        contextRequest.setProfileId(profileId);
+        contextRequest.setEvents(Arrays.asList(event));
+
+        final Set<PropertyType> typeProps = new HashSet<>();
+        PropertyType floatProp = new PropertyType();
+        floatProp.setItemId("floatProperty");
+        floatProp.setValueTypeId("float");
+        typeProps.add(floatProp);
+        this.registerEventType(eventType, typeProps);
+
+        //Act
+        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
+        request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
+        TestUtils.executeContextJSONRequest(request);
+        refreshPersistence();
+        Thread.sleep(2000); //Making sure event is updated in DB
+
+        //Assert
+        event = this.eventService.getEvent(eventId);
+        assertNull(event);
+    }
+
+    @Test
+    public void testCreateEventWithPropertyNameValidation_Failure() throws 
IOException, InterruptedException {
+        //Arrange
+        String eventId = "invalid-event-prop-id-" + System.currentTimeMillis();
+        String profileId = "invalid-profile-id";
+        String eventType = "invalid-event-prop-type";
+        Event event = new Event();
+        event.setEventType(eventType);
+        event.setItemId(eventId);
+        Map<String, Object> props = new HashMap<>();
+        props.put("floatProperty", 3.14159);
+        event.setProperties(props);
+
+        ContextRequest contextRequest = new ContextRequest();
+        contextRequest.setProfileId(profileId);
+        contextRequest.setEvents(Arrays.asList(event));
+
+        final Set<PropertyType> typeProps = new HashSet<>();
+        PropertyType floatProp = new PropertyType();
+        floatProp.setItemId("floatProperty");
+        floatProp.setValueTypeId("float");
+        PropertyType geopointProp = new PropertyType();
+        geopointProp.setItemId("geopointProperty");
+        geopointProp.setValueTypeId("geopoint");
+        typeProps.add(geopointProp);
+        this.registerEventType(eventType, typeProps);
+
+        //Act
+        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
+        request.setEntity(new 
StringEntity(objectMapper.writeValueAsString(contextRequest), 
ContentType.create("application/json")));
+        TestUtils.executeContextJSONRequest(request);
+        refreshPersistence();
+        Thread.sleep(2000); //Making sure event is updated in DB
+
+        //Assert
+        event = this.eventService.getEvent(eventId);
+        assertNull(event);
+    }
+
+    @Test
+    public void testOGNLVulnerability() throws IOException, 
InterruptedException {
+
+        File vulnFile = new File("target/vuln-file.txt");
+        if (vulnFile.exists()) {
+            vulnFile.delete();
+        }
+        String vulnFileCanonicalPath = vulnFile.getCanonicalPath();
+        vulnFileCanonicalPath = vulnFileCanonicalPath.replace("\\", "\\\\"); 
// this is required for Windows support
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
+        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        request.setEntity(new 
StringEntity(getValidatedBundleJSON("security/ognl-payload-1.json", 
parameters), ContentType.create("application/json")));
+        TestUtils.executeContextJSONRequest(request);
+        refreshPersistence();
+        Thread.sleep(2000); //Making sure event is updated in DB
+
+        assertFalse("Vulnerability successfully executed ! File created at " + 
vulnFileCanonicalPath, vulnFile.exists());
+
+    }
+
+    @Test
+    public void testMVELVulnerability() throws IOException, 
InterruptedException {
+
+        File vulnFile = new File("target/vuln-file.txt");
+        if (vulnFile.exists()) {
+            vulnFile.delete();
+        }
+        String vulnFileCanonicalPath = vulnFile.getCanonicalPath();
+        vulnFileCanonicalPath = vulnFileCanonicalPath.replace("\\", "\\\\"); 
// this is required for Windows support
+
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
+        HttpPost request = new HttpPost(URL + CONTEXT_URL);
+        request.setEntity(new 
StringEntity(getValidatedBundleJSON("security/mvel-payload-1.json", 
parameters), ContentType.create("application/json")));
+        TestUtils.executeContextJSONRequest(request);
+        refreshPersistence();
+        Thread.sleep(2000); //Making sure event is updated in DB
+
+        assertFalse("Vulnerability successfully executed ! File created at " + 
vulnFileCanonicalPath, vulnFile.exists());
+
+    }
 }
diff --git 
a/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
 
b/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
index 6d51f16..3f5cd23 100644
--- 
a/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
+++ 
b/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
@@ -131,6 +131,10 @@ public class EventServiceImpl implements EventService {
         return true;
     }
 
+    public boolean isEventValid(Event event) {
+        return this.eventTypeRegistry.isValid(event);
+    }
+
     public String authenticateThirdPartyServer(String key, String ip) {
         logger.debug("Authenticating third party server with key: " + key + " 
and IP: " + ip);
         if (key != null) {
@@ -207,6 +211,10 @@ public class EventServiceImpl implements EventService {
         return eventTypeRegistry.get(typeName);
     }
 
+    public void registerEventType(final EventType eventType) {
+        eventTypeRegistry.register(eventType);
+    }
+
     @Override
     public List<EventProperty> getEventProperties() {
         Map<String, Map<String, Object>> mappings = 
persistenceService.getPropertiesMapping(Event.ITEM_TYPE);
diff --git 
a/services/src/main/java/org/apache/unomi/services/impl/events/EventTypeRegistryImpl.java
 
b/services/src/main/java/org/apache/unomi/services/impl/events/EventTypeRegistryImpl.java
index 9525e19..02b1079 100644
--- 
a/services/src/main/java/org/apache/unomi/services/impl/events/EventTypeRegistryImpl.java
+++ 
b/services/src/main/java/org/apache/unomi/services/impl/events/EventTypeRegistryImpl.java
@@ -17,8 +17,11 @@
 
 package org.apache.unomi.services.impl.events;
 
+import org.apache.unomi.api.Event;
 import org.apache.unomi.api.EventType;
+import org.apache.unomi.api.GeoPoint;
 import org.apache.unomi.api.PluginType;
+import org.apache.unomi.api.PropertyType;
 import org.apache.unomi.api.services.EventTypeRegistry;
 import org.apache.unomi.persistence.spi.CustomObjectMapper;
 import org.osgi.framework.Bundle;
@@ -31,11 +34,13 @@ import org.slf4j.LoggerFactory;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 public class EventTypeRegistryImpl implements EventTypeRegistry, 
SynchronousBundleListener {
 
@@ -89,6 +94,78 @@ public class EventTypeRegistryImpl implements 
EventTypeRegistry, SynchronousBund
         eventTypes.put(eventType.getType(), eventType);
     }
 
+    @Override
+    public boolean isValid(Event event) {
+        if (event == null) {
+            return false;
+        }
+        final EventType eventType = this.get(event.getEventType());
+        if (eventType == null) {
+            return false;
+        }
+
+        return areAllPropertiesValid(event.getProperties(), 
eventType.getPropertyTypes());
+    }
+
+    /**
+     * Checks that all properties from map are defined in the property type 
set.
+     * Does not require all defined properties to be present in map.
+     *
+     * @param props map of properties to validate
+     * @param types set of a predefined event type properties
+     * @return boolean result of validation
+     */
+    private boolean areAllPropertiesValid(Map<String, Object> props, 
Set<PropertyType> types) {
+        if (props == null || props.isEmpty() || types == null || 
types.isEmpty()) {
+            return true;
+        }
+        return props.entrySet().stream().allMatch(entry -> {
+            return types.stream().anyMatch(type -> {
+                if (!type.getItemId().equals(entry.getKey())) {
+                    return false;
+                }
+                final Set<PropertyType> childTypes = 
type.getChildPropertyTypes();
+                if (childTypes.size() > 0 && entry.getValue() != null) {
+                    try {
+                        final Map<String, Object> childProps = (Map<String, 
Object>) entry.getValue();
+                        return areAllPropertiesValid(childProps, childTypes);
+                    } catch (ClassCastException e) {
+                        logger.error("Event property '{}' value is invalid: 
{}", entry.getKey(), e.getCause());
+                        return false;
+                    }
+                } else {
+                    return testValueType(entry.getValue(), 
type.getValueTypeId());
+                }
+            });
+        });
+    }
+
+    private boolean testValueType(final Object value, final String 
valueTypeId) {
+        switch (valueTypeId) {
+            case "integer":
+                return value instanceof Integer;
+            case "long":
+                return value instanceof Long;
+            case "float":
+                return value instanceof Double;
+            case "set":
+            case "json":
+                return value instanceof Map;
+            case "geopoint":
+                return value instanceof GeoPoint;
+            case "date":
+                return value instanceof Date;
+            case "boolean":
+                return value instanceof Boolean;
+            case "id":
+            case "string":
+                return value instanceof String;
+            default:
+                // return true if type is unknown cuz it may be custom
+                return true;
+        }
+    }
+
     public Collection<EventType> getAll() {
         return this.eventTypes.values();
     }
diff --git a/wab/src/main/java/org/apache/unomi/web/ServletCommon.java 
b/wab/src/main/java/org/apache/unomi/web/ServletCommon.java
index 597295f..bf700c3 100644
--- a/wab/src/main/java/org/apache/unomi/web/ServletCommon.java
+++ b/wab/src/main/java/org/apache/unomi/web/ServletCommon.java
@@ -55,8 +55,8 @@ public class ServletCommon {
     }
 
     public static Changes handleEvents(List<Event> events, Session session, 
Profile profile,
-                                    ServletRequest request, ServletResponse 
response, Date timestamp,
-                                    PrivacyService privacyService, 
EventService eventService) {
+                                       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"),
@@ -69,6 +69,11 @@ public class ServletCommon {
             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.getScope(), event.getSource(),
                             event.getTarget(), event.getProperties(), 
timestamp, event.isPersistent());
                     if (!eventService.isEventAllowed(event, thirdPartyId)) {
@@ -90,7 +95,7 @@ public class ServletCommon {
                     
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);
+                            + (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) {
@@ -99,7 +104,7 @@ public class ServletCommon {
                     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());
+                        logger.error("Error processing events. Total number of 
processed events: {}/{}", processedEventsCnt, events.size());
                         break;
                     }
                 }

Reply via email to