http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/services/RulesServiceImpl.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/services/RulesServiceImpl.java b/services/src/main/java/org/apache/unomi/services/services/RulesServiceImpl.java new file mode 100644 index 0000000..09cd0ec --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/services/RulesServiceImpl.java @@ -0,0 +1,387 @@ +/* + * 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.*; +import org.apache.unomi.api.actions.Action; +import org.apache.unomi.api.actions.ActionExecutor; +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.query.Query; +import org.apache.unomi.api.rules.Rule; +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.RulesService; +import org.apache.unomi.persistence.spi.CustomObjectMapper; +import org.apache.unomi.persistence.spi.PersistenceService; +import org.apache.unomi.services.actions.ActionExecutorDispatcher; +import org.osgi.framework.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.URL; +import java.util.*; + +public class RulesServiceImpl implements RulesService, EventListenerService, SynchronousBundleListener { + + public static final String RULE_QUERY_PREFIX = "rule_"; + private static final Logger logger = LoggerFactory.getLogger(RulesServiceImpl.class.getName()); + private BundleContext bundleContext; + + private PersistenceService persistenceService; + + private DefinitionsService definitionsService; + + private EventService eventService; + + private ActionExecutorDispatcher actionExecutorDispatcher; + private List<Rule> allRules; + + private Timer rulesTimer; + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void setPersistenceService(PersistenceService persistenceService) { + this.persistenceService = persistenceService; + } + + public void setDefinitionsService(DefinitionsService definitionsService) { + this.definitionsService = definitionsService; + } + + public void setEventService(EventService eventService) { + this.eventService = eventService; + } + + public void setActionExecutorDispatcher(ActionExecutorDispatcher actionExecutorDispatcher) { + this.actionExecutorDispatcher = actionExecutorDispatcher; + } + + public void postConstruct() { + logger.debug("postConstruct {" + bundleContext.getBundle() + "}"); + + loadPredefinedRules(bundleContext); + for (Bundle bundle : bundleContext.getBundles()) { + if (bundle.getBundleContext() != null) { + loadPredefinedRules(bundle.getBundleContext()); + } + } + try { + for (ServiceReference<ActionExecutor> reference : bundleContext.getServiceReferences(ActionExecutor.class, null)) { + ActionExecutor service = bundleContext.getService(reference); + actionExecutorDispatcher.addExecutor(reference.getProperty("actionExecutorId").toString(), reference.getBundle().getBundleId(), service); + } + } catch (Exception e) { + logger.error("Cannot get services",e); + } + + bundleContext.addBundleListener(this); + + initializeTimer(); + } + + public void preDestroy() { + bundleContext.removeBundleListener(this); + cancelTimers(); + } + + private void cancelTimers() { + if(rulesTimer != null) { + rulesTimer.cancel(); + } + logger.info("Rule purge: Purge unscheduled"); + } + + private void processBundleStartup(BundleContext bundleContext) { + if (bundleContext == null) { + return; + } + loadPredefinedRules(bundleContext); + + List<PluginType> types = definitionsService.getTypesByPlugin().get(bundleContext.getBundle().getBundleId()); + List<String> addedConditions = new ArrayList<String>(); + List<String> addedActions = new ArrayList<String>(); + if (types != null) { + for (PluginType type : types) { + if (type instanceof ConditionType) { + addedConditions.add(((ConditionType) type).getId()); + } else if (type instanceof ActionType) { + addedActions.add(((ActionType) type).getId()); + } + } + } + if (!addedConditions.isEmpty() || !addedActions.isEmpty()) { + for (Rule rule : persistenceService.query("missingPlugins", "true", null, Rule.class)) { + boolean succeed = ParserHelper.resolveConditionType(definitionsService, rule.getCondition()) && + ParserHelper.resolveActionTypes(definitionsService, rule.getActions()); + if (succeed) { + logger.info("Enable rule " + rule.getItemId()); + rule.getMetadata().setMissingPlugins(false); + setRule(rule); + } + } + } + if (bundleContext.getBundle().getRegisteredServices() != null) { + for (ServiceReference<?> reference : bundleContext.getBundle().getRegisteredServices()) { + Object service = bundleContext.getService(reference); + if (service instanceof ActionExecutor) { + actionExecutorDispatcher.addExecutor(reference.getProperty("actionExecutorId").toString(), bundleContext.getBundle().getBundleId(), (ActionExecutor) service); + } + } + } + } + + private void processBundleStop(BundleContext bundleContext) { + if (bundleContext == null) { + return; + } + List<PluginType> types = definitionsService.getTypesByPlugin().get(bundleContext.getBundle().getBundleId()); + List<String> removedConditions = new ArrayList<String>(); + List<String> removedActions = new ArrayList<String>(); + if (types != null) { + for (PluginType type : types) { + if (type instanceof ConditionType) { + removedConditions.add(((ConditionType) type).getId()); + } else if (type instanceof ActionType) { + removedActions.add(((ActionType) type).getId()); + } + } + } + if (!removedConditions.isEmpty() || !removedActions.isEmpty()) { + for (Rule rule : persistenceService.getAllItems(Rule.class)) { + List<String> conditions = ParserHelper.getConditionTypeIds(rule.getCondition()); + List<String> actions = new ArrayList<String>(); + for (Action action : rule.getActions()) { + actions.add(action.getActionTypeId()); + } + if (!Collections.disjoint(conditions, removedConditions) || !Collections.disjoint(actions, removedActions)) { + logger.info("Disable rule " + rule.getItemId()); + rule.getMetadata().setMissingPlugins(true); + setRule(rule); + } + } + } + actionExecutorDispatcher.removeExecutors(bundleContext.getBundle().getBundleId()); + } + + private void loadPredefinedRules(BundleContext bundleContext) { + Enumeration<URL> predefinedRuleEntries = bundleContext.getBundle().findEntries("META-INF/cxs/rules", "*.json", true); + if (predefinedRuleEntries == null) { + return; + } + + while (predefinedRuleEntries.hasMoreElements()) { + URL predefinedSegmentURL = predefinedRuleEntries.nextElement(); + logger.debug("Found predefined segment at " + predefinedSegmentURL + ", loading... "); + + try { + Rule rule = CustomObjectMapper.getObjectMapper().readValue(predefinedSegmentURL, Rule.class); + if (rule.getMetadata().getScope() == null) { + rule.getMetadata().setScope("systemscope"); + } + if (getRule(rule.getMetadata().getId()) == null) { + setRule(rule); + } + } catch (IOException e) { + logger.error("Error while loading segment definition " + predefinedSegmentURL, e); + } + + } + } + + public Set<Rule> getMatchingRules(Event event) { + Set<Rule> matchedRules = new LinkedHashSet<Rule>(); + + Boolean hasEventAlreadyBeenRaisedForSession = null; + Boolean hasEventAlreadyBeenRaisedForProfile = null; + + List<Rule> allItems = allRules; + + for (Rule rule : allItems) { + String scope = rule.getMetadata().getScope(); + if (scope.equals(Metadata.SYSTEM_SCOPE) || scope.equals(event.getScope())) { + Condition eventCondition = definitionsService.extractConditionByTag(rule.getCondition(), "eventCondition"); + + if (eventCondition == null) { + continue; + } + + if (!persistenceService.testMatch(eventCondition, event)) { + continue; + } + + Condition sourceCondition = definitionsService.extractConditionByTag(rule.getCondition(), "sourceEventCondition"); + if (sourceCondition != null && !persistenceService.testMatch(sourceCondition, event.getSource())) { + continue; + } + + if (rule.isRaiseEventOnlyOnceForProfile()) { + hasEventAlreadyBeenRaisedForProfile = hasEventAlreadyBeenRaisedForProfile != null ? hasEventAlreadyBeenRaisedForProfile : eventService.hasEventAlreadyBeenRaised(event, false); + if (hasEventAlreadyBeenRaisedForProfile) { + continue; + } + } else if (rule.isRaiseEventOnlyOnceForSession()) { + hasEventAlreadyBeenRaisedForSession = hasEventAlreadyBeenRaisedForSession != null ? hasEventAlreadyBeenRaisedForSession : eventService.hasEventAlreadyBeenRaised(event, true); + if (hasEventAlreadyBeenRaisedForSession) { + continue; + } + } + + Condition profileCondition = definitionsService.extractConditionByTag(rule.getCondition(), "profileCondition"); + if (profileCondition != null && !persistenceService.testMatch(profileCondition, event.getProfile())) { + continue; + } + Condition sessionCondition = definitionsService.extractConditionByTag(rule.getCondition(), "sessionCondition"); + if (sessionCondition != null && !persistenceService.testMatch(sessionCondition, event.getSession())) { + continue; + } + matchedRules.add(rule); + } + } + + return matchedRules; + } + + private List<Rule> getAllRules() { + List<Rule> allItems = persistenceService.getAllItems(Rule.class, 0, -1, "priority").getList(); + for (Rule rule : allItems) { + ParserHelper.resolveConditionType(definitionsService, rule.getCondition()); + ParserHelper.resolveActionTypes(definitionsService, rule.getActions()); + } + return allItems; + } + + + public boolean canHandle(Event event) { + return true; + } + + public int onEvent(Event event) { + Set<Rule> rules = getMatchingRules(event); + + int changes = EventService.NO_CHANGE; + for (Rule rule : rules) { + logger.debug("Fired rule " + rule.getMetadata().getId() + " for " + event.getEventType() + " - " + event.getItemId()); + for (Action action : rule.getActions()) { + changes |= actionExecutorDispatcher.execute(action, event); + } + + Event ruleFired = new Event("ruleFired", event.getSession(), event.getProfile(), event.getScope(), event, rule, event.getTimeStamp()); + ruleFired.getAttributes().putAll(event.getAttributes()); + ruleFired.setPersistent(false); + changes |= eventService.send(ruleFired); + } + return changes; + } + + public Set<Metadata> getRuleMetadatas() { + Set<Metadata> metadatas = new HashSet<Metadata>(); + for (Rule rule : persistenceService.getAllItems(Rule.class, 0, 50, null).getList()) { + metadatas.add(rule.getMetadata()); + } + return metadatas; + } + + public PartialList<Metadata> getRuleMetadatas(Query query) { + if(query.isForceRefresh()){ + persistenceService.refresh(); + } + definitionsService.resolveConditionType(query.getCondition()); + List<Metadata> descriptions = new LinkedList<>(); + PartialList<Rule> rules = persistenceService.query(query.getCondition(), query.getSortby(), Rule.class, query.getOffset(), query.getLimit()); + for (Rule definition : rules.getList()) { + descriptions.add(definition.getMetadata()); + } + return new PartialList<>(descriptions, rules.getOffset(), rules.getPageSize(), rules.getTotalSize()); + } + + public Rule getRule(String ruleId) { + Rule rule = persistenceService.load(ruleId, Rule.class); + if (rule != null) { + if (rule.getCondition() != null) { + ParserHelper.resolveConditionType(definitionsService, rule.getCondition()); + } + if (rule.getActions() != null) { + ParserHelper.resolveActionTypes(definitionsService, rule.getActions()); + } + } + return rule; + } + + public void setRule(Rule rule) { + Condition condition = rule.getCondition(); + if (condition != null) { + if (rule.getMetadata().isEnabled() && !rule.getMetadata().isMissingPlugins()) { + ParserHelper.resolveConditionType(definitionsService, condition); + definitionsService.extractConditionByTag(condition, "eventCondition"); + } + } + persistenceService.save(rule); + } + + public Set<Condition> getTrackedConditions(Item source){ + Set<Condition> trackedConditions = new HashSet<>(); + for (Rule r : allRules) { + Condition trackedCondition = definitionsService.extractConditionByTag(r.getCondition(), "trackedCondition"); + if(trackedCondition != null){ + Condition sourceEventPropertyCondition = definitionsService.extractConditionByTag(r.getCondition(), "sourceEventCondition"); + if(source != null && sourceEventPropertyCondition != null) { + ParserHelper.resolveConditionType(definitionsService, sourceEventPropertyCondition); + if(persistenceService.testMatch(sourceEventPropertyCondition, source)){ + trackedConditions.add(trackedCondition); + } + } else { + trackedConditions.add(trackedCondition); + } + + } + } + return trackedConditions; + } + + public void removeRule(String ruleId) { + persistenceService.remove(ruleId, Rule.class); + } + + private void initializeTimer() { + rulesTimer = new Timer(); + TimerTask task = new TimerTask() { + @Override + public void run() { + allRules = getAllRules(); + } + }; + rulesTimer.schedule(task, 0, 1000); + } + + public void bundleChanged(BundleEvent event) { + switch (event.getType()) { + case BundleEvent.STARTED: + processBundleStartup(event.getBundle().getBundleContext()); + break; + case BundleEvent.STOPPING: + processBundleStop(event.getBundle().getBundleContext()); + break; + } + } +}
http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/apache/unomi/services/services/SegmentServiceImpl.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/apache/unomi/services/services/SegmentServiceImpl.java b/services/src/main/java/org/apache/unomi/services/services/SegmentServiceImpl.java new file mode 100644 index 0000000..2636daa --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/services/SegmentServiceImpl.java @@ -0,0 +1,816 @@ +/* + * 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 com.fasterxml.jackson.core.JsonProcessingException; +import org.apache.unomi.api.*; +import org.apache.unomi.api.actions.Action; +import org.apache.unomi.api.conditions.Condition; +import org.apache.unomi.api.conditions.ConditionType; +import org.apache.unomi.api.query.Query; +import org.apache.unomi.api.rules.Rule; +import org.apache.unomi.api.segments.Scoring; +import org.apache.unomi.api.segments.ScoringElement; +import org.apache.unomi.api.segments.Segment; +import org.apache.unomi.api.segments.SegmentsAndScores; +import org.apache.unomi.api.services.DefinitionsService; +import org.apache.unomi.api.services.RulesService; +import org.apache.unomi.api.services.SegmentService; +import org.apache.unomi.persistence.spi.CustomObjectMapper; +import org.apache.unomi.persistence.spi.PersistenceService; +import org.apache.unomi.persistence.spi.aggregate.TermsAggregate; +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 javax.json.*; +import java.io.IOException; +import java.net.URL; +import java.security.MessageDigest; +import java.util.*; + +public class SegmentServiceImpl implements SegmentService, SynchronousBundleListener { + + private static final Logger logger = LoggerFactory.getLogger(SegmentServiceImpl.class.getName()); + + private BundleContext bundleContext; + + private PersistenceService persistenceService; + + private DefinitionsService definitionsService; + + private RulesService rulesService; + + private long taskExecutionPeriod = 24L * 60L * 60L * 1000L; + private List<Segment> allSegments; + private List<Scoring> allScoring; + private Timer segmentTimer; + + public SegmentServiceImpl() { + logger.info("Initializing segment service..."); + } + + public static void dumpJSON(JsonValue tree, String key, String depthPrefix) { + if (key != null) + logger.info(depthPrefix + "Key " + key + ": "); + switch (tree.getValueType()) { + case OBJECT: + logger.info(depthPrefix + "OBJECT"); + JsonObject object = (JsonObject) tree; + for (String name : object.keySet()) + dumpJSON(object.get(name), name, depthPrefix + " "); + break; + case ARRAY: + logger.info(depthPrefix + "ARRAY"); + JsonArray array = (JsonArray) tree; + for (JsonValue val : array) + dumpJSON(val, null, depthPrefix + " "); + break; + case STRING: + JsonString st = (JsonString) tree; + logger.info(depthPrefix + "STRING " + st.getString()); + break; + case NUMBER: + JsonNumber num = (JsonNumber) tree; + logger.info(depthPrefix + "NUMBER " + num.toString()); + break; + case TRUE: + case FALSE: + case NULL: + logger.info(depthPrefix + tree.getValueType().toString()); + break; + } + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void setPersistenceService(PersistenceService persistenceService) { + this.persistenceService = persistenceService; + } + + public void setDefinitionsService(DefinitionsService definitionsService) { + this.definitionsService = definitionsService; + } + + public void setRulesService(RulesService rulesService) { + this.rulesService = rulesService; + } + + public void postConstruct() { + logger.debug("postConstruct {" + bundleContext.getBundle() + "}"); + loadPredefinedSegments(bundleContext); + loadPredefinedScorings(bundleContext); + for (Bundle bundle : bundleContext.getBundles()) { + if (bundle.getBundleContext() != null) { + loadPredefinedSegments(bundle.getBundleContext()); + loadPredefinedScorings(bundle.getBundleContext()); + } + } + bundleContext.addBundleListener(this); + initializeTimer(); + } + + public void preDestroy() { + bundleContext.removeBundleListener(this); + cancelTimers(); + } + + private void cancelTimers() { + if(segmentTimer != null) { + segmentTimer.cancel(); + } + logger.info("Segment purge: Purge unscheduled"); + } + + private void processBundleStartup(BundleContext bundleContext) { + if (bundleContext == null) { + return; + } + loadPredefinedSegments(bundleContext); + loadPredefinedScorings(bundleContext); + + List<PluginType> types = definitionsService.getTypesByPlugin().get(bundleContext.getBundle().getBundleId()); + List<String> addedConditions = new ArrayList<String>(); + if (types != null) { + for (PluginType type : types) { + if (type instanceof ConditionType) { + addedConditions.add(((ConditionType) type).getId()); + } + } + } + if (!addedConditions.isEmpty()) { + for (Segment segment : persistenceService.query("missingPlugins", "true", null, Segment.class)) { + boolean succeed = ParserHelper.resolveConditionType(definitionsService, segment.getCondition()); + if (succeed) { + logger.info("Enable segment " + segment.getItemId()); + segment.getMetadata().setMissingPlugins(false); + setSegmentDefinition(segment); + } + } + } + } + + private void processBundleStop(BundleContext bundleContext) { + if (bundleContext == null) { + return; + } + List<PluginType> types = definitionsService.getTypesByPlugin().get(bundleContext.getBundle().getBundleId()); + List<String> removedConditions = new ArrayList<String>(); + if (types != null) { + for (PluginType type : types) { + if (type instanceof ConditionType) { + removedConditions.add(((ConditionType) type).getId()); + } + } + } + if (!removedConditions.isEmpty()) { + for (Segment segment : persistenceService.getAllItems(Segment.class)) { + List<String> conditions = ParserHelper.getConditionTypeIds(segment.getCondition()); + if (!Collections.disjoint(conditions, removedConditions)) { + logger.info("Disable segment " + segment.getItemId()); + segment.getMetadata().setMissingPlugins(true); + setSegmentDefinition(segment); + } + } + } + } + + private void loadPredefinedSegments(BundleContext bundleContext) { + Enumeration<URL> predefinedSegmentEntries = bundleContext.getBundle().findEntries("META-INF/cxs/segments", "*.json", true); + if (predefinedSegmentEntries == null) { + return; + } + while (predefinedSegmentEntries.hasMoreElements()) { + URL predefinedSegmentURL = predefinedSegmentEntries.nextElement(); + logger.debug("Found predefined segment at " + predefinedSegmentURL + ", loading... "); + + try { + Segment segment = CustomObjectMapper.getObjectMapper().readValue(predefinedSegmentURL, Segment.class); + if (segment.getMetadata().getScope() == null) { + segment.getMetadata().setScope("systemscope"); + } + if (getSegmentDefinition(segment.getMetadata().getId()) == null) { + setSegmentDefinition(segment); + } + } catch (IOException e) { + logger.error("Error while loading segment definition " + predefinedSegmentURL, e); + } + } + } + + private void loadPredefinedScorings(BundleContext bundleContext) { + Enumeration<URL> predefinedScoringEntries = bundleContext.getBundle().findEntries("META-INF/cxs/scoring", "*.json", true); + if (predefinedScoringEntries == null) { + return; + } + while (predefinedScoringEntries.hasMoreElements()) { + URL predefinedScoringURL = predefinedScoringEntries.nextElement(); + logger.debug("Found predefined scoring at " + predefinedScoringURL + ", loading... "); + + try { + Scoring scoring = CustomObjectMapper.getObjectMapper().readValue(predefinedScoringURL, Scoring.class); + if (scoring.getMetadata().getScope() == null) { + scoring.getMetadata().setScope("systemscope"); + } + if (getScoringDefinition(scoring.getMetadata().getId()) == null) { + setScoringDefinition(scoring); + } + } catch (IOException e) { + logger.error("Error while loading segment definition " + predefinedScoringURL, e); + } + } + } + + public PartialList<Metadata> getSegmentMetadatas(int offset, int size, String sortBy) { + return getMetadatas(offset, size, sortBy, Segment.class); + } + + public PartialList<Metadata> getSegmentMetadatas(String scope, int offset, int size, String sortBy) { + PartialList<Segment> segments = persistenceService.query("metadata.scope", scope, sortBy, Segment.class, offset, size); + List<Metadata> details = new LinkedList<>(); + for (Segment definition : segments.getList()) { + details.add(definition.getMetadata()); + } + return new PartialList<>(details, segments.getOffset(), segments.getPageSize(), segments.getTotalSize()); + } + + public PartialList<Metadata> getSegmentMetadatas(Query query) { + return getMetadatas(query, Segment.class); + } + + private List<Segment> getAllSegmentDefinitions() { + List<Segment> allItems = persistenceService.getAllItems(Segment.class); + for (Segment segment : allItems) { + ParserHelper.resolveConditionType(definitionsService, segment.getCondition()); + } + return allItems; + } + + public Segment getSegmentDefinition(String segmentId) { + Segment definition = persistenceService.load(segmentId, Segment.class); + if (definition != null) { + ParserHelper.resolveConditionType(definitionsService, definition.getCondition()); + } + return definition; + } + + public void setSegmentDefinition(Segment segment) { + ParserHelper.resolveConditionType(definitionsService, segment.getCondition()); + if (segment.getMetadata().isEnabled() && !segment.getMetadata().isMissingPlugins()) { + updateAutoGeneratedRules(segment.getMetadata(), segment.getCondition()); + } + // make sure we update the name and description metadata that might not match, so first we remove the entry from the map + persistenceService.save(segment); + + updateExistingProfilesForSegment(segment); + } + + private void checkIfSegmentIsImpacted(Segment segment, Condition condition, String segmentToDeleteId, Set<Segment> impactedSegments) { + if(condition != null) { + @SuppressWarnings("unchecked") + final List<Condition> subConditions = (List<Condition>) condition.getParameter("subConditions"); + if (subConditions != null) { + for (Condition subCondition : subConditions) { + checkIfSegmentIsImpacted(segment, subCondition, segmentToDeleteId, impactedSegments); + } + } else if ("profileSegmentCondition".equals(condition.getConditionTypeId())) { + @SuppressWarnings("unchecked") + final List<String> referencedSegmentIds = (List<String>) condition.getParameter("segments"); + + if (referencedSegmentIds.indexOf(segmentToDeleteId) >= 0) { + impactedSegments.add(segment); + } + } + } + } + + /** + * Return an updated condition that do not contain a condition on the segmentId anymore + * it's remove the unnecessary boolean condition (if a condition is the only one of a boolean the boolean will be remove and the subcondition returned) + * it's return null when there is no more condition after (if the condition passed was only a segment condition on the segmentId) + * @param condition the condition to update + * @param segmentId the segment id to remove in the condition + * @return updated condition + */ + private Condition updateImpactedCondition(Condition condition, String segmentId) { + if ("booleanCondition".equals(condition.getConditionTypeId())) { + @SuppressWarnings("unchecked") + final List<Condition> subConditions = (List<Condition>) condition.getParameter("subConditions"); + List<Condition> updatedSubConditions = new LinkedList<>(); + for (Condition subCondition : subConditions) { + Condition updatedCondition = updateImpactedCondition(subCondition, segmentId); + if(updatedCondition != null) { + updatedSubConditions.add(updatedCondition); + } + } + if(!updatedSubConditions.isEmpty()){ + if(updatedSubConditions.size() == 1) { + return updatedSubConditions.get(0); + } else { + condition.setParameter("subConditions", updatedSubConditions); + return condition; + } + } else { + return null; + } + } else if("profileSegmentCondition".equals(condition.getConditionTypeId())) { + @SuppressWarnings("unchecked") + final List<String> referencedSegmentIds = (List<String>) condition.getParameter("segments"); + if (referencedSegmentIds.indexOf(segmentId) >= 0) { + referencedSegmentIds.remove(segmentId); + if(referencedSegmentIds.isEmpty()) { + return null; + } else { + condition.setParameter("segments", referencedSegmentIds); + } + } + } + return condition; + } + + private Set<Segment> getImpactedSegments(String segmentId) { + Set<Segment> impactedSegments = new HashSet<>(this.allSegments.size()); + for (Segment segment : this.allSegments) { + checkIfSegmentIsImpacted(segment, segment.getCondition(), segmentId, impactedSegments); + } + return impactedSegments; + } + + public List<Metadata> getImpactedSegmentMetadata(String segmentId) { + List<Metadata> details = new LinkedList<>(); + for (Segment definition : getImpactedSegments(segmentId)) { + details.add(definition.getMetadata()); + } + + return details; + } + + public List<Metadata> removeSegmentDefinition(String segmentId, boolean validate) { + Set<Segment> impactedSegments = getImpactedSegments(segmentId); + if (!validate || impactedSegments.isEmpty()) { + // update profiles + Condition segmentCondition = new Condition(); + segmentCondition.setConditionType(definitionsService.getConditionType("profilePropertyCondition")); + segmentCondition.setParameter("propertyName", "segments"); + segmentCondition.setParameter("comparisonOperator", "equals"); + segmentCondition.setParameter("propertyValue", segmentId); + + List<Profile> previousProfiles = persistenceService.query(segmentCondition, null, Profile.class); + for (Profile profileToRemove : previousProfiles) { + profileToRemove.getSegments().remove(segmentId); + persistenceService.update(profileToRemove.getItemId(), null, Profile.class, "segments", profileToRemove.getSegments()); + } + + // update impacted segments + for (Segment segment : impactedSegments) { + Condition updatedCondition = updateImpactedCondition(segment.getCondition(), segmentId); + segment.setCondition(updatedCondition); + if(updatedCondition == null) { + clearAutoGeneratedRules(persistenceService.query("linkedItems", segment.getMetadata().getId(), null, Rule.class), segment.getMetadata().getId()); + segment.getMetadata().setEnabled(false); + } + setSegmentDefinition(segment); + } + + persistenceService.remove(segmentId, Segment.class); + List<Rule> previousRules = persistenceService.query("linkedItems", segmentId, null, Rule.class); + clearAutoGeneratedRules(previousRules, segmentId); + } + + List<Metadata> metadata = new LinkedList<>(); + for (Segment definition : impactedSegments) { + metadata.add(definition.getMetadata()); + } + return metadata; + } + + + public PartialList<Profile> getMatchingIndividuals(String segmentID, int offset, int size, String sortBy) { + Segment segment = getSegmentDefinition(segmentID); + if (segment == null) { + return new PartialList<Profile>(); + } + return persistenceService.query(segment.getCondition(), sortBy, Profile.class, offset, size); + } + + public long getMatchingIndividualsCount(String segmentID) { + if (getSegmentDefinition(segmentID) == null) { + return 0; + } + + Condition excludeMergedProfilesCondition = new Condition(definitionsService.getConditionType("profilePropertyCondition")); + excludeMergedProfilesCondition.setParameter("propertyName", "mergedWith"); + excludeMergedProfilesCondition.setParameter("comparisonOperator", "missing"); + Condition condition = new Condition(definitionsService.getConditionType("booleanCondition")); + condition.setParameter("operator", "and"); + condition.setParameter("subConditions", Arrays.asList(getSegmentDefinition(segmentID).getCondition(), excludeMergedProfilesCondition)); + + return persistenceService.queryCount(condition, Profile.ITEM_TYPE); + } + + public Boolean isProfileInSegment(Profile profile, String segmentId) { + Set<String> matchingSegments = getSegmentsAndScoresForProfile(profile).getSegments(); + + return matchingSegments.contains(segmentId); + } + + public SegmentsAndScores getSegmentsAndScoresForProfile(Profile profile) { + Set<String> segments = new HashSet<String>(); + Map<String,Integer> scores = new HashMap<String, Integer>(); + + List<Segment> allSegments = this.allSegments; + for (Segment segment : allSegments) { + if (persistenceService.testMatch(segment.getCondition(), profile)) { + segments.add(segment.getMetadata().getId()); + } + } + + List<Scoring> allScoring = this.allScoring; + for (Scoring scoring : allScoring) { + if (scoring.getMetadata().isEnabled()) { + int score = 0; + for (ScoringElement scoringElement : scoring.getElements()) { + if (persistenceService.testMatch(scoringElement.getCondition(), profile)) { + score += scoringElement.getValue(); + } + } + if (score > 0) { + scores.put(scoring.getMetadata().getId(), score); + } + } + } + + return new SegmentsAndScores(segments, scores); + } + + public List<Metadata> getSegmentMetadatasForProfile(Profile profile) { + List<Metadata> metadatas = new ArrayList<>(); + + List<Segment> allSegments = this.allSegments; + for (Segment segment : allSegments) { + if (persistenceService.testMatch(segment.getCondition(), profile)) { + metadatas.add(segment.getMetadata()); + } + } + + return metadatas; + } + + public PartialList<Metadata> getScoringMetadatas(int offset, int size, String sortBy) { + return getMetadatas(offset, size, sortBy, Scoring.class); + } + + public PartialList<Metadata> getScoringMetadatas(Query query) { + return getMetadatas(query, Scoring.class); + } + + private List<Scoring> getAllScoringDefinitions() { + List<Scoring> allItems = persistenceService.getAllItems(Scoring.class); + for (Scoring scoring : allItems) { + for (ScoringElement element : scoring.getElements()) { + ParserHelper.resolveConditionType(definitionsService, element.getCondition()); + } + } + return allItems; + } + + public Scoring getScoringDefinition(String scoringId) { + Scoring definition = persistenceService.load(scoringId, Scoring.class); + if (definition != null) { + for (ScoringElement element : definition.getElements()) { + ParserHelper.resolveConditionType(definitionsService, element.getCondition()); + } + } + return definition; + } + + public void setScoringDefinition(Scoring scoring) { + for (ScoringElement element : scoring.getElements()) { + ParserHelper.resolveConditionType(definitionsService, element.getCondition()); + } + for (ScoringElement element : scoring.getElements()) { + if (scoring.getMetadata().isEnabled() && !scoring.getMetadata().isMissingPlugins()) { + updateAutoGeneratedRules(scoring.getMetadata(), element.getCondition()); + } + } + // make sure we update the name and description metadata that might not match, so first we remove the entry from the map + persistenceService.save(scoring); + + updateExistingProfilesForScoring(scoring); + } + + public void createScoringDefinition(String scope, String scoringId, String name, String description) { + Metadata metadata = new Metadata(scope, scoringId, name, description); + Scoring scoring = new Scoring(metadata); + Condition rootCondition = new Condition(); + rootCondition.setConditionType(definitionsService.getConditionType("booleanCondition")); + rootCondition.setParameter("operator", "and"); + rootCondition.setParameter("subConditions", new ArrayList<Condition>()); + scoring.setElements(new ArrayList<ScoringElement>()); + + setScoringDefinition(scoring); + } + + public void removeScoringDefinition(String scoringId) { + persistenceService.remove(scoringId, Scoring.class); + + updateExistingProfilesForRemovedScoring(scoringId); + } + + public void updateAutoGeneratedRules(Metadata metadata, Condition condition) { + List<Rule> previousRules = persistenceService.query("linkedItems", metadata.getId(), null, Rule.class); + List<Rule> rules = new ArrayList<Rule>(); + if (condition != null) { + getAutoGeneratedRules(metadata, condition, null, rules); + } + for (Rule rule : rules) { + rulesService.setRule(rule); + } + previousRules.removeAll(rules); + clearAutoGeneratedRules(previousRules, metadata.getId()); + } + + private void clearAutoGeneratedRules(List<Rule> rules, String idWithScope) { + for (Rule previousRule : rules) { + previousRule.getLinkedItems().remove(idWithScope); + if (previousRule.getLinkedItems().isEmpty()) { + // todo remove profile properties ? + persistenceService.remove(previousRule.getItemId(), Rule.class); + } else { + persistenceService.update(previousRule.getItemId(), null, Rule.class, "linkedItems", previousRule.getLinkedItems()); + } + } + } + + private void getAutoGeneratedRules(Metadata metadata, Condition condition, Condition parentCondition, List<Rule> rules) { + if (condition.getConditionType().getTagIDs().contains("eventCondition") && !condition.getConditionType().getTagIDs().contains("profileCondition")) { + try { + Map<String,Object> m = new HashMap<>(3); + m.put("scope",metadata.getScope()); + m.put("condition", condition); + m.put("numberOfDays", parentCondition.getParameter("numberOfDays")); + String key = CustomObjectMapper.getObjectMapper().writeValueAsString(m); + key = "eventTriggered" + getMD5(key); + parentCondition.setParameter("generatedPropertyKey", key); + Rule rule = rulesService.getRule(key); + if (rule == null) { + rule = new Rule(new Metadata(metadata.getScope(), key, "Auto generated rule for "+metadata.getName(), "")); + rule.setCondition(condition); + rule.getMetadata().setHidden(true); + final Action action = new Action(); + action.setActionType(definitionsService.getActionType("setEventOccurenceCountAction")); + action.setParameter("pastEventCondition", parentCondition); + + rule.setActions(Arrays.asList(action)); + rule.setLinkedItems(Arrays.asList(metadata.getId())); + rules.add(rule); + + updateExistingProfilesForPastEventCondition(condition, parentCondition); + } else { + rule.getLinkedItems().add(metadata.getId()); + rules.add(rule); + } + } catch (JsonProcessingException e) { + logger.error(e.getMessage(), e); + } + } else { + Collection<Object> values = new ArrayList<>(condition.getParameterValues().values()); + for (Object parameterValue : values) { + if (parameterValue instanceof Condition) { + getAutoGeneratedRules(metadata, (Condition) parameterValue, condition, rules); + } else if (parameterValue instanceof Collection) { + for (Object subCondition : (Collection<?>) parameterValue) { + if (subCondition instanceof Condition) { + getAutoGeneratedRules(metadata, (Condition) subCondition, condition, rules); + } + } + } + } + } + } + + private void updateExistingProfilesForPastEventCondition(Condition eventCondition, Condition parentCondition) { + long t = System.currentTimeMillis(); + List<Condition> l = new ArrayList<Condition>(); + Condition andCondition = new Condition(); + andCondition.setConditionType(definitionsService.getConditionType("booleanCondition")); + andCondition.setParameter("operator", "and"); + andCondition.setParameter("subConditions", l); + + l.add(eventCondition); + + Integer numberOfDays = (Integer) parentCondition.getParameter("numberOfDays"); + if (numberOfDays != null) { + Condition numberOfDaysCondition = new Condition(); + numberOfDaysCondition.setConditionType(definitionsService.getConditionType("sessionPropertyCondition")); + numberOfDaysCondition.setParameter("propertyName", "timeStamp"); + numberOfDaysCondition.setParameter("comparisonOperator", "greaterThan"); + numberOfDaysCondition.setParameter("propertyValue", "now-" + numberOfDays + "d"); + l.add(numberOfDaysCondition); + } + String propertyKey = (String) parentCondition.getParameter("generatedPropertyKey"); + Map<String, Long> res = persistenceService.aggregateQuery(andCondition, new TermsAggregate("profileId"), Event.ITEM_TYPE); + for (Map.Entry<String, Long> entry : res.entrySet()) { + if (!entry.getKey().startsWith("_")) { + Map<String,Object> p = new HashMap<>(); + p.put(propertyKey, entry.getValue()); + Map<String,Object> p2 = new HashMap<>(); + p2.put("pastEvents",p); + try { + persistenceService.update(entry.getKey(), null, Profile.class, "systemProperties", p2); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } + } + + logger.info("Profiles past condition updated in {}", System.currentTimeMillis()-t); + } + + private void updateExistingProfilesForSegment(Segment segment) { + long t = System.currentTimeMillis(); + Condition segmentCondition = new Condition(); + + segmentCondition.setConditionType(definitionsService.getConditionType("profilePropertyCondition")); + segmentCondition.setParameter("propertyName", "segments"); + segmentCondition.setParameter("comparisonOperator", "equals"); + segmentCondition.setParameter("propertyValue", segment.getItemId()); + + if(segment.getMetadata().isEnabled()) { + List<Profile> previousProfiles = persistenceService.query(segmentCondition, null, Profile.class); + List<Profile> newProfiles = persistenceService.query(segment.getCondition(), null, Profile.class); + + List<Profile> add = new ArrayList<>(newProfiles); + add.removeAll(previousProfiles); + previousProfiles.removeAll(newProfiles); + + for (Profile profileToAdd : add) { + profileToAdd.getSegments().add(segment.getItemId()); + persistenceService.update(profileToAdd.getItemId(), null, Profile.class, "segments", profileToAdd.getSegments()); + } + for (Profile profileToRemove : previousProfiles) { + profileToRemove.getSegments().remove(segment.getItemId()); + persistenceService.update(profileToRemove.getItemId(), null, Profile.class, "segments", profileToRemove.getSegments()); + } + } else { + List<Profile> previousProfiles = persistenceService.query(segmentCondition, null, Profile.class); + for (Profile profileToRemove : previousProfiles) { + profileToRemove.getSegments().remove(segment.getItemId()); + persistenceService.update(profileToRemove.getItemId(), null, Profile.class, "segments", profileToRemove.getSegments()); + } + } + logger.info("Profiles updated in {}", System.currentTimeMillis()-t); + } + + private void updateExistingProfilesForScoring(Scoring scoring) { + long t = System.currentTimeMillis(); + Condition scoringCondition = new Condition(); + scoringCondition.setConditionType(definitionsService.getConditionType("profilePropertyCondition")); + scoringCondition.setParameter("propertyName", "scores." + scoring.getItemId()); + scoringCondition.setParameter("comparisonOperator", "exists"); + List<Profile> previousProfiles = persistenceService.query(scoringCondition, null, Profile.class); + + HashMap<String, Object> scriptParams = new HashMap<>(); + scriptParams.put("scoringId", scoring.getItemId()); + + for (Profile profileToRemove : previousProfiles) { + persistenceService.update(profileToRemove.getItemId(), null, Profile.class, "ctx._source.scores.remove(scoringId)", scriptParams); + } + if(scoring.getMetadata().isEnabled()) { + String script = "if (ctx._source.scores.containsKey(scoringId)) { ctx._source.scores[scoringId] += scoringValue } else { ctx._source.scores[scoringId] = scoringValue }"; + for (ScoringElement element : scoring.getElements()) { + scriptParams.put("scoringValue", element.getValue()); + for (Profile p : persistenceService.query(element.getCondition(), null, Profile.class)) { + persistenceService.update(p.getItemId(), null, Profile.class, script, scriptParams); + } + } + } + logger.info("Profiles updated in {}", System.currentTimeMillis()-t); + } + + private void updateExistingProfilesForRemovedScoring(String scoringId) { + long t = System.currentTimeMillis(); + Condition scoringCondition = new Condition(); + scoringCondition.setConditionType(definitionsService.getConditionType("profilePropertyCondition")); + scoringCondition.setParameter("propertyName", "scores." + scoringId); + scoringCondition.setParameter("comparisonOperator", "exists"); + List<Profile> previousProfiles = persistenceService.query(scoringCondition, null, Profile.class); + + HashMap<String, Object> scriptParams = new HashMap<>(); + scriptParams.put("scoringId", scoringId); + + for (Profile profileToRemove : previousProfiles) { + persistenceService.update(profileToRemove.getItemId(), null, Profile.class, "ctx._source.scores.remove(scoringId)", scriptParams); + } + logger.info("Profiles updated in {}", System.currentTimeMillis()-t); + } + + private String getMD5(String md5) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] array = md.digest(md5.getBytes()); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < array.length; ++i) { + sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1, 3)); + } + return sb.toString(); + } catch (java.security.NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + 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 initializeTimer() { + segmentTimer = new Timer(); + TimerTask task = new TimerTask() { + @Override + public void run() { + for (Metadata metadata : rulesService.getRuleMetadatas()) { + Rule rule = rulesService.getRule(metadata.getId()); + for (Action action : rule.getActions()) { + if (action.getActionTypeId().equals("setEventOccurenceCountAction")) { + Condition pastEventCondition = (Condition) action.getParameterValues().get("pastEventCondition"); + if (pastEventCondition.containsParameter("numberOfDays")) { + updateExistingProfilesForPastEventCondition(rule.getCondition(), pastEventCondition); + } + } + } + } + } + }; + segmentTimer.scheduleAtFixedRate(task, getDay(1).getTime(), taskExecutionPeriod); + + task = new TimerTask() { + @Override + public void run() { + allSegments = getAllSegmentDefinitions(); + allScoring = getAllScoringDefinitions(); + } + }; + segmentTimer.scheduleAtFixedRate(task, 0, 1000); + } + + private GregorianCalendar getDay(int offset) { + GregorianCalendar gc = new GregorianCalendar(); + gc = new GregorianCalendar(gc.get(Calendar.YEAR), gc.get(Calendar.MONTH), gc.get(Calendar.DAY_OF_MONTH)); + gc.add(Calendar.DAY_OF_MONTH, offset); + return gc; + } + + public void setTaskExecutionPeriod(long taskExecutionPeriod) { + this.taskExecutionPeriod = taskExecutionPeriod; + } + + private <T extends MetadataItem> PartialList<Metadata> getMetadatas(int offset, int size, String sortBy, Class<T> clazz) { + PartialList<T> items = persistenceService.getAllItems(clazz, offset, size, sortBy); + List<Metadata> details = new LinkedList<>(); + for (T definition : items.getList()) { + details.add(definition.getMetadata()); + } + return new PartialList<>(details, items.getOffset(), items.getPageSize(), items.getTotalSize()); + } + + private <T extends MetadataItem> PartialList<Metadata> getMetadatas(Query query, Class<T> clazz) { + if(query.isForceRefresh()){ + persistenceService.refresh(); + } + definitionsService.resolveConditionType(query.getCondition()); + PartialList<T> items = persistenceService.query(query.getCondition(), query.getSortby(), clazz, query.getOffset(), query.getLimit()); + List<Metadata> details = new LinkedList<>(); + for (T definition : items.getList()) { + details.add(definition.getMetadata()); + } + return new PartialList<>(details, items.getOffset(), items.getPageSize(), items.getTotalSize()); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/oasis_open/contextserver/impl/actions/ActionExecutorDispatcher.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/oasis_open/contextserver/impl/actions/ActionExecutorDispatcher.java b/services/src/main/java/org/oasis_open/contextserver/impl/actions/ActionExecutorDispatcher.java deleted file mode 100644 index c272244..0000000 --- a/services/src/main/java/org/oasis_open/contextserver/impl/actions/ActionExecutorDispatcher.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * 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.oasis_open.contextserver.impl.actions; - -import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.commons.beanutils.PropertyUtils; -import org.apache.commons.lang3.StringUtils; -import org.mvel2.MVEL; -import org.mvel2.ParserConfiguration; -import org.mvel2.ParserContext; -import org.oasis_open.contextserver.api.Event; -import org.oasis_open.contextserver.api.actions.Action; -import org.oasis_open.contextserver.api.actions.ActionExecutor; -import org.oasis_open.contextserver.api.services.EventService; -import org.osgi.framework.BundleContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ActionExecutorDispatcher { - private static final Logger logger = LoggerFactory.getLogger(ActionExecutorDispatcher.class.getName()); - private static final String VALUE_NAME_SEPARATOR = "::"; - - private BundleContext bundleContext; - - private Map<String, ActionExecutor> executors = new ConcurrentHashMap<>(); - private Map<Long, List<String>> executorsByBundle = new ConcurrentHashMap<>(); - private final Map<String, Serializable> mvelExpressions = new ConcurrentHashMap<>(); - private final Map<String, ValueExtractor> valueExtractors = new HashMap<>(11); - - private interface ValueExtractor { - Object extract(String valueAsString, Event event) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException; - } - - public ActionExecutorDispatcher() { - valueExtractors.put("profileProperty", new ValueExtractor() { - @Override - public Object extract(String valueAsString, Event event) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { - return PropertyUtils.getProperty(event.getProfile(), "properties." + valueAsString); - } - }); - valueExtractors.put("simpleProfileProperty", new ValueExtractor() { - @Override - public Object extract(String valueAsString, Event event) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { - return event.getProfile().getProperty(valueAsString); - } - }); - valueExtractors.put("sessionProperty", new ValueExtractor() { - @Override - public Object extract(String valueAsString, Event event) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { - return PropertyUtils.getProperty(event.getSession(), "properties." + valueAsString); - } - }); - valueExtractors.put("simpleSessionProperty", new ValueExtractor() { - @Override - public Object extract(String valueAsString, Event event) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { - return event.getSession().getProperty(valueAsString); - } - }); - valueExtractors.put("eventProperty", new ValueExtractor() { - @Override - public Object extract(String valueAsString, Event event) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { - return PropertyUtils.getProperty(event, valueAsString); - } - }); - valueExtractors.put("simpleEventProperty", new ValueExtractor() { - @Override - public Object extract(String valueAsString, Event event) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { - return event.getProperty(valueAsString); - } - }); - valueExtractors.put("script", new ValueExtractor() { - @Override - public Object extract(String valueAsString, Event event) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { - if (!mvelExpressions.containsKey(valueAsString)) { - ParserConfiguration parserConfiguration = new ParserConfiguration(); - parserConfiguration.setClassLoader(getClass().getClassLoader()); - mvelExpressions.put(valueAsString, MVEL.compileExpression(valueAsString, new ParserContext(parserConfiguration))); - } - Map<String, Object> ctx = new HashMap<>(); - ctx.put("event", event); - ctx.put("session", event.getSession()); - ctx.put("profile", event.getProfile()); - return MVEL.executeExpression(mvelExpressions.get(valueAsString), ctx); - } - }); - } - - - public void addExecutor(String name, long bundleId, ActionExecutor evaluator) { - executors.put(name, evaluator); - if (!executorsByBundle.containsKey(bundleId)) { - executorsByBundle.put(bundleId, new ArrayList<String>()); - } - executorsByBundle.get(bundleId).add(name); - } - - public void removeExecutors(long bundleId) { - if (executorsByBundle.containsKey(bundleId)) { - for (String s : executorsByBundle.get(bundleId)) { - executors.remove(s); - } - executorsByBundle.remove(bundleId); - } - } - - - public Action getContextualAction(Action action, Event event) { - if (!hasContextualParameter(action.getParameterValues())) { - return action; - } - - Map<String, Object> values = parseMap(event, action.getParameterValues()); - Action n = new Action(action.getActionType()); - n.setParameterValues(values); - return n; - } - - @SuppressWarnings("unchecked") - private Map<String, Object> parseMap(Event event, Map<String, Object> map) { - Map<String, Object> values = new HashMap<>(); - for (Map.Entry<String, Object> entry : map.entrySet()) { - Object value = entry.getValue(); - if (value instanceof String) { - String s = (String) value; - try { - // check if we have special values - if (s.contains(VALUE_NAME_SEPARATOR)) { - final String valueType = StringUtils.substringBefore(s, VALUE_NAME_SEPARATOR); - final String valueAsString = StringUtils.substringAfter(s, VALUE_NAME_SEPARATOR); - final ValueExtractor extractor = valueExtractors.get(valueType); - if (extractor != null) { - value = extractor.extract(valueAsString, event); - } - } - } catch (UnsupportedOperationException e) { - throw e; - } catch (Exception e) { - throw new UnsupportedOperationException(e); - } - } else if (value instanceof Map) { - value = parseMap(event, (Map<String, Object>) value); - } - values.put(entry.getKey(), value); - } - return values; - } - - @SuppressWarnings("unchecked") - private boolean hasContextualParameter(Map<String, Object> values) { - for (Map.Entry<String, Object> entry : values.entrySet()) { - Object value = entry.getValue(); - if (value instanceof String) { - String s = (String) value; - if (s.contains(VALUE_NAME_SEPARATOR) && valueExtractors.containsKey(StringUtils.substringBefore(s, VALUE_NAME_SEPARATOR))) { - return true; - } - } else if (value instanceof Map) { - if (hasContextualParameter((Map<String, Object>) value)) { - return true; - } - } - } - return false; - } - - public void setBundleContext(BundleContext bundleContext) { - this.bundleContext = bundleContext; - } - - public int execute(Action action, Event event) { - String actionKey = action.getActionType().getActionExecutor(); - if (actionKey == null) { - throw new UnsupportedOperationException("No service defined for : " + action.getActionType()); - } - - if (executors.containsKey(actionKey)) { - ActionExecutor actionExecutor = executors.get(actionKey); - return actionExecutor.execute(getContextualAction(action, event), event); - } - return EventService.NO_CHANGE; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/dc1d1520/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/ComparisonOperatorChoiceListInitializer.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/ComparisonOperatorChoiceListInitializer.java b/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/ComparisonOperatorChoiceListInitializer.java deleted file mode 100644 index 77aab5a..0000000 --- a/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/ComparisonOperatorChoiceListInitializer.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.oasis_open.contextserver.impl.conditions.initializers; - -import java.util.ArrayList; -import java.util.List; - -import org.oasis_open.contextserver.api.conditions.initializers.ChoiceListInitializer; -import org.oasis_open.contextserver.api.conditions.initializers.ChoiceListValue; -import org.oasis_open.contextserver.api.conditions.initializers.I18nSupport; -import org.osgi.framework.BundleContext; - -/** - * 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/oasis_open/contextserver/impl/conditions/initializers/ComparisonOperatorChoiceListValue.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/ComparisonOperatorChoiceListValue.java b/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/ComparisonOperatorChoiceListValue.java deleted file mode 100644 index 44759fc..0000000 --- a/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/ComparisonOperatorChoiceListValue.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.oasis_open.contextserver.impl.conditions.initializers; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.oasis_open.contextserver.api.PluginType; -import org.oasis_open.contextserver.api.conditions.initializers.ChoiceListValue; - -/** - * 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/oasis_open/contextserver/impl/conditions/initializers/CountryChoiceListInitializer.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/CountryChoiceListInitializer.java b/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/CountryChoiceListInitializer.java deleted file mode 100644 index 4fcccc9..0000000 --- a/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/CountryChoiceListInitializer.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.oasis_open.contextserver.impl.conditions.initializers; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import org.oasis_open.contextserver.api.conditions.initializers.ChoiceListInitializer; -import org.oasis_open.contextserver.api.conditions.initializers.ChoiceListValue; - -/** - * 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/oasis_open/contextserver/impl/conditions/initializers/EventPropertyChoiceListInitializer.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/EventPropertyChoiceListInitializer.java b/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/EventPropertyChoiceListInitializer.java deleted file mode 100644 index e370437..0000000 --- a/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/EventPropertyChoiceListInitializer.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.oasis_open.contextserver.impl.conditions.initializers; - -import org.oasis_open.contextserver.api.EventProperty; -import org.oasis_open.contextserver.api.conditions.initializers.ChoiceListInitializer; -import org.oasis_open.contextserver.api.conditions.initializers.ChoiceListValue; -import org.oasis_open.contextserver.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/oasis_open/contextserver/impl/conditions/initializers/EventTypeIdChoiceListInitializer.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/EventTypeIdChoiceListInitializer.java b/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/EventTypeIdChoiceListInitializer.java deleted file mode 100644 index 4703858..0000000 --- a/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/EventTypeIdChoiceListInitializer.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.oasis_open.contextserver.impl.conditions.initializers; - -import org.oasis_open.contextserver.api.conditions.initializers.ChoiceListInitializer; -import org.oasis_open.contextserver.api.conditions.initializers.ChoiceListValue; -import org.oasis_open.contextserver.api.conditions.initializers.I18nSupport; -import org.oasis_open.contextserver.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/oasis_open/contextserver/impl/conditions/initializers/GoalsChoiceListInitializer.java ---------------------------------------------------------------------- diff --git a/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/GoalsChoiceListInitializer.java b/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/GoalsChoiceListInitializer.java deleted file mode 100644 index a2683ed..0000000 --- a/services/src/main/java/org/oasis_open/contextserver/impl/conditions/initializers/GoalsChoiceListInitializer.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.oasis_open.contextserver.impl.conditions.initializers; - -import org.oasis_open.contextserver.api.Metadata; -import org.oasis_open.contextserver.api.conditions.initializers.ChoiceListInitializer; -import org.oasis_open.contextserver.api.conditions.initializers.ChoiceListValue; -import org.oasis_open.contextserver.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; - } -}
