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

你可能感兴趣的:(apache)