http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/conditions/initializers/ComparisonOperatorChoiceListInitializer.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/conditions/initializers/ComparisonOperatorChoiceListInitializer.java b/services/src/main/java/org/apache/unomi/services/conditions/initializers/ComparisonOperatorChoiceListInitializer.java new file mode 100644 index 0000000..3d98548 --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/conditions/initializers/ComparisonOperatorChoiceListInitializer.java @@ -0,0 +1,76 @@ +/* + * 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.conditions.initializers; + +import org.apache.unomi.api.conditions.initializers.ChoiceListInitializer; +import org.apache.unomi.api.conditions.initializers.ChoiceListValue; +import org.apache.unomi.api.conditions.initializers.I18nSupport; +import org.osgi.framework.BundleContext; + +import java.util.ArrayList; +import java.util.List; + +/** + * Initializer for the set of available comparison operators. + */ +public class ComparisonOperatorChoiceListInitializer implements ChoiceListInitializer, I18nSupport { + + private List<ChoiceListValue> operators; + + private BundleContext bundleContext; + + @Override + public List<ChoiceListValue> getValues(Object context) { + return operators; + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + + operators = new ArrayList<>(12); + operators.add(new ComparisonOperatorChoiceListValue("equals", "comparisonOperator.equals", + "string", "integer", "email")); + operators.add(new ComparisonOperatorChoiceListValue("notEquals", "comparisonOperator.notEquals", + "string", "integer", "email")); + operators.add(new ComparisonOperatorChoiceListValue("lessThan", "comparisonOperator.lessThan", "integer", "date")); + operators.add(new ComparisonOperatorChoiceListValue("greaterThan", "comparisonOperator.greaterThan", "integer", "date")); + operators.add(new ComparisonOperatorChoiceListValue("lessThanOrEqualTo", "comparisonOperator.lessThanOrEqualTo", + "integer", "date")); + operators.add(new ComparisonOperatorChoiceListValue("greaterThanOrEqualTo", "comparisonOperator.greaterThanOrEqualTo", + "integer", "date")); + operators.add(new ComparisonOperatorChoiceListValue("between", "comparisonOperator.between", + "integer", "date")); + operators.add(new ComparisonOperatorChoiceListValue("startsWith", "comparisonOperator.startsWith", "string", "email")); + operators.add(new ComparisonOperatorChoiceListValue("endsWith", "comparisonOperator.endsWith", "string", "email")); + operators.add(new ComparisonOperatorChoiceListValue("matchesRegex", "comparisonOperator.matchesRegularExpression", + "string", "email")); + operators.add(new ComparisonOperatorChoiceListValue("contains", "comparisonOperator.contains", "string", "email")); + operators.add(new ComparisonOperatorChoiceListValue("exists", "comparisonOperator.exists")); + operators.add(new ComparisonOperatorChoiceListValue("missing", "comparisonOperator.missing")); + operators.add(new ComparisonOperatorChoiceListValue("in", "comparisonOperator.in", "string", "integer", "email")); + operators.add(new ComparisonOperatorChoiceListValue("notIn", "comparisonOperator.notIn", "string", "integer", "email")); + operators.add(new ComparisonOperatorChoiceListValue("all", "comparisonOperator.all")); + operators.add(new ComparisonOperatorChoiceListValue("isDay", "comparisonOperator.isDay", "date")); + operators.add(new ComparisonOperatorChoiceListValue("isNotDay", "comparisonOperator.isNotDay", "date")); + + for (ChoiceListValue op : operators) { + ((ComparisonOperatorChoiceListValue) op).setPluginId(bundleContext.getBundle().getBundleId()); + } + + } +}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/conditions/initializers/ComparisonOperatorChoiceListValue.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/conditions/initializers/ComparisonOperatorChoiceListValue.java b/services/src/main/java/org/apache/unomi/services/conditions/initializers/ComparisonOperatorChoiceListValue.java new file mode 100644 index 0000000..d2fc050 --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/conditions/initializers/ComparisonOperatorChoiceListValue.java @@ -0,0 +1,89 @@ +/* + * 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.conditions.initializers; + +import org.apache.unomi.api.PluginType; +import org.apache.unomi.api.conditions.initializers.ChoiceListValue; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Choice list value for the comparison operator, which also includes the information about applicable value types for the operator. + * + * @author Sergiy Shyrkov + */ +public class ComparisonOperatorChoiceListValue extends ChoiceListValue implements PluginType { + + private Set<String> appliesTo = Collections.emptySet(); + + private long pluginId; + + /** + * Initializes an instance of this class. + * + * @param id + * the ID of the property + * @param name + * the display name + */ + public ComparisonOperatorChoiceListValue(String id, String name) { + super(id, name); + } + + /** + * Initializes an instance of this class. + * + * @param id + * the ID of the property + * @param name + * the display name + * @param appliesTo + * array of value types this operator applies to; if not specified the operator is applicable for all value types + */ + public ComparisonOperatorChoiceListValue(String id, String name, String... appliesTo) { + this(id, name); + if (appliesTo != null && appliesTo.length > 0) { + this.appliesTo = new HashSet<>(appliesTo.length); + for (String at : appliesTo) { + this.appliesTo.add(at); + } + } + } + + /** + * Returns a set of value types this comparison operator is applicable to. Returns an empty set in case there are no type restrictions, + * i.e. operator can be applied to any value type. + * + * @return a set of value types this comparison operator is applicable to. Returns an empty set in case there are no type restrictions, + * i.e. operator can be applied to any value type + */ + public Set<String> getAppliesTo() { + return appliesTo; + } + + @Override + public long getPluginId() { + return pluginId; + } + + @Override + public void setPluginId(long pluginId) { + this.pluginId = pluginId; + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/conditions/initializers/CountryChoiceListInitializer.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/conditions/initializers/CountryChoiceListInitializer.java b/services/src/main/java/org/apache/unomi/services/conditions/initializers/CountryChoiceListInitializer.java new file mode 100644 index 0000000..c63e406 --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/conditions/initializers/CountryChoiceListInitializer.java @@ -0,0 +1,68 @@ +/* + * 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.conditions.initializers; + +import org.apache.unomi.api.conditions.initializers.ChoiceListInitializer; +import org.apache.unomi.api.conditions.initializers.ChoiceListValue; + +import java.util.*; + +/** + * Initializer for the set of known countries. + * + * @author Sergiy Shyrkov + */ +public class CountryChoiceListInitializer implements ChoiceListInitializer { + + private static final Comparator<ChoiceListValue> NAME_COMPARATOR = new Comparator<ChoiceListValue>() { + @Override + public int compare(ChoiceListValue o1, ChoiceListValue o2) { + // we do not need to deal with null values, so make it straight + return o1.getName().compareTo(o2.getName()); + } + }; + + private Map<String, Locale> countryLocales; + + private Map<String, Locale> getContryLocales() { + if (countryLocales == null) { + Map<String, Locale> l = new HashMap<>(); + String[] countryCodes = Locale.getISOCountries(); + for (String code : countryCodes) { + l.put(code, new Locale("en", code)); + } + countryLocales = l; + } + + return countryLocales; + } + + @Override + public List<ChoiceListValue> getValues(Object context) { + Locale locale = context instanceof Locale ? (Locale) context : Locale.ENGLISH; + + Map<String, Locale> locales = getContryLocales(); + List<ChoiceListValue> options = new ArrayList<ChoiceListValue>(locales.size()); + for (Map.Entry<String, Locale> entry : locales.entrySet()) { + options.add(new ChoiceListValue(entry.getKey(), entry.getValue().getDisplayCountry(locale))); + } + Collections.sort(options, NAME_COMPARATOR); + + return options; + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/conditions/initializers/EventPropertyChoiceListInitializer.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/conditions/initializers/EventPropertyChoiceListInitializer.java b/services/src/main/java/org/apache/unomi/services/conditions/initializers/EventPropertyChoiceListInitializer.java new file mode 100644 index 0000000..0a8a04e --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/conditions/initializers/EventPropertyChoiceListInitializer.java @@ -0,0 +1,47 @@ +/* + * 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.conditions.initializers; + +import org.apache.unomi.api.EventProperty; +import org.apache.unomi.api.conditions.initializers.ChoiceListInitializer; +import org.apache.unomi.api.conditions.initializers.ChoiceListValue; +import org.apache.unomi.api.services.EventService; + +import java.util.ArrayList; +import java.util.List; + +/** + * Initializer for the set of available event properties. + */ +public class EventPropertyChoiceListInitializer implements ChoiceListInitializer { + + EventService eventService; + + public List<ChoiceListValue> getValues(Object context) { + List<EventProperty> eventProperties = eventService.getEventProperties(); + List<ChoiceListValue> choiceListValues = new ArrayList<>(eventProperties.size()); + for (EventProperty eventProperty : eventProperties) { + choiceListValues.add(new PropertyTypeChoiceListValue(eventProperty.getId(), eventProperty.getId(), eventProperty.getValueType())); + } + return choiceListValues; + } + + public void setEventService(EventService eventService) { + this.eventService = eventService; + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/conditions/initializers/EventTypeIdChoiceListInitializer.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/conditions/initializers/EventTypeIdChoiceListInitializer.java b/services/src/main/java/org/apache/unomi/services/conditions/initializers/EventTypeIdChoiceListInitializer.java new file mode 100644 index 0000000..005d19e --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/conditions/initializers/EventTypeIdChoiceListInitializer.java @@ -0,0 +1,46 @@ +/* + * 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.conditions.initializers; + +import org.apache.unomi.api.conditions.initializers.ChoiceListInitializer; +import org.apache.unomi.api.conditions.initializers.ChoiceListValue; +import org.apache.unomi.api.conditions.initializers.I18nSupport; +import org.apache.unomi.api.services.EventService; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class EventTypeIdChoiceListInitializer implements ChoiceListInitializer, I18nSupport { + + EventService eventService; + + public void setEventService(EventService eventService) { + this.eventService = eventService; + } + + public List<ChoiceListValue> getValues(Object context) { + List<ChoiceListValue> choiceListValues = new ArrayList<ChoiceListValue>(); + Set<String> eventTypeIds = eventService.getEventTypeIds(); + for (String eventProperty : eventTypeIds) { + String resourceKey = "eventType." + eventProperty; + choiceListValues.add(new ChoiceListValue(eventProperty, resourceKey)); + } + return choiceListValues; + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/conditions/initializers/GoalsChoiceListInitializer.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/conditions/initializers/GoalsChoiceListInitializer.java b/services/src/main/java/org/apache/unomi/services/conditions/initializers/GoalsChoiceListInitializer.java new file mode 100644 index 0000000..5f9a309 --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/conditions/initializers/GoalsChoiceListInitializer.java @@ -0,0 +1,44 @@ +/* + * 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.conditions.initializers; + +import org.apache.unomi.api.Metadata; +import org.apache.unomi.api.conditions.initializers.ChoiceListInitializer; +import org.apache.unomi.api.conditions.initializers.ChoiceListValue; +import org.apache.unomi.api.services.GoalsService; + +import java.util.ArrayList; +import java.util.List; + +public class GoalsChoiceListInitializer implements ChoiceListInitializer { + + private GoalsService goalsService; + + public void setGoalsService(GoalsService goalsService) { + this.goalsService = goalsService; + } + + @Override + public List<ChoiceListValue> getValues(Object context) { + List<ChoiceListValue> r = new ArrayList<>(); + for (Metadata metadata : goalsService.getGoalMetadatas()) { + r.add(new ChoiceListValue(metadata.getId(), metadata.getName())); + } + return r; + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/conditions/initializers/PropertyTypeChoiceListValue.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/conditions/initializers/PropertyTypeChoiceListValue.java b/services/src/main/java/org/apache/unomi/services/conditions/initializers/PropertyTypeChoiceListValue.java new file mode 100644 index 0000000..97809f5 --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/conditions/initializers/PropertyTypeChoiceListValue.java @@ -0,0 +1,135 @@ +/* + * 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.conditions.initializers; + +import org.apache.unomi.api.PluginType; +import org.apache.unomi.api.conditions.initializers.ChoiceListValue; + +/** + * Choice list value for the properties, which also includes the required value type of the property. + * + * @author Sergiy Shyrkov + */ +public class PropertyTypeChoiceListValue extends ChoiceListValue implements PluginType { + + private boolean multivalued; + + private String valueType = "string"; + + private long pluginId; + + /** + * Initializes an instance of this class. + * + * @param id + * the ID of the property + * @param name + * the display name + * @param valueType + * the required property value type + */ + public PropertyTypeChoiceListValue(String id, String name, String valueType) { + this(id, name, valueType, false); + } + + /** + * Initializes an instance of this class. + * + * @param id + * the ID of the property + * @param name + * the display name + * @param valueType + * the required property value type + * @param multivalued + * {@code true} if the property supports multiple values; {@code false} - in case it is a single value property + */ + public PropertyTypeChoiceListValue(String id, String name, String valueType, boolean multivalued) { + super(id, name); + this.valueType = valueType; + this.multivalued = multivalued; + } + + /** + * Initializes an instance of this class. + * + * @param id + * the ID of the property + * @param name + * the display name + * @param valueType + * the required property value type + * @param multivalued + * {@code true} if the property supports multiple values; {@code false} - in case it is a single value property + * @param pluginId + * the PropertyType PluginId to retrieve bundle + */ + public PropertyTypeChoiceListValue(String id, String name, String valueType, boolean multivalued, long pluginId) { + super(id, name); + this.multivalued = multivalued; + this.valueType = valueType; + this.pluginId = pluginId; + } + + /** + * Returns the required property value type. + * + * @return the required property value type + */ + public String getValueType() { + return valueType; + } + + /** + * Sets the required property value type. + * + * @param valueType + * the required value type for this property + */ + public void setValueType(String valueType) { + this.valueType = valueType; + } + + /** + * Indicates if the property supports multiple values. + * + * @return {@code true} if the property supports multiple values; {@code false} - in case it is a single value property + */ + public boolean isMultivalued() { + return multivalued; + } + + /** + * Sets the indicator if the property supports multiple values. + * + * @param multivalued + * {@code true} if the property supports multiple values; {@code false} - in case it is a single value property + */ + public void setMultivalued(boolean multivalued) { + this.multivalued = multivalued; + } + + @Override + public long getPluginId() { + return pluginId; + } + + @Override + public void setPluginId(long pluginId) { + this.pluginId = pluginId; + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/conditions/initializers/SegmentsChoiceListInitializer.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/conditions/initializers/SegmentsChoiceListInitializer.java b/services/src/main/java/org/apache/unomi/services/conditions/initializers/SegmentsChoiceListInitializer.java new file mode 100644 index 0000000..4b3a9cb --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/conditions/initializers/SegmentsChoiceListInitializer.java @@ -0,0 +1,47 @@ +/* + * 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.conditions.initializers; + +import org.apache.unomi.api.Metadata; +import org.apache.unomi.api.conditions.initializers.ChoiceListInitializer; +import org.apache.unomi.api.conditions.initializers.ChoiceListValue; +import org.apache.unomi.api.services.SegmentService; + +import java.util.ArrayList; +import java.util.List; + +/** + * Initializer for segment choice list. + */ +public class SegmentsChoiceListInitializer implements ChoiceListInitializer { + + SegmentService segmentService; + + public void setSegmentService(SegmentService segmentService) { + this.segmentService = segmentService; + } + + public List<ChoiceListValue> getValues(Object context) { + List<ChoiceListValue> choiceListValues = new ArrayList<ChoiceListValue>(); + List<Metadata> profileProperties = segmentService.getSegmentMetadatas(0, 50, null).getList(); + for (Metadata profileProperty : profileProperties) { + choiceListValues.add(new ChoiceListValue(profileProperty.getId(), profileProperty.getName())); + } + return choiceListValues; + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/mergers/AddPropertyMergeStrategyExecutor.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/mergers/AddPropertyMergeStrategyExecutor.java b/services/src/main/java/org/apache/unomi/services/mergers/AddPropertyMergeStrategyExecutor.java new file mode 100644 index 0000000..3231567 --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/mergers/AddPropertyMergeStrategyExecutor.java @@ -0,0 +1,78 @@ +/* + * 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.mergers; + +import org.apache.unomi.api.Profile; +import org.apache.unomi.api.PropertyMergeStrategyExecutor; +import org.apache.unomi.api.PropertyType; + +import java.util.List; + +public class AddPropertyMergeStrategyExecutor implements PropertyMergeStrategyExecutor { + public boolean mergeProperty(String propertyName, PropertyType propertyType, List<Profile> profilesToMerge, Profile targetProfile) { + Object targetPropertyValue = targetProfile.getProperty(propertyName); + Object result = targetPropertyValue; + if (result == null) { + if (propertyType.getValueTypeId() != null) { + if (propertyType.getValueTypeId().equals("integer")) { + result = new Integer(0); + } else if (propertyType.getValueTypeId().equals("long")) { + result = new Long(0); + } else if (propertyType.getValueTypeId().equals("double")) { + result = new Double(0.0); + } else if (propertyType.getValueTypeId().equals("float")) { + result = new Float(0.0); + } else { + result = new Long(0); + } + } else { + result = new Long(0); + } + } + + for (Profile profileToMerge : profilesToMerge) { + + Object property = profileToMerge.getProperty(propertyName); + if (property == null) { + continue; + } + + if (propertyType != null) { + if (propertyType.getValueTypeId().equals("integer") || (property instanceof Integer)) { + result = (Integer) result + (Integer) property; + } else if (propertyType.getValueTypeId().equals("long") || (property instanceof Long)) { + result = (Long) result + (Long) property; + } else if (propertyType.getValueTypeId().equals("double") || (property instanceof Double)) { + result = (Double) result + (Double) property; + } else if (propertyType.getValueTypeId().equals("float") || (property instanceof Float)) { + result = (Float) result + (Float) property; + } else { + result = (Long) result + Long.parseLong(property.toString()); + } + } else { + result = (Long) result + Long.parseLong(property.toString()); + } + + } + if (targetPropertyValue == null || !targetPropertyValue.equals(result)) { + targetProfile.setProperty(propertyName, result); + return true; + } + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/mergers/DefaultPropertyMergeStrategyExecutor.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/mergers/DefaultPropertyMergeStrategyExecutor.java b/services/src/main/java/org/apache/unomi/services/mergers/DefaultPropertyMergeStrategyExecutor.java new file mode 100644 index 0000000..e77f7e3 --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/mergers/DefaultPropertyMergeStrategyExecutor.java @@ -0,0 +1,38 @@ +/* + * 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.mergers; + +import org.apache.unomi.api.Profile; +import org.apache.unomi.api.PropertyMergeStrategyExecutor; +import org.apache.unomi.api.PropertyType; + +import java.util.List; + +public class DefaultPropertyMergeStrategyExecutor implements PropertyMergeStrategyExecutor { + public boolean mergeProperty(String propertyName, PropertyType propertyType, List<Profile> profilesToMerge, Profile targetProfile) { + boolean modified = false; + for (Profile profileToMerge : profilesToMerge) { + if (profileToMerge.getProperty(propertyName) != null && + profileToMerge.getProperty(propertyName).toString().length() > 0) { + targetProfile.setProperty(propertyName, profileToMerge.getProperty(propertyName)); + modified = true; + } + } + return modified; + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/mergers/MostRecentPropertyMergeStrategyExecutor.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/mergers/MostRecentPropertyMergeStrategyExecutor.java b/services/src/main/java/org/apache/unomi/services/mergers/MostRecentPropertyMergeStrategyExecutor.java new file mode 100644 index 0000000..451da2c --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/mergers/MostRecentPropertyMergeStrategyExecutor.java @@ -0,0 +1,40 @@ +/* + * 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.mergers; + +import org.apache.unomi.api.Profile; +import org.apache.unomi.api.PropertyMergeStrategyExecutor; +import org.apache.unomi.api.PropertyType; + +import java.util.List; + +public class MostRecentPropertyMergeStrategyExecutor implements PropertyMergeStrategyExecutor { + public boolean mergeProperty(String propertyName, PropertyType propertyType, List<Profile> profilesToMerge, Profile targetProfile) { + Object result = null; + int i = profilesToMerge.size() - 1; + while (result == null && i >=0) { + result = profilesToMerge.get(i).getProperty(propertyName); + i--; + } + if (result != null && (targetProfile.getProperty(propertyName) == null || !result.equals(targetProfile.getProperty(propertyName)))) { + targetProfile.setProperty(propertyName, result); + return true; + } + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/mergers/NonEmptyPropertyMergeStrategyExecutor.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/mergers/NonEmptyPropertyMergeStrategyExecutor.java b/services/src/main/java/org/apache/unomi/services/mergers/NonEmptyPropertyMergeStrategyExecutor.java new file mode 100644 index 0000000..44c026c --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/mergers/NonEmptyPropertyMergeStrategyExecutor.java @@ -0,0 +1,30 @@ +/* + * 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.mergers; + +import org.apache.unomi.api.Profile; +import org.apache.unomi.api.PropertyMergeStrategyExecutor; +import org.apache.unomi.api.PropertyType; + +import java.util.List; + +public class NonEmptyPropertyMergeStrategyExecutor implements PropertyMergeStrategyExecutor { + public boolean mergeProperty(String propertyName, PropertyType propertyType, List<Profile> profilesToMerge, Profile targetProfile) { + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/mergers/OldestPropertyMergeStrategyExecutor.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/mergers/OldestPropertyMergeStrategyExecutor.java b/services/src/main/java/org/apache/unomi/services/mergers/OldestPropertyMergeStrategyExecutor.java new file mode 100644 index 0000000..0d877fa --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/mergers/OldestPropertyMergeStrategyExecutor.java @@ -0,0 +1,30 @@ +/* + * 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.mergers; + +import org.apache.unomi.api.Profile; +import org.apache.unomi.api.PropertyMergeStrategyExecutor; +import org.apache.unomi.api.PropertyType; + +import java.util.List; + +public class OldestPropertyMergeStrategyExecutor implements PropertyMergeStrategyExecutor { + public boolean mergeProperty(String propertyName, PropertyType propertyType, List<Profile> profilesToMerge, Profile targetProfile) { + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/services/DefinitionsServiceImpl.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/services/DefinitionsServiceImpl.java b/services/src/main/java/org/apache/unomi/services/services/DefinitionsServiceImpl.java new file mode 100644 index 0000000..f4b39c2 --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/services/DefinitionsServiceImpl.java @@ -0,0 +1,456 @@ +/* + * 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.services; + +import org.apache.unomi.api.PluginType; +import org.apache.unomi.api.PropertyMergeStrategyType; +import org.apache.unomi.api.Tag; +import org.apache.unomi.api.ValueType; +import org.apache.unomi.api.actions.ActionType; +import org.apache.unomi.api.conditions.Condition; +import org.apache.unomi.api.conditions.ConditionType; +import org.apache.unomi.api.services.DefinitionsService; +import org.apache.unomi.persistence.spi.CustomObjectMapper; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.SynchronousBundleListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.URL; +import java.util.*; + +public class DefinitionsServiceImpl implements DefinitionsService, SynchronousBundleListener { + + private static final Logger logger = LoggerFactory.getLogger(DefinitionsServiceImpl.class.getName()); + + private Map<String, Tag> tags = new HashMap<>(); + private Set<Tag> rootTags = new LinkedHashSet<>(); + private Map<String, ConditionType> conditionTypeById = new HashMap<>(); + private Map<String, ActionType> actionTypeById = new HashMap<>(); + private Map<String, ValueType> valueTypeById = new HashMap<>(); + private Map<Tag, Set<ConditionType>> conditionTypeByTag = new HashMap<>(); + private Map<Tag, Set<ActionType>> actionTypeByTag = new HashMap<>(); + private Map<Tag, Set<ValueType>> valueTypeByTag = new HashMap<>(); + private Map<Long, List<PluginType>> pluginTypes = new HashMap<>(); + private Map<String, PropertyMergeStrategyType> propertyMergeStrategyTypeById = new HashMap<>(); + + private BundleContext bundleContext; + public DefinitionsServiceImpl() { + logger.info("Instantiating definitions service..."); + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void postConstruct() { + logger.debug("postConstruct {" + bundleContext.getBundle() + "}"); + + processBundleStartup(bundleContext); + + // process already started bundles + for (Bundle bundle : bundleContext.getBundles()) { + if (bundle.getBundleContext() != null) { + processBundleStartup(bundle.getBundleContext()); + } + } + + bundleContext.addBundleListener(this); + } + + private void processBundleStartup(BundleContext bundleContext) { + if (bundleContext == null) { + return; + } + + pluginTypes.put(bundleContext.getBundle().getBundleId(), new ArrayList<PluginType>()); + + loadPredefinedTags(bundleContext); + + loadPredefinedConditionTypes(bundleContext); + loadPredefinedActionTypes(bundleContext); + loadPredefinedValueTypes(bundleContext); + loadPredefinedPropertyMergeStrategies(bundleContext); + + } + + private void processBundleStop(BundleContext bundleContext) { + if (bundleContext == null) { + return; + } + List<PluginType> types = pluginTypes.get(bundleContext.getBundle().getBundleId()); + if (types != null) { + for (PluginType type : types) { + if (type instanceof ActionType) { + ActionType actionType = (ActionType) type; + actionTypeById.remove(actionType.getId()); + for (Tag tag : actionType.getTags()) { + actionTypeByTag.get(tag).remove(actionType); + } + } else if (type instanceof ConditionType) { + ConditionType conditionType = (ConditionType) type; + conditionTypeById.remove(conditionType.getId()); + for (Tag tag : conditionType.getTags()) { + conditionTypeByTag.get(tag).remove(conditionType); + } + } else if (type instanceof ValueType) { + ValueType valueType = (ValueType) type; + valueTypeById.remove(valueType.getId()); + for (Tag tag : valueType.getTags()) { + valueTypeByTag.get(tag).remove(valueType); + } + } + } + } + } + + + public void preDestroy() { + bundleContext.removeBundleListener(this); + } + + private void loadPredefinedTags(BundleContext bundleContext) { + Enumeration<URL> predefinedTagEntries = bundleContext.getBundle().findEntries("META-INF/cxs/tags", "*.json", true); + if (predefinedTagEntries == null) { + return; + } + while (predefinedTagEntries.hasMoreElements()) { + URL predefinedTagURL = predefinedTagEntries.nextElement(); + logger.debug("Found predefined tags at " + predefinedTagURL + ", loading... "); + + try { + Tag tag = CustomObjectMapper.getObjectMapper().readValue(predefinedTagURL, Tag.class); + tag.setPluginId(bundleContext.getBundle().getBundleId()); + tags.put(tag.getId(), tag); + } catch (IOException e) { + logger.error("Error while loading segment definition " + predefinedTagEntries, e); + } + } + + // now let's resolve all the children. + for (Tag tag : tags.values()) { + if (tag.getParentId() != null && tag.getParentId().length() > 0) { + Tag parentTag = tags.get(tag.getParentId()); + if (parentTag != null) { + parentTag.getSubTags().add(tag); + } + } else { + rootTags.add(tag); + } + } + } + + private void loadPredefinedConditionTypes(BundleContext bundleContext) { + Enumeration<URL> predefinedConditionEntries = bundleContext.getBundle().findEntries("META-INF/cxs/conditions", "*.json", true); + if (predefinedConditionEntries == null) { + return; + } + ArrayList<PluginType> pluginTypeArrayList = (ArrayList<PluginType>) pluginTypes.get(bundleContext.getBundle().getBundleId()); + while (predefinedConditionEntries.hasMoreElements()) { + URL predefinedConditionURL = predefinedConditionEntries.nextElement(); + logger.debug("Found predefined conditions at " + predefinedConditionURL + ", loading... "); + + try { + ConditionType conditionType = CustomObjectMapper.getObjectMapper().readValue(predefinedConditionURL, ConditionType.class); + conditionType.setPluginId(bundleContext.getBundle().getBundleId()); + conditionTypeById.put(conditionType.getId(), conditionType); + pluginTypeArrayList.add(conditionType); + for (String tagId : conditionType.getTagIDs()) { + Tag tag = tags.get(tagId); + if (tag != null) { + conditionType.getTags().add(tag); + Set<ConditionType> conditionTypes = conditionTypeByTag.get(tag); + if (conditionTypes == null) { + conditionTypes = new LinkedHashSet<ConditionType>(); + } + conditionTypes.add(conditionType); + conditionTypeByTag.put(tag, conditionTypes); + } else { + // we found a tag that is not defined, we will define it automatically + logger.warn("Unknown tag " + tagId + " used in condition definition " + predefinedConditionURL); + } + } + } catch (Exception e) { + logger.error("Error while loading condition definition " + predefinedConditionURL, e); + } + } + for (ConditionType type : conditionTypeById.values()) { + if (type.getParentCondition() != null) { + ParserHelper.resolveConditionType(this, type.getParentCondition()); + } + } + } + + private void loadPredefinedActionTypes(BundleContext bundleContext) { + Enumeration<URL> predefinedActionsEntries = bundleContext.getBundle().findEntries("META-INF/cxs/actions", "*.json", true); + if (predefinedActionsEntries == null) { + return; + } + ArrayList<PluginType> pluginTypeArrayList = (ArrayList<PluginType>) pluginTypes.get(bundleContext.getBundle().getBundleId()); + while (predefinedActionsEntries.hasMoreElements()) { + URL predefinedActionURL = predefinedActionsEntries.nextElement(); + logger.debug("Found predefined action at " + predefinedActionURL + ", loading... "); + + try { + ActionType actionType = CustomObjectMapper.getObjectMapper().readValue(predefinedActionURL, ActionType.class); + actionType.setPluginId(bundleContext.getBundle().getBundleId()); + actionTypeById.put(actionType.getId(), actionType); + pluginTypeArrayList.add(actionType); + for (String tagId : actionType.getTagIds()) { + Tag tag = tags.get(tagId); + if (tag != null) { + actionType.getTags().add(tag); + Set<ActionType> actionTypes = actionTypeByTag.get(tag); + if (actionTypes == null) { + actionTypes = new LinkedHashSet<>(); + } + actionTypes.add(actionType); + actionTypeByTag.put(tag, actionTypes); + } else { + // we found a tag that is not defined, we will define it automatically + logger.warn("Unknown tag " + tagId + " used in action definition " + predefinedActionURL); + } + } + } catch (Exception e) { + logger.error("Error while loading action definition " + predefinedActionURL, e); + } + } + + } + + private void loadPredefinedValueTypes(BundleContext bundleContext) { + Enumeration<URL> predefinedPropertiesEntries = bundleContext.getBundle().findEntries("META-INF/cxs/values", "*.json", true); + if (predefinedPropertiesEntries == null) { + return; + } + ArrayList<PluginType> pluginTypeArrayList = (ArrayList<PluginType>) pluginTypes.get(bundleContext.getBundle().getBundleId()); + while (predefinedPropertiesEntries.hasMoreElements()) { + URL predefinedPropertyURL = predefinedPropertiesEntries.nextElement(); + logger.debug("Found predefined property type at " + predefinedPropertyURL + ", loading... "); + + try { + ValueType valueType = CustomObjectMapper.getObjectMapper().readValue(predefinedPropertyURL, ValueType.class); + valueType.setPluginId(bundleContext.getBundle().getBundleId()); + valueTypeById.put(valueType.getId(), valueType); + pluginTypeArrayList.add(valueType); + for (String tagId : valueType.getTagIds()) { + Tag tag = tags.get(tagId); + if (tag != null) { + valueType.getTags().add(tag); + Set<ValueType> valueTypes = valueTypeByTag.get(tag); + if (valueTypes == null) { + valueTypes = new LinkedHashSet<ValueType>(); + } + valueTypes.add(valueType); + valueTypeByTag.put(tag, valueTypes); + } else { + // we found a tag that is not defined, we will define it automatically + logger.warn("Unknown tag " + tagId + " used in property type definition " + predefinedPropertyURL); + } + } + } catch (Exception e) { + logger.error("Error while loading property type definition " + predefinedPropertyURL, e); + } + } + + } + + public Set<Tag> getAllTags() { + return new HashSet<Tag>(tags.values()); + } + + public Set<Tag> getRootTags() { + return rootTags; + } + + public Tag getTag(String tagId) { + Tag completeTag = tags.get(tagId); + if (completeTag == null) { + return null; + } + return completeTag; + } + + public Collection<ConditionType> getAllConditionTypes() { + return conditionTypeById.values(); + } + + public Map<Long, List<PluginType>> getTypesByPlugin() { + return pluginTypes; + } + + public Set<ConditionType> getConditionTypesByTag(Tag tag, boolean includeFromSubtags) { + Set<ConditionType> conditionTypes = new LinkedHashSet<ConditionType>(); + Set<ConditionType> directConditionTypes = conditionTypeByTag.get(tag); + if (directConditionTypes != null) { + conditionTypes.addAll(directConditionTypes); + } + if (includeFromSubtags) { + for (Tag subTag : tag.getSubTags()) { + Set<ConditionType> childConditionTypes = getConditionTypesByTag(subTag, true); + conditionTypes.addAll(childConditionTypes); + } + } + return conditionTypes; + } + + public ConditionType getConditionType(String id) { + return conditionTypeById.get(id); + } + + public Collection<ActionType> getAllActionTypes() { + return actionTypeById.values(); + } + + public Set<ActionType> getActionTypeByTag(Tag tag, boolean includeFromSubtags) { + Set<ActionType> actionTypes = new LinkedHashSet<ActionType>(); + Set<ActionType> directActionTypes = actionTypeByTag.get(tag); + if (directActionTypes != null) { + actionTypes.addAll(directActionTypes); + } + if (includeFromSubtags) { + for (Tag subTag : tag.getSubTags()) { + Set<ActionType> childActionTypes = getActionTypeByTag(subTag, true); + actionTypes.addAll(childActionTypes); + } + } + return actionTypes; + } + + public ActionType getActionType(String id) { + return actionTypeById.get(id); + } + + public Collection<ValueType> getAllValueTypes() { + return valueTypeById.values(); + } + + public Set<ValueType> getValueTypeByTag(Tag tag, boolean includeFromSubtags) { + Set<ValueType> valueTypes = new LinkedHashSet<ValueType>(); + Set<ValueType> directValueTypes = valueTypeByTag.get(tag); + if (directValueTypes != null) { + valueTypes.addAll(directValueTypes); + } + if (includeFromSubtags) { + for (Tag subTag : tag.getSubTags()) { + Set<ValueType> childValueTypes = getValueTypeByTag(subTag, true); + valueTypes.addAll(childValueTypes); + } + } + return valueTypes; + } + + public ValueType getValueType(String id) { + return valueTypeById.get(id); + } + + public void bundleChanged(BundleEvent event) { + switch (event.getType()) { + case BundleEvent.STARTED: + processBundleStartup(event.getBundle().getBundleContext()); + break; + case BundleEvent.STOPPING: + processBundleStop(event.getBundle().getBundleContext()); + break; + } + } + + private void loadPredefinedPropertyMergeStrategies(BundleContext bundleContext) { + Enumeration<URL> predefinedPropertyMergeStrategyEntries = bundleContext.getBundle().findEntries("META-INF/cxs/mergers", "*.json", true); + if (predefinedPropertyMergeStrategyEntries == null) { + return; + } + ArrayList<PluginType> pluginTypeArrayList = (ArrayList<PluginType>) pluginTypes.get(bundleContext.getBundle().getBundleId()); + while (predefinedPropertyMergeStrategyEntries.hasMoreElements()) { + URL predefinedPropertyMergeStrategyURL = predefinedPropertyMergeStrategyEntries.nextElement(); + logger.debug("Found predefined property merge strategy type at " + predefinedPropertyMergeStrategyURL + ", loading... "); + + try { + PropertyMergeStrategyType propertyMergeStrategyType = CustomObjectMapper.getObjectMapper().readValue(predefinedPropertyMergeStrategyURL, PropertyMergeStrategyType.class); + propertyMergeStrategyType.setPluginId(bundleContext.getBundle().getBundleId()); + propertyMergeStrategyTypeById.put(propertyMergeStrategyType.getId(), propertyMergeStrategyType); + pluginTypeArrayList.add(propertyMergeStrategyType); + } catch (Exception e) { + logger.error("Error while loading property type definition " + predefinedPropertyMergeStrategyURL, e); + } + } + + } + + public PropertyMergeStrategyType getPropertyMergeStrategyType(String id) { + return propertyMergeStrategyTypeById.get(id); + } + + public Set<Condition> extractConditionsByType(Condition rootCondition, String typeId) { + if (rootCondition.containsParameter("subConditions")) { + @SuppressWarnings("unchecked") + List<Condition> subConditions = (List<Condition>) rootCondition.getParameter("subConditions"); + Set<Condition> matchingConditions = new HashSet<>(); + for (Condition condition : subConditions) { + matchingConditions.addAll(extractConditionsByType(condition, typeId)); + } + return matchingConditions; + } else if (rootCondition.getConditionTypeId() != null && rootCondition.getConditionTypeId().equals(typeId)) { + return Collections.singleton(rootCondition); + } else { + return Collections.emptySet(); + } + } + + public Condition extractConditionByTag(Condition rootCondition, String tagId) { + if (rootCondition.containsParameter("subConditions")) { + @SuppressWarnings("unchecked") + List<Condition> subConditions = (List<Condition>) rootCondition.getParameter("subConditions"); + List<Condition> matchingConditions = new ArrayList<Condition>(); + for (Condition condition : subConditions) { + Condition c = extractConditionByTag(condition, tagId); + if (c != null) { + matchingConditions.add(c); + } + } + if (matchingConditions.size() == 0) { + return null; + } else if (matchingConditions.equals(subConditions)) { + return rootCondition; + } else if (rootCondition.getConditionTypeId().equals("booleanCondition") && "and".equals(rootCondition.getParameter("operator"))) { + if (matchingConditions.size() == 1) { + return matchingConditions.get(0); + } else { + Condition res = new Condition(); + res.setConditionType(getConditionType("booleanCondition")); + res.setParameter("operator", "and"); + res.setParameter("subConditions", matchingConditions); + return res; + } + } + throw new IllegalArgumentException(); + } else if (rootCondition.getConditionType() != null && rootCondition.getConditionType().getTagIDs().contains(tagId)) { + return rootCondition; + } else { + return null; + } + } + + @Override + public boolean resolveConditionType(Condition rootCondition) { + return ParserHelper.resolveConditionType(this, rootCondition); + } +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/services/EventServiceImpl.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/services/EventServiceImpl.java b/services/src/main/java/org/apache/unomi/services/services/EventServiceImpl.java new file mode 100644 index 0000000..4b207d9 --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/services/EventServiceImpl.java @@ -0,0 +1,223 @@ +/* + * 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.services; + +import org.apache.commons.lang3.StringUtils; +import org.apache.unomi.api.*; +import org.apache.unomi.api.actions.ActionPostExecutor; +import org.apache.unomi.api.conditions.Condition; +import org.apache.unomi.api.services.DefinitionsService; +import org.apache.unomi.api.services.EventListenerService; +import org.apache.unomi.api.services.EventService; +import org.apache.unomi.api.services.ProfileService; +import org.apache.unomi.persistence.spi.PersistenceService; +import org.apache.unomi.persistence.spi.aggregate.TermsAggregate; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; + +import java.util.*; + +public class EventServiceImpl implements EventService { + + private List<EventListenerService> eventListeners = new ArrayList<EventListenerService>(); + + private PersistenceService persistenceService; + + private ProfileService profileService; + + private DefinitionsService definitionsService; + + private BundleContext bundleContext; + + private Set<String> predefinedEventTypeIds = new LinkedHashSet<String>(); + + public void setPredefinedEventTypeIds(Set<String> predefinedEventTypeIds) { + this.predefinedEventTypeIds = predefinedEventTypeIds; + } + + public void setPersistenceService(PersistenceService persistenceService) { + this.persistenceService = persistenceService; + } + + public void setProfileService(ProfileService profileService) { + this.profileService = profileService; + } + + public void setDefinitionsService(DefinitionsService definitionsService) { + this.definitionsService = definitionsService; + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public int send(Event event) { + if (event.isPersistent()) { + persistenceService.save(event); + } + + int changes = NO_CHANGE; + + Profile profile = event.getProfile(); + final Session session = event.getSession(); + if (event.isPersistent() && session != null) { + session.setLastEventDate(event.getTimeStamp()); + } + + if (profile != null) { + for (EventListenerService eventListenerService : eventListeners) { + if (eventListenerService.canHandle(event)) { + changes |= eventListenerService.onEvent(event); + } + } + // At the end of the processing event execute the post executor actions + for (ActionPostExecutor actionPostExecutor : event.getActionPostExecutors()) { + changes |= actionPostExecutor.execute() ? changes : NO_CHANGE; + } + + if ((changes & PROFILE_UPDATED) == PROFILE_UPDATED) { + Event profileUpdated = new Event("profileUpdated", session, profile, event.getScope(), event.getSource(), profile, event.getTimeStamp()); + profileUpdated.setPersistent(false); + profileUpdated.getAttributes().putAll(event.getAttributes()); + changes |= send(profileUpdated); + if (session != null) { + changes |= SESSION_UPDATED; + session.setProfile(profile); + } + } + } + return changes; + } + + @Override + public List<EventProperty> getEventProperties() { + Map<String, Map<String, Object>> mappings = persistenceService.getMapping(Event.ITEM_TYPE); + List<EventProperty> props = new ArrayList<>(mappings.size()); + getEventProperties(mappings, props, ""); + return props; + } + + @SuppressWarnings("unchecked") + private void getEventProperties(Map<String, Map<String, Object>> mappings, List<EventProperty> props, String prefix) { + for (Map.Entry<String, Map<String, Object>> e : mappings.entrySet()) { + if (e.getValue().get("properties") != null) { + getEventProperties((Map<String, Map<String, Object>>) e.getValue().get("properties"), props, prefix + e.getKey() + "."); + } else { + props.add(new EventProperty(prefix + e.getKey(), (String) e.getValue().get("type"))); + } + } + } + + public Set<String> getEventTypeIds() { + Map<String, Long> dynamicEventTypeIds = persistenceService.aggregateQuery(null, new TermsAggregate("eventType"), Event.ITEM_TYPE); + Set<String> eventTypeIds = new LinkedHashSet<String>(predefinedEventTypeIds); + eventTypeIds.addAll(dynamicEventTypeIds.keySet()); + return eventTypeIds; + } + + @Override + public PartialList<Event> searchEvents(Condition condition, int offset, int size) { + return persistenceService.query(condition, "timeStamp", Event.class, offset, size); + } + + @Override + public PartialList<Event> searchEvents(String sessionId, String[] eventTypes, String query, int offset, int size, String sortBy) { + List<Condition> conditions = new ArrayList<Condition>(); + + Condition condition = new Condition(definitionsService.getConditionType("eventPropertyCondition")); + condition.setParameter("propertyName", "sessionId"); + condition.setParameter("propertyValue", sessionId); + condition.setParameter("comparisonOperator", "equals"); + conditions.add(condition); + + condition = new Condition(definitionsService.getConditionType("booleanCondition")); + condition.setParameter("operator", "or"); + List<Condition> subConditions = new ArrayList<Condition>(); + for (String eventType : eventTypes) { + Condition subCondition = new Condition(definitionsService.getConditionType("eventPropertyCondition")); + subCondition.setParameter("propertyName", "eventType"); + subCondition.setParameter("propertyValue", eventType); + subCondition.setParameter("comparisonOperator", "equals"); + subConditions.add(subCondition); + } + condition.setParameter("subConditions", subConditions); + conditions.add(condition); + + condition = new Condition(definitionsService.getConditionType("booleanCondition")); + condition.setParameter("operator", "and"); + condition.setParameter("subConditions", conditions); + + if (StringUtils.isNotBlank(query)) { + return persistenceService.queryFullText(query, condition, sortBy, Event.class, offset, size); + } else { + return persistenceService.query(condition, sortBy, Event.class, offset, size); + } + } + + public boolean hasEventAlreadyBeenRaised(Event event, boolean session) { + List<Condition> conditions = new ArrayList<Condition>(); + + Condition profileIdCondition = new Condition(definitionsService.getConditionType("eventPropertyCondition")); + if (session) { + profileIdCondition.setParameter("propertyName", "sessionId"); + profileIdCondition.setParameter("propertyValue", event.getSessionId()); + } else { + profileIdCondition.setParameter("propertyName", "profileId"); + profileIdCondition.setParameter("propertyValue", event.getProfileId()); + } + profileIdCondition.setParameter("comparisonOperator", "equals"); + conditions.add(profileIdCondition); + + Condition condition = new Condition(definitionsService.getConditionType("eventPropertyCondition")); + condition.setParameter("propertyName", "eventType"); + condition.setParameter("propertyValue", event.getEventType()); + condition.setParameter("comparisonOperator", "equals"); + conditions.add(condition); + + condition = new Condition(definitionsService.getConditionType("eventPropertyCondition")); + condition.setParameter("propertyName", "target.itemId"); + condition.setParameter("propertyValue", event.getTarget().getItemId()); + condition.setParameter("comparisonOperator", "equals"); + conditions.add(condition); + + condition = new Condition(definitionsService.getConditionType("eventPropertyCondition")); + condition.setParameter("propertyName", "target.itemType"); + condition.setParameter("propertyValue", event.getTarget().getItemType()); + condition.setParameter("comparisonOperator", "equals"); + conditions.add(condition); + + Condition andCondition = new Condition(definitionsService.getConditionType("booleanCondition")); + andCondition.setParameter("operator", "and"); + andCondition.setParameter("subConditions", conditions); + long size = persistenceService.queryCount(andCondition, Event.ITEM_TYPE); + return size > 0; + } + + + public void bind(ServiceReference<EventListenerService> serviceReference) { + EventListenerService eventListenerService = bundleContext.getService(serviceReference); + eventListeners.add(eventListenerService); + } + + public void unbind(ServiceReference<EventListenerService> serviceReference) { + if (serviceReference != null) { + EventListenerService eventListenerService = bundleContext.getService(serviceReference); + eventListeners.remove(eventListenerService); + } + } +}
