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.";
+}