You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2005/12/20 16:05:28 UTC
svn commit: r358016 - in /incubator/jackrabbit/trunk/jackrabbit:
applications/test/workspaces/default/ src/main/config/
src/main/java/org/apache/jackrabbit/core/
src/main/java/org/apache/jackrabbit/core/query/lucene/
Author: mreutegg
Date: Tue Dec 20 07:05:17 2005
New Revision: 358016
URL: http://svn.apache.org/viewcvs?rev=358016&view=rev
Log:
JCR-257: Use separate index for jcr:system tree
- shut down unused query handler when it had been idle for some configurable time
Modified:
incubator/jackrabbit/trunk/jackrabbit/applications/test/workspaces/default/workspace.xml
incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/SearchManager.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
Modified: incubator/jackrabbit/trunk/jackrabbit/applications/test/workspaces/default/workspace.xml
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/applications/test/workspaces/default/workspace.xml?rev=358016&r1=358015&r2=358016&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/applications/test/workspaces/default/workspace.xml (original)
+++ incubator/jackrabbit/trunk/jackrabbit/applications/test/workspaces/default/workspace.xml Tue Dec 20 07:05:17 2005
@@ -20,6 +20,11 @@
-->
<SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
<param name="path" value="${wsp.home}/index" />
+ <!--
+ Set to zero for test purpose. This is *not* recommended at all for a
+ real system!
+ -->
+ <param name="idleTime" value="0"/>
</SearchIndex>
</Workspace>
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml?rev=358016&r1=358015&r2=358016&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml Tue Dec 20 07:05:17 2005
@@ -214,6 +214,9 @@
- analyzer: class name of a lucene analyzer to use for fulltext indexing of text.
- queryClass: class name that implements the javax.jcr.query.Query interface.
this class must extend the class: org.apache.jackrabbit.core.query.AbstractQueryImpl
+ - idleTime: idle time in seconds after which an unused query handler is shut down.
+ If the query handler is later used again it is automatically started.
+ Default value -1 disables this feature.
Note: all parameters (except path) in this SearchIndex config are default
values and can be omitted.
@@ -231,6 +234,7 @@
<param name="autoRepair" value="true"/>
<param name="analyzer" value="org.apache.lucene.analysis.standard.StandardAnalyzer"/>
<param name="queryClass" value="org.apache.jackrabbit.core.query.QueryImpl"/>
+ <param name="idleTime" value="-1"/>
</SearchIndex>
</Workspace>
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/SearchManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/SearchManager.java?rev=358016&r1=358015&r2=358016&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/SearchManager.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/SearchManager.java Tue Dec 20 07:05:17 2005
@@ -50,6 +50,12 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.Properties;
+import java.util.WeakHashMap;
+import java.util.Map;
+import java.util.Collections;
/**
* Acts as a global entry point to execute queries and index nodes.
@@ -80,11 +86,43 @@
private static final String PARAM_QUERY_IMPL = "queryClass";
/**
+ * Name of the parameter that specifies the idle time for a query handler.
+ */
+ private static final String PARAM_IDLE_TIME = "idleTime";
+
+ /**
* Name of the default query implementation class.
*/
private static final String DEFAULT_QUERY_IMPL_CLASS = QueryImpl.class.getName();
/**
+ * Class instance that is shared for all <code>SearchManager</code> instances.
+ * Each workspace will schedule a task to check if the query handler can
+ * be shutdown after it had been idle for some time.
+ */
+ private static final Timer IDLE_TIMER = new Timer(true);
+
+ /**
+ * Idle time in seconds after which the query handler is shut down.
+ */
+ private static final int DEFAULT_IDLE_TIME = -1;
+
+ /**
+ * The time when the query handler was last accessed.
+ */
+ private long lastAccess = System.currentTimeMillis();
+
+ /**
+ * The search configuration.
+ */
+ private final SearchConfig config;
+
+ /**
+ * The node type registry.
+ */
+ private final NodeTypeRegistry ntReg;
+
+ /**
* The shared item state manager instance for the workspace.
*/
private final ItemStateManager itemMgr;
@@ -95,9 +133,20 @@
private final FileSystem fs;
/**
+ * The root node for this search manager.
+ */
+ private final String rootNodeUUID;
+
+ /**
* QueryHandler where query execution is delegated to
*/
- private final QueryHandler handler;
+ private QueryHandler handler;
+
+ /**
+ * QueryHandler of the parent search manager or <code>null</code> if there
+ * is none.
+ */
+ private final QueryHandler parentHandler;
/**
* Namespace resolver that is based on the namespace registry itself.
@@ -105,6 +154,12 @@
private final NamespaceResolver nsResolver;
/**
+ * UUID of the node that should be excluded from indexing or <code>null</code>
+ * if no node should be excluded.
+ */
+ private final String excludedNodeUUID;
+
+ /**
* Path that will be excluded from indexing.
*/
private Path excludePath;
@@ -116,12 +171,35 @@
private final String queryImplClassName;
/**
+ * Task that checks if the query handler can be shut down because it
+ * had been idle for {@link #idleTime} seconds.
+ */
+ private final TimerTask idleChecker;
+
+ /**
+ * Idle time in seconds. After the query handler had been idle for this
+ * amount of time it is shut down. Defaults to -1 and causes the search
+ * manager to never shut down.
+ */
+ private int idleTime;
+
+ /**
+ * Weakly references all {@link javax.jcr.query.Query} instances created
+ * by this <code>SearchManager</code>.
+ * If this map is empty and this search manager had been idle for at least
+ * {@link #idleTime} seconds, then the query handler is shut down.
+ */
+ private final Map activeQueries = Collections.synchronizedMap(new WeakHashMap() {
+
+ });
+
+ /**
* Creates a new <code>SearchManager</code>.
*
- * @param config the search configuration.
+ * @param config the search configuration.
* @param nsReg the namespace registry.
- * @param ntReg the node type registry.
- * @param itemMgr the shared item state manager.
+ * @param ntReg the node type registry.
+ * @param itemMgr the shared item state manager.
* @param rootNodeUUID the uuid of the root node.
* @param parentMgr the parent search manager or <code>null</code> if
* there is no parent search manager.
@@ -138,7 +216,12 @@
SearchManager parentMgr,
String excludedNodeUUID) throws RepositoryException {
this.fs = config.getFileSystem();
+ this.config = config;
+ this.ntReg = ntReg;
this.itemMgr = itemMgr;
+ this.rootNodeUUID = rootNodeUUID;
+ this.parentHandler = (parentMgr != null) ? parentMgr.handler : null;
+ this.excludedNodeUUID = excludedNodeUUID;
this.nsResolver = new AbstractNamespaceResolver() {
public String getURI(String prefix) throws NamespaceException {
try {
@@ -171,11 +254,13 @@
nsReg.registerNamespace(NS_FN_PREFIX, NS_FN_URI);
}
- queryImplClassName = config.getParameters().getProperty(PARAM_QUERY_IMPL, DEFAULT_QUERY_IMPL_CLASS);
-
- QueryHandler parentHandler = null;
- if (parentMgr != null) {
- parentHandler = parentMgr.handler;
+ Properties params = config.getParameters();
+ queryImplClassName = params.getProperty(PARAM_QUERY_IMPL, DEFAULT_QUERY_IMPL_CLASS);
+ String idleTimeString = params.getProperty(PARAM_IDLE_TIME, String.valueOf(DEFAULT_IDLE_TIME));
+ try {
+ idleTime = Integer.decode(idleTimeString).intValue();
+ } catch (NumberFormatException e) {
+ idleTime = DEFAULT_IDLE_TIME;
}
if (excludedNodeUUID != null) {
@@ -184,14 +269,28 @@
}
// initialize query handler
- try {
- handler = (QueryHandler) config.newInstance();
- QueryHandlerContext context
- = new QueryHandlerContext(fs, itemMgr, rootNodeUUID, ntReg,
- parentHandler, excludedNodeUUID);
- handler.init(context);
- } catch (Exception e) {
- throw new RepositoryException(e.getMessage(), e);
+ initializeQueryHandler();
+
+ idleChecker = new TimerTask() {
+ public void run() {
+ if (lastAccess + (idleTime * 1000) < System.currentTimeMillis()) {
+ int inUse = activeQueries.size();
+ if (inUse == 0) {
+ try {
+ shutdownQueryHandler();
+ } catch (IOException e) {
+ log.warn("Unable to shutdown idle query handler", e);
+ }
+ } else {
+ log.debug("SearchManager is idle but " + inUse +
+ " queries are still in use.");
+ }
+ }
+ }
+ };
+
+ if (idleTime > -1) {
+ IDLE_TIMER.schedule(idleChecker, 0, 1000);
}
}
@@ -201,7 +300,9 @@
*/
public void close() {
try {
- handler.close();
+ idleChecker.cancel();
+ shutdownQueryHandler();
+
if (fs != null) {
fs.close();
}
@@ -230,6 +331,7 @@
String statement,
String language)
throws InvalidQueryException, RepositoryException {
+ ensureInitialized();
AbstractQueryImpl query = createQueryInstance();
query.init(session, itemMgr, handler, statement, language);
return query;
@@ -251,6 +353,7 @@
ItemManager itemMgr,
Node node)
throws InvalidQueryException, RepositoryException {
+ ensureInitialized();
AbstractQueryImpl query = createQueryInstance();
query.init(session, itemMgr, handler, node);
return query;
@@ -332,12 +435,15 @@
return item;
}
};
- try {
- handler.updateNodes(removedNodes.iterator(), addedStates);
- } catch (RepositoryException e) {
- log.error("Error indexing node.", e);
- } catch (IOException e) {
- log.error("Error indexing node.", e);
+ if (removedNodes.size() > 0 || addedNodes.size() > 0) {
+ try {
+ ensureInitialized();
+ handler.updateNodes(removedNodes.iterator(), addedStates);
+ } catch (RepositoryException e) {
+ log.error("Error indexing node.", e);
+ } catch (IOException e) {
+ log.error("Error indexing node.", e);
+ }
}
if (log.isDebugEnabled()) {
@@ -359,6 +465,8 @@
try {
Object obj = Class.forName(queryImplClassName).newInstance();
if (obj instanceof AbstractQueryImpl) {
+ // track query instances
+ activeQueries.put(obj, null);
return (AbstractQueryImpl) obj;
} else {
throw new IllegalArgumentException(queryImplClassName +
@@ -366,6 +474,53 @@
}
} catch (Throwable t) {
throw new RepositoryException("Unable to create query: " + t.toString());
+ }
+ }
+
+ //------------------------< internal >--------------------------------------
+
+ /**
+ * Initializes the query handler.
+ *
+ * @throws RepositoryException if the query handler cannot be initialized.
+ */
+ private void initializeQueryHandler() throws RepositoryException {
+ // initialize query handler
+ try {
+ handler = (QueryHandler) config.newInstance();
+ QueryHandlerContext context
+ = new QueryHandlerContext(fs, itemMgr, rootNodeUUID,
+ ntReg, parentHandler, excludedNodeUUID);
+ handler.init(context);
+ } catch (Exception e) {
+ throw new RepositoryException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Shuts down the query handler. If the query handler is already shut down
+ * this method does nothing.
+ *
+ * @throws IOException if an error occurs while shutting down the query
+ * handler.
+ */
+ private synchronized void shutdownQueryHandler() throws IOException {
+ if (handler != null) {
+ handler.close();
+ handler = null;
+ }
+ }
+
+ /**
+ * Ensures that the query handler is initialized and updates the last
+ * access to the current time.
+ *
+ * @throws RepositoryException if the query handler cannot be initialized.
+ */
+ private synchronized void ensureInitialized() throws RepositoryException {
+ lastAccess = System.currentTimeMillis();
+ if (handler == null) {
+ initializeQueryHandler();
}
}
}
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java?rev=358016&r1=358015&r2=358016&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java Tue Dec 20 07:05:17 2005
@@ -164,7 +164,13 @@
/**
* Timer to schedule flushes of this index after some idle time.
*/
- private final Timer flushTimer = new Timer(true);
+ private static final Timer FLUSH_TIMER = new Timer(true);
+
+ /**
+ * Task that is periodically called by {@link #FLUSH_TIMER} and checks
+ * if index should be flushed.
+ */
+ private final TimerTask flushTask;
/**
* The RedoLog of this <code>MultiIndex</code>.
@@ -189,10 +195,10 @@
/**
* Creates a new MultiIndex.
*
- * @param indexDir the base file system
- * @param handler the search handler
- * @param stateMgr shared item state manager
- * @param rootUUID uuid of the root node
+ * @param indexDir the base file system
+ * @param handler the search handler
+ * @param stateMgr shared item state manager
+ * @param rootUUID uuid of the root node
* @param excludedUUIDs Set<String> that contains uuids that should not
* be indexed nor further traversed.
* @throws IOException if an error occurs
@@ -206,7 +212,7 @@
this.indexDir = indexDir;
this.handler = handler;
this.cache = new DocNumberCache(handler.getCacheSize());
- this.redoLog = new RedoLog(new File(indexDir, REDO_LOG));
+ this.redoLog = new RedoLog(new File(indexDir, REDO_LOG));
this.excludedUUIDs = new HashSet(excludedUUIDs);
if (indexNames.exists(indexDir)) {
@@ -277,7 +283,13 @@
throw new IOException("Error indexing root node: " + e.getMessage());
}
- startFlushTimer();
+ lastFlushTime = System.currentTimeMillis();
+ flushTask = new TimerTask() {
+ public void run() {
+ checkFlush();
+ }
+ };
+ FLUSH_TIMER.schedule(flushTask, 0, 1000);
}
/**
@@ -596,7 +608,7 @@
synchronized (this) {
// stop timer
- flushTimer.cancel();
+ flushTask.cancel();
// commit / close indexes
if (multiReader != null) {
@@ -808,7 +820,7 @@
resetVolatileIndex();
time = System.currentTimeMillis() - time;
- log.info("Committed in-memory index in " + time + "ms.");
+ log.debug("Committed in-memory index in " + time + "ms.");
}
}
@@ -902,19 +914,6 @@
}
/**
- * Starts the flush timer that periodically checks if the index
- * should be flushed. The timer task will call {@link #checkFlush()}.
- */
- private void startFlushTimer() {
- lastFlushTime = System.currentTimeMillis();
- flushTimer.schedule(new TimerTask() {
- public void run() {
- checkFlush();
- }
- }, 0, 1000);
- }
-
- /**
* Checks the duration between the last commit to this index and the
* current time and flushes the index (if there are changes at all)
* if the duration (idle time) is more than {@link SearchIndex#getVolatileIdleTime()}
@@ -927,7 +926,7 @@
&& idleTime > handler.getVolatileIdleTime() * 1000) {
try {
if (redoLog.hasEntries()) {
- log.info("Flushing index after being idle for " +
+ log.debug("Flushing index after being idle for " +
idleTime + " ms.");
synchronized (updateMonitor) {
updateInProgress = true;
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java?rev=358016&r1=358015&r2=358016&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java Tue Dec 20 07:05:17 2005
@@ -163,6 +163,12 @@
private int cacheSize = 1000;
/**
+ * Indicates if this <code>SearchIndex</code> is closed and cannot be used
+ * anymore.
+ */
+ private boolean closed = false;
+
+ /**
* Default constructor.
*/
public SearchIndex() {
@@ -211,6 +217,7 @@
log.warn("Failed to run consistency check on index: " + e);
}
}
+ log.info("Index initialized: " + path);
}
/**
@@ -247,6 +254,7 @@
*/
public void updateNodes(Iterator remove, Iterator add)
throws RepositoryException, IOException {
+ checkOpen();
index.update(new AbstractIteratorDecorator(remove) {
public Object next() {
String uuid = (String) super.next();
@@ -303,7 +311,8 @@
public void close() {
index.close();
getContext().destroy();
- log.info("Search index closed.");
+ closed = true;
+ log.info("Index closed: " + path);
}
/**
@@ -321,6 +330,7 @@
Query query,
QName[] orderProps,
boolean[] orderSpecs) throws IOException {
+ checkOpen();
QueryHandler parentHandler = getContext().getParentHandler();
IndexReader parentReader = null;
if (parentHandler instanceof SearchIndex) {
@@ -730,5 +740,19 @@
delim = ",";
}
return names.toString();
+ }
+
+ //----------------------------< internal >----------------------------------
+
+ /**
+ * Checks if this <code>SearchIndex</code> is open, otherwise throws
+ * an <code>IOException</code>.
+ *
+ * @throws IOException if this <code>SearchIndex</code> had been closed.
+ */
+ private void checkOpen() throws IOException {
+ if (closed) {
+ throw new IOException("query handler closed and cannot be used anymore.");
+}
}
}