本文家下来分析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源码研究系本人原创
转载请注明出处 博客园 刺猬的温驯
本文链接 http://www.cnblogs.com/chenying99/archive/2013/04/07/3003303.html