| /* |
| * 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.api; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.unomi.api.segments.Scoring; |
| import org.apache.unomi.api.segments.Segment; |
| |
| import javax.xml.bind.annotation.XmlTransient; |
| import java.util.*; |
| |
| /** |
| * A user profile gathering all known information about a given user as well as segments it is part of and scores. |
| * <p> |
| * Contrary to other unomi {@link Item}s, profiles are not part of a scope since we want to be able to track the associated user across applications. For this reason, data |
| * collected for a given profile in a specific scope is still available to any scoped item that accesses the profile information. |
| * <p> |
| * It is interesting to note that there is not necessarily a one to one mapping between users and profiles as users can be captured across applications and different observation |
| * contexts. As identifying information might not be available in all contexts in which data is collected, resolving profiles to a single physical user can become complex because |
| * physical users are not observed directly. Rather, their portrait is progressively patched together and made clearer as unomi captures more and more traces of their actions. |
| * Unomi will merge related profiles as soon as collected data permits positive association between distinct profiles, usually as a result of the user performing some identifying |
| * action in a context where the user hadn’t already been positively identified. |
| * |
| * @see Segment |
| */ |
| public class Profile extends Item { |
| |
| /** |
| * The Profile ITEM_TYPE |
| * |
| * @see Item for a discussion of ITEM_TYPE |
| */ |
| public static final String ITEM_TYPE = "profile"; |
| private static final long serialVersionUID = -7409439322939712238L; |
| private Map<String, Object> properties = new HashMap<>(); |
| |
| private Map<String, Object> systemProperties = new HashMap<>(); |
| |
| private Set<String> segments = new HashSet<>(); |
| |
| private Map<String, Integer> scores; |
| |
| /** |
| * @deprecated since 2.0.0 merge mechanism is now based on profile aliases, and this property is not used anymore |
| */ |
| @Deprecated |
| private String mergedWith; |
| |
| private Map<String, Consent> consents = new LinkedHashMap<>(); |
| |
| /** |
| * Instantiates a new Profile. |
| */ |
| public Profile() { |
| } |
| |
| /** |
| * Instantiates a new Profile with the specified identifier. |
| * |
| * @param profileId the profile identifier |
| */ |
| public Profile(String profileId) { |
| super(profileId); |
| } |
| |
| /** |
| * Sets the property identified by the specified name to the specified value. If a property with that name already exists, replaces its value, otherwise adds the new |
| * property with the specified name and value. |
| * |
| * @param name the name of the property to set |
| * @param value the value of the property |
| */ |
| public void setProperty(String name, Object value) { |
| properties.put(name, value); |
| } |
| |
| /** |
| * Retrieves the property identified by the specified name. |
| * |
| * @param name the name of the property to retrieve |
| * @return the value of the specified property or {@code null} if no such property exists |
| */ |
| public Object getProperty(String name) { |
| return properties.get(name); |
| } |
| |
| /** |
| * Retrieves the value of the nested property identified by the specified name. |
| * |
| * @param name the name of the property to be retrieved, splited in the nested properties with "." |
| * @return the value of the property identified by the specified name |
| */ |
| public Object getNestedProperty(String name) { |
| if (!name.contains(".")) { |
| return getProperty(name); |
| } |
| |
| Map properties = this.properties; |
| String[] propertyPath = StringUtils.substringBeforeLast(name, ".").split("\\."); |
| String propertyName = StringUtils.substringAfterLast(name, "."); |
| |
| for (String property: propertyPath) { |
| properties = (Map) properties.get(property); |
| if (properties == null) { |
| return null; |
| } |
| } |
| return properties.get(propertyName); |
| } |
| |
| /** |
| * Retrieves a Map of all property name - value pairs for this profile. |
| * |
| * @return a Map of all property name - value pairs for this profile |
| */ |
| public Map<String, Object> getProperties() { |
| return properties; |
| } |
| |
| /** |
| * Sets the property name - value pairs for this profile. |
| * |
| * @param properties a Map containing the property name - value pairs for this profile |
| */ |
| public void setProperties(Map<String, Object> properties) { |
| this.properties = properties; |
| } |
| |
| /** |
| * Retrieves a Map of system property name - value pairs for this profile. System properties can be used by implementations to store non-user visible properties needed for |
| * internal purposes. |
| * |
| * @return a Map of system property name - value pairs for this profile |
| */ |
| public Map<String, Object> getSystemProperties() { |
| return systemProperties; |
| } |
| |
| /** |
| * Specifies the system property name - value pairs for this profile. |
| * |
| * @param systemProperties a Map of system property name - value pairs for this profile |
| */ |
| public void setSystemProperties(Map<String, Object> systemProperties) { |
| this.systemProperties = systemProperties; |
| } |
| |
| /** |
| * Sets a system property, overwriting an existing one if it existed. This call will also created the system |
| * properties hash map if it didn't exist. |
| * @param key the key for the system property hash map |
| * @param value the value for the system property hash map |
| * @return the previous value object if it existing. |
| */ |
| public Object setSystemProperty(String key, Object value) { |
| if (this.systemProperties == null) { |
| this.systemProperties = new LinkedHashMap<>(); |
| } |
| return this.systemProperties.put(key, value); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * Note that Profiles are always in the shared system scope ({@link Metadata#SYSTEM_SCOPE}). |
| */ |
| @XmlTransient |
| public String getScope() { |
| return Metadata.SYSTEM_SCOPE; |
| } |
| |
| /** |
| * Retrieves the identifiers of the segments this profile is a member of. |
| * |
| * @return the identifiers of the segments this profile is a member of |
| */ |
| public Set<String> getSegments() { |
| return segments; |
| } |
| |
| /** |
| * Sets the identifiers of the segments this profile is a member of. |
| * |
| * TODO: should be removed from the API |
| * |
| * @param segments the segments |
| */ |
| public void setSegments(Set<String> segments) { |
| this.segments = segments; |
| } |
| |
| /** |
| * @deprecated since 2.0.0 merge mechanism is now based on profile aliases, and this property is not used anymore |
| */ |
| @Deprecated |
| public String getMergedWith() { |
| return mergedWith; |
| } |
| |
| /** |
| * @deprecated since 2.0.0 merge mechanism is now based on profile aliases, and this property is not used anymore |
| */ |
| @Deprecated |
| public void setMergedWith(String mergedWith) { |
| this.mergedWith = mergedWith; |
| } |
| |
| /** |
| * Retrieves the scores associated to this profile. |
| * |
| * @return the scores associated to this profile as a Map of {@link Scoring} identifier - score pairs |
| */ |
| public Map<String, Integer> getScores() { |
| return scores; |
| } |
| |
| /** |
| * TODO: should be removed from the API |
| * @param scores new value for scores |
| */ |
| public void setScores(Map<String, Integer> scores) { |
| this.scores = scores; |
| } |
| |
| /** |
| * Returns all the consents, including the revokes ones. |
| * @return a map that contains as a key the scope + "/" + consent type ID (or just the consent type ID if no scope was set on the consent), and the consent itself as a value |
| */ |
| public Map<String, Consent> getConsents() { |
| return consents; |
| } |
| |
| /** |
| * Returns true if this profile is an anonymous profile. |
| * @return true of the profile has been marked as an anonymous profile, false otherwise. |
| */ |
| @XmlTransient |
| public boolean isAnonymousProfile() { |
| Boolean anonymous = (Boolean) getSystemProperties().get("isAnonymousProfile"); |
| return anonymous != null && anonymous; |
| } |
| |
| /** |
| * Set a consent into the profile. |
| * @param consent if the consent is REVOKED, it will try to remove a consent with the same type id if it |
| * exists for the profile. |
| * @return true if the operation was successful (inserted exception in the case of a revoked consent, in which case |
| * it is successful if there was a consent to revoke). |
| */ |
| @XmlTransient |
| public boolean setConsent(Consent consent) { |
| if (ConsentStatus.REVOKED.equals(consent.getStatus())) { |
| if (consent.getScope() != null) { |
| if (consents.containsKey(consent.getScope() + "/" + consent.getTypeIdentifier())) { |
| consents.remove(consent.getScope() + "/" + consent.getTypeIdentifier()); |
| return true; |
| } |
| } else { |
| if (consents.containsKey(consent.getTypeIdentifier())) { |
| consents.remove(consent.getTypeIdentifier()); |
| return true; |
| } |
| } |
| return false; |
| } |
| if (consent.getScope() != null) { |
| consents.put(consent.getScope() + "/" + consent.getTypeIdentifier(), consent); |
| } else { |
| consents.put(consent.getTypeIdentifier(), consent); |
| } |
| return true; |
| } |
| |
| @Override |
| public String toString() { |
| final StringBuilder sb = new StringBuilder("Profile{"); |
| sb.append("properties=").append(properties); |
| sb.append(", systemProperties=").append(systemProperties); |
| sb.append(", segments=").append(segments); |
| sb.append(", scores=").append(scores); |
| sb.append(", consents=").append(consents); |
| sb.append(", itemId='").append(itemId).append('\''); |
| sb.append(", itemType='").append(itemType).append('\''); |
| sb.append(", scope='").append(scope).append('\''); |
| sb.append(", version=").append(version); |
| sb.append('}'); |
| return sb.toString(); |
| } |
| } |