org.modeshape.jcr.JcrQueryManager Maven / Gradle / Ivy
/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.jcr;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.jcr.AccessDeniedException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.query.InvalidQueryException;
import javax.jcr.query.Query;
import javax.jcr.version.VersionException;
import org.modeshape.common.annotation.Immutable;
import org.modeshape.common.logging.Logger;
import org.modeshape.common.text.ParsingException;
import org.modeshape.common.util.CheckArg;
import org.modeshape.jcr.JcrRepository.QueryLanguage;
import org.modeshape.jcr.api.monitor.DurationMetric;
import org.modeshape.jcr.api.query.QueryManager;
import org.modeshape.jcr.cache.CachedNode;
import org.modeshape.jcr.cache.NodeCache;
import org.modeshape.jcr.cache.NodeKey;
import org.modeshape.jcr.cache.RepositoryCache;
import org.modeshape.jcr.query.BufferManager;
import org.modeshape.jcr.query.CancellableQuery;
import org.modeshape.jcr.query.JcrQuery;
import org.modeshape.jcr.query.JcrQueryContext;
import org.modeshape.jcr.query.JcrTypeSystem;
import org.modeshape.jcr.query.model.QueryCommand;
import org.modeshape.jcr.query.model.QueryObjectModel;
import org.modeshape.jcr.query.model.QueryObjectModelFactory;
import org.modeshape.jcr.query.model.SelectQuery;
import org.modeshape.jcr.query.model.SetQuery;
import org.modeshape.jcr.query.model.SetQueryObjectModel;
import org.modeshape.jcr.query.model.TypeSystem;
import org.modeshape.jcr.query.model.Visitors;
import org.modeshape.jcr.query.parse.QueryParser;
import org.modeshape.jcr.query.parse.QueryParsers;
import org.modeshape.jcr.query.plan.PlanHints;
import org.modeshape.jcr.query.validate.Schemata;
import org.modeshape.jcr.value.BinaryValue;
import org.modeshape.jcr.value.Name;
import org.modeshape.jcr.value.NamespaceRegistry;
import org.modeshape.jcr.value.Path;
import org.modeshape.jcr.value.Reference;
import org.modeshape.jcr.value.ValueFactories;
/**
* Place-holder implementation of {@link QueryManager} interface.
*/
@Immutable
class JcrQueryManager implements QueryManager {
protected static final Logger LOGGER = Logger.getLogger(JcrQueryManager.class);
private final JcrSession session;
private final JcrQueryContext context;
private final JcrTypeSystem typeSystem;
private final QueryObjectModelFactory factory;
JcrQueryManager( JcrSession session ) {
this.session = session;
this.context = new SessionQueryContext(this.session);
this.typeSystem = new SessionTypeSystem(this.session);
this.factory = new QueryObjectModelFactory(this.context);
}
@Override
public org.modeshape.jcr.api.query.qom.QueryObjectModelFactory getQOMFactory() {
return factory;
}
@Override
public org.modeshape.jcr.api.query.Query createQuery( String statement,
String language ) throws InvalidQueryException, RepositoryException {
CheckArg.isNotNull(statement, "statement");
CheckArg.isNotNull(language, "language");
return createQuery(statement, language, null);
}
/**
* Creates a new JCR {@link Query} by specifying the query expression itself, the language in which the query is stated, the
* {@link QueryCommand} representation and, optionally, the node from which the query was loaded. The language must be a
* string from among those returned by {@code QueryManager#getSupportedQueryLanguages()}.
*
* @param expression the original query expression as supplied by the client; may not be null
* @param language the language in which the expression is represented; may not be null
* @param storedAtPath the path at which this query was stored, or null if this is not a stored query
* @return query the JCR query object; never null
* @throws InvalidQueryException if expression is invalid or language is unsupported
* @throws RepositoryException if the session is no longer live
*/
public org.modeshape.jcr.api.query.Query createQuery( String expression,
String language,
Path storedAtPath ) throws InvalidQueryException, RepositoryException {
session.checkLive();
// Look for a parser for the specified language ...
QueryParsers queryParsers = session.repository().runningState().queryParsers();
QueryParser parser = queryParsers.getParserFor(language);
if (parser == null) {
Set languages = queryParsers.getLanguages();
throw new InvalidQueryException(JcrI18n.invalidQueryLanguage.text(language, languages));
}
try {
// Parsing must be done now ...
QueryCommand command = parser.parseQuery(expression, typeSystem);
if (command == null) {
// The query is not well-formed and cannot be parsed ...
throw new InvalidQueryException(JcrI18n.queryCannotBeParsedUsingLanguage.text(language, expression));
}
// Set up the hints ...
PlanHints hints = new PlanHints();
hints.showPlan = true;
hints.hasFullTextSearch = true; // always include the score
hints.validateColumnExistance = false; // see MODE-1055
if (parser.getLanguage().equals(QueryLanguage.JCR_SQL2)) {
hints.qualifyExpandedColumnNames = true;
}
return resultWith(expression, parser.getLanguage(), command, hints, storedAtPath);
} catch (ParsingException e) {
// The query is not well-formed and cannot be parsed ...
String reason = e.getMessage();
throw new InvalidQueryException(JcrI18n.queryCannotBeParsedUsingLanguage.text(language, expression, reason));
} catch (org.modeshape.jcr.query.parse.InvalidQueryException e) {
// The query was parsed, but there is an error in the query
String reason = e.getMessage();
throw new InvalidQueryException(JcrI18n.queryInLanguageIsNotValid.text(language, expression, reason));
}
}
/**
* Creates a new JCR {@link Query} by specifying the query expression itself, the language in which the query is stated, the
* {@link QueryCommand} representation. This method is more efficient than {@link #createQuery(String, String, Path)} if the
* QueryCommand is created directly.
*
* @param command the query command; may not be null
* @return query the JCR query object; never null
* @throws InvalidQueryException if expression is invalid or language is unsupported
* @throws RepositoryException if the session is no longer live
*/
public Query createQuery( QueryCommand command ) throws InvalidQueryException, RepositoryException {
session.checkLive();
if (command == null) {
// The query is not well-formed and cannot be parsed ...
throw new InvalidQueryException(JcrI18n.queryInLanguageIsNotValid.text(QueryLanguage.JCR_SQL2, command));
}
// Produce the expression string ...
String expression = Visitors.readable(command);
try {
// Parsing must be done now ...
PlanHints hints = new PlanHints();
hints.showPlan = true;
hints.hasFullTextSearch = true; // always include the score
hints.qualifyExpandedColumnNames = true; // always qualify expanded names with the selector name in JCR-SQL2
return resultWith(expression, QueryLanguage.JCR_SQL2, command, hints, null);
} catch (org.modeshape.jcr.query.parse.InvalidQueryException e) {
// The query was parsed, but there is an error in the query
String reason = e.getMessage();
throw new InvalidQueryException(JcrI18n.queryInLanguageIsNotValid.text(QueryLanguage.JCR_SQL2, expression, reason));
}
}
@Override
public org.modeshape.jcr.api.query.Query getQuery( Node node ) throws InvalidQueryException, RepositoryException {
AbstractJcrNode jcrNode = CheckArg.getInstanceOf(node, AbstractJcrNode.class, "node");
// Check the type of the node ...
JcrNodeType nodeType = jcrNode.getPrimaryNodeType();
if (!nodeType.getInternalName().equals(JcrNtLexicon.QUERY)) {
NamespaceRegistry registry = session.context().getNamespaceRegistry();
throw new InvalidQueryException(JcrI18n.notStoredQuery.text(jcrNode.path().getString(registry)));
}
// These are both mandatory properties for nodes of nt:query
String statement = jcrNode.getProperty(JcrLexicon.STATEMENT).getString();
String language = jcrNode.getProperty(JcrLexicon.LANGUAGE).getString();
return createQuery(statement, language, jcrNode.path());
}
@Override
public String[] getSupportedQueryLanguages() {
// Make a defensive copy ...
Set languages = session.repository().runningState().queryParsers().getLanguages();
return languages.toArray(new String[languages.size()]);
}
protected org.modeshape.jcr.api.query.Query resultWith( String expression,
String language,
QueryCommand command,
PlanHints hints,
Path storedAtPath ) {
if (command instanceof SelectQuery) {
SelectQuery query = (SelectQuery)command;
return new QueryObjectModel(context, expression, language, query, hints, storedAtPath);
}
if (command instanceof SetQuery) {
SetQuery query = (SetQuery)command;
return new SetQueryObjectModel(this.context, expression, language, query, hints, storedAtPath);
}
JcrQueryContext context = new SessionQueryContext(session);
return new JcrQuery(context, expression, language, command, hints, storedAtPath);
}
protected static class SessionQueryContext implements JcrQueryContext {
private final JcrSession session;
private final ValueFactories factories;
protected SessionQueryContext( JcrSession session ) {
this.session = session;
this.factories = session.context().getValueFactories();
}
@Override
public BufferManager getBufferManager() {
return session.bufferManager();
}
@Override
public String getWorkspaceName() {
return session.workspaceName();
}
@Override
public Value createValue( int propertyType,
Object value ) {
return value == null ? null : new JcrValue(factories, propertyType, value);
}
@Override
public CancellableQuery createExecutableQuery( QueryCommand query,
PlanHints hints,
Map variables ) throws RepositoryException {
session.checkLive();
// Submit immediately to the workspace graph ...
Schemata schemata = session.workspace().nodeTypeManager().schemata();
NodeTypes nodeTypes = session.repository().nodeTypeManager().getNodeTypes();
RepositoryIndexes indexDefns = session.repository().queryManager().getIndexes();
ExecutionContext context = session.context();
String workspaceName = session.workspaceName();
JcrRepository.RunningState state = session.repository().runningState();
RepositoryQueryManager queryManager = state.queryManager();
RepositoryCache repoCache = state.repositoryCache();
NodeCache nodeCache = hints.useSessionContent ? session.cache() : session.cache().getWorkspace();
Map overriddenNodeCaches = new HashMap();
overriddenNodeCaches.put(workspaceName, nodeCache);
Set workspaceNames = null;
if (hints.includeSystemContent) {
workspaceNames = new LinkedHashSet();
workspaceNames.add(workspaceName);
workspaceNames.add(repoCache.getSystemWorkspaceName());
} else {
workspaceNames = Collections.singleton(workspaceName);
}
return queryManager.query(context, repoCache, workspaceNames, overriddenNodeCaches, query, schemata, indexDefns,
nodeTypes, hints, variables);
}
@Override
public ExecutionContext getExecutionContext() {
return session.context();
}
@Override
public void checkValid() throws RepositoryException {
session.checkLive();
}
@Override
public Node getNode( CachedNode node ) {
if (node == null) {
return null;
}
// this *does not* check permissions because it is expected that the correct sequence already wraps the results and
// therefore it should not be possible to return a Node/Row from a batch on which there aren't any read permissions
return session.node(node, (AbstractJcrNode.Type)null);
}
@Override
public boolean canRead( CachedNode node ) {
if (node == null) {
return false;
}
Path path = getPath(node);
try {
session.checkPermission(path, ModeShapePermissions.READ);
return true;
} catch (AccessDeniedException ade) {
LOGGER.debug("READ access denied on '{0}'", path);
return false;
}
}
@SuppressWarnings( "deprecation" )
@Override
public String getUuid( CachedNode node ) {
Node jcrNode = getNode(node);
if (jcrNode != null) {
try {
return jcrNode.getUUID();
} catch (UnsupportedRepositoryOperationException e) {
return null; // it's not referenceable
} catch (RepositoryException e) {
Logger.getLogger(getClass()).debug(e, "Error obtaining UUID from node");
}
}
return null;
}
@Override
public Path getPath( CachedNode node ) {
return node.getPath(session.cache());
}
@Override
public Name getName( CachedNode node ) {
return node.getName(session.cache());
}
@Override
public long getDepth( CachedNode node ) {
assert node != null;
return node.getDepth(session.cache());
}
@Override
public long getChildCount( CachedNode node ) {
assert node != null;
return node.getChildReferences(session.cache()).size();
}
@Override
public String getIdentifier( CachedNode node ) {
// the identifier format varies depending upon the node ...
return session.nodeIdentifier(node.getKey());
}
@Override
public Node storeQuery( String absolutePath,
Name nodeType,
String language,
String statement ) throws RepositoryException {
session.checkLive();
NamespaceRegistry namespaces = session.namespaces();
Path path;
try {
path = session.pathFactory().create(absolutePath);
} catch (IllegalArgumentException iae) {
throw new RepositoryException(JcrI18n.invalidPathParameter.text("absolutePath", absolutePath));
}
Path parentPath = path.getParent();
Node parentNode = session.node(parentPath);
if (!parentNode.isCheckedOut()) {
throw new VersionException(JcrI18n.nodeIsCheckedIn.text(parentNode.getPath()));
}
Node queryNode = parentNode.addNode(path.relativeTo(parentPath).getString(namespaces),
JcrNtLexicon.QUERY.getString(namespaces));
queryNode.setProperty(JcrLexicon.LANGUAGE.getString(namespaces), language);
queryNode.setProperty(JcrLexicon.STATEMENT.getString(namespaces), statement);
return queryNode;
}
@Override
public void recordDuration( long nanos,
TimeUnit unit,
String query,
String language ) {
Map payload = new HashMap();
payload.put("query", query);
payload.put("language", language);
session.repository().statistics().recordDuration(DurationMetric.QUERY_EXECUTION_TIME, nanos, unit, payload);
}
}
protected static class SessionTypeSystem extends JcrTypeSystem {
protected final JcrSession session;
protected final TypeSystem delegate;
protected SessionTypeSystem( JcrSession session ) {
this.session = session;
this.delegate = session.context().getValueFactories().getTypeSystem();
}
@Override
public Set getTypeNames() {
return delegate.getTypeNames();
}
@Override
public TypeFactory> getTypeFactory( Object prototype ) {
return delegate.getTypeFactory(prototype);
}
@Override
public TypeFactory> getTypeFactory( String typeName ) {
return delegate.getTypeFactory(typeName);
}
@Override
public TypeFactory getStringFactory() {
return delegate.getStringFactory();
}
@Override
public TypeFactory getReferenceFactory() {
return delegate.getReferenceFactory();
}
@Override
public TypeFactory getPathFactory() {
return delegate.getPathFactory();
}
@Override
public TypeFactory getNameFactory() {
return delegate.getNameFactory();
}
@Override
public TypeFactory getNodeKeyFactory() {
return delegate.getNodeKeyFactory();
}
@Override
public TypeFactory getLongFactory() {
return delegate.getLongFactory();
}
@Override
public TypeFactory getDoubleFactory() {
return delegate.getDoubleFactory();
}
@Override
public String getDefaultType() {
return delegate.getDefaultType();
}
@Override
public Comparator
© 2015 - 2024 Weber Informatics LLC | Privacy Policy