/* * 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.clustering.recommender.jdo; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.jdo.PersistenceManager; import javax.jdo.Query; import io.seldon.clustering.recommender.ClusterReferrer; import io.seldon.clustering.recommender.IClusterFromReferrer; import io.seldon.db.jdo.JDOFactory; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; public class JdoClusterFromReferrer implements IClusterFromReferrer { private static Logger logger = Logger.getLogger( JdoClusterFromReferrer.class.getName() ); public static final int RELOAD_SECS = 900; String client; ConcurrentHashMap<String,Integer> clusterMap = new ConcurrentHashMap<>(); ReentrantReadWriteLock lock; Timer reloadTimer; public JdoClusterFromReferrer(String client) { this.client = client; lock = new ReentrantReadWriteLock(true); updateClusterMap(); startReloadTransientTimer(RELOAD_SECS); } public JdoClusterFromReferrer(String client,int reloadSecs) { this.client = client; lock = new ReentrantReadWriteLock(true); updateClusterMap(); startReloadTransientTimer(reloadSecs); } private void updateClusterMap() { PersistenceManager pm = JDOFactory.get().getPersistenceManager(client); if (pm != null) { Query query = pm.newQuery( "javax.jdo.query.SQL","select referrer,cluster from cluster_referrer"); query.setResultClass(ClusterReferrer.class); List<ClusterReferrer> res = (List<ClusterReferrer>) query.execute(); logger.info("Getting READ lock"); lock.writeLock().lock(); try { ConcurrentHashMap<String,Integer> clusterMapNew = new ConcurrentHashMap<>(); for(ClusterReferrer r : res) { logger.info("Updating cluster map for "+client+" with referrer:"+r.getReferrer()+" cluster:"+r.getCluster()); clusterMapNew.put(r.getReferrer(), r.getCluster()); } clusterMap = clusterMapNew; } finally { lock.writeLock().unlock(); logger.info("Released WRITE lock"); } } else logger.error("Failed to get persistence manager for client "+client); } @Override public Set<Integer> getClusters(String referrer) { if (StringUtils.isNotEmpty(referrer)) { lock.readLock().lock(); try { Set<Integer> clusters = new HashSet<>(); for(Map.Entry<String,Integer> e : clusterMap.entrySet()) { if (referrer.startsWith(e.getKey())) clusters.add(e.getValue()); } return clusters; } finally { lock.readLock().unlock(); } } return null; } public void shutdown() { if (reloadTimer != null) reloadTimer.cancel(); } private void startReloadTransientTimer(final int delaySeconds) { reloadTimer = new Timer(true); int period = 1000 * delaySeconds; int delay = 1000 * delaySeconds; reloadTimer.scheduleAtFixedRate(new TimerTask() { public void run() { try { logger.info("About to update cluster map for client "+client); updateClusterMap(); logger.info("Updated cluster map for client "+client); } catch (Exception e) { logger.error("Caught exception trying to load transient clusters",e); } finally { JDOFactory.get().cleanupPM(); } } }, delay, period); } }