UNOMI-32 : Improved profile updates to only merge specified properties. Send event only if profile is modifed. Also merge proeprty types on save.
Project: http://git-wip-us.apache.org/repos/asf/incubator-unomi/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-unomi/commit/ec58db2c Tree: http://git-wip-us.apache.org/repos/asf/incubator-unomi/tree/ec58db2c Diff: http://git-wip-us.apache.org/repos/asf/incubator-unomi/diff/ec58db2c Branch: refs/heads/UNOMI-28-ES-2-X-UPGRADE Commit: ec58db2c9edc75ea5d5acbf2d0bc902a724748bc Parents: b2cb9ee Author: Thomas Draier <dra...@apache.org> Authored: Fri Aug 26 11:41:54 2016 +0200 Committer: Thomas Draier <dra...@apache.org> Committed: Fri Aug 26 11:41:54 2016 +0200 ---------------------------------------------------------------------- .../java/org/apache/unomi/api/PropertyType.java | 16 ++-- .../unomi/api/services/ProfileService.java | 8 ++ .../unomi/rest/ProfileServiceEndPoint.java | 12 +-- .../services/services/ProfileServiceImpl.java | 77 ++++++++++++++++++-- 4 files changed, 94 insertions(+), 19 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/ec58db2c/api/src/main/java/org/apache/unomi/api/PropertyType.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/unomi/api/PropertyType.java b/api/src/main/java/org/apache/unomi/api/PropertyType.java index ab58990..4d5bf6f 100644 --- a/api/src/main/java/org/apache/unomi/api/PropertyType.java +++ b/api/src/main/java/org/apache/unomi/api/PropertyType.java @@ -44,12 +44,12 @@ public class PropertyType extends MetadataItem { private List<NumericRange> numericRanges = new ArrayList<>(); private List<IpRange> ipRanges = new ArrayList<>(); private Set<String> automaticMappingsFrom = new HashSet<>(); - private double rank; + private Double rank; private String mergeStrategy; private Set<Tag> tags = new TreeSet<Tag>(); private Set<String> tagIds = new LinkedHashSet<String>(); - private boolean multivalued; - private boolean protekted = false; + private Boolean multivalued; + private Boolean protekted; /** * Instantiates a new Property type. @@ -210,7 +210,7 @@ public class PropertyType extends MetadataItem { * * @return the rank of this PropertyType for ordering purpose */ - public double getRank() { + public Double getRank() { return rank; } @@ -219,7 +219,7 @@ public class PropertyType extends MetadataItem { * * @param rank the rank of this PropertyType for ordering purpose */ - public void setRank(double rank) { + public void setRank(Double rank) { this.rank = rank; } @@ -300,7 +300,7 @@ public class PropertyType extends MetadataItem { * * @return {@code true} if properties of this type should be multi-valued, {@code false} otherwise */ - public boolean isMultivalued() { + public Boolean isMultivalued() { return multivalued; } @@ -309,7 +309,7 @@ public class PropertyType extends MetadataItem { * * @param multivalued {@code true} if properties of this type should be multi-valued, {@code false} otherwise */ - public void setMultivalued(boolean multivalued) { + public void setMultivalued(Boolean multivalued) { this.multivalued = multivalued; } @@ -320,7 +320,7 @@ public class PropertyType extends MetadataItem { * * @return {@code true} if properties of this type are protected, {@code false} otherwise */ - public boolean isProtected() { + public Boolean isProtected() { return protekted; } http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/ec58db2c/api/src/main/java/org/apache/unomi/api/services/ProfileService.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/unomi/api/services/ProfileService.java b/api/src/main/java/org/apache/unomi/api/services/ProfileService.java index c13036a..a998e5e 100644 --- a/api/src/main/java/org/apache/unomi/api/services/ProfileService.java +++ b/api/src/main/java/org/apache/unomi/api/services/ProfileService.java @@ -106,6 +106,14 @@ public interface ProfileService { Profile save(Profile profile); /** + * Merge the specified profile properties in an existing profile,or save new profile if it does not exist yet + * + * @param profile the profile to be saved + * @return the newly saved profile + */ + boolean saveOrmerge(Profile profile); + + /** * Removes the profile (or persona if the {@code persona} parameter is set to {@code true}) identified by the specified identifier. * * @param profileId the identifier of the profile or persona to delete http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/ec58db2c/rest/src/main/java/org/apache/unomi/rest/ProfileServiceEndPoint.java ---------------------------------------------------------------------- diff --git a/rest/src/main/java/org/apache/unomi/rest/ProfileServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/ProfileServiceEndPoint.java index b806378..038178d 100644 --- a/rest/src/main/java/org/apache/unomi/rest/ProfileServiceEndPoint.java +++ b/rest/src/main/java/org/apache/unomi/rest/ProfileServiceEndPoint.java @@ -193,11 +193,13 @@ public class ProfileServiceEndPoint { @POST @Path("/") public Profile save(Profile profile) { - // TODO: check that the profile was actually updated correctly before sending the event - Event profileUpdated = new Event("profileUpdated", null, profile, null, null, profile, new Date()); - profileUpdated.setPersistent(false); - eventService.send(profileUpdated); - return profileService.save(profile); + if (profileService.saveOrmerge(profile)) { + Event profileUpdated = new Event("profileUpdated", null, profile, null, null, profile, new Date()); + profileUpdated.setPersistent(false); + eventService.send(profileUpdated); + } + + return profile; } /** http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/ec58db2c/services/src/main/java/org/apache/unomi/services/services/ProfileServiceImpl.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/services/ProfileServiceImpl.java b/services/src/main/java/org/apache/unomi/services/services/ProfileServiceImpl.java index 22e6227..aee8832 100644 --- a/services/src/main/java/org/apache/unomi/services/services/ProfileServiceImpl.java +++ b/services/src/main/java/org/apache/unomi/services/services/ProfileServiceImpl.java @@ -17,6 +17,8 @@ package org.apache.unomi.services.services; +import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.lang3.StringUtils; import org.apache.unomi.api.*; import org.apache.unomi.api.conditions.Condition; @@ -278,12 +280,13 @@ public class ProfileServiceImpl implements ProfileService, SynchronousBundleList @Override public boolean setPropertyType(PropertyType property) { - PropertyType previous = null; - if ((previous = persistenceService.load(property.getItemId(), PropertyType.class)) != null) { - previous.getAutomaticMappingsFrom().addAll(property.getAutomaticMappingsFrom()); - property = previous; + PropertyType previousProperty = persistenceService.load(property.getItemId(), PropertyType.class); + if (previousProperty == null) { + return persistenceService.save(property); + } else if (merge(previousProperty, property)) { + return persistenceService.save(previousProperty); } - return persistenceService.save(property); + return false; } @Override @@ -362,7 +365,7 @@ public class ProfileServiceImpl implements ProfileService, SynchronousBundleList // TODO may be moved this in a specific Export Utils Class and improve it to handle date format, ... private void handleExportProperty(StringBuilder sb, Object propertyValue, PropertyType propertyType) { - if (propertyValue instanceof Collection && propertyType != null && propertyType.isMultivalued()) { + if (propertyValue instanceof Collection && propertyType != null && propertyType.isMultivalued() != null && propertyType.isMultivalued() ) { Collection propertyValues = (Collection) propertyValue; Collection encodedValues = new ArrayList(propertyValues.size()); for (Object value : propertyValues) { @@ -397,6 +400,17 @@ public class ProfileServiceImpl implements ProfileService, SynchronousBundleList return persistenceService.load(profile.getItemId(), Profile.class); } + public boolean saveOrmerge(Profile profile) { + Profile previousProfile = persistenceService.load(profile.getItemId(), Profile.class); + if (previousProfile == null) { + return persistenceService.save(profile); + } else if (merge(previousProfile, profile)) { + return persistenceService.save(previousProfile); + } + + return false; + } + public Persona savePersona(Persona profile) { if (persistenceService.load(profile.getItemId(), Persona.class) == null) { Session session = new PersonaSession(UUID.randomUUID().toString(), profile, new Date()); @@ -736,4 +750,55 @@ public class ProfileServiceImpl implements ProfileService, SynchronousBundleList } } + private <T> boolean merge(T target, T object) { + if (object != null) { + try { + Map<String,Object> objectValues = PropertyUtils.describe(object); + Map<String,Object> targetValues = PropertyUtils.describe(target); + if (merge(targetValues, objectValues)) { + BeanUtils.populate(target, targetValues); + return true; + } + } catch (ReflectiveOperationException e) { + logger.error("Cannot merge properties",e); + } + } + return false; + } + + private boolean merge(Map<String,Object> target, Map<String,Object> object) { + boolean changed = false; + for (Map.Entry<String, Object> previousEntry : object.entrySet()) { + if (previousEntry.getValue() != null) { + if (previousEntry.getValue() instanceof Collection) { + Collection currentCollection = (Collection) target.get(previousEntry.getKey()); + if (currentCollection != null) { + if (!currentCollection.containsAll((Collection) previousEntry.getValue())) { + changed |= currentCollection.addAll((Collection) previousEntry.getValue()); + } + } else { + target.put(previousEntry.getKey(), previousEntry.getValue()); + changed = true; + } + } else if (previousEntry.getValue() instanceof Map) { + Map<String,Object> currentMap = (Map) target.get(previousEntry.getKey()); + if (currentMap == null) { + target.put(previousEntry.getKey(), previousEntry.getValue()); + changed = true; + } else { + changed |= merge(currentMap, (Map) previousEntry.getValue()); + } + } else if (previousEntry.getValue().getClass().getPackage().getName().equals("java.lang")) { + if (previousEntry.getValue() != null && !previousEntry.getValue().equals(target.get(previousEntry.getKey()))) { + target.put(previousEntry.getKey(), previousEntry.getValue()); + changed = true; + } + } else { + changed |= merge(target.get(previousEntry.getKey()), previousEntry.getValue()); + } + } + } + return changed; + } + }