Java tutorial
/* * Seldon -- open source prediction engine * ======================================= * * Copyright 2011-2015 Seldon Technologies Ltd and Rummble Ltd (http://www.seldon.io/) * * ******************************************************************************************** * * Licensed 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 io.seldon.api.resource.service; import io.seldon.api.APIException; import io.seldon.api.Constants; import io.seldon.api.Util; import io.seldon.api.caching.ClientIdCacheStore; import io.seldon.api.resource.ActionBean; import io.seldon.api.resource.ConsumerBean; import io.seldon.api.resource.DimensionBean; import io.seldon.api.resource.ItemBean; import io.seldon.api.resource.ItemTypeBean; import io.seldon.api.resource.ListBean; import io.seldon.api.resource.ResourceBean; import io.seldon.general.Dimension; import io.seldon.general.Item; import io.seldon.general.ItemAttr; import io.seldon.general.ItemStorage; import io.seldon.general.ItemType; import io.seldon.general.RecommendationStorage; import io.seldon.memcache.MemCacheKeys; import io.seldon.memcache.MemCachePeer; import io.seldon.recommendation.RecommendationPeer; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author claudio */ @Service public class ItemService { private static Logger logger = Logger.getLogger(ItemService.class.getName()); public static final int ITEM_NAME_LENGTH = 255; private static final int ITEMS_CACHING_TIME_SECS = 300; @Autowired private RecommendationPeer recommendationPeer; @Autowired private RecommendationStorage recommendationStorage; @Autowired private ItemStorage itemStorage; @Autowired private ClientIdCacheStore idCache; public ItemBean getItem(final ConsumerBean c, final String iid, final boolean full) throws APIException { String memKey = MemCacheKeys.getItemBeanKey(c.getShort_name(), iid, full); ItemBean bean = (ItemBean) MemCachePeer.get(memKey); if (bean == null) { Item i = Util.getItemPeer(c).getItem(iid); if (i == null) { // TODO We should throw a checked exception (using APIException for now; minimal surprises). throw new APIException(APIException.ITEM_NOT_FOUND); } bean = new ItemBean(i, full, c); if (Constants.CACHING) MemCachePeer.put(memKey, bean, Constants.CACHING_TIME); } return bean; } public ItemBean getItemLocalized(final ConsumerBean c, final String iid, final boolean full, String locale) throws APIException { String memKey = MemCacheKeys.getItemBeanKeyWithLocale(c.getShort_name(), iid, full, locale); ItemBean bean = (ItemBean) MemCachePeer.get(memKey); if (bean == null) { Item i = Util.getItemPeer(c).getItem(iid); if (i == null) { // TODO We should throw a checked exception (using APIException for now; minimal surprises). throw new APIException(APIException.ITEM_NOT_FOUND); } bean = new ItemBean(i, full, c, locale); if (Constants.CACHING) MemCachePeer.put(memKey, bean, Constants.CACHING_TIME); } return bean; } public static ListBean getItems(ConsumerBean c, int limit, boolean full, String sort, int dimension) throws APIException { ListBean bean = (ListBean) MemCachePeer .get(MemCacheKeys.getItemsBeanKey(c.getShort_name(), full, sort, dimension)); bean = Util.getLimitedBean(bean, limit); if (bean == null) { bean = new ListBean(); bean.setRequested(limit); Collection<Item> res = null; if (sort == null || sort.length() == 0 || sort.toLowerCase().equals(Constants.SORT_ID)) { res = Util.getItemPeer(c).getItems(limit, dimension, c); } else if (sort.toLowerCase().equals(Constants.SORT_NAME)) { res = Util.getItemPeer(c).getAlphabeticItems(limit, dimension, c); } else if (sort.toLowerCase().equals(Constants.SORT_LAST_ACTION)) { res = Util.getItemPeer(c).getRecentItems(limit, dimension, c); } if (res != null) { for (Item i : res) { bean.addBean(new ItemBean(i, full, c)); } } if (bean != null) bean.setSize(bean.getList().size()); if (Constants.CACHING) MemCachePeer.put(MemCacheKeys.getItemsBeanKey(c.getShort_name(), full, sort, dimension), bean, ITEMS_CACHING_TIME_SECS); } return bean; } public static ListBean getItemsByName(ConsumerBean c, int limit, boolean full, String name, int dimension) throws APIException { ListBean bean = (ListBean) MemCachePeer .get(MemCacheKeys.getItemsBeanKeyByName(c.getShort_name(), full, name, dimension)); bean = Util.getLimitedBean(bean, limit); if (bean == null) { bean = new ListBean(); Collection<Item> res = Util.getItemPeer(c).getItemsByName(name, limit, dimension, c); for (Item i : res) { bean.addBean(new ItemBean(i, full, c)); } bean.setRequested(limit); bean.setSize(res.size()); if (Constants.CACHING) MemCachePeer.put(MemCacheKeys.getItemsBeanKeyByName(c.getShort_name(), full, name, dimension), bean, ITEMS_CACHING_TIME_SECS); } return bean; } public String[] getDimensionName(ConsumerBean c, int dimension) { return Util.getItemPeer(c).getAttributesNames(dimension); } public static DimensionBean getDimension(ConsumerBean c, int dimension) throws APIException { if (dimension == Constants.DEFAULT_DIMENSION) return null; DimensionBean bean = (DimensionBean) MemCachePeer .get(MemCacheKeys.getDimensionBeanKey(c.getShort_name(), dimension)); if (bean == null) { bean = new DimensionBean(); bean.setDimId(dimension); int itemType = Util.getItemPeer(c).getDimensionItemType(dimension); Integer[] attr = Util.getItemPeer(c).getAttributes(dimension); String[] attrName = Util.getItemPeer(c).getAttributesNames(dimension); bean.setItemType(itemType); bean.setAttr(attr[0]); bean.setVal(attr[1]); // TODO address this properly: if (attrName == null) { bean.setAttrName("<all>"); bean.setValName("<all>"); } else { bean.setAttrName(attrName[0]); bean.setValName(attrName[1]); } MemCachePeer.put(MemCacheKeys.getDimensionBeanKey(c.getShort_name(), dimension), bean, Constants.CACHING_TIME); } return bean; } public static DimensionBean getDimension(ConsumerBean c, int attr, int val) throws APIException { DimensionBean bean = (DimensionBean) MemCachePeer .get(MemCacheKeys.getDimensionBeanKey(c.getShort_name(), attr, val)); if (bean == null) { bean = new DimensionBean(); bean.setAttr(attr); bean.setVal(val); int dimension = Util.getItemPeer(c).getDimension(attr, val); int itemType = Util.getItemPeer(c).getDimensionItemType(dimension); String[] attrName = Util.getItemPeer(c).getAttributesNames(dimension); bean.setDimId(dimension); bean.setItemType(itemType); bean.setAttrName(attrName[0]); bean.setValName(attrName[1]); MemCachePeer.put(MemCacheKeys.getDimensionBeanKey(c.getShort_name(), attr, val), bean, Constants.CACHING_TIME); } return bean; } public static DimensionBean getDimension(ConsumerBean c, String attrName, String valName) throws APIException { DimensionBean bean = (DimensionBean) MemCachePeer .get(MemCacheKeys.getDimensionBeanKey(c.getShort_name(), attrName, valName)); if (bean == null) { int dimension = Util.getItemPeer(c).getDimension(attrName, valName); if (dimension != Constants.DEFAULT_DIMENSION) { bean = new DimensionBean(); bean.setAttrName(attrName); bean.setValName(valName); int itemType = Util.getItemPeer(c).getDimensionItemType(dimension); Integer[] attr = Util.getItemPeer(c).getAttributes(dimension); bean.setDimId(dimension); bean.setItemType(itemType); bean.setAttr(attr[0]); bean.setVal(attr[1]); MemCachePeer.put(MemCacheKeys.getDimensionBeanKey(c.getShort_name(), attrName, valName), bean, Constants.CACHING_TIME); } else return null; } return bean; } public static ListBean getDimensions(ConsumerBean c) throws APIException { ListBean bean = (ListBean) MemCachePeer.get(MemCacheKeys.getDimensionsBeanKey(c.getShort_name())); if (bean == null) { bean = new ListBean(); Collection<Dimension> dims = Util.getItemPeer(c).getDimensions(); for (Dimension d : dims) { bean.addBean(new DimensionBean(d)); } MemCachePeer.put(MemCacheKeys.getDimensionsBeanKey(c.getShort_name()), bean, Constants.CACHING_TIME); } return bean; } public static Integer getDimensionbyItemType(ConsumerBean c, int itemType) throws APIException { Integer res = (Integer) MemCachePeer .get(MemCacheKeys.getDimensionBeanByItemTypeKey(c.getShort_name(), itemType)); if (res == null) { res = Util.getItemPeer(c).getDimensionByItemType(itemType); if (res != null) MemCachePeer.put(MemCacheKeys.getDimensionBeanByItemTypeKey(c.getShort_name(), itemType), res, Constants.CACHING_TIME); else logger.warn("Can't get dimension for item type " + itemType); } return res; } public Long getInternalItemId(ConsumerBean c, String id) throws APIException { Long res = null; res = idCache.getInternalItemId(c.getShort_name(), id); if (res == null) res = (Long) MemCachePeer.get(MemCacheKeys.getItemInternalId(c.getShort_name(), id)); if (res == null) { Item i = Util.getItemPeer(c).getItem(id); if (i != null) { res = i.getItemId(); idCache.putItemId(c.getShort_name(), id, res); cacheInternalItemId(c, id, res); } else { logger.info("getInternalItemId(" + id + "): ITEM NOT FOUND"); throw new APIException(APIException.ITEM_NOT_FOUND); } } return res; } /** * Cache an item's internal ID keyed by client ID * @param consumerBean - * @param clientId the client item ID (cache key) * @param internalId the internal item ID (cache value) */ public static void cacheInternalItemId(ConsumerBean consumerBean, String clientId, Long internalId) { final String clientIdKey = MemCacheKeys.getItemInternalId(consumerBean.getShort_name(), clientId); MemCachePeer.put(clientIdKey, internalId, Constants.CACHING_TIME); } public String getClientItemId(ConsumerBean c, Long id) throws APIException { if (id == null) { throw new APIException(APIException.ITEM_NOT_FOUND); } String res = null; res = idCache.getExternalItemId(c.getShort_name(), id); if (res == null) res = (String) MemCachePeer.get(MemCacheKeys.getItemClientId(c.getShort_name(), id)); if (res == null) { Item i = Util.getItemPeer(c).getItem(id); if (i != null) { res = i.getClientItemId(); idCache.putItemId(c.getShort_name(), res, id); cacheClientItemId(c, id, res); } else { logger.info("getClientItemId(" + id + "): ITEM NOT FOUND"); throw new APIException(APIException.ITEM_NOT_FOUND); } } return res; } /** * Cache an item's client ID keyed by item ID * @param consumerBean - * @param internalId the internal item ID (cache key) * @param clientId the client item ID (cache value) */ public static void cacheClientItemId(ConsumerBean consumerBean, Long internalId, String clientId) { final String internalItemKey = MemCacheKeys.getItemClientId(consumerBean.getShort_name(), internalId); MemCachePeer.put(internalItemKey, clientId, Constants.CACHING_TIME); } /** * Bidirectionally cache the supplied item * (see {@link ItemService#cacheClientItemId(io.seldon.api.resource.ConsumerBean, Long, String)} * and {@link ItemService#cacheInternalItemId(io.seldon.api.resource.ConsumerBean, String, Long)}). * @param consumerBean - * @param internalId user ID * @param clientId client ID */ public static void cacheItem(ConsumerBean consumerBean, Long internalId, String clientId) { cacheClientItemId(consumerBean, internalId, clientId); cacheInternalItemId(consumerBean, clientId, internalId); } public static String getAttrType(ConsumerBean c, int itemType, String attrName) { String res = (String) MemCachePeer.get(MemCacheKeys.getItemAttrType(c.getShort_name(), itemType, attrName)); if (res == null) { ItemAttr a = Util.getItemPeer(c).getItemAttr(itemType, attrName); if (a != null) { res = a.getType(); MemCachePeer.put(MemCacheKeys.getItemAttrType(c.getShort_name(), itemType, attrName), res, Constants.CACHING_TIME); } } return res; } private static void truncateItemName(ItemBean itemBean, int newLength) { final String name = itemBean.getName(); final String truncatedName = StringUtils.left(name, newLength); logger.info("Truncating itemBean name '" + name + "' to '" + truncatedName + "'"); itemBean.setName(truncatedName); } public Item addItem(ConsumerBean c, ItemBean bean) { //check if the item is already in the system try { getInternalItemId(c, bean.getId()); throw new APIException(APIException.ITEM_DUPLICATED); } catch (APIException e) { if (e.getError_id() != APIException.ITEM_NOT_FOUND) { throw e; } } truncateItemName(bean, ITEM_NAME_LENGTH); Item i = bean.createItem(c); // double check the type is valid, ItemType type = ItemService.getItemType(c, i.getType()); if (type == null) { throw new APIException(APIException.ITEM_TYPE_NOT_FOUND); } i = Util.getItemPeer(c).addItem(i, c); long itemId = i.getItemId(); if (bean.getAttributesName() != null && bean.getAttributesName().size() > 0) { Util.getItemPeer(c).addItemAttributeNames(itemId, type.getTypeId(), bean.getAttributesName(), c); } else if (bean.getAttributes() != null && bean.getAttributes().size() > 0) { Util.getItemPeer(c).addItemAttribute(itemId, type.getTypeId(), bean.getAttributes(), c); } return i; } public void updateItem(ConsumerBean c, ItemBean bean) { Long itemId = null; truncateItemName(bean, ITEM_NAME_LENGTH); //check if the item is already in the system try { itemId = getInternalItemId(c, bean.getId()); Item i = bean.createItem(c); i.setItemId(itemId); // i = Util.getItemPeer(c).addItem(i,c); ItemType type = ItemService.getItemType(c, i.getType()); if (type == null) { throw new APIException(APIException.ITEM_TYPE_NOT_FOUND); } i = Util.getItemPeer(c).saveOrUpdate(i, c); // TODO: if (bean.getAttributesName() != null && bean.getAttributesName().size() > 0) { Util.getItemPeer(c).addItemAttributeNames(itemId, bean.getType(), bean.getAttributesName(), c); } else if (bean.getAttributes() != null && bean.getAttributes().size() > 0) { Util.getItemPeer(c).addItemAttribute(itemId, bean.getType(), bean.getAttributes(), c); } //invalidate memcache entry (both full and short version) MemCachePeer.delete(MemCacheKeys.getItemBeanKey(c.getShort_name(), bean.getId(), true)); MemCachePeer.delete(MemCacheKeys.getItemBeanKey(c.getShort_name(), bean.getId(), false)); MemCachePeer.delete(MemCacheKeys.getItemDimensions(c.getShort_name(), itemId)); } catch (APIException e) { if (e.getError_id() == APIException.ITEM_NOT_FOUND) { try { addItem(c, bean); } catch (APIException additionException) { if (additionException.getError_id() == APIException.ITEM_DUPLICATED) { throw new APIException(APIException.CONCURRENT_ITEM_UPDATE); } else { throw additionException; } } } else { throw e; } } } public static Collection<Integer> getItemDimensions(ConsumerBean c, long itemId) { Collection<Integer> res = (Collection<Integer>) MemCachePeer .get(MemCacheKeys.getItemDimensions(c.getShort_name(), itemId)); if (res == null) { res = Util.getItemPeer(c).getItemDimensions(itemId); MemCachePeer.put(MemCacheKeys.getItemDimensions(c.getShort_name(), itemId), res, Constants.CACHING_TIME); } return res; } public static Integer getItemCluster(ConsumerBean c, long itemId) { Integer res = (Integer) MemCachePeer.get(MemCacheKeys.getItemCluster(c.getShort_name(), itemId)); if (res == null) { res = Util.getItemPeer(c).getItemCluster(itemId); MemCachePeer.put(MemCacheKeys.getItemCluster(c.getShort_name(), itemId), res, ITEMS_CACHING_TIME_SECS); } return res; } public static ItemType getItemType(ConsumerBean c, String name) { ItemType res = (ItemType) MemCachePeer.get(MemCacheKeys.getItemTypeByName(c.getShort_name(), name)); if (res == null) { ItemType t = Util.getItemPeer(c).getItemType(name); if (t == null) { throw new APIException(APIException.ITEM_TYPE_NOT_FOUND); } MemCachePeer.put(MemCacheKeys.getItemTypeByName(c.getShort_name(), name), t, ITEMS_CACHING_TIME_SECS); res = t; } return res; } public static ItemType getItemType(ConsumerBean c, int typeId) { ItemType res = (ItemType) MemCachePeer.get(MemCacheKeys.getItemTypeById(c.getShort_name(), typeId)); if (res == null) { ItemType t = Util.getItemPeer(c).getItemType(typeId); MemCachePeer.put(MemCacheKeys.getItemTypeById(c.getShort_name(), typeId), t, ITEMS_CACHING_TIME_SECS); res = t; } return res; } public static ResourceBean getItemTypes(ConsumerBean c) { ListBean bean = (ListBean) MemCachePeer.get(MemCacheKeys.getItemTypes(c.getShort_name())); if (bean == null) { bean = new ListBean(); Collection<ItemType> types = Util.getItemPeer(c).getItemTypes(); for (ItemType t : types) { bean.addBean(new ItemTypeBean(t)); } MemCachePeer.put(MemCacheKeys.getItemTypes(c.getShort_name()), bean, ITEMS_CACHING_TIME_SECS); } return bean; } public static List<String> getItemSemanticAttributes(ConsumerBean c, long itemId) { List<String> res = (List<String>) MemCachePeer .get(MemCacheKeys.getItemSemanticAttributes(c.getShort_name(), itemId)); if (res == null) { res = Util.getItemPeer(c).getItemSemanticAttributes(itemId); MemCachePeer.put(MemCacheKeys.getItemSemanticAttributes(c.getShort_name(), itemId), res, Constants.CACHING_TIME); } return res; } public static ItemBean filter(ItemBean itemBean, List<String> attributeList) { if (attributeList != null && attributeList.size() > 0) { Map<String, String> oldAttributes = itemBean.getAttributesName(); Map<String, String> newAttributes = new HashMap<>(); for (String attribute : attributeList) { newAttributes.put(attribute, oldAttributes.get(attribute)); } itemBean.setAttributesName(newAttributes); } return itemBean; } public void updateIgnoredItems(ConsumerBean consumerBean, ActionBean actionBean, List<Long> ignoredFromLastRecs) { Set<Long> resultingSet = new HashSet<>(); Set<Long> ignoredItems = itemStorage.retrieveIgnoredItems(consumerBean.getShort_name(), actionBean.getUser()); if (ignoredItems != null) { resultingSet.addAll(ignoredItems); } resultingSet.addAll(ignoredFromLastRecs); itemStorage.persistIgnoredItems(consumerBean.getShort_name(), actionBean.getUser(), resultingSet); } public Map<String, Integer> getDimensionIdsForItem(ConsumerBean c, long itemId) { final String key = MemCacheKeys.getItemAttrDims(c.getShort_name(), itemId); Map<String, Integer> res = (Map<String, Integer>) MemCachePeer.get(key); if (res == null) { res = Util.getItemPeer(c).getDimensionIdsForItem(itemId); MemCachePeer.put(key, res, Constants.CACHING_TIME); } return res; } }