Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/* $This file is distributed under the terms of the license in LICENSE$ */
package edu.cornell.mannlib.vitro.webapp.dao.jena;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jena.ontology.OntModel;
import org.apache.jena.rdf.listeners.StatementListener;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ResourceFactory;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.shared.Lock;
import org.apache.jena.vocabulary.OWL;
import org.apache.jena.vocabulary.RDF;
import org.apache.jena.vocabulary.RDFS;
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
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.VClassGroupsForRequest;
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.dao.jena.event.BulkUpdateEvent;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngineException;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchFacetField;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchFacetField.Count;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResponse;
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer;
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer.Event;
import edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread;
/**
* This is a cache of classgroups with classes. Each class should have a count
* of individuals. These counts are cached so they don't have to be recomputed.
*
* The cache is updated asynchronously by the thread RebuildGroupCacheThread.
* A synchronous rebuild can be performed with VClassGroupCache.doSynchronousRebuild()
*
* This class should handle the condition where the search engine is not available.
* It will not throw an exception but it will return a empty list of classes
* or class groups.
*
* VClassGroupCache.doSynchronousRebuild() and the RebuildGroupCacheThread will try
* to connect to the search engine a couple of times and then give up.
*
* As of VIVO release 1.4, the counts come from the search index. If the
* search index is not built or if there were problems building the index,
* the class counts from VClassGroupCache will be incorrect.
*/
public class VClassGroupCache implements SearchIndexer.Listener {
private static final Log log = LogFactory.getLog(VClassGroupCache.class);
private static final String ATTRIBUTE_NAME = "VClassGroupCache";
private static final boolean ORDER_BY_DISPLAYRANK = true;
private static final boolean INCLUDE_UNINSTANTIATED = true;
private static final boolean DONT_INCLUDE_INDIVIDUAL_COUNT = false;
/**
* This is the cache of VClassGroups. It is a list of VClassGroups. If this
* is null then the cache is not built.
*/
private List _groupList;
/**
* Also keep track of the classes here, makes it easier to get the counts
* and other information */
private Map VclassMap = new HashMap();
private final RebuildGroupCacheThread _cacheRebuildThread;
/**
* Need a pointer to the context to get DAOs and models.
*/
private final ServletContext context;
private VClassGroupCache(ServletContext context) {
this.context = context;
this._groupList = null;
if (StartupStatus.getBean(context).isStartupAborted()) {
_cacheRebuildThread = null;
return;
}
/* Need to register for changes of rdf:type for individuals in abox
* and for changes of classgroups for classes. */
VClassGroupCacheChangeListener bccl = new VClassGroupCacheChangeListener();
ModelContext.registerListenerForChanges(context, bccl);
_cacheRebuildThread = new RebuildGroupCacheThread(this);
_cacheRebuildThread.setDaemon(true);
_cacheRebuildThread.start();
}
public synchronized VClassGroup getGroup(String vClassGroupURI) {
if (vClassGroupURI == null || vClassGroupURI.isEmpty())
return null;
List cgList = getGroups();
for (VClassGroup cg : cgList) {
if (vClassGroupURI.equals(cg.getURI()))
return cg;
}
return null;
}
public synchronized List getGroups() {
//try to build the cache if it doesn't exist
if (_groupList == null){
doSynchronousRebuild();
}
if (_groupList == null){
requestCacheUpdate();
return Collections.emptyList();
}else{
return _groupList;
}
}
// Get specific VClass corresponding to Map
public synchronized VClass getCachedVClass(String classUri) {
//try to build the cache if it doesn't exist
if ( VclassMap == null ){
doSynchronousRebuild();
}
if( VclassMap == null){
requestCacheUpdate();
return null;
}else{
if (VclassMap.containsKey(classUri)) {
return VclassMap.get(classUri);
}else{
return null;
}
}
}
protected synchronized void setCache(List newGroups, Map classMap){
_groupList = newGroups;
VclassMap = classMap;
}
private boolean paused = false;
private boolean updateRequested = false;
public void pause() {
this.paused = true;
}
public void unpause() {
this.paused = false;
if(updateRequested) {
updateRequested = false;
requestCacheUpdate();
}
}
public void requestCacheUpdate() {
log.debug("requesting update");
if(paused) {
updateRequested = true;
} else {
_cacheRebuildThread.informOfQueueChange();
}
}
protected void requestStop() {
if (_cacheRebuildThread != null) {
_cacheRebuildThread.kill();
try {
_cacheRebuildThread.join();
} catch (InterruptedException e) {
//don't log message since shutting down
}
}
}
protected VClassGroupDao getVCGDao() {
return ModelAccess.on(context).getWebappDaoFactory().getVClassGroupDao();
}
public void doSynchronousRebuild(){
//try to rebuild a couple times since the search engine may not yet be up.
int attempts = 0;
int maxTries = 5;
SearchEngineException exception = null;
while( attempts < maxTries ){
try {
attempts++;
rebuildCacheUsingSearch(this);
break;
} catch (SearchEngineException e) {
exception = e;
try { Thread.sleep(1000); }
catch (InterruptedException e1) {/*ignore interrupt*/}
}
}
if( exception != null )
log.error("Could not rebuild cache. " + exception.getCause().getMessage() );
}
/**
* Handle notification of events from the IndexBuilder.
*/
@Override
public void receiveSearchIndexerEvent(Event event) {
switch (event.getType()) {
case STOP_URIS:
log.debug("rebuilding because of IndexBuilder " + event.getType());
requestCacheUpdate();
break;
default:
log.debug("ignoring event type " + event.getType());
break;
}
}
/* **************** static utility methods ***************** */
/**
* Use getVClassGroupCache(ServletContext) to get a VClassGroupCache.
*/
public static VClassGroupCache getVClassGroupCache(ServletContext sc) {
return (VClassGroupCache) sc.getAttribute(ATTRIBUTE_NAME);
}
/**
* Use getVClassGroups(HttpServletRequest) to get a language-aware image of
* the cached groups and classes.
*/
public static VClassGroupsForRequest getVClassGroups(HttpServletRequest req) {
return new VClassGroupsForRequest(req, getVClassGroupCache(req.getSession().getServletContext()));
}
/**
* Method that rebuilds the cache. This will use a WebappDaoFactory and a SearchEngine.
*/
protected static void rebuildCacheUsingSearch( VClassGroupCache cache ) throws SearchEngineException{
long start = System.currentTimeMillis();
WebappDaoFactory wdFactory = ModelAccess.on(cache.context).getWebappDaoFactory();
SearchEngine searchEngine = ApplicationUtils.instance().getSearchEngine();
VitroFilters vFilters = VitroFilterUtils.getPublicFilter(cache.context);
VClassGroupDao vcgDao = new WebappDaoFactoryFiltering(wdFactory, vFilters).getVClassGroupDao();
List groups = vcgDao.getPublicGroupsWithVClasses(ORDER_BY_DISPLAYRANK,
INCLUDE_UNINSTANTIATED, DONT_INCLUDE_INDIVIDUAL_COUNT);
addCountsUsingSearch(groups, searchEngine);
cache.setCache(groups, classMapForGroups(groups));
log.debug("msec to build cache: " + (System.currentTimeMillis() - start));
}
protected static Map classMapForGroups( List groups){
Map newClassMap = new HashMap();
for (VClassGroup vcg : groups) {
List vclasses = vcg.getVitroClassList();
for (VClass vclass : vclasses) {
String classUri = vclass.getURI();
if (!newClassMap.containsKey(classUri)) {
newClassMap.put(classUri, vclass);
}
}
}
return newClassMap;
}
/**
* Add the Individual count to classes in groups.
* @throws SearchEngineException
*/
protected static void addCountsUsingSearch(List groups, SearchEngine searchEngine)
throws SearchEngineException {
if( groups == null || searchEngine == null )
return;
for( VClassGroup group : groups){
addClassCountsToGroup(group, searchEngine);
}
}
protected static void addClassCountsToGroup(VClassGroup group, SearchEngine searchEngine)
throws SearchEngineException {
if( group == null ) return;
String groupUri = group.getURI();
String facetOnField = VitroSearchTermNames.RDFTYPE;
SearchQuery query = searchEngine.createQuery().
setRows(0).
setQuery(VitroSearchTermNames.CLASSGROUP_URI + ":" + groupUri ).
addFacetFields( facetOnField ). //facet on type to get counts for classes in classgroup
setFacetMinCount(0);
log.debug("query: " + query);
SearchResponse rsp = searchEngine.query(query);
//Get individual count
long individualCount = rsp.getResults().getNumFound();
log.debug("Number of individuals found " + individualCount);
group.setIndividualCount((int) individualCount);
//get counts for classes
SearchFacetField ff = rsp.getFacetField( facetOnField );
if( ff != null ){
List counts = ff.getValues();
if( counts != null ){
for( Count ct: counts){
if( ct != null ){
String classUri = ct.getName();
long individualsInClass = ct.getCount();
setClassCount( group, classUri, individualsInClass);
}
}
}else{
log.debug("no Counts found for FacetField " + facetOnField);
}
}else{
log.debug("no FaccetField found for " + facetOnField);
}
}
protected static void setClassCount(VClassGroup group, String classUri,
long individualsInClass) {
for( VClass clz : group){
if( clz.getURI().equals(classUri)){
clz.setEntityCount( (int) individualsInClass );
}
}
}
protected static boolean isClassNameChange(Statement stmt, OntModel jenaOntModel) {
// Check if the stmt is a rdfs:label change and that the
// subject is an owl:Class.
if( RDFS.label.equals( stmt.getPredicate() )) {
jenaOntModel.enterCriticalSection(Lock.READ);
try{
return jenaOntModel.contains(
ResourceFactory.createStatement(
ResourceFactory.createResource(stmt.getSubject().getURI()),
RDF.type,
OWL.Class));
}finally{
jenaOntModel.leaveCriticalSection();
}
}else{
return false;
}
}
/* ******************** RebuildGroupCacheThread **************** */
protected class RebuildGroupCacheThread extends VitroBackgroundThread {
private final VClassGroupCache cache;
private long queueChangeMillis = 0L;
private boolean rebuildRequested = false;
private volatile boolean die = false;
private int failedAttempts = 0;
private final int maxFailedAttempts = 5;
RebuildGroupCacheThread(VClassGroupCache cache) {
super("VClassGroupCache.RebuildGroupCacheThread");
this.cache = cache;
}
@Override
public void run() {
while (!die) {
int delay;
if ( !rebuildRequested ) {
log.debug("rebuildGroupCacheThread.run() -- nothing to do, sleep");
delay = 1000 * 60;
} else if ((System.currentTimeMillis() - queueChangeMillis ) < 500) {
log.debug("rebuildGroupCacheThread.run() -- delay start of rebuild");
delay = 500;
} else {
setWorkLevel(WorkLevel.WORKING);
rebuildRequested = false;
try {
rebuildCacheUsingSearch( cache );
log.debug("rebuildGroupCacheThread.run() -- rebuilt cache ");
failedAttempts = 0;
delay = 100;
} catch (SearchEngineException e) {
failedAttempts++;
if( failedAttempts >= maxFailedAttempts ){
log.error("Could not build VClassGroupCache. " +
"Could not connect with the search engine after " +
failedAttempts + " attempts.", e.getCause());
rebuildRequested = false;
failedAttempts = 0;
delay = 1000;
}else{
rebuildRequested = true;
delay = (int) (( Math.pow(2, failedAttempts) ) * 1000);
log.debug("Could not connect with the search engine, will attempt " +
"again in " + delay + " msec.");
}
}catch(Exception ex){
log.error("could not build cache",ex);
delay = 1000;
}
setWorkLevel(WorkLevel.IDLE);
}
if (delay > 0) {
synchronized (this) {
try {
wait(delay);
} catch (InterruptedException e) {
log.warn("Waiting " + delay
+ " milliseconds, but interrupted.", e);
}
}
}
}
log.debug("rebuildGroupCacheThread.run() -- die()");
}
synchronized void informOfQueueChange() {
queueChangeMillis = System.currentTimeMillis();
rebuildRequested = true;
this.notifyAll();
}
synchronized void kill() {
die = true;
this.notifyAll();
}
}
/* ****************** Jena Model Change Listener***************************** */
/**
* Listen for changes to what class group classes are in and their display rank.
*/
protected class VClassGroupCacheChangeListener extends StatementListener {
@Override
public void addedStatement(Statement stmt) {
checkAndDoUpdate(stmt);
}
@Override
public void removedStatement(Statement stmt) {
checkAndDoUpdate(stmt);
}
protected 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.equals(stmt.getPredicate())) {
requestCacheUpdate();
} else if (VitroVocabulary.IN_CLASSGROUP.equals(stmt.getPredicate().getURI())) {
requestCacheUpdate();
} else if(VitroVocabulary.DISPLAY_RANK.equals(stmt.getPredicate().getURI())){
requestCacheUpdate();
} else if (RDFS.label.equals(stmt.getPredicate())){
OntModel jenaOntModel = ModelAccess.on(context).getOntModelSelector().getTBoxModel();
if( isClassNameChange(stmt, jenaOntModel) ) {
requestCacheUpdate();
}
}
}
public void notifyEvent(Model model, Object event) {
if (event instanceof BulkUpdateEvent) {
if(((BulkUpdateEvent) event).getBegin()) {
pause();
} else {
unpause();
}
}
}
}
/* ******************** ServletContextListener **************** */
public static class Setup implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
VClassGroupCache vcgc = new VClassGroupCache(context);
vcgc.requestCacheUpdate();
context.setAttribute(ATTRIBUTE_NAME,vcgc);
log.info("VClassGroupCache added to context");
SearchIndexer searchIndexer = ApplicationUtils.instance().getSearchIndexer();
searchIndexer.addListener(vcgc);
log.info("VClassGroupCache set to listen to events from IndexBuilder");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
Object o = sce.getServletContext().getAttribute(ATTRIBUTE_NAME);
if (o instanceof VClassGroupCache) {
((VClassGroupCache) o).requestStop();
}
}
}
}