VIVO-873 Adapt the client code to the new interface.
Create a bridge implementation of SearchIndexerImpl that just wraps around an old IndexBuilder. Modify client code: Application, BasicAuthenticator, SearchServiceController, SparqlUpdateApiController, UpdateUrisInIndex and VClassGroupCache Rewrite IndexController to use AJAX and to show the current status and history of the indexer events.
This commit is contained in:
parent
3bc42c1456
commit
2ceab6e3df
21 changed files with 1038 additions and 204 deletions
45
webapp/config/applicationSetup.n3
Normal file
45
webapp/config/applicationSetup.n3
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
@prefix : <http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#> .
|
||||||
|
|
||||||
|
:application
|
||||||
|
a <java:edu.cornell.mannlib.vitro.webapp.application.ApplicationImpl> ,
|
||||||
|
<java:edu.cornell.mannlib.vitro.webapp.modules.Application> ;
|
||||||
|
:hasSearchEngine :instrumentedSearchEngineWrapper ;
|
||||||
|
:hasSearchIndexer :basicSearchIndexer ;
|
||||||
|
:hasImageProcessor :jaiImageProcessor ;
|
||||||
|
:hasFileStorage :ptiFileStorage ;
|
||||||
|
:hasContentTripleSource :sdbContentTripleSource ;
|
||||||
|
:hasConfigurationTripleSource :tdbConfigurationTripleSource ;
|
||||||
|
:hasTBoxReasonerModule :jfactTBoxReasonerModule .
|
||||||
|
|
||||||
|
:jaiImageProcessor
|
||||||
|
a <java:edu.cornell.mannlib.vitro.webapp.imageprocessor.jai.JaiImageProcessor> ,
|
||||||
|
<java:edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor> .
|
||||||
|
|
||||||
|
:ptiFileStorage
|
||||||
|
a <java:edu.cornell.mannlib.vitro.webapp.filestorage.impl.FileStorageImplWrapper> ,
|
||||||
|
<java:edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage> .
|
||||||
|
|
||||||
|
:instrumentedSearchEngineWrapper
|
||||||
|
a <java:edu.cornell.mannlib.vitro.webapp.searchengine.InstrumentedSearchEngineWrapper> ,
|
||||||
|
<java:edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine> ;
|
||||||
|
:wraps :solrSearchEngine .
|
||||||
|
|
||||||
|
:solrSearchEngine
|
||||||
|
a <java:edu.cornell.mannlib.vitro.webapp.searchengine.solr.SolrSearchEngine> ,
|
||||||
|
<java:edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine> .
|
||||||
|
|
||||||
|
:basicSearchIndexer
|
||||||
|
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.SearchIndexerImpl> ,
|
||||||
|
<java:edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer> .
|
||||||
|
|
||||||
|
:sdbContentTripleSource
|
||||||
|
a <java:edu.cornell.mannlib.vitro.webapp.triplesource.impl.sdb.ContentTripleSourceSDB> ,
|
||||||
|
<java:edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource> .
|
||||||
|
|
||||||
|
:tdbConfigurationTripleSource
|
||||||
|
a <java:edu.cornell.mannlib.vitro.webapp.triplesource.impl.tdb.ConfigurationTripleSourceTDB> ,
|
||||||
|
<java:edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource> .
|
||||||
|
|
||||||
|
:jfactTBoxReasonerModule
|
||||||
|
a <java:edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl.jfact.JFactTBoxReasonerModule> ,
|
||||||
|
<java:edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule> .
|
|
@ -16,13 +16,14 @@ import edu.cornell.mannlib.vitro.webapp.modules.ComponentStartupStatus;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
|
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
|
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource;
|
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource;
|
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.triplesource.impl.BasicCombinedTripleSource;
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.startup.ComponentStartupStatusImpl;
|
import edu.cornell.mannlib.vitro.webapp.startup.ComponentStartupStatusImpl;
|
||||||
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
|
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.triplesource.impl.BasicCombinedTripleSource;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property;
|
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation;
|
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation;
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ public class ApplicationImpl implements Application {
|
||||||
private VitroHomeDirectory homeDirectory;
|
private VitroHomeDirectory homeDirectory;
|
||||||
|
|
||||||
private SearchEngine searchEngine;
|
private SearchEngine searchEngine;
|
||||||
|
private SearchIndexer searchIndexer;
|
||||||
private ImageProcessor imageProcessor;
|
private ImageProcessor imageProcessor;
|
||||||
private FileStorage fileStorage;
|
private FileStorage fileStorage;
|
||||||
private ContentTripleSource contentTripleSource;
|
private ContentTripleSource contentTripleSource;
|
||||||
|
@ -73,11 +75,27 @@ public class ApplicationImpl implements Application {
|
||||||
searchEngine = se;
|
searchEngine = se;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Configuration includes multiple SearchEngine instancess: "
|
"Configuration includes multiple SearchEngine instances: "
|
||||||
+ searchEngine + ", and " + se);
|
+ searchEngine + ", and " + se);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchIndexer getSearchIndexer() {
|
||||||
|
return searchIndexer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasSearchIndexer")
|
||||||
|
public void setSearchIndexer(SearchIndexer si) {
|
||||||
|
if (searchIndexer == null) {
|
||||||
|
searchIndexer = si;
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Configuration includes multiple SearchIndexer instances: "
|
||||||
|
+ searchIndexer + ", and " + si);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImageProcessor getImageProcessor() {
|
public ImageProcessor getImageProcessor() {
|
||||||
return imageProcessor;
|
return imageProcessor;
|
||||||
|
@ -89,7 +107,7 @@ public class ApplicationImpl implements Application {
|
||||||
imageProcessor = ip;
|
imageProcessor = ip;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Configuration includes multiple ImageProcessor instancess: "
|
"Configuration includes multiple ImageProcessor instances: "
|
||||||
+ imageProcessor + ", and " + ip);
|
+ imageProcessor + ", and " + ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,7 +123,7 @@ public class ApplicationImpl implements Application {
|
||||||
fileStorage = fs;
|
fileStorage = fs;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Configuration includes multiple FileStorage intances: "
|
"Configuration includes multiple FileStorage instances: "
|
||||||
+ fileStorage + ", and " + fs);
|
+ fileStorage + ", and " + fs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,7 +139,7 @@ public class ApplicationImpl implements Application {
|
||||||
contentTripleSource = source;
|
contentTripleSource = source;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Configuration includes multiple intances of ContentTripleSource: "
|
"Configuration includes multiple instances of ContentTripleSource: "
|
||||||
+ contentTripleSource + ", and " + source);
|
+ contentTripleSource + ", and " + source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +155,7 @@ public class ApplicationImpl implements Application {
|
||||||
configurationTripleSource = source;
|
configurationTripleSource = source;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Configuration includes multiple intances of ConfigurationTripleSource: "
|
"Configuration includes multiple instances of ConfigurationTripleSource: "
|
||||||
+ configurationTripleSource + ", and " + source);
|
+ configurationTripleSource + ", and " + source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,7 +171,7 @@ public class ApplicationImpl implements Application {
|
||||||
tboxReasonerModule = module;
|
tboxReasonerModule = module;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Configuration includes multiple intances of TBoxReasonerModule: "
|
"Configuration includes multiple instances of TBoxReasonerModule: "
|
||||||
+ tboxReasonerModule + ", and " + module);
|
+ tboxReasonerModule + ", and " + module);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,6 +182,10 @@ public class ApplicationImpl implements Application {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Configuration did not include a SearchEngine.");
|
"Configuration did not include a SearchEngine.");
|
||||||
}
|
}
|
||||||
|
if (searchIndexer == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Configuration did not include a SearchIndexer.");
|
||||||
|
}
|
||||||
if (imageProcessor == null) {
|
if (imageProcessor == null) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Configuration did not include an ImageProcessor.");
|
"Configuration did not include an ImageProcessor.");
|
||||||
|
|
|
@ -25,11 +25,12 @@ import com.hp.hpl.jena.update.UpdateAction;
|
||||||
import com.hp.hpl.jena.update.UpdateFactory;
|
import com.hp.hpl.jena.update.UpdateFactory;
|
||||||
import com.hp.hpl.jena.update.UpdateRequest;
|
import com.hp.hpl.jena.update.UpdateRequest;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
|
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
|
||||||
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest;
|
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset;
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset;
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process SPARQL Updates, as an API.
|
* Process SPARQL Updates, as an API.
|
||||||
|
@ -98,13 +99,14 @@ public class SparqlUpdateApiController extends VitroApiServlet {
|
||||||
ServletContext ctx = req.getSession().getServletContext();
|
ServletContext ctx = req.getSession().getServletContext();
|
||||||
VitroRequest vreq = new VitroRequest(req);
|
VitroRequest vreq = new VitroRequest(req);
|
||||||
|
|
||||||
IndexBuilder.getBuilder(ctx).pause();
|
SearchIndexer indexer = ApplicationUtils.instance().getSearchIndexer();
|
||||||
|
indexer.pause();
|
||||||
try {
|
try {
|
||||||
Dataset ds = new RDFServiceDataset(vreq.getUnfilteredRDFService());
|
Dataset ds = new RDFServiceDataset(vreq.getUnfilteredRDFService());
|
||||||
GraphStore graphStore = GraphStoreFactory.create(ds);
|
GraphStore graphStore = GraphStoreFactory.create(ds);
|
||||||
UpdateAction.execute(parsed, graphStore);
|
UpdateAction.execute(parsed, graphStore);
|
||||||
} finally {
|
} finally {
|
||||||
IndexBuilder.getBuilder(ctx).unpause();
|
indexer.unpause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vedit.beans.LoginStatusBean;
|
import edu.cornell.mannlib.vedit.beans.LoginStatusBean;
|
||||||
import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource;
|
import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.auth.identifier.RequestIdentifiers;
|
import edu.cornell.mannlib.vitro.webapp.auth.identifier.RequestIdentifiers;
|
||||||
import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.IsRootUser;
|
import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.IsRootUser;
|
||||||
import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean.RoleLevel;
|
import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean.RoleLevel;
|
||||||
|
@ -29,7 +30,8 @@ import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.LoginEvent;
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.LoginEvent;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.LogoutEvent;
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.LogoutEvent;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
|
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngineException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "standard" implementation of Authenticator.
|
* The "standard" implementation of Authenticator.
|
||||||
|
@ -162,7 +164,17 @@ public class BasicAuthenticator extends Authenticator {
|
||||||
|
|
||||||
if (IsRootUser.isRootUser(RequestIdentifiers
|
if (IsRootUser.isRootUser(RequestIdentifiers
|
||||||
.getIdBundleForRequest(request))) {
|
.getIdBundleForRequest(request))) {
|
||||||
IndexBuilder.checkIndexOnRootLogin(request);
|
try {
|
||||||
|
SearchEngine engine = ApplicationUtils.instance()
|
||||||
|
.getSearchEngine();
|
||||||
|
if (engine.documentCount() == 0) {
|
||||||
|
log.info("Search index is empty. Running a full index rebuild.");
|
||||||
|
ApplicationUtils.instance().getSearchIndexer()
|
||||||
|
.rebuildIndex();
|
||||||
|
}
|
||||||
|
} catch (SearchEngineException e) {
|
||||||
|
log.warn("Unable to check for search index", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +274,8 @@ public class BasicAuthenticator extends Authenticator {
|
||||||
* Get a reference to the UserAccountsDao, or null.
|
* Get a reference to the UserAccountsDao, or null.
|
||||||
*/
|
*/
|
||||||
private UserAccountsDao getUserAccountsDao() {
|
private UserAccountsDao getUserAccountsDao() {
|
||||||
UserAccountsDao userAccountsDao = getWebappDaoFactory().getUserAccountsDao();
|
UserAccountsDao userAccountsDao = getWebappDaoFactory()
|
||||||
|
.getUserAccountsDao();
|
||||||
if (userAccountsDao == null) {
|
if (userAccountsDao == null) {
|
||||||
log.error("getUserAccountsDao: no UserAccountsDao");
|
log.error("getUserAccountsDao: no UserAccountsDao");
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,9 +40,9 @@ 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.SearchFacetField.Count;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery;
|
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.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.search.VitroSearchTermNames;
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexingEventListener;
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
|
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread;
|
import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread;
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread;
|
||||||
* search index is not built or if there were problems building the index,
|
* search index is not built or if there were problems building the index,
|
||||||
* the class counts from VClassGroupCache will be incorrect.
|
* the class counts from VClassGroupCache will be incorrect.
|
||||||
*/
|
*/
|
||||||
public class VClassGroupCache implements IndexingEventListener {
|
public class VClassGroupCache implements SearchIndexer.Listener {
|
||||||
private static final Log log = LogFactory.getLog(VClassGroupCache.class);
|
private static final Log log = LogFactory.getLog(VClassGroupCache.class);
|
||||||
|
|
||||||
private static final String ATTRIBUTE_NAME = "VClassGroupCache";
|
private static final String ATTRIBUTE_NAME = "VClassGroupCache";
|
||||||
|
@ -208,19 +208,17 @@ public class VClassGroupCache implements IndexingEventListener {
|
||||||
* Handle notification of events from the IndexBuilder.
|
* Handle notification of events from the IndexBuilder.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void notifyOfIndexingEvent(EventTypes event) {
|
public void receiveSearchIndexerEvent(Event event) {
|
||||||
switch( event ){
|
switch (event.getType()) {
|
||||||
case FINISH_FULL_REBUILD:
|
case STOP_PROCESSING_URIS:
|
||||||
case FINISHED_UPDATE:
|
log.debug("rebuilding because of IndexBuilder " + event.getType());
|
||||||
log.debug("rebuilding because of IndexBuilder " + event.name());
|
requestCacheUpdate();
|
||||||
requestCacheUpdate();
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
log.debug("ignoring event type " + event.getType());
|
||||||
log.debug("ignoring event type " + event.name());
|
break;
|
||||||
break;
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* **************** static utility methods ***************** */
|
/* **************** static utility methods ***************** */
|
||||||
|
|
||||||
|
@ -489,8 +487,8 @@ public class VClassGroupCache implements IndexingEventListener {
|
||||||
context.setAttribute(ATTRIBUTE_NAME,vcgc);
|
context.setAttribute(ATTRIBUTE_NAME,vcgc);
|
||||||
log.info("VClassGroupCache added to context");
|
log.info("VClassGroupCache added to context");
|
||||||
|
|
||||||
IndexBuilder indexBuilder = IndexBuilder.getBuilder(context);
|
SearchIndexer searchIndexer = ApplicationUtils.instance().getSearchIndexer();
|
||||||
indexBuilder.addIndexBuilderListener(vcgc);
|
searchIndexer.addListener(vcgc);
|
||||||
log.info("VClassGroupCache set to listen to events from IndexBuilder");
|
log.info("VClassGroupCache set to listen to events from IndexBuilder");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import edu.cornell.mannlib.vitro.webapp.application.VitroHomeDirectory;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
|
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
|
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource;
|
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource;
|
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource;
|
||||||
|
@ -22,6 +23,8 @@ public interface Application {
|
||||||
|
|
||||||
SearchEngine getSearchEngine();
|
SearchEngine getSearchEngine();
|
||||||
|
|
||||||
|
SearchIndexer getSearchIndexer();
|
||||||
|
|
||||||
ImageProcessor getImageProcessor();
|
ImageProcessor getImageProcessor();
|
||||||
|
|
||||||
FileStorage getFileStorage();
|
FileStorage getFileStorage();
|
||||||
|
@ -39,8 +42,16 @@ public interface Application {
|
||||||
NEW, ACTIVE, STOPPED
|
NEW, ACTIVE, STOPPED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This should be called only once, and should be the first call on this
|
||||||
|
* Component.
|
||||||
|
*/
|
||||||
void startup(Application application, ComponentStartupStatus ss);
|
void startup(Application application, ComponentStartupStatus ss);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This should be called only once, and should be the last call on this
|
||||||
|
* Component.
|
||||||
|
*/
|
||||||
void shutdown(Application application);
|
void shutdown(Application application);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.modules.searchIndexer;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.Application;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for the code that controls the contents of the search index.
|
||||||
|
*
|
||||||
|
* The only calls that are valid after shutdown are shutdown(), getStatus() and
|
||||||
|
* removeListener().
|
||||||
|
*/
|
||||||
|
public interface SearchIndexer extends Application.Module {
|
||||||
|
/**
|
||||||
|
* Update the search documents for these URIs.
|
||||||
|
*
|
||||||
|
* For each URI that belongs in the index, a new search document is built,
|
||||||
|
* replacing any document that may already exist for that URI. For each URI
|
||||||
|
* that does not belong in the index, any existing document is removed.
|
||||||
|
*
|
||||||
|
* A URI belongs in the index if it refers to an existing individual in the
|
||||||
|
* model, and is not excluded.
|
||||||
|
*
|
||||||
|
* @param uris
|
||||||
|
* if null or empty, this call has no effect.
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if called after shutdown()
|
||||||
|
*/
|
||||||
|
void scheduleUpdatesForUris(Collection<String> uris);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all of the existing documents in the index, and schedule updates
|
||||||
|
* for all of the individuals in the model.
|
||||||
|
*
|
||||||
|
* If a rebuild is already pending or in progress, this method has no
|
||||||
|
* effect.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if called after shutdown()
|
||||||
|
*/
|
||||||
|
void rebuildIndex();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop processing new tasks. Requests will be queued until a call to
|
||||||
|
* unpause().
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if called after shutdown()
|
||||||
|
*/
|
||||||
|
void pause();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resume processing new tasks. Any requests that were received since the
|
||||||
|
* call to pause() will now be scheduled for processing.
|
||||||
|
*
|
||||||
|
* Has no effect if called after shutdown().
|
||||||
|
*/
|
||||||
|
void unpause();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What is the current status of the indexer?
|
||||||
|
*
|
||||||
|
* Still valid after shutdown().
|
||||||
|
*/
|
||||||
|
SearchIndexerStatus getStatus();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add this listener, allowing it to receive events from the indexer. If
|
||||||
|
* this listener has already been added, this method has no effect.
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
* if null, this method has no effect.
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if called after shutdown()
|
||||||
|
*/
|
||||||
|
void addListener(Listener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove this listener, meaning that it will no longer receive events from
|
||||||
|
* the indexer. If this listener is not active, this method has no effect.
|
||||||
|
*
|
||||||
|
* Has no effect if called after shutdown().
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
* if null, this method has no effect.
|
||||||
|
*/
|
||||||
|
void removeListener(Listener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop processing and release resources. This call should block until the
|
||||||
|
* dependent threads are stopped.
|
||||||
|
*
|
||||||
|
* Repeated calls have no effect.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
void shutdown(Application app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A listener that will be notified of events from the SearchIndexer.
|
||||||
|
*/
|
||||||
|
public static interface Listener {
|
||||||
|
void receiveSearchIndexerEvent(Event event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable event object. The event type describes just what happened.
|
||||||
|
* The status object describes what the indexer is doing now.
|
||||||
|
*/
|
||||||
|
public static class Event {
|
||||||
|
public enum Type {
|
||||||
|
START_PROCESSING_URIS,
|
||||||
|
|
||||||
|
PROGRESS_PROCESSING_URIS,
|
||||||
|
|
||||||
|
STOP_PROCESSING_URIS,
|
||||||
|
|
||||||
|
START_PROCESSING_STATEMENTS,
|
||||||
|
|
||||||
|
PROGRESS_PROCESSING_STATEMENTS,
|
||||||
|
|
||||||
|
STOP_PROCESSING_STATEMENTS,
|
||||||
|
|
||||||
|
REBUILD_REQUESTED,
|
||||||
|
|
||||||
|
SHUTDOWN_REQUESTED
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Type type;
|
||||||
|
private final SearchIndexerStatus status;
|
||||||
|
|
||||||
|
public Event(Type type, SearchIndexerStatus status) {
|
||||||
|
this.type = type;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchIndexerStatus getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.modules.searchIndexer;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable summary of the status of the SearchIndexer, at some point in
|
||||||
|
* time.Contains the current state, and some counts.
|
||||||
|
*
|
||||||
|
* If the indexer is processing URIs, processing statements, or preparing a
|
||||||
|
* rebuild, the counts are URI_COUNTS, STATEMENT_COUNTS, or REBUILD_COUNTS.
|
||||||
|
*
|
||||||
|
* When the indexer starts up, and when it is is shut down, the counts are
|
||||||
|
* NO_COUNTS.
|
||||||
|
*
|
||||||
|
* If the indexer is idle, the counts are carried over from the previous
|
||||||
|
* operation.
|
||||||
|
*/
|
||||||
|
public class SearchIndexerStatus {
|
||||||
|
public enum State {
|
||||||
|
IDLE, PROCESSING_URIS, PROCESSING_STMTS, PREPARING_REBUILD, SHUTDOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
private final State state;
|
||||||
|
private final Date since;
|
||||||
|
private final Counts counts;
|
||||||
|
|
||||||
|
public SearchIndexerStatus(State state, Date since, Counts counts) {
|
||||||
|
this.state = state;
|
||||||
|
this.since = since;
|
||||||
|
this.counts = counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public State getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getSince() {
|
||||||
|
return since;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Counts getCounts() {
|
||||||
|
return counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class Counts {
|
||||||
|
public enum Type {
|
||||||
|
URI_COUNTS, STATEMENT_COUNTS, REBUILD_COUNTS, NO_COUNTS
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Type type;
|
||||||
|
|
||||||
|
public Counts(Type type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UriCounts asUriCounts() {
|
||||||
|
return (UriCounts) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatementCounts asStatementCounts() {
|
||||||
|
return (StatementCounts) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RebuildCounts asRebuildCounts() {
|
||||||
|
return (RebuildCounts) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NoCounts asNoCounts() {
|
||||||
|
return (NoCounts) this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UriCounts extends Counts {
|
||||||
|
private final int deleted;
|
||||||
|
private final int updated;
|
||||||
|
private final int remaining;
|
||||||
|
private final int total;
|
||||||
|
|
||||||
|
public UriCounts(int deleted, int updated, int remaining, int total) {
|
||||||
|
super(Type.URI_COUNTS);
|
||||||
|
this.deleted = deleted;
|
||||||
|
this.updated = updated;
|
||||||
|
this.remaining = remaining;
|
||||||
|
this.total = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeleted() {
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUpdated() {
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemaining() {
|
||||||
|
return remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotal() {
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class StatementCounts extends Counts {
|
||||||
|
private final int processed;
|
||||||
|
private final int remaining;
|
||||||
|
private final int total;
|
||||||
|
|
||||||
|
public StatementCounts(int processed, int remaining, int total) {
|
||||||
|
super(Type.STATEMENT_COUNTS);
|
||||||
|
this.processed = processed;
|
||||||
|
this.remaining = remaining;
|
||||||
|
this.total = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProcessed() {
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemaining() {
|
||||||
|
return remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotal() {
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RebuildCounts extends Counts {
|
||||||
|
private final int numberOfIndividuals;
|
||||||
|
|
||||||
|
public RebuildCounts(int numberOfIndividuals) {
|
||||||
|
super(Type.REBUILD_COUNTS);
|
||||||
|
this.numberOfIndividuals = numberOfIndividuals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumberOfIndividuals() {
|
||||||
|
return numberOfIndividuals;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NoCounts extends Counts {
|
||||||
|
public NoCounts() {
|
||||||
|
super(Type.NO_COUNTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,12 +3,10 @@
|
||||||
package edu.cornell.mannlib.vitro.webapp.search.controller;
|
package edu.cornell.mannlib.vitro.webapp.search.controller;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
@ -18,31 +16,30 @@ import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
|
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
|
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
|
||||||
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest;
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.RequestedAction;
|
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.RequestedAction;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues;
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues;
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread.WorkLevel;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexerStatus;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread.WorkLevelStamp;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexerStatus.State;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexerStatus.StatementCounts;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexerStatus.UriCounts;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.services.freemarker.FreemarkerProcessingServiceSetup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accepts requests to rebuild or update the search index. It uses an
|
* Accepts requests to display the current status of the search index, or to
|
||||||
* IndexBuilder and finds that IndexBuilder from the servletContext using the
|
* initiate a rebuild.
|
||||||
* key "edu.cornel.mannlib.vitro.search.indexing.IndexBuilder"
|
|
||||||
*
|
*
|
||||||
* That IndexBuilder will be associated with a object that implements the
|
* A DISPLAY or REBUILD request is handled like any other FreemarkerHttpServlet.
|
||||||
* IndexerIface.
|
* A STATUS is an AJAX request, we override doGet() so we can format the
|
||||||
|
* template without enclosing it in a body template.
|
||||||
*
|
*
|
||||||
* An example of the IndexerIface is SearchIndexer. An example of the IndexBuilder
|
* When initialized, this servlet adds a listener to the SearchIndexer, so it
|
||||||
* and SearchIndexer setup is in SearchIndexerSetup.
|
* can maintain a history of activity. This will provide the contents of the
|
||||||
*
|
* display.
|
||||||
* @author bdc34
|
|
||||||
*/
|
*/
|
||||||
public class IndexController extends FreemarkerHttpServlet {
|
public class IndexController extends FreemarkerHttpServlet {
|
||||||
private static final Log log = LogFactory.getLog(IndexController.class);
|
private static final Log log = LogFactory.getLog(IndexController.class);
|
||||||
|
@ -50,27 +47,22 @@ public class IndexController extends FreemarkerHttpServlet {
|
||||||
/**
|
/**
|
||||||
* <pre>
|
* <pre>
|
||||||
* This request might be:
|
* This request might be:
|
||||||
* SETUP -- Index is not building and nothing is requested. Solicit requests.
|
* DISPLAY (default) -- Send the template that will contain the status display.
|
||||||
* REFRESH -- Index is building, nothing is requested. Show continuing status.
|
* STATUS -- Send the current status and history.
|
||||||
* REBUILD -- Rebuild is requested. Set the rebuild flag and show continuing status.
|
* REBUILD -- Initiate a rebuild. Then act like DISPLAY.
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
private enum RequestType {
|
private enum RequestType {
|
||||||
SETUP, REFRESH, REBUILD;
|
DISPLAY, STATUS, REBUILD;
|
||||||
|
|
||||||
/** What type of request is this? */
|
/** What type of request is this? */
|
||||||
static RequestType fromRequest(HttpServletRequest req) {
|
static RequestType fromRequest(HttpServletRequest req) {
|
||||||
if (hasParameter(req, "rebuild")) {
|
if (hasParameter(req, "rebuild")) {
|
||||||
return REBUILD;
|
return REBUILD;
|
||||||
|
} else if (hasParameter(req, "status")) {
|
||||||
|
return STATUS;
|
||||||
} else {
|
} else {
|
||||||
ServletContext ctx = req.getSession().getServletContext();
|
return DISPLAY;
|
||||||
IndexBuilder builder = IndexBuilder.getBuilder(ctx);
|
|
||||||
WorkLevelStamp workLevel = builder.getWorkLevel();
|
|
||||||
if (workLevel.getLevel() == WorkLevel.WORKING) {
|
|
||||||
return REFRESH;
|
|
||||||
} else {
|
|
||||||
return SETUP;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,13 +73,43 @@ public class IndexController extends FreemarkerHttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String PAGE_URL = "/SearchIndex";
|
private static final String PAGE_URL = "/SearchIndex";
|
||||||
private static final String TEMPLATE_NAME = "searchIndex.ftl";
|
private static final String PAGE_TEMPLATE_NAME = "searchIndex.ftl";
|
||||||
|
private static final String STATUS_TEMPLATE_NAME = "searchIndexStatus.ftl";
|
||||||
|
|
||||||
public static final RequestedAction REQUIRED_ACTIONS = SimplePermission.MANAGE_SEARCH_INDEX.ACTION;
|
public static final RequestedAction REQUIRED_ACTIONS = SimplePermission.MANAGE_SEARCH_INDEX.ACTION;
|
||||||
|
|
||||||
|
private SearchIndexer indexer;
|
||||||
|
private IndexHistory history;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AuthorizationRequest requiredActions(VitroRequest vreq) {
|
public void init() throws ServletException {
|
||||||
return REQUIRED_ACTIONS;
|
super.init();
|
||||||
|
this.indexer = ApplicationUtils.instance().getSearchIndexer();
|
||||||
|
this.history = new IndexHistory();
|
||||||
|
this.indexer.addListener(this.history);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
this.indexer.removeListener(this.history);
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doGet(HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
if (!isAuthorizedToDisplayPage(req, resp, REQUIRED_ACTIONS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (RequestType.fromRequest(req)) {
|
||||||
|
case STATUS:
|
||||||
|
showStatus(req, resp);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.doGet(req, resp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -95,78 +117,82 @@ public class IndexController extends FreemarkerHttpServlet {
|
||||||
return "Rebuild Search Index";
|
return "Rebuild Search Index";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doGet(HttpServletRequest req, HttpServletResponse resp)
|
|
||||||
throws IOException, ServletException {
|
|
||||||
if (RequestType.fromRequest(req) == RequestType.REFRESH) {
|
|
||||||
resp.addHeader("Refresh", "5; " + UrlBuilder.getUrl(PAGE_URL));
|
|
||||||
}
|
|
||||||
super.doGet(req, resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ResponseValues processRequest(VitroRequest vreq) {
|
protected ResponseValues processRequest(VitroRequest vreq) {
|
||||||
Map<String, Object> body = new HashMap<String, Object>();
|
switch (RequestType.fromRequest(vreq)) {
|
||||||
body.put("actionUrl", UrlBuilder.getUrl(PAGE_URL));
|
case REBUILD:
|
||||||
|
requestRebuild();
|
||||||
try {
|
return showDisplay();
|
||||||
IndexBuilder builder = IndexBuilder.getBuilder(getServletContext());
|
default:
|
||||||
|
return showDisplay();
|
||||||
switch (RequestType.fromRequest(vreq)) {
|
|
||||||
case REBUILD:
|
|
||||||
builder.doIndexRebuild();
|
|
||||||
Thread.sleep(500);
|
|
||||||
return redirectToRefresh();
|
|
||||||
default:
|
|
||||||
return showCurrentStatus(builder, body);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error rebuilding search index", e);
|
|
||||||
body.put("errorMessage",
|
|
||||||
"There was an error while rebuilding the search index. "
|
|
||||||
+ e.getMessage());
|
|
||||||
return new ExceptionResponseValues(
|
|
||||||
Template.ERROR_MESSAGE.toString(), body, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResponseValues redirectToRefresh() {
|
private ResponseValues showDisplay() {
|
||||||
return new RedirectResponseValues(PAGE_URL);
|
HashMap<String, Object> body = new HashMap<>();
|
||||||
|
body.put("statusUrl", UrlBuilder.getUrl(PAGE_URL, "status", "true"));
|
||||||
|
body.put("rebuildUrl", UrlBuilder.getUrl(PAGE_URL, "rebuild", "true"));
|
||||||
|
return new TemplateResponseValues(PAGE_TEMPLATE_NAME, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResponseValues showCurrentStatus(IndexBuilder builder,
|
private void showStatus(HttpServletRequest req, HttpServletResponse resp)
|
||||||
Map<String, Object> body) {
|
throws IOException {
|
||||||
WorkLevelStamp stamp = builder.getWorkLevel();
|
|
||||||
|
|
||||||
WorkLevel workLevel = stamp.getLevel();
|
|
||||||
long completedCount = builder.getCompletedCount();
|
|
||||||
long totalToDo = builder.getTotalToDo();
|
|
||||||
Date since = stamp.getSince();
|
|
||||||
Date expectedCompletion = figureExpectedCompletion(since, totalToDo,
|
|
||||||
completedCount);
|
|
||||||
|
|
||||||
body.put("worklevel", workLevel.toString());
|
|
||||||
body.put("completedCount", completedCount);
|
|
||||||
body.put("totalToDo", totalToDo);
|
|
||||||
body.put("currentTask", figureCurrentTask(stamp.getFlags()));
|
|
||||||
body.put("since", since);
|
|
||||||
body.put("elapsed", formatElapsedTime(since, new Date()));
|
|
||||||
body.put("expected", formatElapsedTime(since, expectedCompletion));
|
|
||||||
body.put("hasPreviousBuild", since.getTime() > 0L);
|
|
||||||
body.put("indexIsConnected", testIndexConnection());
|
|
||||||
return new TemplateResponseValues(TEMPLATE_NAME, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Boolean testIndexConnection() {
|
|
||||||
try {
|
try {
|
||||||
ApplicationUtils.instance().getSearchEngine().ping();
|
Map<String, Object> body = new HashMap<>();
|
||||||
return Boolean.TRUE;
|
body.put("statusUrl", UrlBuilder.getUrl(PAGE_URL, "status", "true"));
|
||||||
|
body.put("rebuildUrl",
|
||||||
|
UrlBuilder.getUrl(PAGE_URL, "rebuild", "true"));
|
||||||
|
body.put("status", buildStatusMap(indexer.getStatus()));
|
||||||
|
body.put("history", history.toMaps());
|
||||||
|
|
||||||
|
String rendered = FreemarkerProcessingServiceSetup.getService(
|
||||||
|
getServletContext()).renderTemplate(STATUS_TEMPLATE_NAME,
|
||||||
|
body, req);
|
||||||
|
resp.getWriter().write(rendered);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Can't connect to the search engine.", e);
|
resp.setStatus(500);
|
||||||
return Boolean.FALSE;
|
resp.getWriter().write(e.toString());
|
||||||
|
log.error(e, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void requestRebuild() {
|
||||||
|
indexer.rebuildIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> buildStatusMap(SearchIndexerStatus status) {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
State state = status.getState();
|
||||||
|
map.put("statusType", state);
|
||||||
|
map.put("since", status.getSince());
|
||||||
|
|
||||||
|
if (state == State.PROCESSING_URIS) {
|
||||||
|
UriCounts counts = status.getCounts().asUriCounts();
|
||||||
|
map.put("updated", counts.getUpdated());
|
||||||
|
map.put("deleted", counts.getDeleted());
|
||||||
|
map.put("remaining", counts.getRemaining());
|
||||||
|
map.put("total", counts.getTotal());
|
||||||
|
map.put("elapsed", breakDownElapsedTime(status.getSince()));
|
||||||
|
map.put("expectedCompletion",
|
||||||
|
figureExpectedCompletion(status.getSince(),
|
||||||
|
counts.getTotal(),
|
||||||
|
counts.getTotal() - counts.getRemaining()));
|
||||||
|
} else if (state == State.PROCESSING_STMTS) {
|
||||||
|
StatementCounts counts = status.getCounts().asStatementCounts();
|
||||||
|
map.put("processed", counts.getProcessed());
|
||||||
|
map.put("remaining", counts.getRemaining());
|
||||||
|
map.put("total", counts.getTotal());
|
||||||
|
map.put("elapsed", breakDownElapsedTime(status.getSince()));
|
||||||
|
map.put("expectedCompletion",
|
||||||
|
figureExpectedCompletion(status.getSince(),
|
||||||
|
counts.getTotal(), counts.getProcessed()));
|
||||||
|
} else {
|
||||||
|
// nothing for IDLE or SHUTDOWN, except what's already there.
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
private Date figureExpectedCompletion(Date startTime, long totalToDo,
|
private Date figureExpectedCompletion(Date startTime, long totalToDo,
|
||||||
long completedCount) {
|
long completedCount) {
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
|
@ -186,22 +212,12 @@ public class IndexController extends FreemarkerHttpServlet {
|
||||||
return new Date(expectedDuration + startTime.getTime());
|
return new Date(expectedDuration + startTime.getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String formatElapsedTime(Date since, Date until) {
|
private int[] breakDownElapsedTime(Date since) {
|
||||||
long elapsedMillis = until.getTime() - since.getTime();
|
long elapsedMillis = new Date().getTime() - since.getTime();
|
||||||
long seconds = (elapsedMillis / 1000L) % 60L;
|
long seconds = (elapsedMillis / 1000L) % 60L;
|
||||||
long minutes = (elapsedMillis / 60000L) % 60L;
|
long minutes = (elapsedMillis / 60000L) % 60L;
|
||||||
long hours = elapsedMillis / 3600000L;
|
long hours = elapsedMillis / 3600000L;
|
||||||
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
|
return new int[] {(int) hours, (int) minutes, (int) seconds};
|
||||||
}
|
|
||||||
|
|
||||||
private String figureCurrentTask(Collection<String> flags) {
|
|
||||||
if (flags.contains(IndexBuilder.FLAG_REBUILDING)) {
|
|
||||||
return "Rebuilding";
|
|
||||||
} else if (flags.contains(IndexBuilder.FLAG_UPDATING)) {
|
|
||||||
return "Updating";
|
|
||||||
} else {
|
|
||||||
return "Not working on";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.search.controller;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
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.modules.searchIndexer.SearchIndexerStatus;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexerStatus.Counts;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexerStatus.Counts.Type;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexerStatus.RebuildCounts;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexerStatus.StatementCounts;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexerStatus.UriCounts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener keeps a list of the last several SearchIndexer events, and will
|
||||||
|
* format them for display in a Freemarker template.
|
||||||
|
*/
|
||||||
|
public class IndexHistory implements SearchIndexer.Listener {
|
||||||
|
private final static int MAX_EVENTS = 10;
|
||||||
|
|
||||||
|
private final Deque<Event> events = new LinkedList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receiveSearchIndexerEvent(Event event) {
|
||||||
|
synchronized (events) {
|
||||||
|
events.addFirst(event);
|
||||||
|
while (events.size() > MAX_EVENTS) {
|
||||||
|
events.removeLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Map<String, Object>> toMaps() {
|
||||||
|
synchronized (events) {
|
||||||
|
List<Map<String, Object>> list = new ArrayList<>();
|
||||||
|
for (Event event : events) {
|
||||||
|
list.add(toMap(event));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> toMap(Event event) {
|
||||||
|
SearchIndexerStatus status = event.getStatus();
|
||||||
|
Counts counts = status.getCounts();
|
||||||
|
Type countsType = counts.getType();
|
||||||
|
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("event", event.getType());
|
||||||
|
map.put("statusType", status.getState());
|
||||||
|
map.put("since", status.getSince());
|
||||||
|
map.put("countsType", countsType);
|
||||||
|
|
||||||
|
switch (countsType) {
|
||||||
|
case URI_COUNTS:
|
||||||
|
addCounts(counts.asUriCounts(), map);
|
||||||
|
break;
|
||||||
|
case STATEMENT_COUNTS:
|
||||||
|
addCounts(counts.asStatementCounts(), map);
|
||||||
|
break;
|
||||||
|
case REBUILD_COUNTS:
|
||||||
|
addCounts(counts.asRebuildCounts(), map);
|
||||||
|
break;
|
||||||
|
default: // NO_COUNTS
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCounts(UriCounts counts, Map<String, Object> map) {
|
||||||
|
map.put("updated", counts.getUpdated());
|
||||||
|
map.put("deleted", counts.getDeleted());
|
||||||
|
map.put("remaining", counts.getRemaining());
|
||||||
|
map.put("total", counts.getTotal());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCounts(StatementCounts counts, Map<String, Object> map) {
|
||||||
|
map.put("processed", counts.getProcessed());
|
||||||
|
map.put("remaining", counts.getRemaining());
|
||||||
|
map.put("total", counts.getTotal());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCounts(RebuildCounts counts, Map<String, Object> map) {
|
||||||
|
map.put("numberOfIndividuals", counts.getNumberOfIndividuals());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
|
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
|
||||||
import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
|
import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
@ -23,7 +24,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServ
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accepts requests to update a set of URIs in the search index.
|
* Accepts requests to update a set of URIs in the search index.
|
||||||
|
@ -73,8 +74,8 @@ public class SearchServiceController extends FreemarkerHttpServlet {
|
||||||
*/
|
*/
|
||||||
private ResponseValues doUpdateUrisInSearch(HttpServletRequest req)
|
private ResponseValues doUpdateUrisInSearch(HttpServletRequest req)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
IndexBuilder builder = IndexBuilder.getBuilder(getServletContext());
|
SearchIndexer indexer = ApplicationUtils.instance().getSearchIndexer();
|
||||||
int uriCount = new UpdateUrisInIndex().doUpdateUris(req, builder);
|
int uriCount = new UpdateUrisInIndex().doUpdateUris(req, indexer);
|
||||||
|
|
||||||
Map<String, Object> body = new HashMap<>();
|
Map<String, Object> body = new HashMap<>();
|
||||||
body.put("msg", "Received " + uriCount + " URIs.");
|
body.put("msg", "Received " + uriCount + " URIs.");
|
||||||
|
|
|
@ -6,6 +6,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
@ -20,7 +21,7 @@ import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that performs the update of the uris in the search index for the
|
* Class that performs the update of the uris in the search index for the
|
||||||
|
@ -38,7 +39,7 @@ public class UpdateUrisInIndex {
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
protected int doUpdateUris(HttpServletRequest req, IndexBuilder builder)
|
protected int doUpdateUris(HttpServletRequest req, SearchIndexer indexer)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
Map<String, List<FileItem>> map = new VitroRequest(req).getFiles();
|
Map<String, List<FileItem>> map = new VitroRequest(req).getFiles();
|
||||||
if (map == null) {
|
if (map == null) {
|
||||||
|
@ -51,25 +52,25 @@ public class UpdateUrisInIndex {
|
||||||
for (String name : map.keySet()) {
|
for (String name : map.keySet()) {
|
||||||
for (FileItem item : map.get(name)) {
|
for (FileItem item : map.get(name)) {
|
||||||
log.debug("Found " + item.getSize() + " byte file for '" + name + "'");
|
log.debug("Found " + item.getSize() + " byte file for '" + name + "'");
|
||||||
uriCount += processFileItem(builder, item, enc);
|
uriCount += processFileItem(indexer, item, enc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return uriCount;
|
return uriCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int processFileItem(IndexBuilder builder,
|
private int processFileItem(SearchIndexer indexer,
|
||||||
FileItem item, Charset enc) throws IOException {
|
FileItem item, Charset enc) throws IOException {
|
||||||
int count = 0;
|
List<String> uris = new ArrayList<>();
|
||||||
Reader reader = new InputStreamReader(item.getInputStream(), enc.name());
|
Reader reader = new InputStreamReader(item.getInputStream(), enc.name());
|
||||||
try (Scanner scanner = createScanner(reader)) {
|
try (Scanner scanner = createScanner(reader)) {
|
||||||
while (scanner.hasNext()) {
|
while (scanner.hasNext()) {
|
||||||
String uri = scanner.next();
|
String uri = scanner.next();
|
||||||
log.debug("Request to index uri '" + uri + "'");
|
log.debug("Request to index uri '" + uri + "'");
|
||||||
builder.addToChanged(uri);
|
uris.add(uri);
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
indexer.scheduleUpdatesForUris(uris);
|
||||||
|
return uris.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
|
|
|
@ -0,0 +1,220 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.searchindex;
|
||||||
|
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer.Event.Type.REBUILD_REQUESTED;
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer.Event.Type.START_PROCESSING_STATEMENTS;
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer.Event.Type.STOP_PROCESSING_STATEMENTS;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.Application;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.ComponentStartupStatus;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexerStatus;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexerStatus.State;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexingEventListener;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread.WorkLevel;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread.WorkLevelStamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* A silly implementation that just wraps the old IndexBuilder.
|
||||||
|
*/
|
||||||
|
public class SearchIndexerImpl implements SearchIndexer {
|
||||||
|
private static final Log log = LogFactory.getLog(SearchIndexerImpl.class);
|
||||||
|
|
||||||
|
private final ListenerList listeners = new ListenerList();
|
||||||
|
|
||||||
|
private ServletContext ctx;
|
||||||
|
// private Set<SearchIndexExcluder> excluders;
|
||||||
|
// private Set<DocumentModifier> modifiers;
|
||||||
|
// private Set<IndexingUriFinder> uriFinders;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
private IndexBuilder indexBuilder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startup(Application application, ComponentStartupStatus ss) {
|
||||||
|
try {
|
||||||
|
this.ctx = application.getServletContext();
|
||||||
|
// loadConfiguration();
|
||||||
|
// ss.info("Configured SearchIndexer: excluders=" + excluders
|
||||||
|
// + ", modifiers=" + modifiers + ", uriFinders=" + uriFinders);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
this.indexBuilder = (IndexBuilder) ctx
|
||||||
|
.getAttribute(IndexBuilder.class.getName());
|
||||||
|
|
||||||
|
this.indexBuilder.addIndexBuilderListener(new BridgeListener());
|
||||||
|
} catch (Exception e) {
|
||||||
|
ss.fatal("Failed to configure the SearchIndexer", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createAndFire(Event.Type type) {
|
||||||
|
listeners.fireEvent(new Event(type, getStatus()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// private void loadConfiguration() throws ConfigurationBeanLoaderException
|
||||||
|
// {
|
||||||
|
// ConfigurationBeanLoader beanLoader = new ConfigurationBeanLoader(
|
||||||
|
// ModelAccess.on(ctx).getOntModel(DISPLAY), ctx);
|
||||||
|
// excluders = beanLoader.loadAll(SearchIndexExcluder.class);
|
||||||
|
// modifiers = beanLoader.loadAll(DocumentModifier.class);
|
||||||
|
// uriFinders = beanLoader.loadAll(IndexingUriFinder.class);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see
|
||||||
|
* edu.cornell.mannlib.vitro.webapp.modules.Application.Component#shutdown
|
||||||
|
* (edu.cornell.mannlib.vitro.webapp.modules.Application)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void shutdown(Application application) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see
|
||||||
|
* edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer#
|
||||||
|
* scheduleUpdatesForUris(java.util.Collection)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void scheduleUpdatesForUris(Collection<String> uris) {
|
||||||
|
// TODO
|
||||||
|
for (String uri : uris) {
|
||||||
|
indexBuilder.addToChanged(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see
|
||||||
|
* edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer#
|
||||||
|
* rebuildIndex()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void rebuildIndex() {
|
||||||
|
// TODO
|
||||||
|
indexBuilder.doIndexRebuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see
|
||||||
|
* edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer#
|
||||||
|
* pause()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void pause() {
|
||||||
|
// TODO
|
||||||
|
indexBuilder.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see
|
||||||
|
* edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer#
|
||||||
|
* unpause()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void unpause() {
|
||||||
|
// TODO
|
||||||
|
indexBuilder.unpause();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see
|
||||||
|
* edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer#
|
||||||
|
* getStatus()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public SearchIndexerStatus getStatus() {
|
||||||
|
// TODO
|
||||||
|
WorkLevelStamp workLevel = indexBuilder.getWorkLevel();
|
||||||
|
WorkLevel level = workLevel.getLevel();
|
||||||
|
Date since = workLevel.getSince();
|
||||||
|
if (level == WorkLevel.IDLE) {
|
||||||
|
return new SearchIndexerStatus(State.IDLE, since,
|
||||||
|
new SearchIndexerStatus.NoCounts());
|
||||||
|
} else {
|
||||||
|
return new SearchIndexerStatus(State.PROCESSING_URIS, since,
|
||||||
|
new SearchIndexerStatus.UriCounts(1, 2, 3, 6));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addListener(Listener listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeListener(Listener listener) {
|
||||||
|
listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ListenerList {
|
||||||
|
private final List<Listener> list;
|
||||||
|
|
||||||
|
public ListenerList() {
|
||||||
|
list = Collections.synchronizedList(new ArrayList<Listener>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Listener l) {
|
||||||
|
list.add(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Listener l) {
|
||||||
|
list.remove(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fireEvent(Event e) {
|
||||||
|
synchronized (list) {
|
||||||
|
for (Listener l : list) {
|
||||||
|
l.receiveSearchIndexerEvent(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BridgeListener implements IndexingEventListener {
|
||||||
|
@Override
|
||||||
|
public void notifyOfIndexingEvent(EventTypes ie) {
|
||||||
|
switch (ie) {
|
||||||
|
case START_UPDATE:
|
||||||
|
createAndFire(START_PROCESSING_STATEMENTS);
|
||||||
|
break;
|
||||||
|
case FINISHED_UPDATE:
|
||||||
|
createAndFire(STOP_PROCESSING_STATEMENTS);
|
||||||
|
break;
|
||||||
|
case START_FULL_REBUILD:
|
||||||
|
createAndFire(REBUILD_REQUESTED);
|
||||||
|
createAndFire(START_PROCESSING_STATEMENTS);
|
||||||
|
break;
|
||||||
|
default: // FINISH_FULL_REBUILD
|
||||||
|
createAndFire(STOP_PROCESSING_STATEMENTS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ import edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding.DocumentMod
|
||||||
import edu.cornell.mannlib.vitro.webapp.searchindex.exclusions.SearchIndexExcluder;
|
import edu.cornell.mannlib.vitro.webapp.searchindex.exclusions.SearchIndexExcluder;
|
||||||
import edu.cornell.mannlib.vitro.webapp.searchindex.indexing.AdditionalUriFinders;
|
import edu.cornell.mannlib.vitro.webapp.searchindex.indexing.AdditionalUriFinders;
|
||||||
import edu.cornell.mannlib.vitro.webapp.searchindex.indexing.StatementToURIsToUpdate;
|
import edu.cornell.mannlib.vitro.webapp.searchindex.indexing.StatementToURIsToUpdate;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.startup.ComponentStartupStatusImpl;
|
||||||
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
|
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader;
|
import edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoaderException;
|
import edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoaderException;
|
||||||
|
@ -43,6 +44,7 @@ import edu.cornell.mannlib.vitro.webapp.utils.developer.listeners.DeveloperDisab
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
|
* A silly implementation that just wraps the old IndexBuilder with a new SearchIndexerImpl.
|
||||||
*/
|
*/
|
||||||
public class SearchIndexerSetup implements ServletContextListener {
|
public class SearchIndexerSetup implements ServletContextListener {
|
||||||
private static final Log log = LogFactory.getLog(SearchIndexerSetup.class);
|
private static final Log log = LogFactory.getLog(SearchIndexerSetup.class);
|
||||||
|
@ -96,6 +98,8 @@ public class SearchIndexerSetup implements ServletContextListener {
|
||||||
Key.SEARCH_INDEX_SUPPRESS_MODEL_CHANGE_LISTENER));
|
Key.SEARCH_INDEX_SUPPRESS_MODEL_CHANGE_LISTENER));
|
||||||
|
|
||||||
ss.info(this, "Setup of search indexer completed.");
|
ss.info(this, "Setup of search indexer completed.");
|
||||||
|
|
||||||
|
ApplicationUtils.instance().getSearchIndexer().startup(ApplicationUtils.instance(), new ComponentStartupStatusImpl(this, ss));
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
ss.fatal(this, "could not setup search engine", e);
|
ss.fatal(this, "could not setup search engine", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ package stubs.edu.cornell.mannlib.vitro.webapp.modelaccess;
|
||||||
|
|
||||||
import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.ReasoningOption.ASSERTIONS_AND_INFERENCES;
|
import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.ReasoningOption.ASSERTIONS_AND_INFERENCES;
|
||||||
import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.WhichService.CONTENT;
|
import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.WhichService.CONTENT;
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.FULL_UNION;
|
||||||
|
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -18,6 +19,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.jena.OntModelSelector;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ContextModelAccess;
|
import edu.cornell.mannlib.vitro.webapp.modelaccess.ContextModelAccess;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.ReasoningOption;
|
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.ReasoningOption;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.WhichService;
|
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.WhichService;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,10 +31,14 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
||||||
public class ContextModelAccessStub implements ContextModelAccess {
|
public class ContextModelAccessStub implements ContextModelAccess {
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Stub infrastructure
|
// Stub infrastructure
|
||||||
|
//
|
||||||
|
// Warning: ontModelMap and rdfServiceMap are not connected, so it's up to
|
||||||
|
// the user to insure that they are consistent with each other.
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
private final Map<ReasoningOption, WebappDaoFactory> wadfMap = new HashMap<>();
|
private final Map<ReasoningOption, WebappDaoFactory> wadfMap = new HashMap<>();
|
||||||
private final Map<WhichService, RDFService> rdfServiceMap = new EnumMap<>(WhichService.class);
|
private final Map<WhichService, RDFService> rdfServiceMap = new EnumMap<>(WhichService.class);
|
||||||
|
private final Map<String, OntModel> ontModelMap = new HashMap<>();
|
||||||
|
|
||||||
public void setWebappDaoFactory(WebappDaoFactory wadf) {
|
public void setWebappDaoFactory(WebappDaoFactory wadf) {
|
||||||
setWebappDaoFactory(wadf, ASSERTIONS_AND_INFERENCES);
|
setWebappDaoFactory(wadf, ASSERTIONS_AND_INFERENCES);
|
||||||
|
@ -47,6 +53,10 @@ public class ContextModelAccessStub implements ContextModelAccess {
|
||||||
rdfServiceMap.put(which, rdfService);
|
rdfServiceMap.put(which, rdfService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOntModel(String name, OntModel model) {
|
||||||
|
ontModelMap.put(name, model);
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Stub methods
|
// Stub methods
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
@ -66,6 +76,16 @@ public class ContextModelAccessStub implements ContextModelAccess {
|
||||||
return rdfServiceMap.get(which);
|
return rdfServiceMap.get(which);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OntModel getOntModel() {
|
||||||
|
return getOntModel(FULL_UNION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OntModel getOntModel(String name) {
|
||||||
|
return ontModelMap.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Un-implemented methods
|
// Un-implemented methods
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
@ -94,18 +114,6 @@ public class ContextModelAccessStub implements ContextModelAccess {
|
||||||
"ContextModelAccessStub.getModelMaker() not implemented.");
|
"ContextModelAccessStub.getModelMaker() not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public OntModel getOntModel() {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"ContextModelAccessStub.getOntModel() not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OntModel getOntModel(String name) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"ContextModelAccessStub.getOntModel() not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OntModelSelector getOntModelSelector() {
|
public OntModelSelector getOntModelSelector() {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
|
|
|
@ -12,6 +12,7 @@ import edu.cornell.mannlib.vitro.webapp.modules.Application;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
|
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.searchIndexer.SearchIndexer;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
|
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource;
|
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource;
|
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource;
|
||||||
|
@ -104,9 +105,14 @@ public class ApplicationStub implements Application {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TBoxReasonerModule getTBoxReasonerModule() {
|
public TBoxReasonerModule getTBoxReasonerModule() {
|
||||||
// TODO Auto-generated method stub
|
throw new RuntimeException(
|
||||||
throw new RuntimeException("ApplicationStub.getTBoxReasonerModule() not implemented.");
|
"ApplicationStub.getTBoxReasonerModule() not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchIndexer getSearchIndexer() {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Application.getSearchIndexer() not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
17
webapp/web/css/search/searchIndex.css
Normal file
17
webapp/web/css/search/searchIndex.css
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
/* Styles for the search index controller. */
|
||||||
|
|
||||||
|
section#indexer table.history {
|
||||||
|
font-size: smaller;
|
||||||
|
border: 1px solid gray;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
section#indexer table.history th{
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
section#indexer table.history td{
|
||||||
|
padding: 2px 5px 2px 5px;
|
||||||
|
}
|
22
webapp/web/js/search/searchIndex.js
Normal file
22
webapp/web/js/search/searchIndex.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Functions for use by searchIndex.ftl
|
||||||
|
*/
|
||||||
|
|
||||||
|
function updateSearchIndexerStatus() {
|
||||||
|
$.ajax({
|
||||||
|
url: searchIndexerStatusUrl,
|
||||||
|
dataType: "html",
|
||||||
|
complete: function(xhr, status) {
|
||||||
|
updatePanelContents(xhr.responseText);
|
||||||
|
setTimeout(updateSearchIndexerStatus,5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePanelContents(contents) {
|
||||||
|
document.getElementById("searchIndexerStatus").innerHTML = contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(updateSearchIndexerStatus());
|
|
@ -1,43 +1,21 @@
|
||||||
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
||||||
|
|
||||||
<#--
|
<#--
|
||||||
Template for the page that controls the updating or rebuilding of the Search Index.
|
Template for the page that displays the status of the Search Indexer.
|
||||||
|
Most of it is provided by the AJAX call.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<h2>${i18n().search_index_status}</h2>
|
<h2>${i18n().search_index_status}</h2>
|
||||||
|
|
||||||
<#if !indexIsConnected>
|
<div id="searchIndexerStatus">
|
||||||
<!-- Can't contact the search engine. Indexing would be impossible. Show an error message. -->
|
Search Indexer Status
|
||||||
<section id="error-alert" role="alert">
|
</div>
|
||||||
<img src="${urls.images}/iconAlert.png" width="24" height="24" alt="Error alert icon" />
|
|
||||||
<p>${i18n().search_index_not_connected}</p>
|
|
||||||
<p><tt>SearchEngine.ping()</tt> ${i18n().failed}.
|
|
||||||
<p>${i18n().check_startup_status}</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<#elseif worklevel == "IDLE">
|
<script>
|
||||||
<!-- Search indexer is idle. Show the button that rebuilds the index. -->
|
searchIndexerStatusUrl = '${statusUrl}'
|
||||||
<h3>${i18n().search_indexer_idle}</h3>
|
</script>
|
||||||
<#if hasPreviousBuild??>
|
|
||||||
<p>${i18n().most_recent_update} ${since?string("hh:mm:ss a, MMMM dd, yyyy")}</p>
|
|
||||||
</#if>
|
|
||||||
|
|
||||||
<form action="${actionUrl}" method="POST">
|
${stylesheets.add('<link rel="stylesheet" href="${urls.base}/css/search/searchIndex.css" />')}
|
||||||
<p>
|
|
||||||
<input class="submit" type="submit" name="rebuild" value="${i18n().rebuild_button}" role="button" />
|
|
||||||
${i18n().reset_search_index}
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<#elseif totalToDo == 0>
|
${scripts.add('<script type="text/javascript" src="${urls.base}/js/search/searchIndex.js"></script>')}
|
||||||
<!-- Search indexer is preparing the list of records. Show elapsed time since request. -->
|
${scripts.add('<script type="text/javascript" src="${urls.base}/js/jquery-ui/js/jquery-ui-1.8.9.custom.min.js"></script>')}
|
||||||
<h3>${i18n().preparing_to_rebuild_index}</h3>
|
|
||||||
<p>${i18n().since_elapsed_time(since?string("hh:mm:ss a, MMMM dd, yyyy"),elapsed)}</p>
|
|
||||||
|
|
||||||
<#else>
|
|
||||||
<!-- Search indexer is re-building the index. Show the progress. -->
|
|
||||||
<h3>${i18n().current_task(currentTask)}</h3>
|
|
||||||
<p>${i18n().since_elapsed_time_est_total(since?string("hh:mm:ss a, MMMM dd, yyyy"),elapsed,expected)}</p>
|
|
||||||
<p>${i18n().index_recs_completed(completedCount,totalToDo)}</p>
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
||||||
|
|
||||||
|
<#--
|
||||||
|
Template for the page that controls the updating or rebuilding of the Search Index.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<section id="indexer" role="region">
|
||||||
|
<#if status.statusType == "IDLE">
|
||||||
|
<h3>The search indexer has been idle since ${status.since?datetime}</h3>
|
||||||
|
|
||||||
|
<#elseif status.statusType = "PROCESSING_URIS">
|
||||||
|
<h3>The search indexer has been processing URIs since ${status.since?datetime}</h3>
|
||||||
|
<p><@showIndexerCounts "URI_COUNTS", status /></p>
|
||||||
|
<p><@showElapsedTime status.elapsed /> Expected completion ${status.expectedCompletion?datetime}.</p>
|
||||||
|
|
||||||
|
<#elseif status.statusType = "PROCESSING_STMTS">
|
||||||
|
<h3>The search indexer has been processing changed statements since ${status.since?datetime}</h3>
|
||||||
|
<p><@showIndexerCounts "STATEMENT_COUNTS", status /></p>
|
||||||
|
<p><@showElapsedTime status.elapsed /> Expected completion ${status.expectedCompletion?datetime}.</p>
|
||||||
|
|
||||||
|
<#elseif status.statusType = "PREPARING_REBUILD">
|
||||||
|
<h3>The search indexer has been preparing to rebuild the index since ${status.since?datetime}</h3>
|
||||||
|
|
||||||
|
<#else>
|
||||||
|
<h3>The search indexer status is: ${status.statusType}
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
|
||||||
|
<form action="${rebuildUrl}" method="POST">
|
||||||
|
<p>
|
||||||
|
<#if status.statusType == "IDLE">
|
||||||
|
<input class="submit" type="submit" name="rebuild" value="${i18n().rebuild_button}" role="button" />
|
||||||
|
${i18n().reset_search_index}
|
||||||
|
</#if>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<h3>History</h3>
|
||||||
|
<table class="history">
|
||||||
|
<tr> <th>Event</th> <th>Status</th> <th>Since</th> <th>Counts</th> </tr>
|
||||||
|
<#list history as ie>
|
||||||
|
<@showIndexerEvent ie />
|
||||||
|
</#list>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<#macro showElapsedTime elapsed>
|
||||||
|
Elapsed time ${elapsed[0]}:${elapsed[1]}:${elapsed[2]}.
|
||||||
|
</#macro>
|
||||||
|
|
||||||
|
|
||||||
|
<#macro showIndexerEvent event>
|
||||||
|
<tr>
|
||||||
|
<td>${event.event}</td>
|
||||||
|
<td>${event.statusType}</td>
|
||||||
|
<td>${event.since?datetime}</td>
|
||||||
|
<td><@showIndexerCounts event.countsType, event /></td>
|
||||||
|
</tr>
|
||||||
|
</#macro>
|
||||||
|
|
||||||
|
|
||||||
|
<#macro showIndexerCounts countsType, counts>
|
||||||
|
<#if countsType == "URI_COUNTS">
|
||||||
|
Updated: ${counts.updated}, deleted: ${counts.deleted}, remaining: ${counts.remaining}, total: ${counts.total}
|
||||||
|
<#elseif countsType == "STATEMENT_COUNTS">
|
||||||
|
Processed: ${counts.processed}, remaining: ${counts.remaining}, total: ${counts.total}
|
||||||
|
<#elseif countsType == "REBUILD_COUNTS">
|
||||||
|
Number of individuals before rebuild: ${counts.numberOfIndividuals}
|
||||||
|
</#if>
|
||||||
|
</#macro>
|
Loading…
Add table
Add a link
Reference in a new issue