2023-02-25 576
本文家下来分析SearchManager类的源码,该类实现了SynchronousEventListener接口,而SynchronousEventListener接口继承自EventListener接口,EventListener接口只有一个事件监听方法
SearchManager类的源码如下:
/**
* Acts as a global entry point to execute queries and index nodes.
*/
public class SearchManager implements SynchronousEventListener {
/**
* Logger instance for this class
*/
private static final Logger log = LoggerFactory.getLogger(SearchManager.class);
/**
* Namespace URI for xpath functions
*/
private static final String NS_FN_PREFIX = "fn";
public static final String NS_FN_URI = "http://www.w3.org/2005/xpath-functions";
/**
* Deprecated namespace URI for xpath functions
*/
private static final String NS_FN_OLD_PREFIX = "fn_old";
public static final String NS_FN_OLD_URI = "http://www.w3.org/2004/10/xpath-functions";
/**
* Namespace URI for XML schema
*/
private static final String NS_XS_PREFIX = "xs";
public static final String NS_XS_URI = "http://www.w3.org/2001/XMLSchema";
/**
* The shared item state manager instance for the workspace.
*/
private final SharedItemStateManager itemMgr;
/**
* QueryHandler where query execution is delegated to
*/
private QueryHandler handler;
/**
* QueryHandler of the parent search manager or <code>null</code> if there
* is none.
*/
private final QueryHandler parentHandler;
/**
* The namespace registry of the repository.
*/
private final NamespaceRegistryImpl nsReg;
/**
* Path that will be excluded from indexing.
*/
private Path excludePath;
/**
* Creates a new <code>SearchManager</code>.
*
* @param config the search configuration.
* @param nsReg the namespace registry.
* @param ntReg the node type registry.
* @param itemMgr the shared item state manager.
* @param pm the underlying persistence manager.
* @param rootNodeId the id of the root node.
* @param parentMgr the parent search manager or <code>null</code> if
* there is no parent search manager.
* @param excludedNodeId id of the node that should be excluded from
* indexing. Any descendant of that node will also be
* excluded from indexing.
* @throws RepositoryException if the search manager cannot be initialized
*/
public SearchManager(
RepositoryContext repositoryContext,
QueryHandlerFactory qhf,
SharedItemStateManager itemMgr,
PersistenceManager pm,
NodeId rootNodeId,
SearchManager parentMgr,
NodeId excludedNodeId,
Executor executor) throws RepositoryException {
this.nsReg = repositoryContext.getNamespaceRegistry();
this.itemMgr = itemMgr;
this.parentHandler = (parentMgr != null) ? parentMgr.handler : null;
// register namespaces
safeRegisterNamespace(NS_XS_PREFIX, NS_XS_URI);
try {
if (nsReg.getPrefix(NS_FN_OLD_URI).equals(NS_FN_PREFIX)) {
// old uri is mapped to 'fn' prefix -> re-map
String prefix = NS_FN_OLD_PREFIX;
try {
// Find a free prefix
for (int i = 2; true; i++) {
nsReg.getURI(prefix);
prefix = NS_FN_OLD_PREFIX + i;
}
} catch (NamespaceException e) {
// Re-map the old fn URI to that prefix
nsReg.registerNamespace(prefix, NS_FN_OLD_URI);
}
}
} catch (NamespaceException e) {
// does not yet exist
safeRegisterNamespace(NS_FN_OLD_PREFIX, NS_FN_OLD_URI);
}
// at this point the 'fn' prefix shouldn't be assigned anymore
safeRegisterNamespace(NS_FN_PREFIX, NS_FN_URI);
if (excludedNodeId != null) {
HierarchyManagerImpl hmgr =
new HierarchyManagerImpl(rootNodeId, itemMgr);
excludePath = hmgr.getPath(excludedNodeId);
}
// initialize query handler
this.handler = qhf.getQueryHandler(new QueryHandlerContext(
repositoryContext,
itemMgr, pm, rootNodeId,
parentHandler, excludedNodeId, executor));
}
/**
* Registers a namespace using the given prefix hint. Does nothing
* if the namespace is already registered. If the given prefix hint
* is not yet registered as a prefix, then it is used as the prefix
* of the registered namespace. Otherwise a unique prefix is generated
* based on the given hint.
*
* @param prefixHint the prefix hint
* @param uri the namespace URI
* @throws NamespaceException if an illegal attempt is made to register
* a mapping
* @throws RepositoryException if an unexpected error occurs
* @see javax.jcr.NamespaceRegistry#registerNamespace(String, String)
*/
private void safeRegisterNamespace(String prefixHint, String uri)
throws NamespaceException, RepositoryException {
try {
// Check if the namespace is already registered
nsReg.getPrefix(uri);
// ... it is, so do nothing.
} catch (NamespaceException e1) {
// ... it is not, try to find a unique prefix.
String prefix = prefixHint;
try {
for (int suffix = 2; true; suffix++) {
// Is this prefix already registered?
nsReg.getURI(prefix);
// ... it is, generate a new prefix and try again.
prefix = prefixHint + suffix;
}
} catch (NamespaceException e2) {
// ... it is not, register the namespace with this prefix.
nsReg.registerNamespace(prefix, uri);
}
}
}
/**
* Closes this <code>SearchManager</code> and also closes the
* {@link FileSystem} configured in {@link SearchConfig}.
*/
public void close() {
try {
shutdownQueryHandler();
} catch (IOException e) {
log.error("Exception closing QueryHandler.", e);
}
}
/**
* Creates a query object that can be executed on the workspace.
*
* @param sessionContext component context of the current session
* @param statement the actual query statement.
* @param language the syntax of the query statement.
* @param node a nt:query node where the query was read from or
* <code>null</code> if it is not a stored query.
* @return a <code>Query</code> instance to execute.
* @throws InvalidQueryException if the query is malformed or the
* <code>language</code> is unknown.
* @throws RepositoryException if any other error occurs.
*/
public Query createQuery(
SessionContext sessionContext,
String statement, String language, Node node)
throws InvalidQueryException, RepositoryException {
AbstractQueryImpl query = createQueryInstance();
query.init(sessionContext, handler, statement, language, node);
return query;
}
/**
* Creates a query object model that can be executed on the workspace.
*
* @param sessionContext component context of the current session
* @param qomTree the query object model tree, representing the query.
* @param langugage the original language of the query statement.
* @param node a nt:query node where the query was read from or
* <code>null</code> if it is not a stored query.
* @return the query object model for the query.
* @throws InvalidQueryException the the query object model tree is
* considered invalid by the query handler
* implementation.
* @throws RepositoryException if any other error occurs.
*/
public QueryObjectModel createQueryObjectModel(
SessionContext sessionContext, QueryObjectModelTree qomTree,
String langugage, Node node)
throws InvalidQueryException, RepositoryException {
QueryObjectModelImpl qom = new QueryObjectModelImpl();
qom.init(sessionContext, handler, qomTree, langugage, node);
return qom;
}
/**
* Returns the ids of the nodes that refer to the node with <code>id</code>
* by weak references.
*
* @param id the id of the target node.
* @return the ids of the referring nodes.
* @throws RepositoryException if an error occurs.
* @throws IOException if an error occurs while reading from the
* index.
*/
public Iterable<NodeId> getWeaklyReferringNodes(NodeId id)
throws RepositoryException, IOException {
return handler.getWeaklyReferringNodes(id);
}
/**
* Checks if the given event should be excluded based on the
* {@link #excludePath} setting.
*
* @param event observation event
* @return <code>true</code> if the event should be excluded,
* <code>false</code> otherwise
*/
private boolean isExcluded(EventImpl event) {
try {
return excludePath != null
&& excludePath.isAncestorOf(event.getQPath());
} catch (MalformedPathException ex) {
log.error("Error filtering events.", ex);
return false;
} catch (RepositoryException ex) {
log.error("Error filtering events.", ex);
return false;
}
}
//------------------------< for testing only >------------------------------
/**
* @return the query handler implementation.
*/
public QueryHandler getQueryHandler() {
return handler;
}
//---------------< EventListener interface >--------------------------------
public void onEvent(EventIterator events) {
log.debug("onEvent: indexing started");
long time = System.currentTimeMillis();
// nodes that need to be removed from the index.
final Set<NodeId> removedNodes = new HashSet<NodeId>();
// nodes that need to be added to the index.
final Map<NodeId, EventImpl> addedNodes = new HashMap<NodeId, EventImpl>();
// property events
List<EventImpl> propEvents = new ArrayList<EventImpl>();
while (events.hasNext()) {
EventImpl e = (EventImpl) events.nextEvent();
if (!isExcluded(e)) {
long type = e.getType();
if (type == Event.NODE_ADDED) {
addedNodes.put(e.getChildId(), e);
// quick'n dirty fix for JCR-905
if (e.isExternal()) {
removedNodes.add(e.getChildId());
}
if (e.isShareableChildNode()) {
// simply re-index shareable nodes
removedNodes.add(e.getChildId());
}
} else if (type == Event.NODE_REMOVED) {
removedNodes.add(e.getChildId());
if (e.isShareableChildNode()) {
// check if there is a node remaining in the shared set
if (itemMgr.hasItemState(e.getChildId())) {
addedNodes.put(e.getChildId(), e);
}
}
} else {
propEvents.add(e);
}
}
}
// sort out property events
for (EventImpl e : propEvents) {
NodeId nodeId = e.getParentId();
if (e.getType() == Event.PROPERTY_ADDED) {
if (addedNodes.put(nodeId, e) == null) {
// only property added
// need to re-index
removedNodes.add(nodeId);
} else {
// the node where this prop belongs to is also new
}
} else if (e.getType() == Event.PROPERTY_CHANGED) {
// need to re-index
addedNodes.put(nodeId, e);
removedNodes.add(nodeId);
} else {
// property removed event is only generated when node still exists
addedNodes.put(nodeId, e);
removedNodes.add(nodeId);
}
}
Iterator<NodeState> addedStates = new Iterator<NodeState>() {
private final Iterator<NodeId> iter = addedNodes.keySet().iterator();
public void remove() {
throw new UnsupportedOperationException();
}
public boolean hasNext() {
return iter.hasNext();
}
public NodeState next() {
NodeState item = null;
NodeId id = (NodeId) iter.next();
try {
item = (NodeState) itemMgr.getItemState(id);
} catch (ItemStateException ise) {
// check whether this item state change originated from
// an external event
EventImpl e = addedNodes.get(id);
if (e == null || !e.isExternal()) {
log.error("Unable to index node " + id + ": does not exist");
} else {
log.info("Node no longer available " + id + ", skipped.");
}
}
return item;
}
};
Iterator<NodeId> removedIds = removedNodes.iterator();
if (removedNodes.size() > 0 || addedNodes.size() > 0) {
try {
handler.updateNodes(removedIds, addedStates);
} catch (RepositoryException e) {
log.error("Error indexing node.", e);
} catch (IOException e) {
log.error("Error indexing node.", e);
}
}
if (log.isDebugEnabled()) {
log.debug("onEvent: indexing finished in "
+ String.valueOf(System.currentTimeMillis() - time)
+ " ms.");
}
}
/**
* Creates a new instance of an {@link AbstractQueryImpl} which is not
* initialized.
*
* @return an new query instance.
* @throws RepositoryException if an error occurs while creating a new query
* instance.
*/
protected AbstractQueryImpl createQueryInstance() throws RepositoryException {
try {
String queryImplClassName = handler.getQueryClass();
Object obj = Class.forName(queryImplClassName).newInstance();
if (obj instanceof AbstractQueryImpl) {
return (AbstractQueryImpl) obj;
} else {
throw new IllegalArgumentException(queryImplClassName
+ " is not of type " + AbstractQueryImpl.class.getName());
}
} catch (Throwable t) {
throw new RepositoryException("Unable to create query: " + t.toString(), t);
}
}
//------------------------< internal >--------------------------------------
/**
* 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 void shutdownQueryHandler() throws IOException {
if (handler != null) {
handler.close();
handler = null;
}
}
}
该类的英文注释说明,SearchManager为全局的检索与索引的入口
它的构造函数如下:
/**
* Creates a new <code>SearchManager</code>.
*
* @param config the search configuration.
* @param nsReg the namespace registry.
* @param ntReg the node type registry.
* @param itemMgr the shared item state manager.
* @param pm the underlying persistence manager.
* @param rootNodeId the id of the root node.
* @param parentMgr the parent search manager or <code>null</code> if
* there is no parent search manager.
* @param excludedNodeId id of the node that should be excluded from
* indexing. Any descendant of that node will also be
* excluded from indexing.
* @throws RepositoryException if the search manager cannot be initialized
*/
public SearchManager(
RepositoryContext repositoryContext,
QueryHandlerFactory qhf,
SharedItemStateManager itemMgr,
PersistenceManager pm,
NodeId rootNodeId,
SearchManager parentMgr,
NodeId excludedNodeId,
Executor executor) throws RepositoryException {
this.nsReg = repositoryContext.getNamespaceRegistry();
this.itemMgr = itemMgr;
this.parentHandler = (parentMgr != null) ? parentMgr.handler : null;
// register namespaces
safeRegisterNamespace(NS_XS_PREFIX, NS_XS_URI);
try {
if (nsReg.getPrefix(NS_FN_OLD_URI).equals(NS_FN_PREFIX)) {
// old uri is mapped to 'fn' prefix -> re-map
String prefix = NS_FN_OLD_PREFIX;
try {
// Find a free prefix
for (int i = 2; true; i++) {
nsReg.getURI(prefix);
prefix = NS_FN_OLD_PREFIX + i;
}
} catch (NamespaceException e) {
// Re-map the old fn URI to that prefix
nsReg.registerNamespace(prefix, NS_FN_OLD_URI);
}
}
} catch (NamespaceException e) {
// does not yet exist
safeRegisterNamespace(NS_FN_OLD_PREFIX, NS_FN_OLD_URI);
}
// at this point the 'fn' prefix shouldn't be assigned anymore
safeRegisterNamespace(NS_FN_PREFIX, NS_FN_URI);
if (excludedNodeId != null) {
HierarchyManagerImpl hmgr =
new HierarchyManagerImpl(rootNodeId, itemMgr);
excludePath = hmgr.getPath(excludedNodeId);
}
// initialize query handler
this.handler = qhf.getQueryHandler(new QueryHandlerContext(
repositoryContext,
itemMgr, pm, rootNodeId,
parentHandler, excludedNodeId, executor));
}
这里实现的是初始化命名空间以及初始化handler成员变量(SearchIndex类型)
它的事件监听方法实现如下:
//---------------< EventListener interface >--------------------------------
public void onEvent(EventIterator events) {
log.debug("onEvent: indexing started");
long time = System.currentTimeMillis();
// nodes that need to be removed from the index.
final Set<NodeId> removedNodes = new HashSet<NodeId>();
// nodes that need to be added to the index.
final Map<NodeId, EventImpl> addedNodes = new HashMap<NodeId, EventImpl>();
// property events
List<EventImpl> propEvents = new ArrayList<EventImpl>();
while (events.hasNext()) {
EventImpl e = (EventImpl) events.nextEvent();
if (!isExcluded(e)) {
long type = e.getType();
if (type == Event.NODE_ADDED) {
addedNodes.put(e.getChildId(), e);
// quick'n dirty fix for JCR-905
if (e.isExternal()) {
removedNodes.add(e.getChildId());
}
if (e.isShareableChildNode()) {
// simply re-index shareable nodes
removedNodes.add(e.getChildId());
}
} else if (type == Event.NODE_REMOVED) {
removedNodes.add(e.getChildId());
if (e.isShareableChildNode()) {
// check if there is a node remaining in the shared set
if (itemMgr.hasItemState(e.getChildId())) {
addedNodes.put(e.getChildId(), e);
}
}
} else {
propEvents.add(e);
}
}
}
// sort out property events
for (EventImpl e : propEvents) {
NodeId nodeId = e.getParentId();
if (e.getType() == Event.PROPERTY_ADDED) {
if (addedNodes.put(nodeId, e) == null) {
// only property added
// need to re-index
removedNodes.add(nodeId);
} else {
// the node where this prop belongs to is also new
}
} else if (e.getType() == Event.PROPERTY_CHANGED) {
// need to re-index
addedNodes.put(nodeId, e);
removedNodes.add(nodeId);
} else {
// property removed event is only generated when node still exists
addedNodes.put(nodeId, e);
removedNodes.add(nodeId);
}
}
Iterator<NodeState> addedStates = new Iterator<NodeState>() {
private final Iterator<NodeId> iter = addedNodes.keySet().iterator();
public void remove() {
throw new UnsupportedOperationException();
}
public boolean hasNext() {
return iter.hasNext();
}
public NodeState next() {
NodeState item = null;
NodeId id = (NodeId) iter.next();
try {
item = (NodeState) itemMgr.getItemState(id);
} catch (ItemStateException ise) {
// check whether this item state change originated from
// an external event
EventImpl e = addedNodes.get(id);
if (e == null || !e.isExternal()) {
log.error("Unable to index node " + id + ": does not exist");
} else {
log.info("Node no longer available " + id + ", skipped.");
}
}
return item;
}
};
Iterator<NodeId> removedIds = removedNodes.iterator();
if (removedNodes.size() > 0 || addedNodes.size() > 0) {
try {
handler.updateNodes(removedIds, addedStates);
} catch (RepositoryException e) {
log.error("Error indexing node.", e);
} catch (IOException e) {
log.error("Error indexing node.", e);
}
}
if (log.isDebugEnabled()) {
log.debug("onEvent: indexing finished in "
+ String.valueOf(System.currentTimeMillis() - time)
+ " ms.");
}
}
jackrabbit节点的删除与添加都是通过该事件处理程序执行的,方法里面通过调用handler的updateNodes方法(SearchIndex类型)
而索引的检索式通过createQuery创建Query对象的(注意 这里的Query不是lucene的query,而是经过jackrabbit封装后的query)
/**
* Creates a query object that can be executed on the workspace.
*
* @param sessionContext component context of the current session
* @param statement the actual query statement.
* @param language the syntax of the query statement.
* @param node a nt:query node where the query was read from or
* <code>null</code> if it is not a stored query.
* @return a <code>Query</code> instance to execute.
* @throws InvalidQueryException if the query is malformed or the
* <code>language</code> is unknown.
* @throws RepositoryException if any other error occurs.
*/
public Query createQuery(
SessionContext sessionContext,
String statement, String language, Node node)
throws InvalidQueryException, RepositoryException {
AbstractQueryImpl query = createQueryInstance();
query.init(sessionContext, handler, statement, language, node);
return query;
}
这里的AbstractQueryImpl类型实例到底是什么呢?
/**
* Creates a new instance of an {@link AbstractQueryImpl} which is not
* initialized.
*
* @return an new query instance.
* @throws RepositoryException if an error occurs while creating a new query
* instance.
*/
protected AbstractQueryImpl createQueryInstance() throws RepositoryException {
try {
String queryImplClassName = handler.getQueryClass();
Object obj = Class.forName(queryImplClassName).newInstance();
if (obj instanceof AbstractQueryImpl) {
return (AbstractQueryImpl) obj;
} else {
throw new IllegalArgumentException(queryImplClassName
+ " is not of type " + AbstractQueryImpl.class.getName());
}
} catch (Throwable t) {
throw new RepositoryException("Unable to create query: " + t.toString(), t);
}
}
这里就要根据handler的queryClass属性了(handler为SearchIndex类型),查看SearchIndex及抽象父类,默认为
/**
* The name of a class that extends {@link AbstractQueryImpl}.
*/
private String queryClass = QueryImpl.class.getName();
这个就到了jackrabbit对数据的检索了,留待后文再分析吧
以上所述是小编给大家介绍的Apache Jackrabbit源码研究(四),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对77isp云服务器技术网的支持!原文链接:https://77isp.com/post/34324.html
=========================================
https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。
数据库技术 2022-03-28
网站技术 2022-11-26
网站技术 2023-01-07
网站技术 2022-11-17
Windows相关 2022-02-23
网站技术 2023-01-14
Windows相关 2022-02-16
Windows相关 2022-02-16
Linux相关 2022-02-27
数据库技术 2022-02-20
抠敌 2023年10月23日
嚼餐 2023年10月23日
男忌 2023年10月22日
瓮仆 2023年10月22日
簿偌 2023年10月22日
扫码二维码
获取最新动态