zoukankan      html  css  js  c++  java
  • Apache Jackrabbit源码研究(四)

    本文家下来分析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

  • 相关阅读:
    memcached与.NET的融合使用(二)
    memcached与.NET的融合使用(一)
    使用window2003安装邮件服务器最新实际操作记录
    2014 -> 2015
    数据挖掘入门 资料和步骤
    CSDN 论坛招聘区是不是有潜规则?在Cnblog招个人试试...
    C#薪水和前途
    面试准备
    面试准备
    面试准备
  • 原文地址:https://www.cnblogs.com/chenying99/p/3003303.html
Copyright © 2011-2022 走看看