diff --git a/webapp/config/web.xml b/webapp/config/web.xml index 17709593a..524e55bc7 100644 --- a/webapp/config/web.xml +++ b/webapp/config/web.xml @@ -924,6 +924,36 @@ edu.cornell.mannlib.vitro.webapp.controller.BrowseController 5 + + + browsecontrollerJsp + edu.cornell.mannlib.vitro.webapp.controller.BrowseControllerJsp + 5 + + + + browsecontrollerStringTemplate + edu.cornell.mannlib.vitro.webapp.controller.BrowseControllerStringTemplate + 5 + + + + browsecontrollerFreeMarker + edu.cornell.mannlib.vitro.webapp.controller.BrowseControllerFreeMarker + 5 + + + + browsecontrollerVelocity + edu.cornell.mannlib.vitro.webapp.controller.BrowseControllerVelocity + 5 + + + + browsecontrollerWicket + edu.cornell.mannlib.vitro.webapp.controller.BrowseControllerWicket + 5 + pubsbyorg @@ -1033,6 +1063,30 @@ browsecontroller /browsecontroller + + browsecontroller + /browsecontroller + + + browsecontrollerJsp + /browsecontroller-jsp + + + browsecontrollerStringTemplate + /browsecontroller-stringtemplate + + + browsecontrollerFreeMarker + /browsecontroller-freemarker + + + browsecontrollerVelocity + /browsecontroller-velocity + + + browsecontrollerWicket + /browsecontroller-wicket + pubsbyorg /pubsbyorg diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerFreeMarker.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerFreeMarker.java new file mode 100644 index 000000000..ebf88a213 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerFreeMarker.java @@ -0,0 +1,329 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller; + +import com.hp.hpl.jena.ontology.OntModel; +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.ApplicationBean; +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.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; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.joda.time.DateTime; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + + +public class BrowseControllerFreeMarker extends BrowseController { + static final long serialVersionUID=2006030721126L; + + private transient ConcurrentHashMap _groupListMap + = new ConcurrentHashMap(); + private transient ConcurrentLinkedQueue _rebuildQueue + = new ConcurrentLinkedQueue(); + private RebuildGroupCacheThread _cacheRebuildThread; + + private static final Log log = LogFactory.getLog(BrowseController.class.getName()); + + public void init(javax.servlet.ServletConfig servletConfig) + throws javax.servlet.ServletException { + super.init(servletConfig); + ServletContext sContext = servletConfig.getServletContext(); + + //BJL23: I'll work on a strategy for avoiding all this craziness. + OntModel model = (OntModel)sContext.getAttribute("jenaOntModel"); + OntModel baseModel = (OntModel)sContext.getAttribute("baseOntModel"); + OntModel infModel = (OntModel)sContext.getAttribute("inferenceOntModel"); + + BrowseControllerChangeListener bccl = new BrowseControllerChangeListener(this); + model.register(bccl); + baseModel.register(bccl); + infModel.register(bccl); + + _rebuildQueue.add(REBUILD_EVERY_PORTAL); + _cacheRebuildThread = new RebuildGroupCacheThread(this); + _cacheRebuildThread.setDaemon(true); + _cacheRebuildThread.start(); + _cacheRebuildThread.informOfQueueChange(); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException,IOException { + doGet(request, response); + } + + /** + * @author bdc34 adapted by jc55 + */ + public void doGet( HttpServletRequest request, HttpServletResponse response ) + throws IOException, ServletException { + try { + //call doGet of super-class (that would be VitroHttpServlet). + super.doGet(request, response); + VitroRequest vreq = new VitroRequest(request); + + if( vreq.getParameter("clearcache") != null ) //mainly for debugging + clearGroupCache(); + + PortalFlag portalState= vreq.getPortalFlag(); + + List groups = getGroups(vreq.getWebappDaoFactory().getVClassGroupDao(), vreq.getPortal().getPortalId()); + if( groups == null || groups.isEmpty() ) + request.setAttribute("classgroupsIsEmpty", true); + + + // stick the data in the requestScope + request.setAttribute("classgroups",groups); + request.setAttribute("portalState",portalState); + + request.setAttribute("title","Index to "+vreq.getPortal().getAppName()+" Contents"); + + request.setAttribute("bodyJsp",Controllers.BROWSE_GROUP_JSP); + //request.setAttribute("bodyJsp",Controllers.DEBUG_JSP); + + //FINALLY: send off to the BASIC_JSP to get turned into HTML + RequestDispatcher rd = request.getRequestDispatcher(Controllers.BASIC_JSP); + // run directly to body for testing: RequestDispatcher rd = request.getRequestDispatcher(Controllers.BROWSE_GROUP_JSP); + rd.forward(request, response); + } catch (Throwable e) { + log.debug("BrowseController.doGet(): "+ e); + request.setAttribute("javax.servlet.jsp.jspException",e); + RequestDispatcher rd = request.getRequestDispatcher("/error.jsp"); + rd.forward(request, response); + } + } + + public void destroy(){ + _cacheRebuildThread.kill(); + } + + private List getGroups( VClassGroupDao vcgDao, int portalId ){ + 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); + + // now cull out the groups with no populated classes + //removeUnpopulatedClasses( groups); + vcgDao.removeUnpopulatedGroups(groups); + + _groupListMap.put(portalId, groups); + return groups; + } else { + return grp; + } + } + + private static boolean ORDER_BY_DISPLAYRANK = true; + private static boolean INCLUDE_UNINSTANTIATED = true; + +// private void removeUnpopulatedClasses( List groups){ +// if( groups == null || groups.size() == 0 ) return; +// for( VClassGroup grp : groups ){ +// ListIterator it = grp.listIterator(); +// while(it.hasNext()){ +// VClass claz = (VClass)it.next(); +// if( claz.getEntityCount() < 1 ) +// it.remove(); +// } +// } +// } + + 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 portalURIsToRebuild = new HashSet(); + 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 portals; + if( rebuildAll ){ + portals = wdFactory.getPortalDao().getAllPortals(); + } else { + portals = new LinkedList(); + 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; + + 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()); + getGroups(filteringDaoFactory.getVClassGroupDao(),portal.getPortalId()); + } + + private void clearGroupCache(){ + _groupListMap = new ConcurrentHashMap(); + } + + /* ****************** 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."; +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerJsp.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerJsp.java new file mode 100644 index 000000000..47b85beaf --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerJsp.java @@ -0,0 +1,329 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller; + +import com.hp.hpl.jena.ontology.OntModel; +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.ApplicationBean; +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.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; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.joda.time.DateTime; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + + +public class BrowseControllerJsp extends BrowseController { + static final long serialVersionUID=2006030721126L; + + private transient ConcurrentHashMap _groupListMap + = new ConcurrentHashMap(); + private transient ConcurrentLinkedQueue _rebuildQueue + = new ConcurrentLinkedQueue(); + private RebuildGroupCacheThread _cacheRebuildThread; + + private static final Log log = LogFactory.getLog(BrowseController.class.getName()); + + public void init(javax.servlet.ServletConfig servletConfig) + throws javax.servlet.ServletException { + super.init(servletConfig); + ServletContext sContext = servletConfig.getServletContext(); + + //BJL23: I'll work on a strategy for avoiding all this craziness. + OntModel model = (OntModel)sContext.getAttribute("jenaOntModel"); + OntModel baseModel = (OntModel)sContext.getAttribute("baseOntModel"); + OntModel infModel = (OntModel)sContext.getAttribute("inferenceOntModel"); + + BrowseControllerChangeListener bccl = new BrowseControllerChangeListener(this); + model.register(bccl); + baseModel.register(bccl); + infModel.register(bccl); + + _rebuildQueue.add(REBUILD_EVERY_PORTAL); + _cacheRebuildThread = new RebuildGroupCacheThread(this); + _cacheRebuildThread.setDaemon(true); + _cacheRebuildThread.start(); + _cacheRebuildThread.informOfQueueChange(); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException,IOException { + doGet(request, response); + } + + /** + * @author bdc34 adapted by jc55 + */ + public void doGet( HttpServletRequest request, HttpServletResponse response ) + throws IOException, ServletException { + try { + //call doGet of super-class (that would be VitroHttpServlet). + super.doGet(request, response); + VitroRequest vreq = new VitroRequest(request); + + if( vreq.getParameter("clearcache") != null ) //mainly for debugging + clearGroupCache(); + + PortalFlag portalState= vreq.getPortalFlag(); + + List groups = getGroups(vreq.getWebappDaoFactory().getVClassGroupDao(), vreq.getPortal().getPortalId()); + if( groups == null || groups.isEmpty() ) + request.setAttribute("classgroupsIsEmpty", true); + + + // stick the data in the requestScope + request.setAttribute("classgroups",groups); + request.setAttribute("portalState",portalState); + + request.setAttribute("title","Index to "+vreq.getPortal().getAppName()+" Contents"); + + request.setAttribute("bodyJsp",Controllers.BROWSE_GROUP_JSP); + //request.setAttribute("bodyJsp",Controllers.DEBUG_JSP); + + //FINALLY: send off to the BASIC_JSP to get turned into HTML + RequestDispatcher rd = request.getRequestDispatcher(Controllers.BASIC_JSP); + // run directly to body for testing: RequestDispatcher rd = request.getRequestDispatcher(Controllers.BROWSE_GROUP_JSP); + rd.forward(request, response); + } catch (Throwable e) { + log.debug("BrowseController.doGet(): "+ e); + request.setAttribute("javax.servlet.jsp.jspException",e); + RequestDispatcher rd = request.getRequestDispatcher("/error.jsp"); + rd.forward(request, response); + } + } + + public void destroy(){ + _cacheRebuildThread.kill(); + } + + private List getGroups( VClassGroupDao vcgDao, int portalId ){ + 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); + + // now cull out the groups with no populated classes + //removeUnpopulatedClasses( groups); + vcgDao.removeUnpopulatedGroups(groups); + + _groupListMap.put(portalId, groups); + return groups; + } else { + return grp; + } + } + + private static boolean ORDER_BY_DISPLAYRANK = true; + private static boolean INCLUDE_UNINSTANTIATED = true; + +// private void removeUnpopulatedClasses( List groups){ +// if( groups == null || groups.size() == 0 ) return; +// for( VClassGroup grp : groups ){ +// ListIterator it = grp.listIterator(); +// while(it.hasNext()){ +// VClass claz = (VClass)it.next(); +// if( claz.getEntityCount() < 1 ) +// it.remove(); +// } +// } +// } + + 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 portalURIsToRebuild = new HashSet(); + 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 portals; + if( rebuildAll ){ + portals = wdFactory.getPortalDao().getAllPortals(); + } else { + portals = new LinkedList(); + 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; + + 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()); + getGroups(filteringDaoFactory.getVClassGroupDao(),portal.getPortalId()); + } + + private void clearGroupCache(){ + _groupListMap = new ConcurrentHashMap(); + } + + /* ****************** 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."; +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerStringTemplate.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerStringTemplate.java new file mode 100644 index 000000000..946ed7672 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerStringTemplate.java @@ -0,0 +1,329 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller; + +import com.hp.hpl.jena.ontology.OntModel; +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.ApplicationBean; +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.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; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.joda.time.DateTime; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + + +public class BrowseControllerStringTemplate extends BrowseController { + static final long serialVersionUID=2006030721126L; + + private transient ConcurrentHashMap _groupListMap + = new ConcurrentHashMap(); + private transient ConcurrentLinkedQueue _rebuildQueue + = new ConcurrentLinkedQueue(); + private RebuildGroupCacheThread _cacheRebuildThread; + + private static final Log log = LogFactory.getLog(BrowseController.class.getName()); + + public void init(javax.servlet.ServletConfig servletConfig) + throws javax.servlet.ServletException { + super.init(servletConfig); + ServletContext sContext = servletConfig.getServletContext(); + + //BJL23: I'll work on a strategy for avoiding all this craziness. + OntModel model = (OntModel)sContext.getAttribute("jenaOntModel"); + OntModel baseModel = (OntModel)sContext.getAttribute("baseOntModel"); + OntModel infModel = (OntModel)sContext.getAttribute("inferenceOntModel"); + + BrowseControllerChangeListener bccl = new BrowseControllerChangeListener(this); + model.register(bccl); + baseModel.register(bccl); + infModel.register(bccl); + + _rebuildQueue.add(REBUILD_EVERY_PORTAL); + _cacheRebuildThread = new RebuildGroupCacheThread(this); + _cacheRebuildThread.setDaemon(true); + _cacheRebuildThread.start(); + _cacheRebuildThread.informOfQueueChange(); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException,IOException { + doGet(request, response); + } + + /** + * @author bdc34 adapted by jc55 + */ + public void doGet( HttpServletRequest request, HttpServletResponse response ) + throws IOException, ServletException { + try { + //call doGet of super-class (that would be VitroHttpServlet). + super.doGet(request, response); + VitroRequest vreq = new VitroRequest(request); + + if( vreq.getParameter("clearcache") != null ) //mainly for debugging + clearGroupCache(); + + PortalFlag portalState= vreq.getPortalFlag(); + + List groups = getGroups(vreq.getWebappDaoFactory().getVClassGroupDao(), vreq.getPortal().getPortalId()); + if( groups == null || groups.isEmpty() ) + request.setAttribute("classgroupsIsEmpty", true); + + + // stick the data in the requestScope + request.setAttribute("classgroups",groups); + request.setAttribute("portalState",portalState); + + request.setAttribute("title","Index to "+vreq.getPortal().getAppName()+" Contents"); + + request.setAttribute("bodyJsp",Controllers.BROWSE_GROUP_JSP); + //request.setAttribute("bodyJsp",Controllers.DEBUG_JSP); + + //FINALLY: send off to the BASIC_JSP to get turned into HTML + RequestDispatcher rd = request.getRequestDispatcher(Controllers.BASIC_JSP); + // run directly to body for testing: RequestDispatcher rd = request.getRequestDispatcher(Controllers.BROWSE_GROUP_JSP); + rd.forward(request, response); + } catch (Throwable e) { + log.debug("BrowseController.doGet(): "+ e); + request.setAttribute("javax.servlet.jsp.jspException",e); + RequestDispatcher rd = request.getRequestDispatcher("/error.jsp"); + rd.forward(request, response); + } + } + + public void destroy(){ + _cacheRebuildThread.kill(); + } + + private List getGroups( VClassGroupDao vcgDao, int portalId ){ + 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); + + // now cull out the groups with no populated classes + //removeUnpopulatedClasses( groups); + vcgDao.removeUnpopulatedGroups(groups); + + _groupListMap.put(portalId, groups); + return groups; + } else { + return grp; + } + } + + private static boolean ORDER_BY_DISPLAYRANK = true; + private static boolean INCLUDE_UNINSTANTIATED = true; + +// private void removeUnpopulatedClasses( List groups){ +// if( groups == null || groups.size() == 0 ) return; +// for( VClassGroup grp : groups ){ +// ListIterator it = grp.listIterator(); +// while(it.hasNext()){ +// VClass claz = (VClass)it.next(); +// if( claz.getEntityCount() < 1 ) +// it.remove(); +// } +// } +// } + + 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 portalURIsToRebuild = new HashSet(); + 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 portals; + if( rebuildAll ){ + portals = wdFactory.getPortalDao().getAllPortals(); + } else { + portals = new LinkedList(); + 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; + + 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()); + getGroups(filteringDaoFactory.getVClassGroupDao(),portal.getPortalId()); + } + + private void clearGroupCache(){ + _groupListMap = new ConcurrentHashMap(); + } + + /* ****************** 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."; +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerVelocity.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerVelocity.java new file mode 100644 index 000000000..5087a59d3 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerVelocity.java @@ -0,0 +1,329 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller; + +import com.hp.hpl.jena.ontology.OntModel; +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.ApplicationBean; +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.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; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.joda.time.DateTime; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + + +public class BrowseControllerVelocity extends BrowseController { + static final long serialVersionUID=2006030721126L; + + private transient ConcurrentHashMap _groupListMap + = new ConcurrentHashMap(); + private transient ConcurrentLinkedQueue _rebuildQueue + = new ConcurrentLinkedQueue(); + private RebuildGroupCacheThread _cacheRebuildThread; + + private static final Log log = LogFactory.getLog(BrowseController.class.getName()); + + public void init(javax.servlet.ServletConfig servletConfig) + throws javax.servlet.ServletException { + super.init(servletConfig); + ServletContext sContext = servletConfig.getServletContext(); + + //BJL23: I'll work on a strategy for avoiding all this craziness. + OntModel model = (OntModel)sContext.getAttribute("jenaOntModel"); + OntModel baseModel = (OntModel)sContext.getAttribute("baseOntModel"); + OntModel infModel = (OntModel)sContext.getAttribute("inferenceOntModel"); + + BrowseControllerChangeListener bccl = new BrowseControllerChangeListener(this); + model.register(bccl); + baseModel.register(bccl); + infModel.register(bccl); + + _rebuildQueue.add(REBUILD_EVERY_PORTAL); + _cacheRebuildThread = new RebuildGroupCacheThread(this); + _cacheRebuildThread.setDaemon(true); + _cacheRebuildThread.start(); + _cacheRebuildThread.informOfQueueChange(); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException,IOException { + doGet(request, response); + } + + /** + * @author bdc34 adapted by jc55 + */ + public void doGet( HttpServletRequest request, HttpServletResponse response ) + throws IOException, ServletException { + try { + //call doGet of super-class (that would be VitroHttpServlet). + super.doGet(request, response); + VitroRequest vreq = new VitroRequest(request); + + if( vreq.getParameter("clearcache") != null ) //mainly for debugging + clearGroupCache(); + + PortalFlag portalState= vreq.getPortalFlag(); + + List groups = getGroups(vreq.getWebappDaoFactory().getVClassGroupDao(), vreq.getPortal().getPortalId()); + if( groups == null || groups.isEmpty() ) + request.setAttribute("classgroupsIsEmpty", true); + + + // stick the data in the requestScope + request.setAttribute("classgroups",groups); + request.setAttribute("portalState",portalState); + + request.setAttribute("title","Index to "+vreq.getPortal().getAppName()+" Contents"); + + request.setAttribute("bodyJsp",Controllers.BROWSE_GROUP_JSP); + //request.setAttribute("bodyJsp",Controllers.DEBUG_JSP); + + //FINALLY: send off to the BASIC_JSP to get turned into HTML + RequestDispatcher rd = request.getRequestDispatcher(Controllers.BASIC_JSP); + // run directly to body for testing: RequestDispatcher rd = request.getRequestDispatcher(Controllers.BROWSE_GROUP_JSP); + rd.forward(request, response); + } catch (Throwable e) { + log.debug("BrowseController.doGet(): "+ e); + request.setAttribute("javax.servlet.jsp.jspException",e); + RequestDispatcher rd = request.getRequestDispatcher("/error.jsp"); + rd.forward(request, response); + } + } + + public void destroy(){ + _cacheRebuildThread.kill(); + } + + private List getGroups( VClassGroupDao vcgDao, int portalId ){ + 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); + + // now cull out the groups with no populated classes + //removeUnpopulatedClasses( groups); + vcgDao.removeUnpopulatedGroups(groups); + + _groupListMap.put(portalId, groups); + return groups; + } else { + return grp; + } + } + + private static boolean ORDER_BY_DISPLAYRANK = true; + private static boolean INCLUDE_UNINSTANTIATED = true; + +// private void removeUnpopulatedClasses( List groups){ +// if( groups == null || groups.size() == 0 ) return; +// for( VClassGroup grp : groups ){ +// ListIterator it = grp.listIterator(); +// while(it.hasNext()){ +// VClass claz = (VClass)it.next(); +// if( claz.getEntityCount() < 1 ) +// it.remove(); +// } +// } +// } + + 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 portalURIsToRebuild = new HashSet(); + 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 portals; + if( rebuildAll ){ + portals = wdFactory.getPortalDao().getAllPortals(); + } else { + portals = new LinkedList(); + 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; + + 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()); + getGroups(filteringDaoFactory.getVClassGroupDao(),portal.getPortalId()); + } + + private void clearGroupCache(){ + _groupListMap = new ConcurrentHashMap(); + } + + /* ****************** 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."; +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerWicket.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerWicket.java new file mode 100644 index 000000000..cd0e1d9bc --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/BrowseControllerWicket.java @@ -0,0 +1,329 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller; + +import com.hp.hpl.jena.ontology.OntModel; +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.ApplicationBean; +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.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; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.joda.time.DateTime; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + + +public class BrowseControllerWicket extends BrowseController { + static final long serialVersionUID=2006030721126L; + + private transient ConcurrentHashMap _groupListMap + = new ConcurrentHashMap(); + private transient ConcurrentLinkedQueue _rebuildQueue + = new ConcurrentLinkedQueue(); + private RebuildGroupCacheThread _cacheRebuildThread; + + private static final Log log = LogFactory.getLog(BrowseController.class.getName()); + + public void init(javax.servlet.ServletConfig servletConfig) + throws javax.servlet.ServletException { + super.init(servletConfig); + ServletContext sContext = servletConfig.getServletContext(); + + //BJL23: I'll work on a strategy for avoiding all this craziness. + OntModel model = (OntModel)sContext.getAttribute("jenaOntModel"); + OntModel baseModel = (OntModel)sContext.getAttribute("baseOntModel"); + OntModel infModel = (OntModel)sContext.getAttribute("inferenceOntModel"); + + BrowseControllerChangeListener bccl = new BrowseControllerChangeListener(this); + model.register(bccl); + baseModel.register(bccl); + infModel.register(bccl); + + _rebuildQueue.add(REBUILD_EVERY_PORTAL); + _cacheRebuildThread = new RebuildGroupCacheThread(this); + _cacheRebuildThread.setDaemon(true); + _cacheRebuildThread.start(); + _cacheRebuildThread.informOfQueueChange(); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException,IOException { + doGet(request, response); + } + + /** + * @author bdc34 adapted by jc55 + */ + public void doGet( HttpServletRequest request, HttpServletResponse response ) + throws IOException, ServletException { + try { + //call doGet of super-class (that would be VitroHttpServlet). + super.doGet(request, response); + VitroRequest vreq = new VitroRequest(request); + + if( vreq.getParameter("clearcache") != null ) //mainly for debugging + clearGroupCache(); + + PortalFlag portalState= vreq.getPortalFlag(); + + List groups = getGroups(vreq.getWebappDaoFactory().getVClassGroupDao(), vreq.getPortal().getPortalId()); + if( groups == null || groups.isEmpty() ) + request.setAttribute("classgroupsIsEmpty", true); + + + // stick the data in the requestScope + request.setAttribute("classgroups",groups); + request.setAttribute("portalState",portalState); + + request.setAttribute("title","Index to "+vreq.getPortal().getAppName()+" Contents"); + + request.setAttribute("bodyJsp",Controllers.BROWSE_GROUP_JSP); + //request.setAttribute("bodyJsp",Controllers.DEBUG_JSP); + + //FINALLY: send off to the BASIC_JSP to get turned into HTML + RequestDispatcher rd = request.getRequestDispatcher(Controllers.BASIC_JSP); + // run directly to body for testing: RequestDispatcher rd = request.getRequestDispatcher(Controllers.BROWSE_GROUP_JSP); + rd.forward(request, response); + } catch (Throwable e) { + log.debug("BrowseController.doGet(): "+ e); + request.setAttribute("javax.servlet.jsp.jspException",e); + RequestDispatcher rd = request.getRequestDispatcher("/error.jsp"); + rd.forward(request, response); + } + } + + public void destroy(){ + _cacheRebuildThread.kill(); + } + + private List getGroups( VClassGroupDao vcgDao, int portalId ){ + 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); + + // now cull out the groups with no populated classes + //removeUnpopulatedClasses( groups); + vcgDao.removeUnpopulatedGroups(groups); + + _groupListMap.put(portalId, groups); + return groups; + } else { + return grp; + } + } + + private static boolean ORDER_BY_DISPLAYRANK = true; + private static boolean INCLUDE_UNINSTANTIATED = true; + +// private void removeUnpopulatedClasses( List groups){ +// if( groups == null || groups.size() == 0 ) return; +// for( VClassGroup grp : groups ){ +// ListIterator it = grp.listIterator(); +// while(it.hasNext()){ +// VClass claz = (VClass)it.next(); +// if( claz.getEntityCount() < 1 ) +// it.remove(); +// } +// } +// } + + 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 portalURIsToRebuild = new HashSet(); + 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 portals; + if( rebuildAll ){ + portals = wdFactory.getPortalDao().getAllPortals(); + } else { + portals = new LinkedList(); + 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; + + 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()); + getGroups(filteringDaoFactory.getVClassGroupDao(),portal.getPortalId()); + } + + private void clearGroupCache(){ + _groupListMap = new ConcurrentHashMap(); + } + + /* ****************** 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."; +}