Java tutorial
/* * Copyright 2015 Research Studios Austria Forschungsgesellschaft mBH * * This file is part of easyrec. * * easyrec is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * easyrec is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with easyrec. If not, see <http://www.gnu.org/licenses/>. */ package org.easyrec.plugin.aggregator.impl; import com.google.common.base.Strings; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.PathNotFoundException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.easyrec.plugin.aggregator.model.AggregatorConfiguration; import org.easyrec.service.core.TenantService; import org.easyrec.service.domain.TypeMappingService; import java.util.*; import java.util.Map.Entry; import org.apache.commons.collections.KeyValue; import org.apache.commons.collections.keyvalue.DefaultKeyValue; import org.easyrec.model.core.ActionTypeVO; import org.easyrec.model.core.ActionVO; import org.easyrec.model.core.ItemVO; import org.easyrec.model.core.web.Item; import org.easyrec.plugin.aggregator.AggregatorGenerator; import org.easyrec.plugin.aggregator.AggregatorService; import org.easyrec.plugin.aggregator.model.AggregatorConfigurationInt; import org.easyrec.plugin.aggregator.model.AggregatorStatistics; import org.easyrec.plugin.aggregator.model.FieldConfiguration; import org.easyrec.plugin.aggregator.store.dao.AggregatorActionDAO; import org.easyrec.plugin.aggregator.store.dao.AggregatorLogEntryDAO; import org.easyrec.service.core.impl.JSONProfileServiceImpl; import org.easyrec.store.dao.IDMappingDAO; import org.easyrec.store.dao.core.ItemDAO; import org.easyrec.store.dao.core.types.ActionTypeDAO; import org.easyrec.store.dao.core.types.ItemTypeDAO; /** * <DESCRIPTION> * <p/> * <p><b>Company: </b> * SAT, Research Studios Austria</p> * <p/> * <p><b>Copyright: </b> * (c) 2015</p> * <p/> * * @author Stephan Zavrel */ public class AggregatorServiceImpl implements AggregatorService { private TypeMappingService typeMappingService; private TenantService tenantService; private AggregatorActionDAO aggregatorActionDAO; private JSONProfileServiceImpl jsonProfileService; private ItemDAO itemDAO; private IDMappingDAO idMappingDAO; private ItemTypeDAO itemTypeDAO; private AggregatorLogEntryDAO logEntryDAO; private ActionTypeDAO actionTypeDAO; // logging private final Log logger = LogFactory.getLog(this.getClass()); public AggregatorServiceImpl(TypeMappingService typeMappingService, TenantService tenantService, AggregatorActionDAO aggregatorActionDAO, JSONProfileServiceImpl jsonProfileService, ItemDAO itemDAO, IDMappingDAO idMappingDAO, ItemTypeDAO itemTypeDAO, AggregatorLogEntryDAO logEntryDAO, ActionTypeDAO actionTypeDAO) { this.typeMappingService = typeMappingService; this.tenantService = tenantService; this.aggregatorActionDAO = aggregatorActionDAO; this.jsonProfileService = jsonProfileService; this.itemDAO = itemDAO; this.idMappingDAO = idMappingDAO; this.itemTypeDAO = itemTypeDAO; this.logEntryDAO = logEntryDAO; this.actionTypeDAO = actionTypeDAO; } @Override public List<Integer> getUsersWithActions(AggregatorConfigurationInt configurationInt) { return aggregatorActionDAO.getUsersWithActions(configurationInt); } @Override public List<ActionVO<Integer, Integer>> getActionsForUser(Integer userId, AggregatorConfigurationInt configurationInt) { return aggregatorActionDAO.getActionsForUsers(userId, configurationInt); } @Override public void aggregateUserProfile(Integer userId, List<ActionVO<Integer, Integer>> actions, AggregatorConfigurationInt configurationInt) { //if user profile does not exist, create it String userIdStr = idMappingDAO.lookup(userId); Item userItem = itemDAO.get(configurationInt.getTenantId(), userIdStr, AggregatorGenerator.ITEMTYPE_USER); LinkedHashMap<String, Object> userProfile = null; if (userItem == null) { userItem = itemDAO.add(configurationInt.getTenantId(), userIdStr, AggregatorGenerator.ITEMTYPE_USER, userIdStr, "", ""); jsonProfileService.storeProfile(userItem.getTenantId(), userItem.getItemId(), userItem.getItemType(), "{}"); } HashMap<String, HashMap<String, Integer>> tmpProfile = new HashMap<>(); ItemVO<Integer, Integer> prevItem = null; for (ActionVO<Integer, Integer> action : actions) { // only if config for actionInfo, evaluate actions, otherwise continue until next itemID,typeID if (!configurationInt.getProfileFields().isEmpty()) { // item profile content is interesting if (!action.getItem().equals(prevItem)) { // in case the item is new -> load profile and get the fields prevItem = action.getItem(); Object profile = jsonProfileService.getProfileParsed(action.getItem()); if (profile != null) { //TODO: Move out of prevItem if-clause; now multiple actions on item only count as 1!!! //get fields stuff here for (FieldConfiguration fc : configurationInt.getProfileFields().values()) { if ((fc.getItemType() == null) || (action.getItem().getType().equals(fc.getItemType()))) { addFieldToTmpProfile(fc, profile, tmpProfile, configurationInt.getWeights().get(action.getActionType())); } } } } } if (!configurationInt.getActionFields().isEmpty()) { //in case actionInfo content is interesting String actionInfo = action.getActionInfo(); if (!Strings.isNullOrEmpty(actionInfo)) { Object actionProfile = configurationInt.getConfiguration().jsonProvider().parse(actionInfo); for (FieldConfiguration fc : configurationInt.getActionFields().values()) { addFieldToTmpProfile(fc, actionProfile, tmpProfile, configurationInt.getWeights().get(action.getActionType())); } } } } userProfile = convertAndOrderProfile(tmpProfile, configurationInt); String path = "$"; String field = "upa"; if (configurationInt.getActionType() != null) { String actionType = actionTypeDAO.getTypeById(configurationInt.getTenantId(), configurationInt.getActionType()); path += ".upa"; field = actionType.toLowerCase(); } try { jsonProfileService.storeProfileFieldParsed(userItem.getTenantId(), userItem.getItemId(), userItem.getItemType(), path, field, userProfile); } catch (Exception ex) { logger.error("An error occured storing the user profile", ex); } } private void addFieldToTmpProfile(FieldConfiguration fc, Object sourceProfile, HashMap<String, HashMap<String, Integer>> userProfile, Integer weight) { try { ArrayList<String> fields = new ArrayList<>(); Object field = fc.getJsonPath().read(sourceProfile); if (!(field instanceof List)) { if (!(field instanceof String)) { field = field.toString(); } fields.add((String) field); } else { for (Object field1 : (List) field) { if (!(field1 instanceof String) && (field1 != null)) { field1 = field1.toString(); } fields.add((String) field1); } } HashMap<String, Integer> outputField = userProfile.get(fc.getOutputField()); if (outputField == null) { outputField = new HashMap<>(); userProfile.put(fc.getOutputField(), outputField); } for (String field1 : fields) { Integer counter = outputField.get(field1); if (counter == null) { counter = weight; } else { counter += weight; } outputField.put(field1, counter); } } catch (PathNotFoundException pnfe) { logger.debug("Path not found in profile " + pnfe.getMessage()); } } private LinkedHashMap<String, Object> convertAndOrderProfile( HashMap<String, HashMap<String, Integer>> tmpProfile, AggregatorConfigurationInt configuration) { LinkedHashMap<String, Object> profile = new LinkedHashMap<>(); for (String field : tmpProfile.keySet()) { Integer threshold = null; if (configuration.getActionFields().get(field) != null) { threshold = configuration.getActionFields().get(field).getThreshold(); } else if (configuration.getProfileFields().get(field) != null) { threshold = configuration.getProfileFields().get(field).getThreshold(); } if (threshold == null) threshold = Integer.MAX_VALUE; profile.put(field, convertAndOrderField(tmpProfile.get(field), threshold)); } return profile; } private List<KeyValue> convertAndOrderField(HashMap<String, Integer> tmpField, Integer threshold) { List<Entry<String, Integer>> list = new ArrayList<>(tmpField.entrySet()); Collections.sort(list, new Comparator<Entry<String, Integer>>() { @Override public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) { return o2.getValue().compareTo(o1.getValue()); //Note: exchanged o2 and o1 for descending order! } }); List<KeyValue> result = new ArrayList<>(); int i = 0; for (Iterator<Entry<String, Integer>> it = list.iterator(); it.hasNext(); i++) { if (i >= threshold) break; Entry<String, Integer> entry = it.next(); result.add(new DefaultKeyValue(entry.getKey(), entry.getValue())); } return result; } @Override public Integer getNumberOfActions(AggregatorConfigurationInt intConfiguration, Date lastRun) { return aggregatorActionDAO.getNumberOfActions(intConfiguration.getTenantId(), intConfiguration.getActionType(), lastRun); } @Override public AggregatorConfigurationInt mapTypesToConfiguration(AggregatorConfiguration configuration) throws Exception { AggregatorConfigurationInt ret; ret = new AggregatorConfigurationInt(configuration.getTenantId(), null, new ArrayList<Integer>(), null, configuration.getMaxRulesPerItem(), configuration.getDoDeltaUpdate(), configuration.getLastRun()); Integer actionId; try { actionId = typeMappingService.getIdOfActionType(configuration.getTenantId(), configuration.getActionType()); } catch (IllegalArgumentException iae) { actionId = null; String sb = "Action '" + configuration.getActionType() + "' not valid for Tenant '" + configuration.getTenantId() + "'! Action will not be considered for Aggragation!"; logger.debug(sb); } ret.setActionType(actionId); Integer typeUser; try { typeUser = itemTypeDAO.getIdOfType(configuration.getTenantId(), AggregatorGenerator.ITEMTYPE_USER); } catch (IllegalArgumentException iae) { logger.info("itemType USER not found for tenant " + configuration.getTenantId() + "! Adding now."); typeUser = itemTypeDAO.insertOrUpdate(configuration.getTenantId(), AggregatorGenerator.ITEMTYPE_USER, Boolean.TRUE); } ret.setItemTypeUser(typeUser); for (String type : configuration.getItemTypes()) { Integer itemTypeId = typeMappingService.getIdOfItemType(configuration.getTenantId(), type); if (itemTypeId != null) { ret.getItemTypes().add(itemTypeId); } else { logger.info("ItemType '" + type + "' not valid for Tenant '" + configuration.getTenantId() + "'! ItemType will not be considered in rulemining!"); } } if (ret.getItemTypes() == null) { String sb = "No valid ItemTypes defined for Tenant '" + configuration.getTenantId() + "'! Skipping this rulemining configuration!"; logger.info(sb); throw new Exception(sb); } Integer assocTypeId = typeMappingService.getIdOfAssocType(configuration.getTenantId(), configuration.getAssociationType()); if (assocTypeId == null) { String sb = "AssocType '" + configuration.getAssociationType() + "' not valid for tenant '" + configuration.getTenantId() + "'! Skipping analysis!"; logger.info(sb); throw new Exception(sb); } ret.setAssocType(assocTypeId); Set<ActionTypeVO> actionTypes = actionTypeDAO.getTypeVOs(configuration.getTenantId()); Map<Integer, Integer> weights = ret.getWeights(); for (ActionTypeVO actionType : actionTypes) { weights.put(actionType.getId(), actionType.getWeight()); } if (!Strings.isNullOrEmpty(configuration.getActionInfoFields())) { String[] fields = configuration.getActionInfoFields().replaceAll("\n", "").split(";"); for (String field : fields) { String[] fieldInfo = field.split(","); JsonPath jp = JsonPath.compile(fieldInfo[1]); FieldConfiguration fc = new FieldConfiguration(fieldInfo[0], jp); if (fieldInfo.length > 2) { Integer it = null; try { it = itemTypeDAO.getIdOfType(configuration.getTenantId(), fieldInfo[2]); } catch (IllegalArgumentException iae) { logger.debug("ItemType " + fieldInfo[2] + " not found for tenant " + configuration.getTenantId() + "! Will aggregate all itemTypes."); it = null; } fc.setItemType(it); } if (fieldInfo.length > 3) { //percentage Threshold not feasible because of deltaUpdate Integer thres = null; try { thres = Integer.parseInt(fieldInfo[3]); } catch (NumberFormatException iae) { logger.debug("Threshold is not an integer: " + fieldInfo[3] + " !" + configuration.getTenantId() + "! Ignoring threshold."); thres = null; } fc.setThreshold(thres); ret.setHasActionFieldThreshold(true); } ret.getActionFields().put(fc.getOutputField(), fc); } } if (!Strings.isNullOrEmpty(configuration.getItemProfileFields())) { String[] fields = configuration.getItemProfileFields().replaceAll("\n", "").split(";"); for (String field : fields) { String[] fieldInfo = field.split(","); JsonPath jp = JsonPath.compile(fieldInfo[1]); FieldConfiguration fc = new FieldConfiguration(fieldInfo[0], jp); if (fieldInfo.length > 2) { Integer it = null; try { it = itemTypeDAO.getIdOfType(configuration.getTenantId(), fieldInfo[2]); } catch (IllegalArgumentException iae) { logger.debug("ItemType " + fieldInfo[2] + " not found for tenant " + configuration.getTenantId() + "! Will aggregate all itemTypes."); it = null; } fc.setItemType(it); } if (fieldInfo.length > 3) { Integer thres = null; try { thres = Integer.parseInt(fieldInfo[3]); } catch (NumberFormatException iae) { logger.debug("Threshold is not an integer: " + fieldInfo[3] + " !" + configuration.getTenantId() + "! Ignoring threshold."); thres = null; } fc.setThreshold(thres); ret.setHasProfileFieldThreshold(true); } ret.getProfileFields().put(fc.getOutputField(), fc); } } if (configuration.getDoDeltaUpdate()) { ret.setLastRun(logEntryDAO.getLastLogEntryForTenant(configuration.getTenantId(), assocTypeId, AggregatorGenerator.ID, AggregatorGenerator.VERSION)); } return ret; } private void pruneProfile(LinkedHashMap<String, Object> profileField, Integer threshold) { for (Iterator<Entry<String, Object>> it = profileField.entrySet().iterator(); it.hasNext();) { Entry<String, Object> entry = it.next(); Integer val = (Integer) entry.getValue(); if (val < threshold) { it.remove(); // using iterator to avoid ConcurrentModificationExceptions!! } } } @Override public void removeOldRules(AggregatorConfigurationInt configuration, AggregatorStatistics stats) { // itemAssocDAO.removeItemAssocByTenant(configuration.getTenantId(), configuration.getAssocType(), // typeMappingService.getIdOfSourceType(configuration.getTenantId(), AggregatorGenerator.ID.toString() + "/" + AggregatorGenerator.VERSION), // stats.getStartDate()); } // getters and setters public TypeMappingService getTypeMappingService() { return typeMappingService; } public void setTypeMappingService(TypeMappingService typeMappingService) { this.typeMappingService = typeMappingService; } public TenantService getTenantService() { return tenantService; } public void setTenantService(TenantService tenantService) { this.tenantService = tenantService; } public AggregatorActionDAO getAggregatorActionDAO() { return aggregatorActionDAO; } public void setAggregatorActionDAO(AggregatorActionDAO aggregatorActionDAO) { this.aggregatorActionDAO = aggregatorActionDAO; } public JSONProfileServiceImpl getJsonProfileService() { return jsonProfileService; } public void setJsonProfileService(JSONProfileServiceImpl jsonProfileService) { this.jsonProfileService = jsonProfileService; } public ItemDAO getItemDAO() { return itemDAO; } public void setItemDAO(ItemDAO itemDAO) { this.itemDAO = itemDAO; } public IDMappingDAO getIdMappingDAO() { return idMappingDAO; } public void setIdMappingDAO(IDMappingDAO idMappingDAO) { this.idMappingDAO = idMappingDAO; } public ItemTypeDAO getItemTypeDAO() { return itemTypeDAO; } public void setItemTypeDAO(ItemTypeDAO itemTypeDAO) { this.itemTypeDAO = itemTypeDAO; } public AggregatorLogEntryDAO getLogEntryDAO() { return logEntryDAO; } public void setLogEntryDAO(AggregatorLogEntryDAO logEntryDAO) { this.logEntryDAO = logEntryDAO; } }