blob: 1d2cfd7603d536d72fb30237cb65138d04556424 [file] [log] [blame]
/*
* 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.services.impl.personalization;
import org.apache.unomi.api.PersonalizationResult;
import org.apache.unomi.api.PersonalizationStrategy;
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.EventService;
import org.apache.unomi.api.services.PersonalizationService;
import org.apache.unomi.api.services.ProfileService;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class PersonalizationServiceImpl implements PersonalizationService {
public static final String CONTROL_GROUPS_PROPERTY_NAME = "unomiControlGroups";
private BundleContext bundleContext;
private ProfileService profileService;
private Map<String, PersonalizationStrategy> personalizationStrategies = new ConcurrentHashMap<>();
private Random controlGroupRandom = new Random();
public void setProfileService(ProfileService profileService) {
this.profileService = profileService;
}
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
public void addPersonalizationStrategy(ServiceReference<PersonalizationStrategy> personalizationStrategyRef) {
PersonalizationStrategy personalizationStrategy = bundleContext.getService(personalizationStrategyRef);
personalizationStrategies.put(personalizationStrategyRef.getProperty("personalizationStrategyId").toString(), personalizationStrategy);
}
public void removePersonalizationStrategy(ServiceReference<PersonalizationStrategy> personalizationStrategyRef) {
if (personalizationStrategyRef == null) {
return;
}
personalizationStrategies.remove(personalizationStrategyRef.getProperty("personalizationStrategyId").toString());
}
@Override
public boolean filter(Profile profile, Session session, PersonalizedContent personalizedContent) {
boolean result = true;
if (personalizedContent.getFilters() != null) {
for (Filter filter : personalizedContent.getFilters()) {
Condition condition = filter.getCondition();
if (condition != null && condition.getConditionTypeId() != null) {
result &= profileService.matchCondition(condition, profile, session);
}
}
}
return result;
}
@Override
public String bestMatch(Profile profile, Session session, PersonalizationRequest personalizationRequest) {
PersonalizationResult result = personalizeList(profile,session,personalizationRequest);
if (result.getContentIds().size() > 0) {
return result.getContentIds().get(0);
}
return null;
}
@Override
public PersonalizationResult personalizeList(Profile profile, Session session, PersonalizationRequest personalizationRequest) {
PersonalizationStrategy strategy = personalizationStrategies.get(personalizationRequest.getStrategy());
int changeType = EventService.NO_CHANGE;
if (strategy != null) {
if (personalizationRequest.getStrategyOptions() != null && personalizationRequest.getStrategyOptions().containsKey("controlGroup")) {
Map<String,Object> controlGroupMap = (Map<String,Object>) personalizationRequest.getStrategyOptions().get("controlGroup");
boolean storeInSession = false;
if (controlGroupMap.containsKey("storeInSession")) {
storeInSession = (Boolean) controlGroupMap.get("storeInSession");
}
boolean profileInControlGroup = false;
Optional<ControlGroup> currentControlGroup;
List<ControlGroup> controlGroups = null;
if (storeInSession) {
if (session.getProperty(CONTROL_GROUPS_PROPERTY_NAME) != null) {
controlGroups = ((List<Map<String, Object>>) session.getProperty(CONTROL_GROUPS_PROPERTY_NAME)).stream().map(ControlGroup::fromMap).collect(Collectors.toList());
}
} else {
if (profile.getProperty(CONTROL_GROUPS_PROPERTY_NAME) != null) {
controlGroups = ((List<Map<String, Object>>) profile.getProperty(CONTROL_GROUPS_PROPERTY_NAME)).stream().map(ControlGroup::fromMap).collect(Collectors.toList());
}
}
if (controlGroups == null) {
controlGroups = new ArrayList<>();
}
currentControlGroup = controlGroups.stream().filter(controlGroup -> controlGroup.id.equals(personalizationRequest.getId())).findFirst();
if (currentControlGroup.isPresent()) {
// we already have an entry for this personalization so this means the profile is in the control group
profileInControlGroup = true;
} else {
double randomDouble = controlGroupRandom.nextDouble() * 100.0;
Object percentageObject = controlGroupMap.get("percentage");
Double controlGroupPercentage = null;
if (percentageObject != null) {
if (percentageObject instanceof Double) {
controlGroupPercentage = (Double) percentageObject;
} else if (percentageObject instanceof Integer) {
controlGroupPercentage = ((Integer) percentageObject).doubleValue();
}
}
if (randomDouble <= controlGroupPercentage) {
// Profile is elected to be in control group
profileInControlGroup = true;
ControlGroup controlGroup = new ControlGroup(personalizationRequest.getId(),
(String) controlGroupMap.get("displayName"),
(String) controlGroupMap.get("path"),
new Date());
controlGroups.add(controlGroup);
List<Map<String,Object>> controlGroupsMap = controlGroups.stream().map(ControlGroup::toMap).collect(Collectors.toList());
if (storeInSession) {
session.setProperty(CONTROL_GROUPS_PROPERTY_NAME, controlGroupsMap);
changeType = EventService.SESSION_UPDATED;
} else {
profile.setProperty(CONTROL_GROUPS_PROPERTY_NAME, controlGroupsMap);
changeType = EventService.PROFILE_UPDATED;
}
}
}
if (profileInControlGroup) {
// if profile is in control group we return the unmodified list.
return new PersonalizationResult(personalizationRequest.getContents().stream().map(PersonalizedContent::getId).collect(Collectors.toList()), changeType);
}
}
return new PersonalizationResult(strategy.personalizeList(profile, session, personalizationRequest), changeType);
}
throw new IllegalArgumentException("Unknown strategy : "+ personalizationRequest.getStrategy());
}
}