Moving vclass group caching out of BrowsController and into its own object.
This commit is contained in:
parent
25b14ad78c
commit
a4ed705668
3 changed files with 395 additions and 306 deletions
|
@ -6,6 +6,7 @@ import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -148,15 +149,21 @@ public class VitroRequest implements HttpServletRequest {
|
||||||
setAttribute("portal", p);
|
setAttribute("portal", p);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPortalId(){
|
public int getPortalId(){
|
||||||
String idstr = (String)getAttribute("home");
|
String idstr = (String)getAttribute("home");
|
||||||
if( idstr == null )
|
|
||||||
throw new Error("home parameter was not set in request");
|
if( idstr == null ){
|
||||||
|
WebappDaoFactory wdf = getWebappDaoFactory();
|
||||||
|
Portal[] portals = wdf.getPortalDao().getAllPortals().toArray(new Portal[0]);
|
||||||
|
return portals[0].getPortalId();
|
||||||
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
return Integer.parseInt(idstr);
|
return Integer.parseInt(idstr);
|
||||||
}catch( Throwable th){
|
}catch( Throwable th){
|
||||||
throw new Error("home parameter was not set in request");
|
throw new Error("home parameter was not set in request");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public void setPortalId(String in){
|
public void setPortalId(String in){
|
||||||
setAttribute("home",in);
|
setAttribute("home",in);
|
||||||
|
|
|
@ -37,39 +37,16 @@ import edu.cornell.mannlib.vitro.webapp.dao.filtering.WebappDaoFactoryFiltering;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilterUtils;
|
import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilterUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters;
|
import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.ModelContext;
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.ModelContext;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.VClassGroupCache;
|
||||||
import edu.cornell.mannlib.vitro.webapp.flags.PortalFlag;
|
import edu.cornell.mannlib.vitro.webapp.flags.PortalFlag;
|
||||||
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.VClassGroupTemplateModel;
|
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.VClassGroupTemplateModel;
|
||||||
|
|
||||||
public class BrowseController extends FreemarkerHttpServlet {
|
public class BrowseController extends FreemarkerHttpServlet {
|
||||||
static final long serialVersionUID=2006030721126L;
|
static final long serialVersionUID=2006030721126L;
|
||||||
|
|
||||||
private transient ConcurrentHashMap<Integer, List> _groupListMap
|
|
||||||
= new ConcurrentHashMap<Integer, List>();
|
|
||||||
private transient ConcurrentLinkedQueue<String> _rebuildQueue
|
|
||||||
= new ConcurrentLinkedQueue<String>();
|
|
||||||
private RebuildGroupCacheThread _cacheRebuildThread;
|
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(BrowseController.class);
|
private static final Log log = LogFactory.getLog(BrowseController.class);
|
||||||
|
|
||||||
private static final String TEMPLATE_DEFAULT = "classGroups.ftl";
|
private static final String TEMPLATE_DEFAULT = "classGroups.ftl";
|
||||||
|
|
||||||
public void init(javax.servlet.ServletConfig servletConfig)
|
|
||||||
throws javax.servlet.ServletException {
|
|
||||||
super.init(servletConfig);
|
|
||||||
ServletContext sContext = servletConfig.getServletContext();
|
|
||||||
|
|
||||||
BrowseControllerChangeListener bccl = new BrowseControllerChangeListener(this);
|
|
||||||
ModelContext.getJenaOntModel(sContext).register(bccl);
|
|
||||||
ModelContext.getBaseOntModel(sContext).register(bccl);
|
|
||||||
ModelContext.getInferenceOntModel(sContext).register(bccl);
|
|
||||||
ModelContext.getUnionOntModelSelector(sContext).getABoxModel().register(bccl);
|
|
||||||
|
|
||||||
_rebuildQueue.add(REBUILD_EVERY_PORTAL);
|
|
||||||
_cacheRebuildThread = new RebuildGroupCacheThread(this);
|
|
||||||
_cacheRebuildThread.setDaemon(true);
|
|
||||||
_cacheRebuildThread.start();
|
|
||||||
_cacheRebuildThread.informOfQueueChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getTitle(String siteName) {
|
protected String getTitle(String siteName) {
|
||||||
|
@ -85,22 +62,27 @@ public class BrowseController extends FreemarkerHttpServlet {
|
||||||
|
|
||||||
if( vreq.getParameter("clearcache") != null ) //mainly for debugging
|
if( vreq.getParameter("clearcache") != null ) //mainly for debugging
|
||||||
clearGroupCache();
|
clearGroupCache();
|
||||||
|
|
||||||
//PortalFlag portalState= vreq.getPortalFlag();
|
int portalId = vreq.getPortalId();
|
||||||
|
|
||||||
int portalId = vreq.getPortal().getPortalId();
|
List<VClassGroup> groups = null;
|
||||||
List<VClassGroup> groups = getGroups(vreq.getWebappDaoFactory().getVClassGroupDao(), portalId);
|
VClassGroupCache vcgc = VClassGroupCache.getVClassGroupCache(getServletContext());
|
||||||
_groupListMap.put(portalId, groups);
|
if( vcgc == null ){
|
||||||
if (groups == null || groups.isEmpty()) {
|
log.error("Could not get VClassGroupCache");
|
||||||
message = "There are not yet any items in the system.";
|
message = "The system is not configured correctly. Please check your logs for error messages.";
|
||||||
}
|
}else{
|
||||||
else {
|
groups =vcgc.getGroups( vreq.getPortalId());
|
||||||
List<VClassGroupTemplateModel> vcgroups = new ArrayList<VClassGroupTemplateModel>(groups.size());
|
if (groups == null || groups.isEmpty()) {
|
||||||
for (VClassGroup group : groups) {
|
message = "There are not yet any items in the system.";
|
||||||
vcgroups.add(new VClassGroupTemplateModel(group));
|
}
|
||||||
}
|
else {
|
||||||
body.put("classGroups", vcgroups);
|
List<VClassGroupTemplateModel> vcgroups = new ArrayList<VClassGroupTemplateModel>(groups.size());
|
||||||
}
|
for (VClassGroup group : groups) {
|
||||||
|
vcgroups.add(new VClassGroupTemplateModel(group));
|
||||||
|
}
|
||||||
|
body.put("classGroups", vcgroups);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
body.put("message", message);
|
body.put("message", message);
|
||||||
|
@ -109,268 +91,8 @@ public class BrowseController extends FreemarkerHttpServlet {
|
||||||
|
|
||||||
return new TemplateResponseValues(templateName, body);
|
return new TemplateResponseValues(templateName, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy(){
|
|
||||||
_cacheRebuildThread.kill();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List getGroups( VClassGroupDao vcgDao, int portalId) {
|
protected void clearGroupCache(){
|
||||||
return getGroups( vcgDao, portalId, INCLUDE_INDIVIDUAL_COUNT);
|
VClassGroupCache.getVClassGroupCache(getServletContext()).clearGroupCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List getGroups( VClassGroupDao vcgDao, int portalId, boolean includeIndividualCount ){
|
|
||||||
List grp = _groupListMap.get(portalId);
|
|
||||||
if( grp == null ){
|
|
||||||
log.debug("needed to build vclassGroups for portal " + portalId);
|
|
||||||
// Get all classgroups, each populated with a list of their member vclasses
|
|
||||||
List groups = vcgDao.getPublicGroupsWithVClasses(ORDER_BY_DISPLAYRANK, !INCLUDE_UNINSTANTIATED, includeIndividualCount);
|
|
||||||
|
|
||||||
// remove classes that have been configured to be hidden from search results
|
|
||||||
vcgDao.removeClassesHiddenFromSearch(groups);
|
|
||||||
|
|
||||||
// now cull out the groups with no populated classes
|
|
||||||
vcgDao.removeUnpopulatedGroups(groups);
|
|
||||||
|
|
||||||
return groups;
|
|
||||||
} else {
|
|
||||||
return grp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean ORDER_BY_DISPLAYRANK = true;
|
|
||||||
private static boolean INCLUDE_UNINSTANTIATED = true;
|
|
||||||
private static boolean INCLUDE_INDIVIDUAL_COUNT = true;
|
|
||||||
|
|
||||||
void requestCacheUpdate(String portalUri){
|
|
||||||
log.debug("requesting update for portal " + portalUri);
|
|
||||||
_rebuildQueue.add(portalUri);
|
|
||||||
_cacheRebuildThread.informOfQueueChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized void refreshGroupCache() {
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
try{
|
|
||||||
boolean rebuildAll = false;
|
|
||||||
HashSet<String> portalURIsToRebuild = new HashSet<String>();
|
|
||||||
String portalUri;
|
|
||||||
while ( null != (portalUri = _rebuildQueue.poll()) ){
|
|
||||||
if( portalUri.equals(REBUILD_EVERY_PORTAL)){
|
|
||||||
rebuildAll = true;
|
|
||||||
_rebuildQueue.clear();
|
|
||||||
break;
|
|
||||||
}else{
|
|
||||||
portalURIsToRebuild.add(portalUri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ServletContext sContext = getServletConfig().getServletContext();
|
|
||||||
ApplicationBean appBean = new ApplicationBean();
|
|
||||||
WebappDaoFactory wdFactory = (WebappDaoFactory)sContext.getAttribute("webappDaoFactory");
|
|
||||||
if( wdFactory == null ){
|
|
||||||
log.error("Unable to rebuild cache: could not get 'webappDaoFactory' from Servletcontext");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<Portal> portals;
|
|
||||||
if( rebuildAll ){
|
|
||||||
portals = wdFactory.getPortalDao().getAllPortals();
|
|
||||||
} else {
|
|
||||||
portals = new LinkedList<Portal>();
|
|
||||||
for( String uri : portalURIsToRebuild){
|
|
||||||
Portal p =wdFactory.getPortalDao().getPortalByURI(uri);
|
|
||||||
if( p!= null)
|
|
||||||
portals.add(wdFactory.getPortalDao().getPortalByURI(uri));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(Portal portal : portals){
|
|
||||||
rebuildCacheForPortal(portal,appBean,wdFactory);
|
|
||||||
}
|
|
||||||
log.info("rebuilt ClassGroup cache in " + (System.currentTimeMillis() - start) + " msec");
|
|
||||||
}catch (Exception ex){
|
|
||||||
log.error("could not rebuild cache", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized void rebuildCacheForPortalUri(String uri){
|
|
||||||
ServletContext sContext = getServletConfig().getServletContext();
|
|
||||||
WebappDaoFactory wdFactory = (WebappDaoFactory)sContext.getAttribute("webappDaoFactory");
|
|
||||||
if( wdFactory == null ){
|
|
||||||
log.error("Unable to rebuild cache: could not get 'webappDaoFactory' from Servletcontext");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ApplicationBean appBean = new ApplicationBean();
|
|
||||||
Portal portal = wdFactory.getPortalDao().getPortalByURI(uri);
|
|
||||||
rebuildCacheForPortal(portal,appBean,wdFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized void rebuildCacheForPortal(Portal portal, ApplicationBean appBean, WebappDaoFactory wdFactory){
|
|
||||||
VitroFilters vFilters = null;
|
|
||||||
|
|
||||||
boolean singlePortalApplication = wdFactory.getPortalDao().getAllPortals().size() == 1;
|
|
||||||
|
|
||||||
if ( singlePortalApplication ) {
|
|
||||||
if ( vFilters == null )
|
|
||||||
vFilters = VitroFilterUtils.getDisplayFilterByRoleLevel(RoleLevel.PUBLIC, wdFactory);
|
|
||||||
} else if ( portal.isFlag1Filtering() ){
|
|
||||||
PortalFlag pflag = new PortalFlag(portal.getPortalId());
|
|
||||||
if( vFilters == null)
|
|
||||||
vFilters = VitroFilterUtils.getFilterFromPortalFlag(pflag);
|
|
||||||
else
|
|
||||||
vFilters = vFilters.and( VitroFilterUtils.getFilterFromPortalFlag(pflag));
|
|
||||||
}
|
|
||||||
|
|
||||||
WebappDaoFactory filteringDaoFactory ;
|
|
||||||
|
|
||||||
if( vFilters !=null ){
|
|
||||||
filteringDaoFactory = new WebappDaoFactoryFiltering(wdFactory,vFilters);
|
|
||||||
}else{
|
|
||||||
filteringDaoFactory = wdFactory;
|
|
||||||
}
|
|
||||||
_groupListMap.remove(portal.getPortalId());
|
|
||||||
if ( !singlePortalApplication ) {
|
|
||||||
_groupListMap.put(portal.getPortalId(),
|
|
||||||
getGroups(filteringDaoFactory.getVClassGroupDao(),portal.getPortalId()));
|
|
||||||
} else {
|
|
||||||
List<VClassGroup> unfilteredGroups = getGroups(wdFactory.getVClassGroupDao(), portal.getPortalId(), INCLUDE_INDIVIDUAL_COUNT);
|
|
||||||
List<VClassGroup> filteredGroups = getGroups(filteringDaoFactory.getVClassGroupDao(),portal.getPortalId(), !INCLUDE_INDIVIDUAL_COUNT);
|
|
||||||
_groupListMap.put(portal.getPortalId(), removeFilteredOutGroupsAndClasses(unfilteredGroups, filteredGroups));
|
|
||||||
// BJL23: You may be wondering, why this extra method?
|
|
||||||
// Can't we just use the filtering DAO?
|
|
||||||
// Yes, but using the filtered DAO involves an expensive method
|
|
||||||
// called correctVClassCounts() that requires each individual
|
|
||||||
// in a VClass to be retrieved and filtered. This is fine in memory,
|
|
||||||
// but awful when using a database. We can't (yet) avoid all
|
|
||||||
// this work when portal filtering is involved, but we can
|
|
||||||
// short-circuit it when we have a single portal by using
|
|
||||||
// the filtering DAO only to filter groups and classes,
|
|
||||||
// and the unfiltered DAO to get the counts.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<VClassGroup> removeFilteredOutGroupsAndClasses(List<VClassGroup> unfilteredGroups, List<VClassGroup> filteredGroups) {
|
|
||||||
List<VClassGroup> groups = new ArrayList<VClassGroup>();
|
|
||||||
Set<String> allowedGroups = new HashSet<String>();
|
|
||||||
Set<String> allowedVClasses = new HashSet<String>();
|
|
||||||
for (VClassGroup group : filteredGroups) {
|
|
||||||
if (group.getURI() != null) {
|
|
||||||
allowedGroups.add(group.getURI());
|
|
||||||
}
|
|
||||||
for (VClass vcl : group) {
|
|
||||||
if (vcl.getURI() != null) {
|
|
||||||
allowedVClasses.add(vcl.getURI());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (VClassGroup group : unfilteredGroups) {
|
|
||||||
if (allowedGroups.contains(group.getURI())) {
|
|
||||||
groups.add(group);
|
|
||||||
}
|
|
||||||
List<VClass> tmp = new ArrayList<VClass>();
|
|
||||||
for (VClass vcl : group) {
|
|
||||||
if (allowedVClasses.contains(vcl.getURI())) {
|
|
||||||
tmp.add(vcl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
group.setVitroClassList(tmp);
|
|
||||||
}
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void clearGroupCache(){
|
|
||||||
_groupListMap = new ConcurrentHashMap<Integer, List>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ****************** Jena Model Change Listener***************************** */
|
|
||||||
private class BrowseControllerChangeListener extends StatementListener {
|
|
||||||
private BrowseController controller = null;
|
|
||||||
public BrowseControllerChangeListener(BrowseController controller){
|
|
||||||
this.controller=controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addedStatement(Statement stmt) {
|
|
||||||
checkAndDoUpdate(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removedStatement(Statement stmt) {
|
|
||||||
checkAndDoUpdate(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkAndDoUpdate(Statement stmt){
|
|
||||||
if( stmt==null ) return;
|
|
||||||
if( log.isDebugEnabled()){
|
|
||||||
log.debug("subject: " + stmt.getSubject().getURI());
|
|
||||||
log.debug("predicate: " + stmt.getPredicate().getURI());
|
|
||||||
}
|
|
||||||
if( RDF.type.getURI().equals( stmt.getPredicate().getURI()) ){
|
|
||||||
requestCacheUpdate(REBUILD_EVERY_PORTAL);
|
|
||||||
} else if( VitroVocabulary.PORTAL_FLAG1FILTERING.equals( stmt.getPredicate().getURI())){
|
|
||||||
requestCacheUpdate(stmt.getSubject().getURI());
|
|
||||||
} else if( VitroVocabulary.IN_CLASSGROUP.equals( stmt.getPredicate().getURI() )){
|
|
||||||
requestCacheUpdate(REBUILD_EVERY_PORTAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* ******************** RebuildGroupCacheThread **************** */
|
|
||||||
protected class RebuildGroupCacheThread extends Thread {
|
|
||||||
BrowseController controller;
|
|
||||||
boolean die = false;
|
|
||||||
boolean queueChange = false;
|
|
||||||
long queueChangeMills = 0;
|
|
||||||
private boolean awareOfQueueChange = false;
|
|
||||||
|
|
||||||
RebuildGroupCacheThread(BrowseController controller) {
|
|
||||||
this.controller = controller;
|
|
||||||
}
|
|
||||||
public void run() {
|
|
||||||
while(true){
|
|
||||||
try{
|
|
||||||
synchronized (this){
|
|
||||||
if( _rebuildQueue.isEmpty() ){
|
|
||||||
log.debug("rebuildGroupCacheThread.run() -- queye empty, sleep");
|
|
||||||
wait(1000 * 60 );
|
|
||||||
}
|
|
||||||
if( die ) {
|
|
||||||
log.debug("doing rebuildGroupCacheThread.run() -- die()");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if( queueChange && !awareOfQueueChange){
|
|
||||||
log.debug("rebuildGroupCacheThread.run() -- awareOfQueueChange, delay start of rebuild");
|
|
||||||
awareOfQueueChange = true;
|
|
||||||
wait(200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( awareOfQueueChange && System.currentTimeMillis() - queueChangeMills > 200){
|
|
||||||
log.debug("rebuildGroupCacheThread.run() -- refreshGroupCache()");
|
|
||||||
controller.refreshGroupCache();
|
|
||||||
synchronized( this){
|
|
||||||
queueChange = false;
|
|
||||||
}
|
|
||||||
awareOfQueueChange = false;
|
|
||||||
}else {
|
|
||||||
synchronized( this ){
|
|
||||||
wait(200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(InterruptedException e){}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void informOfQueueChange(){
|
|
||||||
queueChange = true;
|
|
||||||
queueChangeMills = System.currentTimeMillis();
|
|
||||||
this.notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void kill(){
|
|
||||||
die = true;
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static String REBUILD_EVERY_PORTAL ="Rebuild every portal.";
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,360 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.dao.jena;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.ServletContextEvent;
|
||||||
|
import javax.servlet.ServletContextListener;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.rdf.listeners.StatementListener;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Statement;
|
||||||
|
import com.hp.hpl.jena.vocabulary.RDF;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.Portal;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean.RoleLevel;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.VClassGroupDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.filtering.WebappDaoFactoryFiltering;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilterUtils;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.flags.PortalFlag;
|
||||||
|
|
||||||
|
public class VClassGroupCache implements ServletContextListener{
|
||||||
|
|
||||||
|
/* This is the cache of VClassGroups. It is a portal id to list of VClassGroups */
|
||||||
|
private transient ConcurrentHashMap<Integer, List<VClassGroup>> _groupListMap;
|
||||||
|
|
||||||
|
private transient ConcurrentLinkedQueue<String> _rebuildQueue;
|
||||||
|
private RebuildGroupCacheThread _cacheRebuildThread;
|
||||||
|
private ServletContext context;
|
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(VClassGroupCache.class);
|
||||||
|
|
||||||
|
private static boolean ORDER_BY_DISPLAYRANK = true;
|
||||||
|
private static boolean INCLUDE_UNINSTANTIATED = true;
|
||||||
|
private static boolean INCLUDE_INDIVIDUAL_COUNT = true;
|
||||||
|
|
||||||
|
public VClassGroupCache(){}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use getVClassGroupCache(ServletContext) to get a VClassGroupCache.
|
||||||
|
*/
|
||||||
|
private VClassGroupCache(ServletContext context) {
|
||||||
|
this.context = context;
|
||||||
|
this._groupListMap = new ConcurrentHashMap<Integer, List<VClassGroup>>();
|
||||||
|
this._rebuildQueue = new ConcurrentLinkedQueue<String>();
|
||||||
|
|
||||||
|
VClassGroupCacheChangeListener bccl = new VClassGroupCacheChangeListener(this);
|
||||||
|
ModelContext.getJenaOntModel(context).register(bccl);
|
||||||
|
ModelContext.getBaseOntModel(context).register(bccl);
|
||||||
|
ModelContext.getInferenceOntModel(context).register(bccl);
|
||||||
|
ModelContext.getUnionOntModelSelector(context).getABoxModel().register(bccl);
|
||||||
|
|
||||||
|
_rebuildQueue.add(REBUILD_EVERY_PORTAL);
|
||||||
|
_cacheRebuildThread = new RebuildGroupCacheThread(this);
|
||||||
|
_cacheRebuildThread.setDaemon(true);
|
||||||
|
_cacheRebuildThread.start();
|
||||||
|
_cacheRebuildThread.informOfQueueChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VClassGroupCache getVClassGroupCache(ServletContext sc){
|
||||||
|
return (VClassGroupCache) sc.getAttribute("VClassGroupCache");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<VClassGroup> getGroups( int portalId ){
|
||||||
|
return getGroups(getVCGDao(),portalId );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearGroupCache(){
|
||||||
|
_groupListMap = new ConcurrentHashMap<Integer, List<VClassGroup>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<VClassGroup> getGroups( VClassGroupDao vcgDao , int portalId, boolean includeIndividualCount ){
|
||||||
|
List<VClassGroup> groupList = _groupListMap.get(portalId);
|
||||||
|
if( groupList == null ){
|
||||||
|
log.debug("needed to build vclassGroups for portal " + portalId);
|
||||||
|
// Get all classgroups, each populated with a list of their member vclasses
|
||||||
|
List<VClassGroup> groups =
|
||||||
|
vcgDao.getPublicGroupsWithVClasses(ORDER_BY_DISPLAYRANK, !INCLUDE_UNINSTANTIATED, includeIndividualCount);
|
||||||
|
|
||||||
|
// remove classes that have been configured to be hidden from search results
|
||||||
|
vcgDao.removeClassesHiddenFromSearch(groups);
|
||||||
|
|
||||||
|
// now cull out the groups with no populated classes
|
||||||
|
vcgDao.removeUnpopulatedGroups(groups);
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
} else {
|
||||||
|
return groupList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<VClassGroup> getGroups( VClassGroupDao vcgDao, int portalId) {
|
||||||
|
return getGroups( vcgDao, portalId, INCLUDE_INDIVIDUAL_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestCacheUpdate(String portalUri){
|
||||||
|
log.debug("requesting update for portal " + portalUri);
|
||||||
|
_rebuildQueue.add(portalUri);
|
||||||
|
_cacheRebuildThread.informOfQueueChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void refreshGroupCache() {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
try{
|
||||||
|
boolean rebuildAll = false;
|
||||||
|
HashSet<String> portalURIsToRebuild = new HashSet<String>();
|
||||||
|
String portalUri;
|
||||||
|
while ( null != (portalUri = _rebuildQueue.poll()) ){
|
||||||
|
if( portalUri.equals(REBUILD_EVERY_PORTAL)){
|
||||||
|
rebuildAll = true;
|
||||||
|
_rebuildQueue.clear();
|
||||||
|
break;
|
||||||
|
}else{
|
||||||
|
portalURIsToRebuild.add(portalUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebappDaoFactory wdFactory = (WebappDaoFactory)context.getAttribute("webappDaoFactory");
|
||||||
|
if( wdFactory == null ){
|
||||||
|
log.error("Unable to rebuild cache: could not get 'webappDaoFactory' from Servletcontext");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<Portal> portals;
|
||||||
|
if( rebuildAll ){
|
||||||
|
portals = wdFactory.getPortalDao().getAllPortals();
|
||||||
|
} else {
|
||||||
|
portals = new LinkedList<Portal>();
|
||||||
|
for( String uri : portalURIsToRebuild){
|
||||||
|
Portal p =wdFactory.getPortalDao().getPortalByURI(uri);
|
||||||
|
if( p!= null)
|
||||||
|
portals.add(wdFactory.getPortalDao().getPortalByURI(uri));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Portal portal : portals){
|
||||||
|
rebuildCacheForPortal(portal,wdFactory);
|
||||||
|
}
|
||||||
|
log.info("rebuilt ClassGroup cache in " + (System.currentTimeMillis() - start) + " msec");
|
||||||
|
}catch (Exception ex){
|
||||||
|
log.error("could not rebuild cache", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void rebuildCacheForPortalUri(String uri){
|
||||||
|
WebappDaoFactory wdFactory = (WebappDaoFactory)context.getAttribute("webappDaoFactory");
|
||||||
|
if( wdFactory == null ){
|
||||||
|
log.error("Unable to rebuild cache: could not get 'webappDaoFactory' from Servletcontext");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Portal portal = wdFactory.getPortalDao().getPortalByURI(uri);
|
||||||
|
rebuildCacheForPortal(portal,wdFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void rebuildCacheForPortal(Portal portal, WebappDaoFactory wdFactory){
|
||||||
|
VitroFilters vFilters = null;
|
||||||
|
|
||||||
|
boolean singlePortalApplication = wdFactory.getPortalDao().getAllPortals().size() == 1;
|
||||||
|
|
||||||
|
if ( singlePortalApplication ) {
|
||||||
|
if ( vFilters == null )
|
||||||
|
vFilters = VitroFilterUtils.getDisplayFilterByRoleLevel(RoleLevel.PUBLIC, wdFactory);
|
||||||
|
} else if ( portal.isFlag1Filtering() ){
|
||||||
|
PortalFlag pflag = new PortalFlag(portal.getPortalId());
|
||||||
|
if( vFilters == null)
|
||||||
|
vFilters = VitroFilterUtils.getFilterFromPortalFlag(pflag);
|
||||||
|
else
|
||||||
|
vFilters = vFilters.and( VitroFilterUtils.getFilterFromPortalFlag(pflag));
|
||||||
|
}
|
||||||
|
|
||||||
|
WebappDaoFactory filteringDaoFactory ;
|
||||||
|
|
||||||
|
if( vFilters !=null ){
|
||||||
|
filteringDaoFactory = new WebappDaoFactoryFiltering(wdFactory,vFilters);
|
||||||
|
}else{
|
||||||
|
filteringDaoFactory = wdFactory;
|
||||||
|
}
|
||||||
|
_groupListMap.remove(portal.getPortalId());
|
||||||
|
if ( !singlePortalApplication ) {
|
||||||
|
_groupListMap.put(portal.getPortalId(),
|
||||||
|
getGroups(filteringDaoFactory.getVClassGroupDao(),portal.getPortalId()));
|
||||||
|
} else {
|
||||||
|
List<VClassGroup> unfilteredGroups = getGroups(wdFactory.getVClassGroupDao(), portal.getPortalId(), INCLUDE_INDIVIDUAL_COUNT);
|
||||||
|
List<VClassGroup> filteredGroups = getGroups(filteringDaoFactory.getVClassGroupDao(),portal.getPortalId(), !INCLUDE_INDIVIDUAL_COUNT);
|
||||||
|
_groupListMap.put(portal.getPortalId(), removeFilteredOutGroupsAndClasses(unfilteredGroups, filteredGroups));
|
||||||
|
// BJL23: You may be wondering, why this extra method?
|
||||||
|
// Can't we just use the filtering DAO?
|
||||||
|
// Yes, but using the filtered DAO involves an expensive method
|
||||||
|
// called correctVClassCounts() that requires each individual
|
||||||
|
// in a VClass to be retrieved and filtered. This is fine in memory,
|
||||||
|
// but awful when using a database. We can't (yet) avoid all
|
||||||
|
// this work when portal filtering is involved, but we can
|
||||||
|
// short-circuit it when we have a single portal by using
|
||||||
|
// the filtering DAO only to filter groups and classes,
|
||||||
|
// and the unfiltered DAO to get the counts.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<VClassGroup> removeFilteredOutGroupsAndClasses(List<VClassGroup> unfilteredGroups, List<VClassGroup> filteredGroups) {
|
||||||
|
List<VClassGroup> groups = new ArrayList<VClassGroup>();
|
||||||
|
Set<String> allowedGroups = new HashSet<String>();
|
||||||
|
Set<String> allowedVClasses = new HashSet<String>();
|
||||||
|
for (VClassGroup group : filteredGroups) {
|
||||||
|
if (group.getURI() != null) {
|
||||||
|
allowedGroups.add(group.getURI());
|
||||||
|
}
|
||||||
|
for (VClass vcl : group) {
|
||||||
|
if (vcl.getURI() != null) {
|
||||||
|
allowedVClasses.add(vcl.getURI());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (VClassGroup group : unfilteredGroups) {
|
||||||
|
if (allowedGroups.contains(group.getURI())) {
|
||||||
|
groups.add(group);
|
||||||
|
}
|
||||||
|
List<VClass> tmp = new ArrayList<VClass>();
|
||||||
|
for (VClass vcl : group) {
|
||||||
|
if (allowedVClasses.contains(vcl.getURI())) {
|
||||||
|
tmp.add(vcl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.setVitroClassList(tmp);
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ****************** Jena Model Change Listener***************************** */
|
||||||
|
private class VClassGroupCacheChangeListener extends StatementListener {
|
||||||
|
private VClassGroupCache cache = null;
|
||||||
|
public VClassGroupCacheChangeListener(VClassGroupCache cache){
|
||||||
|
this.cache=cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addedStatement(Statement stmt) {
|
||||||
|
checkAndDoUpdate(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removedStatement(Statement stmt) {
|
||||||
|
checkAndDoUpdate(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAndDoUpdate(Statement stmt){
|
||||||
|
if( stmt==null ) return;
|
||||||
|
if( log.isDebugEnabled()){
|
||||||
|
log.debug("subject: " + stmt.getSubject().getURI());
|
||||||
|
log.debug("predicate: " + stmt.getPredicate().getURI());
|
||||||
|
}
|
||||||
|
if( RDF.type.getURI().equals( stmt.getPredicate().getURI()) ){
|
||||||
|
requestCacheUpdate(REBUILD_EVERY_PORTAL);
|
||||||
|
} else if( VitroVocabulary.PORTAL_FLAG1FILTERING.equals( stmt.getPredicate().getURI())){
|
||||||
|
requestCacheUpdate(stmt.getSubject().getURI());
|
||||||
|
} else if( VitroVocabulary.IN_CLASSGROUP.equals( stmt.getPredicate().getURI() )){
|
||||||
|
requestCacheUpdate(REBUILD_EVERY_PORTAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* ******************** RebuildGroupCacheThread **************** */
|
||||||
|
protected class RebuildGroupCacheThread extends Thread {
|
||||||
|
VClassGroupCache cache;
|
||||||
|
boolean die = false;
|
||||||
|
boolean queueChange = false;
|
||||||
|
long queueChangeMills = 0;
|
||||||
|
private boolean awareOfQueueChange = false;
|
||||||
|
|
||||||
|
RebuildGroupCacheThread(VClassGroupCache cache) {
|
||||||
|
this.cache = cache;
|
||||||
|
}
|
||||||
|
public void run() {
|
||||||
|
while(true){
|
||||||
|
try{
|
||||||
|
synchronized (this){
|
||||||
|
if( _rebuildQueue.isEmpty() ){
|
||||||
|
log.debug("rebuildGroupCacheThread.run() -- queye empty, sleep");
|
||||||
|
wait(1000 * 60 );
|
||||||
|
}
|
||||||
|
if( die ) {
|
||||||
|
log.debug("doing rebuildGroupCacheThread.run() -- die()");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if( queueChange && !awareOfQueueChange){
|
||||||
|
log.debug("rebuildGroupCacheThread.run() -- awareOfQueueChange, delay start of rebuild");
|
||||||
|
awareOfQueueChange = true;
|
||||||
|
wait(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( awareOfQueueChange && System.currentTimeMillis() - queueChangeMills > 200){
|
||||||
|
log.debug("rebuildGroupCacheThread.run() -- refreshGroupCache()");
|
||||||
|
cache.refreshGroupCache();
|
||||||
|
synchronized( this){
|
||||||
|
queueChange = false;
|
||||||
|
}
|
||||||
|
awareOfQueueChange = false;
|
||||||
|
}else {
|
||||||
|
synchronized( this ){
|
||||||
|
wait(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(InterruptedException e){}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void informOfQueueChange(){
|
||||||
|
queueChange = true;
|
||||||
|
queueChangeMills = System.currentTimeMillis();
|
||||||
|
this.notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void kill(){
|
||||||
|
die = true;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected VClassGroupDao getVCGDao(){
|
||||||
|
if( context == null ){
|
||||||
|
log.error("Context was not set for VClassGroupCache");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
WebappDaoFactory wdf =(WebappDaoFactory)context.getAttribute("webappDaoFactory");
|
||||||
|
if( wdf == null ){
|
||||||
|
log.error("Cannot get webappDaoFactory from context");
|
||||||
|
return null;
|
||||||
|
}else
|
||||||
|
return wdf.getVClassGroupDao();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String REBUILD_EVERY_PORTAL ="Rebuild every portal.";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contextDestroyed(ServletContextEvent arg0) {
|
||||||
|
_cacheRebuildThread.kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contextInitialized(ServletContextEvent arg0) {
|
||||||
|
arg0.getServletContext().setAttribute("VClassGroupCache", new VClassGroupCache(arg0.getServletContext()) );
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue