Author: rhauch
Date: 2009-12-17 20:52:46 -0500 (Thu, 17 Dec 2009)
New Revision: 1452
Added:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryQueryManager.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ObservationBus.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequestChannel.java
trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrAccessTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/ImportExportTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryLibrary.java
trunk/extensions/dna-search-lucene/src/main/java/org/jboss/dna/search/lucene/LuceneSearchEngine.java
Log:
DNA-468 Integrated the search engine into the JcrRepository class. By default, the
JcrRepository instances do support executing queries and searches (and thus maintain
indexes if the source does not support queries and searches), and the Session.save() calls
do not wait for the indexes to be updated with the changes. Also, at this point, all of
the previously-passing unit and TCK tests are passing.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2009-12-17 20:38:21
UTC (rev 1451)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2009-12-18 01:52:46
UTC (rev 1452)
@@ -93,6 +93,8 @@
public static I18n nodeAlreadyExistsWithUuid;
public static I18n couldNotAcquireLock;
+ public static I18n errorNotifyingObserver;
+
/* In-Memory Connector */
public static I18n inMemoryNodeDoesNotExist;
public static I18n errorSerializingInMemoryCachePolicyInSource;
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ObservationBus.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ObservationBus.java 2009-12-17
20:38:21 UTC (rev 1451)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/observe/ObservationBus.java 2009-12-18
01:52:46 UTC (rev 1452)
@@ -24,6 +24,8 @@
package org.jboss.dna.graph.observe;
import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.GraphI18n;
/**
* A simple {@link Observer} that is itself {@link Observable}. This class essentially
multiplexes the events from a single
@@ -32,6 +34,7 @@
@ThreadSafe
public class ObservationBus implements Observable, Observer {
private final ChangeObservers observers = new ChangeObservers();
+ private final Logger logger = Logger.getLogger(getClass());
public ObservationBus() {
}
@@ -63,7 +66,11 @@
public void notify( Changes changes ) {
if (changes != null) {
// Broadcast the changes to the registered observers ...
- observers.broadcast(changes);
+ try {
+ observers.broadcast(changes);
+ } catch (RuntimeException t) {
+ logger.error(t, GraphI18n.errorNotifyingObserver,
t.getLocalizedMessage());
+ }
}
}
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java 2009-12-17
20:38:21 UTC (rev 1451)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/process/QueryProcessor.java 2009-12-18
01:52:46 UTC (rev 1452)
@@ -88,8 +88,12 @@
if (component != null) {
// Now execute the component ...
- preExecute(context);
- tuples = component.execute();
+ try {
+ preExecute(context);
+ tuples = component.execute();
+ } finally {
+ postExecute(context);
+ }
} else {
// There must have been an error ...
assert context.getProblems().hasErrors();
@@ -114,6 +118,16 @@
}
/**
+ * A method that can be overridden when a hook is required immediately after the
top-level {@link ProcessingComponent} is
+ * executed and all processing has been completed, even if there was an error. By
default, this method does nothing.
+ *
+ * @param context the context in which the query is being executed; may not be null
+ */
+ protected void postExecute( QueryContext context ) {
+ // do nothing ...
+ }
+
+ /**
* Create an {@link Analyzer} implementation that should be used by the non-access
{@link ProcessingComponent}s that evaluate
* criteria. By default, this method returns null, which means that any criteria
evaluation will likely be pushed down under
* an {@link Type#ACCESS ACCESS} node (and thus handled by an
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequestChannel.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequestChannel.java 2009-12-17
20:38:21 UTC (rev 1451)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequestChannel.java 2009-12-18
01:52:46 UTC (rev 1452)
@@ -287,8 +287,9 @@
* Mark this source as having no more requests to process.
*/
public void close() {
- this.closed.set(true);
- this.queue.add(new LastRequest());
+ if (this.closed.compareAndSet(false, true)) {
+ this.queue.add(new LastRequest());
+ }
}
/**
Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2009-12-17
20:38:21 UTC (rev 1451)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2009-12-18
01:52:46 UTC (rev 1452)
@@ -81,6 +81,8 @@
nodeAlreadyExistsWithUuid = A node with UUID "{0}" already exists at path
"{1}" in workspace "{2}"
couldNotAcquireLock = Could not acquire lock on the node at "{0}" in workspace
"{1}"
+errorNotifyingObserver = Error notifying observer: {0}
+
# In-memory connector
inMemoryNodeDoesNotExist = Could not find an existing node at {0}
errorSerializingInMemoryCachePolicyInSource = Error serializing a {0} instance owned by
the {1} in-memory repository
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java 2009-12-17 20:38:21 UTC
(rev 1451)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrEngine.java 2009-12-18 01:52:46 UTC
(rev 1452)
@@ -47,6 +47,7 @@
import org.jboss.dna.graph.Subgraph;
import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.PathFactory;
@@ -148,7 +149,10 @@
}
};
- scheduler.scheduleAtFixedRate(cleanUpTask, 0, LOCK_SWEEP_INTERVAL_IN_MILLIS,
TimeUnit.MILLISECONDS);
+ scheduler.scheduleAtFixedRate(cleanUpTask,
+ LOCK_SWEEP_INTERVAL_IN_MILLIS,
+ LOCK_SWEEP_INTERVAL_IN_MILLIS,
+ TimeUnit.MILLISECONDS);
}
/**
@@ -246,9 +250,13 @@
}
String sourceName =
context.getValueFactories().getStringFactory().create(property.getFirstValue());
+ // Find the capabilities ...
+ RepositorySource source = getRepositorySource(sourceName);
+ RepositorySourceCapabilities capabilities = source != null ?
source.getCapabilities() : null;
// Create the repository ...
JcrRepository repository = new JcrRepository(context, connectionFactory,
sourceName,
-
getRepositoryService().getRepositoryLibrary(), descriptors, options);
+
getRepositoryService().getRepositoryLibrary(), capabilities, descriptors,
+ options);
// Register all the the node types ...
Node nodeTypesNode = subgraph.getNode(JcrLexicon.NODE_TYPES);
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java 2009-12-17 20:38:21 UTC
(rev 1451)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java 2009-12-18 01:52:46 UTC
(rev 1452)
@@ -120,6 +120,7 @@
public static I18n searchIndexDirectoryOptionSpecifiesDirectoryThatCannotBeRead;
public static I18n
searchIndexDirectoryOptionSpecifiesDirectoryThatCannotBeWrittenTo;
public static I18n searchIndexDirectoryOptionSpecifiesDirectoryThatCannotBeCreated;
+ public static I18n errorUpdatingQueryIndexes;
// Used in AbstractJcrNode#getAncestor
public static I18n noNegativeDepth;
@@ -140,6 +141,7 @@
public static I18n invalidQueryLanguage;
public static I18n queryCannotBeParsedUsingLanguage;
public static I18n queryInLanguageIsNotValid;
+ public static I18n queryIsDisabledInRepository;
// Type registration messages
public static I18n invalidNodeTypeName;
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java 2009-12-17 20:38:21
UTC (rev 1451)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java 2009-12-18 01:52:46
UTC (rev 1452)
@@ -26,6 +26,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
@@ -52,6 +53,7 @@
import org.jboss.dna.graph.query.model.QueryCommand;
import org.jboss.dna.graph.query.model.TypeSystem;
import org.jboss.dna.graph.query.parse.QueryParser;
+import org.jboss.dna.graph.query.plan.PlanHints;
import org.jboss.dna.graph.query.validate.Schemata;
/**
@@ -148,7 +150,9 @@
* @see javax.jcr.query.QueryManager#getSupportedQueryLanguages()
*/
public String[] getSupportedQueryLanguages() {
- return new String[] {Query.XPATH};
+ // Make a defensive copy ...
+ Set<String> languages =
session.repository().queryParsers().getLanguages();
+ return languages.toArray(new String[languages.size()]);
}
@NotThreadSafe
@@ -251,6 +255,8 @@
@NotThreadSafe
protected static class JcrQuery extends AbstractQuery {
private final QueryCommand query;
+ private final PlanHints hints;
+ private final Map<String, Object> variables;
/**
* Creates a new JCR {@link Query} by specifying the query statement itself, the
language in which the query is stated,
@@ -272,6 +278,8 @@
super(session, statement, language, storedAtPath);
assert query != null;
this.query = query;
+ this.hints = null;
+ this.variables = null;
}
/**
@@ -279,13 +287,14 @@
*
* @see javax.jcr.query.Query#execute()
*/
- public QueryResult execute() {
+ public QueryResult execute() throws RepositoryException {
// Submit immediately to the workspace graph ...
Schemata schemata = session.workspace().nodeTypeManager().schemata();
- QueryResults result = session.workspace().graph().query(query, schemata)
- // .using(variables)
- // .using(hints)
- .execute();
+ QueryResults result =
session.repository().queryManager().query(session.workspace(),
+ query,
+ schemata,
+ hints,
+ variables);
return new JcrQueryResult(session, result);
}
@@ -317,9 +326,12 @@
*
* @see javax.jcr.query.Query#execute()
*/
- public QueryResult execute() {
+ public QueryResult execute() throws RepositoryException {
// Submit immediately to the workspace graph ...
- QueryResults result = session.workspace().graph().search(statement,
MAXIMUM_RESULTS_FOR_FULL_TEXT_SEARCH_QUERIES, 0);
+ QueryResults result =
session.repository().queryManager().search(session.workspace(),
+ statement,
+
MAXIMUM_RESULTS_FOR_FULL_TEXT_SEARCH_QUERIES,
+ 0);
return new JcrQueryResult(session, result);
}
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java 2009-12-17 20:38:21
UTC (rev 1451)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java 2009-12-18 01:52:46
UTC (rev 1452)
@@ -23,7 +23,6 @@
*/
package org.jboss.dna.jcr;
-import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.AccessControlContext;
@@ -51,17 +50,15 @@
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
+import javax.jcr.query.Query;
import javax.security.auth.Subject;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.Immutable;
import net.jcip.annotations.ThreadSafe;
-import org.apache.lucene.analysis.Analyzer;
import org.jboss.dna.common.i18n.I18n;
import org.jboss.dna.common.text.Inflector;
-import org.jboss.dna.common.text.TextEncoder;
-import org.jboss.dna.common.text.UrlEncoder;
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.common.util.Logger;
import org.jboss.dna.graph.ExecutionContext;
@@ -98,12 +95,7 @@
import org.jboss.dna.graph.query.parse.QueryParsers;
import org.jboss.dna.graph.query.parse.SqlQueryParser;
import org.jboss.dna.graph.request.InvalidWorkspaceException;
-import org.jboss.dna.graph.search.SearchableRepositorySource;
import org.jboss.dna.jcr.xpath.XPathQueryParser;
-import org.jboss.dna.search.lucene.IndexRules;
-import org.jboss.dna.search.lucene.LuceneConfiguration;
-import org.jboss.dna.search.lucene.LuceneConfigurations;
-import org.jboss.dna.search.lucene.LuceneSearchEngine;
/**
* Creates JCR {@link Session sessions} to an underlying repository (which may be a
federated repository).
@@ -136,7 +128,8 @@
* the workspaces. In production, this needs to be "true" for proper JCR
functionality, but in some debugging cases it can be
* set to false to simplify the architecture by removing the federated connector
layer.
* <p>
- * This should be changed to 'false' only in advanced situations, and never
for production.
+ * This should be changed to 'false' only in advanced situations, and never
for production. Note that this also disables query
+ * execution.
* </p>
*/
static final boolean WORKSPACES_SHARE_SYSTEM_BRANCH = true;
@@ -203,15 +196,15 @@
TABLES_INCLUDE_COLUMNS_FOR_INHERITED_PROPERTIES,
/**
- * A boolean flag that specifies whether this repository is expected to provide
search and query functionality. If client
- * applications will never perform searches or queries, then maintaining the
indexes for these features is an unncessary
- * overhead, and can be disabled. Note that this is merely a hint, and that
searches and queries might still work when
- * this is set to 'false'.
+ * A boolean flag that specifies whether this repository is expected to execute
searches and queries. If client
+ * applications will never perform searches or queries, then maintaining the
query indexes is an unncessary overhead, and
+ * can be disabled. Note that this is merely a hint, and that searches and
queries might still work when this is set to
+ * 'false'.
* <p>
- * The default is 'true', meaning that the repository <i>is
expected</i> to be searched and queried.
+ * The default is 'true', meaning that clients can execute searches and
queries.
* </p>
*/
- SEARCHABLE,
+ QUERY_EXECUTION_ENABLED,
/**
* The system may maintain a set of indexes that improve the performance of
searching and querying the content. These size
@@ -221,20 +214,21 @@
* <p>
* If specified, the value must be a valid path to a writable directory on the
file system. If the path specifies a
* non-existant location, the repository may attempt to create the missing
directories. The path may be absolute or
- * relative to the location where this VM was started.
+ * relative to the location where this VM was started. If the specified location
is not a readable and writable directory
+ * (or cannot be created as such), then this will generate an exception when the
repository is created.
* </p>
* <p>
* The default value is null, meaning the search indexes may not be stored on the
local file system and, if needed, will
* be stored within memory.
* </p>
*/
- SEARCH_INDEX_DIRECTORY,
+ QUERY_INDEX_DIRECTORY,
/**
* A boolean flag that specifies whether updates to the indexes (if used) should
be made synchronously, meaning that a
- * call to {@link Session#save()} will not return until the search indexes have
been updated. The benefit of synchronous
- * updates is that a search or query performed immediately after a
<code>save()</code> will operate upon content that was
- * just changed.
+ * call to {@link Session#save()} will not return until the search indexes have
been completely updated. The benefit of
+ * synchronous updates is that a search or query performed immediately after a
<code>save()</code> will operate upon
+ * content that was just changed. The downside is that the
<code>save()</code> operation will take longer.
* <p>
* With asynchronous updates, however, the only work done during a
<code>save()</code> invocation is that required to
* persist the changes in the underlying repository source, while changes to the
search indexes are made in a different
@@ -245,7 +239,7 @@
* The default is value 'false', meaning the updates are performed
<i>asynchronously</i>.
* </p>
*/
- UPDATE_INDEXES_SYNCHRONOUSLY;
+ QUERY_INDEXES_UPDATED_SYNCHRONOUSLY;
/**
* Determine the option given the option name. This does more than {@link
Option#valueOf(String)}, since this method first
@@ -306,19 +300,19 @@
public static final String TABLES_INCLUDE_COLUMNS_FOR_INHERITED_PROPERTIES =
Boolean.TRUE.toString();
/**
- * The default value for the {@link Option#SEARCHABLE} option is {@value} .
+ * The default value for the {@link Option#QUERY_EXECUTION_ENABLED} option is
{@value} .
*/
- public static final String SEARCHABLE = Boolean.TRUE.toString();
+ public static final String QUERY_EXECUTION_ENABLED = Boolean.TRUE.toString();
/**
- * The default value for the {@link Option#UPDATE_INDEXES_SYNCHRONOUSLY} option
is {@value} .
+ * The default value for the {@link Option#QUERY_INDEXES_UPDATED_SYNCHRONOUSLY}
option is {@value} .
*/
- public static final String UPDATE_INDEXES_SYNCHRONOUSLY =
Boolean.FALSE.toString();
+ public static final String QUERY_INDEXES_UPDATED_SYNCHRONOUSLY =
Boolean.FALSE.toString();
/**
- * The default value for the {@link Option#SEARCH_INDEX_DIRECTORY} option is
{@value} .
+ * The default value for the {@link Option#QUERY_INDEX_DIRECTORY} option is
{@value} .
*/
- public static final String SEARCH_INDEX_DIRECTORY = null;
+ public static final String QUERY_INDEX_DIRECTORY = null;
}
@@ -336,9 +330,9 @@
defaults.put(Option.ANONYMOUS_USER_ROLES, DefaultOption.ANONYMOUS_USER_ROLES);
defaults.put(Option.TABLES_INCLUDE_COLUMNS_FOR_INHERITED_PROPERTIES,
DefaultOption.TABLES_INCLUDE_COLUMNS_FOR_INHERITED_PROPERTIES);
- defaults.put(Option.SEARCHABLE, DefaultOption.SEARCHABLE);
- defaults.put(Option.UPDATE_INDEXES_SYNCHRONOUSLY,
DefaultOption.UPDATE_INDEXES_SYNCHRONOUSLY);
- defaults.put(Option.SEARCH_INDEX_DIRECTORY,
DefaultOption.SEARCH_INDEX_DIRECTORY);
+ defaults.put(Option.QUERY_EXECUTION_ENABLED,
DefaultOption.QUERY_EXECUTION_ENABLED);
+ defaults.put(Option.QUERY_INDEXES_UPDATED_SYNCHRONOUSLY,
DefaultOption.QUERY_INDEXES_UPDATED_SYNCHRONOUSLY);
+ defaults.put(Option.QUERY_INDEX_DIRECTORY, DefaultOption.QUERY_INDEX_DIRECTORY);
DEFAULT_OPTIONS = Collections.<Option, String>unmodifiableMap(defaults);
}
@@ -358,8 +352,9 @@
private final NamespaceRegistry persistentRegistry;
private final RepositoryObservationManager repositoryObservationManager;
private final SecurityContext anonymousUserContext;
- private final QueryParsers queryParsers = new QueryParsers(new SqlQueryParser(), new
XPathQueryParser(),
- new
FullTextSearchParser());
+ private final QueryParsers queryParsers;
+ // Until the federated connector supports queries, we have to use a search engine
...
+ private final RepositoryQueryManager queryManager;
// package-scoped to facilitate testing
final WeakHashMap<JcrSession, Object> activeSessions = new
WeakHashMap<JcrSession, Object>();
@@ -372,8 +367,11 @@
* @param connectionFactory the factory for repository connections
* @param repositorySourceName the name of the repository source (in the connection
factory) that should be used
* @param repositoryObservable the repository library observable associated with this
repository (never <code>null</code>)
+ * @param repositorySourceCapabilities the capabilities of the repository source; may
be null if the capabilities are not
+ * known
* @param descriptors the {@link #getDescriptorKeys() descriptors} for this
repository; may be <code>null</code>.
* @param options the optional {@link Option settings} for this repository; may be
null
+ * @throws RepositoryException if there is a problem setting up this repository
* @throws IllegalArgumentException If <code>executionContext</code>,
<code>connectionFactory</code>,
* <code>repositorySourceName</code>, or
<code>repositoryObservable</code> is <code>null</code>.
*/
@@ -381,8 +379,9 @@
RepositoryConnectionFactory connectionFactory,
String repositorySourceName,
Observable repositoryObservable,
+ RepositorySourceCapabilities repositorySourceCapabilities,
Map<String, String> descriptors,
- Map<Option, String> options ) {
+ Map<Option, String> options ) throws RepositoryException
{
CheckArg.isNotNull(executionContext, "executionContext");
CheckArg.isNotNull(connectionFactory, "connectionFactory");
CheckArg.isNotNull(repositorySourceName, "repositorySourceName");
@@ -482,19 +481,24 @@
InMemoryRepositorySource transientSystemSource = new
InMemoryRepositorySource();
transientSystemSource.setName(systemSourceName);
transientSystemSource.setDefaultWorkspaceName(systemWorkspaceName);
- connectionFactoryWithSystem = new
ConnectionFactoryWithSystem(connectionFactory, transientSystemSource);
+ connectionFactoryWithSystem = new
DelegatingConnectionFactory(connectionFactory, transientSystemSource);
}
+
+ // Set up the query parsers, which we have to have even though queries might be
disabled ...
+ this.queryParsers = new QueryParsers(new SqlQueryParser(), new
XPathQueryParser(), new FullTextSearchParser());
+ assert this.queryParsers.getParserFor(Query.XPATH) != null;
+
this.systemWorkspaceName = systemWorkspaceName;
this.systemSourceName = systemSourceName;
this.connectionFactory = connectionFactoryWithSystem;
assert this.systemSourceName != null;
assert this.connectionFactory != null;
+ this.sourceName = repositorySourceName;
// Set up the "/jcr:system" branch ...
Graph systemGraph = Graph.create(this.systemSourceName, this.connectionFactory,
executionContext);
systemGraph.useWorkspace(systemWorkspaceName);
initializeSystemContent(systemGraph);
- this.sourceName = repositorySourceName;
// Create the namespace registry and corresponding execution context.
// Note that this persistent registry has direct access to the system workspace.
@@ -553,6 +557,37 @@
this.lockManagers = new ConcurrentHashMap<String, WorkspaceLockManager>();
this.locksPath = pathFactory.create(pathFactory.createRootPath(),
JcrLexicon.SYSTEM, DnaLexicon.LOCKS);
+ // If the repository is to support searching ...
+ if (Boolean.valueOf(this.options.get(Option.QUERY_EXECUTION_ENABLED)) &&
WORKSPACES_SHARE_SYSTEM_BRANCH) {
+ // Determine whether the federated source and original source support queries
and searches ...
+ RepositorySourceCapabilities fedCapabilities = federatedSource != null ?
federatedSource.getCapabilities() : null;
+ final boolean canQuerySource = repositorySourceCapabilities != null
+ &&
repositorySourceCapabilities.supportsSearches()
+ &&
repositorySourceCapabilities.supportsQueries();
+ final boolean canQueryFederated = fedCapabilities != null &&
fedCapabilities.supportsSearches()
+ &&
fedCapabilities.supportsQueries();
+
+ // We can query the federated source if it supports queries and searches
+ // AND the original source supports queries and searches ...
+ if (canQuerySource && canQueryFederated) {
+ this.queryManager = new RepositoryQueryManager();
+ } else {
+ // Otherwise create a repository query manager that maintains its own
search engine ...
+ String indexDirectory = this.options.get(Option.QUERY_INDEX_DIRECTORY);
+ boolean updateIndexesSynchronously =
Boolean.valueOf(this.options.get(Option.QUERY_INDEXES_UPDATED_SYNCHRONOUSLY));
+ // At this point in time, we cannot query the federated connector ...
+ String sourceToQuery = this.sourceName;
+ // But we need to observe the federated source (if there is one) ...
+ final String sourceToObserve = federatedSource != null ?
federatedSource.getName() : this.sourceName;
+ // We actually want to use the federated source to crawl and watch for
changes ...
+ this.queryManager = new
RepositoryQueryManager.SelfContained(executionContext, sourceToQuery, sourceToObserve,
+
connectionFactoryWithSystem, repositoryObservable,
+
indexDirectory, updateIndexesSynchronously);
+ }
+ } else {
+ this.queryManager = new RepositoryQueryManager.Disabled();
+ }
+
/*
* Set up the anonymous role, if appropriate
*/
@@ -582,79 +617,6 @@
this.anonymousUserContext = anonymousUserContext;
}
- protected RepositorySource makeSearchable( final RepositorySource source ) throws
RepositoryException {
- if (Boolean.valueOf(this.options.get(Option.SEARCHABLE))) {
- // We need to make sure we can search/query the source. See whether the
source natively supports it ...
- // Check whether the repository source supports search and query ...
- RepositorySourceCapabilities capabilities = source.getCapabilities();
- if (!capabilities.supportsSearches() || !capabilities.supportsQueries()) {
- // Where should the indexes be stored ...
- String indexDirectory = this.options.get(Option.SEARCH_INDEX_DIRECTORY);
-
- // Define the configuration ...
- TextEncoder encoder = new UrlEncoder();
- LuceneConfiguration configuration = null;
- if (indexDirectory != null) {
- File indexDir = new File(indexDirectory);
- if (indexDir.exists()) {
- // The location does exist ...
- if (!indexDir.isDirectory()) {
- // The path is not a directory ...
- I18n msg =
JcrI18n.searchIndexDirectoryOptionSpecifiesFileNotDirectory;
- throw new RepositoryException(msg.text(indexDirectory,
sourceName));
- }
- if (!indexDir.canWrite()) {
- // But we cannot write to it ...
- I18n msg =
JcrI18n.searchIndexDirectoryOptionSpecifiesDirectoryThatCannotBeWrittenTo;
- throw new RepositoryException(msg.text(indexDirectory,
sourceName));
- }
- if (!indexDir.canRead()) {
- // But we cannot write to it ...
- I18n msg =
JcrI18n.searchIndexDirectoryOptionSpecifiesDirectoryThatCannotBeRead;
- throw new RepositoryException(msg.text(indexDirectory,
sourceName));
- }
- // The directory is usable
- } else {
- // The location doesn't exist,so try to make it ...
- if (!indexDir.mkdirs()) {
- I18n msg =
JcrI18n.searchIndexDirectoryOptionSpecifiesDirectoryThatCannotBeCreated;
- throw new RepositoryException(msg.text(indexDirectory,
sourceName));
- }
- // We successfully create the dirctory (or directories)
- }
- configuration = LuceneConfigurations.using(indexDir, encoder,
encoder);
- } else {
- // Use in-memory as a fall-back ...
- configuration = LuceneConfigurations.inMemory();
- }
- assert configuration != null;
-
- // Set up the indexing rules ...
- IndexRules indexRules = null;
-
- // Create a connection factory that allows us to make connections back to
the wrapped source ...
- RepositoryConnectionFactory connectionFactory = new
RepositoryConnectionFactory() {
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.connector.RepositoryConnectionFactory#createConnection(java.lang.String)
- */
- public RepositoryConnection createConnection( String sourceName )
throws RepositorySourceException {
- assert source.getName().equals(sourceName);
- return source.getConnection();
- }
- };
-
- // Wrap this with a searchable wrapper ...
- Analyzer analyzer = null;
- LuceneSearchEngine searchEngine = new
LuceneSearchEngine(source.getName(), connectionFactory, true,
- configuration,
indexRules, analyzer);
- return new SearchableRepositorySource(source, searchEngine);
- }
- }
- return source;
- }
-
protected void initializeSystemContent( Graph systemGraph ) {
// Make sure the "/jcr:system" node exists ...
ExecutionContext context = systemGraph.getContext();
@@ -698,6 +660,15 @@
}
/**
+ * Get the query manager for this repository.
+ *
+ * @return the query manager; never null
+ */
+ RepositoryQueryManager queryManager() {
+ return queryManager;
+ }
+
+ /**
* Returns the repository-level node type manager
*
* @return the repository-level node type manager
@@ -1126,15 +1097,15 @@
}
}
- protected class ConnectionFactoryWithSystem implements RepositoryConnectionFactory {
+ protected class DelegatingConnectionFactory implements RepositoryConnectionFactory {
private final RepositoryConnectionFactory delegate;
- private final RepositorySource system;
+ private final RepositorySource source;
- protected ConnectionFactoryWithSystem( RepositoryConnectionFactory delegate,
+ protected DelegatingConnectionFactory( RepositoryConnectionFactory delegate,
RepositorySource source ) {
assert delegate != null;
this.delegate = delegate;
- this.system = source;
+ this.source = source;
}
/**
@@ -1143,8 +1114,8 @@
* @see
org.jboss.dna.graph.connector.RepositoryConnectionFactory#createConnection(java.lang.String)
*/
public RepositoryConnection createConnection( String sourceName ) throws
RepositorySourceException {
- if (this.system.getName().equals(sourceName)) {
- return this.system.getConnection();
+ if (this.source.getName().equals(sourceName)) {
+ return this.source.getConnection();
}
return delegate.createConnection(sourceName);
}
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryQueryManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryQueryManager.java
(rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryQueryManager.java 2009-12-18
01:52:46 UTC (rev 1452)
@@ -0,0 +1,350 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.InvalidQueryException;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.common.collection.SimpleProblems;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.text.UrlEncoder;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.observe.Changes;
+import org.jboss.dna.graph.observe.Observable;
+import org.jboss.dna.graph.observe.Observer;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.QueryEngine;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.query.QueryResults.Columns;
+import org.jboss.dna.graph.query.model.QueryCommand;
+import org.jboss.dna.graph.query.model.TypeSystem;
+import org.jboss.dna.graph.query.optimize.Optimizer;
+import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
+import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.PlanHints;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.Planner;
+import org.jboss.dna.graph.query.process.AbstractAccessComponent;
+import org.jboss.dna.graph.query.process.ProcessingComponent;
+import org.jboss.dna.graph.query.process.Processor;
+import org.jboss.dna.graph.query.process.QueryProcessor;
+import org.jboss.dna.graph.query.process.SelectComponent.Analyzer;
+import org.jboss.dna.graph.query.validate.Schemata;
+import org.jboss.dna.graph.request.AccessQueryRequest;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+import org.jboss.dna.graph.search.SearchEngine;
+import org.jboss.dna.graph.search.SearchEngineProcessor;
+import org.jboss.dna.search.lucene.IndexRules;
+import org.jboss.dna.search.lucene.LuceneConfiguration;
+import org.jboss.dna.search.lucene.LuceneConfigurations;
+import org.jboss.dna.search.lucene.LuceneSearchEngine;
+
+/**
+ *
+ */
+class RepositoryQueryManager {
+
+ RepositoryQueryManager() {
+ }
+
+ @SuppressWarnings( "unused" )
+ public QueryResults query( JcrWorkspace workspace,
+ QueryCommand query,
+ Schemata schemata,
+ PlanHints hints,
+ Map<String, Object> variables ) throws
InvalidQueryException {
+ Graph.BuildQuery builder = workspace.graph().query(query, schemata);
+ if (variables != null) builder.using(variables);
+ if (hints != null) builder.using(hints);
+ return builder.execute();
+ }
+
+ @SuppressWarnings( "unused" )
+ public QueryResults search( JcrWorkspace workspace,
+ String searchExpression,
+ int maxRowCount,
+ int offset ) throws InvalidQueryException {
+ return workspace.graph().search(searchExpression, maxRowCount, offset);
+ }
+
+ static class Disabled extends RepositoryQueryManager {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.jcr.RepositoryQueryManager#query(org.jboss.dna.jcr.JcrWorkspace,
+ * org.jboss.dna.graph.query.model.QueryCommand,
org.jboss.dna.graph.query.validate.Schemata,
+ * org.jboss.dna.graph.query.plan.PlanHints, java.util.Map)
+ */
+ @Override
+ public QueryResults query( JcrWorkspace workspace,
+ QueryCommand query,
+ Schemata schemata,
+ PlanHints hints,
+ Map<String, Object> variables ) throws
InvalidQueryException {
+ throw new
InvalidQueryException(JcrI18n.queryIsDisabledInRepository.text(workspace.getSourceName()));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.jcr.RepositoryQueryManager#search(org.jboss.dna.jcr.JcrWorkspace,
java.lang.String, int, int)
+ */
+ @Override
+ public QueryResults search( JcrWorkspace workspace,
+ String searchExpression,
+ int maxRowCount,
+ int offset ) throws InvalidQueryException {
+ throw new
InvalidQueryException(JcrI18n.queryIsDisabledInRepository.text(workspace.getSourceName()));
+ }
+ }
+
+ static class SelfContained extends RepositoryQueryManager {
+ private final ExecutionContext context;
+ private final String sourceName;
+ private final LuceneConfiguration configuration;
+ private final SearchEngine searchEngine;
+ private final Observer searchObserver;
+ private final ExecutorService service;
+ private final QueryEngine queryEngine;
+
+ SelfContained( ExecutionContext context,
+ String nameOfSourceToBeSearchable,
+ final String observableSource,
+ RepositoryConnectionFactory connectionFactory,
+ Observable observable,
+ String indexDirectory,
+ boolean updateIndexesSynchronously ) throws RepositoryException {
+ this.context = context;
+ this.sourceName = nameOfSourceToBeSearchable;
+ // Define the configuration ...
+ TextEncoder encoder = new UrlEncoder();
+ if (indexDirectory != null) {
+ File indexDir = new File(indexDirectory);
+ if (indexDir.exists()) {
+ // The location does exist ...
+ if (!indexDir.isDirectory()) {
+ // The path is not a directory ...
+ I18n msg =
JcrI18n.searchIndexDirectoryOptionSpecifiesFileNotDirectory;
+ throw new RepositoryException(msg.text(indexDirectory,
sourceName));
+ }
+ if (!indexDir.canWrite()) {
+ // But we cannot write to it ...
+ I18n msg =
JcrI18n.searchIndexDirectoryOptionSpecifiesDirectoryThatCannotBeWrittenTo;
+ throw new RepositoryException(msg.text(indexDirectory,
sourceName));
+ }
+ if (!indexDir.canRead()) {
+ // But we cannot write to it ...
+ I18n msg =
JcrI18n.searchIndexDirectoryOptionSpecifiesDirectoryThatCannotBeRead;
+ throw new RepositoryException(msg.text(indexDirectory,
sourceName));
+ }
+ // The directory is usable
+ } else {
+ // The location doesn't exist,so try to make it ...
+ if (!indexDir.mkdirs()) {
+ I18n msg =
JcrI18n.searchIndexDirectoryOptionSpecifiesDirectoryThatCannotBeCreated;
+ throw new RepositoryException(msg.text(indexDirectory,
sourceName));
+ }
+ // We successfully create the dirctory (or directories)
+ }
+ configuration = LuceneConfigurations.using(indexDir, encoder, encoder);
+ } else {
+ // Use in-memory as a fall-back ...
+ configuration = LuceneConfigurations.inMemory();
+ }
+ assert configuration != null;
+
+ // Set up the indexing rules ...
+ IndexRules indexRules = null;
+
+ // Set up the search engine ...
+ org.apache.lucene.analysis.Analyzer analyzer = null;
+ searchEngine = new LuceneSearchEngine(nameOfSourceToBeSearchable,
connectionFactory, true, configuration, indexRules,
+ analyzer);
+
+ // Set up an original source observer to keep the index up to date ...
+ if (updateIndexesSynchronously) {
+ this.service = null;
+ this.searchObserver = new Observer() {
+ public void notify( Changes changes ) {
+ if (changes.getSourceName().equals(observableSource)) {
+ process(changes);
+ }
+ }
+ };
+ } else {
+ // It's asynchronous, so create a single-threaded executor and an
observer that enqueues the results
+ this.service = Executors.newCachedThreadPool();
+ this.searchObserver = new Observer() {
+ @SuppressWarnings( "synthetic-access" )
+ public void notify( final Changes changes ) {
+ if (changes.getSourceName().equals(observableSource)) {
+ service.submit(new Runnable() {
+ public void run() {
+ process(changes);
+ }
+ });
+ }
+ }
+ };
+ }
+ observable.register(this.searchObserver);
+
+ // Set up the query engine ...
+ Planner planner = new CanonicalPlanner();
+ Optimizer optimizer = new RuleBasedOptimizer();
+ Processor processor = new QueryProcessor() {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.query.process.QueryProcessor#createAccessComponent(org.jboss.dna.graph.query.model.QueryCommand,
+ * org.jboss.dna.graph.query.QueryContext,
org.jboss.dna.graph.query.plan.PlanNode,
+ * org.jboss.dna.graph.query.QueryResults.Columns,
+ * org.jboss.dna.graph.query.process.SelectComponent.Analyzer)
+ */
+ @Override
+ protected ProcessingComponent createAccessComponent( QueryCommand
originalQuery,
+ QueryContext
context,
+ PlanNode
accessNode,
+ Columns
resultColumns,
+ Analyzer analyzer )
{
+ return new AccessQueryProcessor((GraphQueryContext)context,
resultColumns, accessNode);
+ }
+ };
+ this.queryEngine = new QueryEngine(planner, optimizer, processor);
+
+ }
+
+ protected void process( Changes changes ) {
+ try {
+ Logger.getLogger(getClass()).warn(JcrI18n.errorUpdatingQueryIndexes,
"JUST KIDDING");
+ searchEngine.index(context, changes.getChangeRequests());
+ } catch (RuntimeException e) {
+ Logger.getLogger(getClass()).error(e, JcrI18n.errorUpdatingQueryIndexes,
e.getLocalizedMessage());
+ }
+ }
+
+ @Override
+ public QueryResults query( JcrWorkspace workspace,
+ QueryCommand query,
+ Schemata schemata,
+ PlanHints hints,
+ Map<String, Object> variables ) {
+ TypeSystem typeSystem =
workspace.context().getValueFactories().getTypeSystem();
+ SearchEngineProcessor processor = searchEngine.createProcessor(context, null,
true);
+ try {
+ QueryContext context = new GraphQueryContext(schemata, typeSystem, hints,
new SimpleProblems(), variables,
+ processor,
workspace.getName());
+ return queryEngine.execute(context, query);
+ } finally {
+ processor.close();
+ }
+ }
+
+ protected class GraphQueryContext extends QueryContext {
+ private final RequestProcessor processor;
+ private final String workspaceName;
+
+ protected GraphQueryContext( Schemata schemata,
+ TypeSystem typeSystem,
+ PlanHints hints,
+ Problems problems,
+ Map<String, Object> variables,
+ RequestProcessor processor,
+ String workspaceName ) {
+ super(schemata, typeSystem, hints, problems, variables);
+ this.processor = processor;
+ this.workspaceName = workspaceName;
+ }
+
+ /**
+ * @return processor
+ */
+ public RequestProcessor getProcessor() {
+ return processor;
+ }
+
+ /**
+ * @return workspaceName
+ */
+ public String getWorkspaceName() {
+ return workspaceName;
+ }
+ }
+
+ protected static class AccessQueryProcessor extends AbstractAccessComponent {
+ private final AccessQueryRequest accessRequest;
+
+ protected AccessQueryProcessor( GraphQueryContext context,
+ Columns columns,
+ PlanNode accessNode ) {
+ super(context, columns, accessNode);
+ accessRequest = new AccessQueryRequest(context.getWorkspaceName(),
sourceName, getColumns(), andedConstraints,
+ limit, context.getSchemata(),
context.getVariables());
+ context.getProcessor().process(accessRequest);
+ }
+
+ /**
+ * Get the access query request.
+ *
+ * @return the access query request; never null
+ */
+ public AccessQueryRequest getAccessRequest() {
+ return accessRequest;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.query.process.ProcessingComponent#execute()
+ */
+ @Override
+ public List<Object[]> execute() {
+ if (accessRequest.getError() != null) {
+ I18n msg = GraphI18n.errorWhilePerformingQuery;
+ getContext().getProblems().addError(accessRequest.getError(),
+ msg,
+ accessNode.getString(),
+ accessRequest.workspace(),
+ sourceName);
+ return emptyTuples();
+ }
+ return accessRequest.getTuples();
+ }
+
+ }
+
+ }
+}
Property changes on:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryQueryManager.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties 2009-12-17
20:38:21 UTC (rev 1451)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties 2009-12-18
01:52:46 UTC (rev 1452)
@@ -117,6 +117,7 @@
searchIndexDirectoryOptionSpecifiesDirectoryThatCannotBeRead = The JCR Repository
'{1}' option value "{0}" specifies a directory on the local file system
that cannot be read
searchIndexDirectoryOptionSpecifiesDirectoryThatCannotBeWrittenTo = The JCR Repository
'{1}' option value "{0}" specifies a directory on the local file system
that cannot be written
searchIndexDirectoryOptionSpecifiesDirectoryThatCannotBeCreated = = The JCR Repository
'{1}' option value "{0}" specifies a non-existant directory on the local
file system that cannot be created
+errorUpdatingQueryIndexes = Error updating the query indexes: {0}
noNegativeDepth=Depth parameter ({0}) cannot be negative
tooDeep=Depth parameter ({0}) cannot be greater than the result of getDepth() for this
node
@@ -125,6 +126,7 @@
invalidQueryLanguage="{0}" is not a valid query langauge. Supported languages
are\: {1}
queryCannotBeParsedUsingLanguage=The {0} query "{1}" is not well-formed: {2}
queryInLanguageIsNotValid=The {0} query "{1}" has one or more errors: {2}
+queryIsDisabledInRepository = The {0} repository does not have queries enabled
invalidNodeTypeName=Node types cannot have a null or empty name
noSuchNodeType=Type named '{0}' does not exist
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrAccessTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrAccessTest.java 2009-12-17
20:38:21 UTC (rev 1451)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrAccessTest.java 2009-12-18
01:52:46 UTC (rev 1452)
@@ -83,7 +83,7 @@
}
};
- repository = new JcrRepository(context, connectionFactory, "unused",
new MockObservable(), null, null);
+ repository = new JcrRepository(context, connectionFactory, "unused",
new MockObservable(), null, null, null);
SecurityContext mockSecurityContext = new
MockSecurityContext("testuser",
Collections.singleton(JcrSession.DNA_WRITE_PERMISSION));
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/ImportExportTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/ImportExportTest.java 2009-12-17
20:38:21 UTC (rev 1451)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/ImportExportTest.java 2009-12-18
01:52:46 UTC (rev 1452)
@@ -84,7 +84,7 @@
}
};
- repository = new JcrRepository(context, connectionFactory, "unused",
new MockObservable(), null, null);
+ repository = new JcrRepository(context, connectionFactory, "unused",
new MockObservable(), null, null, null);
SecurityContext mockSecurityContext = new
MockSecurityContext("testuser",
Collections.singleton(JcrSession.DNA_WRITE_PERMISSION));
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java 2009-12-17
20:38:21 UTC (rev 1451)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrConfigurationTest.java 2009-12-18
01:52:46 UTC (rev 1452)
@@ -252,9 +252,9 @@
options.put(Option.ANONYMOUS_USER_ROLES, DefaultOption.ANONYMOUS_USER_ROLES);
options.put(Option.TABLES_INCLUDE_COLUMNS_FOR_INHERITED_PROPERTIES,
DefaultOption.TABLES_INCLUDE_COLUMNS_FOR_INHERITED_PROPERTIES);
- options.put(Option.SEARCHABLE, DefaultOption.SEARCHABLE);
- options.put(Option.SEARCH_INDEX_DIRECTORY,
DefaultOption.SEARCH_INDEX_DIRECTORY);
- options.put(Option.UPDATE_INDEXES_SYNCHRONOUSLY,
DefaultOption.UPDATE_INDEXES_SYNCHRONOUSLY);
+ options.put(Option.QUERY_EXECUTION_ENABLED,
DefaultOption.QUERY_EXECUTION_ENABLED);
+ options.put(Option.QUERY_INDEX_DIRECTORY, DefaultOption.QUERY_INDEX_DIRECTORY);
+ options.put(Option.QUERY_INDEXES_UPDATED_SYNCHRONOUSLY,
DefaultOption.QUERY_INDEXES_UPDATED_SYNCHRONOUSLY);
assertThat(repository.getOptions(), is(options));
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java 2009-12-17
20:38:21 UTC (rev 1451)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRepositoryTest.java 2009-12-18
01:52:46 UTC (rev 1452)
@@ -116,7 +116,7 @@
// Set up the repository ...
descriptors = new HashMap<String, String>();
- repository = new JcrRepository(context, connectionFactory, sourceName, new
MockObservable(), descriptors, null);
+ repository = new JcrRepository(context, connectionFactory, sourceName, new
MockObservable(), null, descriptors, null);
// Set up the graph that goes directly to the source ...
sourceGraph = Graph.create(source, context);
@@ -143,28 +143,28 @@
}
@Test
- public void shouldAllowNullDescriptors() {
- new JcrRepository(context, connectionFactory, sourceName, new MockObservable(),
null, null);
+ public void shouldAllowNullDescriptors() throws Exception {
+ new JcrRepository(context, connectionFactory, sourceName, new MockObservable(),
null, null, null);
}
@Test( expected = IllegalArgumentException.class )
public void shouldNotAllowNullExecutionContext() throws Exception {
- new JcrRepository(null, connectionFactory, sourceName, new MockObservable(),
descriptors, null);
+ new JcrRepository(null, connectionFactory, sourceName, new MockObservable(),
null, descriptors, null);
}
@Test( expected = IllegalArgumentException.class )
public void shouldNotAllowNullConnectionFactories() throws Exception {
- new JcrRepository(context, null, sourceName, new MockObservable(), descriptors,
null);
+ new JcrRepository(context, null, sourceName, new MockObservable(), null,
descriptors, null);
}
@Test( expected = IllegalArgumentException.class )
public void shouldNotAllowNullObservable() throws Exception {
- new JcrRepository(context, connectionFactory, sourceName, null, null, null);
+ new JcrRepository(context, connectionFactory, sourceName, null, null, null,
null);
}
@Test( expected = IllegalArgumentException.class )
public void shouldNotAllowNullSourceName() throws Exception {
- new JcrRepository(context, connectionFactory, null, new MockObservable(),
descriptors, null);
+ new JcrRepository(context, connectionFactory, null, new MockObservable(), null,
descriptors, null);
}
@Test( expected = IllegalArgumentException.class )
@@ -188,8 +188,9 @@
}
@Test
- public void shouldProvideBuiltInDescriptorsWhenNotSuppliedDescriptors() {
- Repository repository = new JcrRepository(context, connectionFactory, sourceName,
new MockObservable(), descriptors, null);
+ public void shouldProvideBuiltInDescriptorsWhenNotSuppliedDescriptors() throws
Exception {
+ Repository repository = new JcrRepository(context, connectionFactory, sourceName,
new MockObservable(), null,
+ descriptors, null);
testDescriptorKeys(repository);
testDescriptorValues(repository);
}
@@ -205,18 +206,19 @@
}
@Test
- public void shouldHaveDefaultOptionsWhenNotOverridden() {
- JcrRepository repository = new JcrRepository(context, connectionFactory,
sourceName, new MockObservable(), descriptors,
- null);
+ public void shouldHaveDefaultOptionsWhenNotOverridden() throws Exception {
+ JcrRepository repository = new JcrRepository(context, connectionFactory,
sourceName, new MockObservable(), null,
+ descriptors, null);
assertThat(repository.getOptions().get(JcrRepository.Option.PROJECT_NODE_TYPES),
is(JcrRepository.DefaultOption.PROJECT_NODE_TYPES));
}
@Test
- public void shouldProvideUserSuppliedDescriptors() {
+ public void shouldProvideUserSuppliedDescriptors() throws Exception {
Map<String, String> descriptors = new HashMap<String, String>();
descriptors.put("property", "value");
- Repository repository = new JcrRepository(context, connectionFactory, sourceName,
new MockObservable(), descriptors, null);
+ Repository repository = new JcrRepository(context, connectionFactory, sourceName,
new MockObservable(), null,
+ descriptors, null);
testDescriptorKeys(repository);
testDescriptorValues(repository);
assertThat(repository.getDescriptor("property"),
is("value"));
@@ -254,8 +256,8 @@
public void shouldAllowLoginWithNoCredentialsIfAnonAccessEnabled() throws Exception
{
Map<JcrRepository.Option, String> options = new
HashMap<JcrRepository.Option, String>();
options.put(JcrRepository.Option.ANONYMOUS_USER_ROLES,
JcrSession.DNA_READ_PERMISSION);
- JcrRepository repository = new JcrRepository(context, connectionFactory,
sourceName, new MockObservable(), descriptors,
- options);
+ JcrRepository repository = new JcrRepository(context, connectionFactory,
sourceName, new MockObservable(), null,
+ descriptors, options);
session = (JcrSession)repository.login();
@@ -492,8 +494,8 @@
// Use a different repository that supports anonymous logins to make this test
cleaner
Map<Option, String> options = new HashMap<Option, String>();
options.put(JcrRepository.Option.ANONYMOUS_USER_ROLES,
JcrSession.DNA_ADMIN_PERMISSION);
- JcrRepository repository = new JcrRepository(context, connectionFactory,
sourceName, new MockObservable(), descriptors,
- options);
+ JcrRepository repository = new JcrRepository(context, connectionFactory,
sourceName, new MockObservable(), null,
+ descriptors, options);
Session session;
@@ -518,8 +520,8 @@
// Use a different repository that supports anonymous logins to make this test
cleaner
Map<Option, String> options = new HashMap<Option, String>();
options.put(JcrRepository.Option.ANONYMOUS_USER_ROLES,
JcrSession.DNA_ADMIN_PERMISSION);
- JcrRepository repository = new JcrRepository(context, connectionFactory,
sourceName, new MockObservable(), descriptors,
- options);
+ JcrRepository repository = new JcrRepository(context, connectionFactory,
sourceName, new MockObservable(), null,
+ descriptors, options);
String lockedNodeName = "lockedNode";
JcrSession locker = (JcrSession)repository.login();
Modified:
trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryLibrary.java
===================================================================
---
trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryLibrary.java 2009-12-17
20:38:21 UTC (rev 1451)
+++
trunk/dna-repository/src/main/java/org/jboss/dna/repository/RepositoryLibrary.java 2009-12-18
01:52:46 UTC (rev 1452)
@@ -253,11 +253,11 @@
}
/**
- * Returns true if this federated repository is in the process of terminating after
{@link ServiceAdministrator#shutdown()}
- * has been called on the {@link #getAdministrator() administrator}, but the
federated repository has connections that have
- * not yet normally been {@link RepositoryConnection#close() closed}. This method may
be useful for debugging. A return of
- * <tt>true</tt> reported a sufficient period after shutdown may indicate
that connection users have ignored or suppressed
- * interruption, causing this repository not to properly terminate.
+ * Returns true if this library is in the process of terminating after {@link
ServiceAdministrator#shutdown()} has been called
+ * on the {@link #getAdministrator() administrator}, but the library has connections
that have not yet normally been
+ * {@link RepositoryConnection#close() closed}. This method may be useful for
debugging. A return of <tt>true</tt> reported a
+ * sufficient period after shutdown may indicate that connection users have ignored
or suppressed interruption, causing this
+ * repository not to properly terminate.
*
* @return true if terminating but not yet terminated, or false otherwise
* @see #isTerminated()
@@ -275,7 +275,7 @@
}
/**
- * Return true if this federated repository has completed its termination and no
longer has any open connections.
+ * Return true if this library has completed its termination and no longer has any
open connections.
*
* @return true if terminated, or false otherwise
* @see #isTerminating()
@@ -356,7 +356,7 @@
}
/**
- * Add the supplied federated source. This method returns false if the source is
null.
+ * Add the supplied source. This method returns false if the source is null.
*
* @param source the source to add
* @return true if the source is added, or false if the reference is null or if there
is already an existing source with the
@@ -367,7 +367,7 @@
}
/**
- * Add the supplied federated source. This method returns false if the source is
null.
+ * Add the supplied source. This method returns false if the source is null.
* <p>
* If a source with the same name already exists, it will be replaced only if
<code>replaceIfExisting</code> is true. If this
* is the case, then the existing source will be removed from the connection pool,
and that pool will be
@@ -463,9 +463,9 @@
}
/**
- * Remove from this federated repository the supplied source (or a source with the
same name as that supplied). This call
- * shuts down the connections in the source in an orderly fashion, allowing those
connection currently in use to be used and
- * closed normally, but preventing further connections from being used.
+ * Remove from this library the supplied source (or a source with the same name as
that supplied). This call shuts down the
+ * connections in the source in an orderly fashion, allowing those connection
currently in use to be used and closed normally,
+ * but preventing further connections from being used.
* <p>
* This method can safely be called while the federation repository is in use.
* </p>
@@ -485,8 +485,8 @@
}
/**
- * Remove from this federated repository the source with the supplied name. This call
shuts down the connections in the source
- * in an orderly fashion, allowing those connection currently in use to be used and
closed normally, but preventing further
+ * Remove from this library the source with the supplied name. This call shuts down
the connections in the source in an
+ * orderly fashion, allowing those connection currently in use to be used and closed
normally, but preventing further
* connections from being used. However, this method never waits until the
connections are all closed, and is equivalent to
* calling <code>removeSource(name,0,TimeUnit.SECONDS)</code>.
*
@@ -511,8 +511,8 @@
}
/**
- * Remove from this federated repository the source with the supplied name. This call
shuts down the connections in the source
- * in an orderly fashion, allowing those connection currently in use to be used and
closed normally, but preventing further
+ * Remove from this library the source with the supplied name. This call shuts down
the connections in the source in an
+ * orderly fashion, allowing those connection currently in use to be used and closed
normally, but preventing further
* connections from being used.
*
* @param name the name of the source to be removed
Modified:
trunk/extensions/dna-search-lucene/src/main/java/org/jboss/dna/search/lucene/LuceneSearchEngine.java
===================================================================
---
trunk/extensions/dna-search-lucene/src/main/java/org/jboss/dna/search/lucene/LuceneSearchEngine.java 2009-12-17
20:38:21 UTC (rev 1451)
+++
trunk/extensions/dna-search-lucene/src/main/java/org/jboss/dna/search/lucene/LuceneSearchEngine.java 2009-12-18
01:52:46 UTC (rev 1452)
@@ -262,7 +262,7 @@
indexer.index(workspaceWork.workspaceName, location,
crawlRequest.depth);
} else if (request instanceof ForwardRequest) {
ForwardRequest forwardRequest = (ForwardRequest)request;
- indexer.process(forwardRequest.changeRequest);
+ indexer.process(forwardRequest.changeRequest.clone());
}
}
}
@@ -292,6 +292,20 @@
public Iterator<WorkspaceWork> iterator() {
return byWorkspaceName.values().iterator();
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (WorkspaceWork work : byWorkspaceName.values()) {
+ sb.append(work.toString()).append('\n');
+ }
+ return sb.toString();
+ }
}
protected static class WorkspaceWork implements Iterable<WorkRequest> {
@@ -425,6 +439,25 @@
}
requestByPath.put(path, new CrawlSubgraph(location, depth));
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(" Workspace:
").append(workspaceName).append('\n');
+ for (Map.Entry<Path, WorkRequest> entry : requestByPath.entrySet()) {
+ sb.append(" ")
+ .append(entry.getKey().getString(context.getNamespaceRegistry()))
+ .append("->")
+ .append(entry.getValue().toString(context))
+ .append('\n');
+ }
+ return sb.toString();
+ }
}
protected static ChangeRequest merge( ExecutionContext context,
@@ -553,6 +586,17 @@
@Immutable
protected static abstract class WorkRequest {
+ public abstract String toString( ExecutionContext context );
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return toString(new ExecutionContext());
+ }
}
@Immutable
@@ -565,6 +609,11 @@
this.location = location;
this.depth = depth;
}
+
+ @Override
+ public String toString( ExecutionContext context ) {
+ return "Crawl " +
location.getPath().getString(context.getNamespaceRegistry());
+ }
}
@Immutable
@@ -574,5 +623,10 @@
protected ForwardRequest( ChangeRequest changeRequest ) {
this.changeRequest = changeRequest;
}
+
+ @Override
+ public String toString( ExecutionContext context ) {
+ return "Forward " + changeRequest;
+ }
}
}