001//
002// Name
003//  $RCSfile: QueryScope.java,v $
004// 
005// Copyright
006//  Copyright 2012 Cloud Software Group, Inc. ALL RIGHTS RESERVED. 
007//  Cloud Software Group, Inc. Confidential Information
008//
009// History
010//  $Revision: 1.1.2.2 $ $Date: 2012/08/03 19:59:24 $
011//
012package com.kabira.platform;
013
014import java.util.HashSet;
015import java.util.Collections;
016
017/**
018 * Class used to define a set of remote nodes used for distributed queries. 
019 * <p>
020 * This class allows applications to define queries that are done on
021 * a subset of nodes in the cluster. 
022 * <p>
023 * When a query is executed using a user defined query scope, the current
024 * audit mode is used to verify the scope. If the default {@link
025 * com.kabira.platform.QueryScope.AuditMode#AUDIT_NODE_LIST} audit mode
026 * is enabled, distributed queries are skipped if distribution is not
027 * active when the query is executed. If distribution is active, only
028 * those nodes that are active in the node list are used in the query,
029 * all other nodes are ignored. Otherwise, the auditing documented in the
030 * {@link com.kabira.platform.QueryScope.AuditMode} enumeration is
031 * performed, and a ResourceUnavailableException exception is thrown
032 * if the audit fails.
033 * <p>
034 * When {@link QueryScope#setAuditMode(AuditMode)} is called, no audits
035 * are done at that time. Only when a query is executed is auditing done.
036 * This insures that auditing is performed before each execution of the query.
037 * <p>
038 * To support cluster wide queries, the {@link QueryScope#QUERY_CLUSTER}
039 * predefined instance of QueryScope can be used.
040 * <p>
041 * To disable node or cluster scoped queries, the {@link QueryScope#QUERY_LOCAL}
042 * predefined instance of QueryScope can be used.
043 *
044 * @see KeyQuery#setQueryScope(QueryScope)
045 * @see ManagedObject#extent(Class, QueryScope, LockMode)
046 * @see ManagedObject#cardinality(Class, QueryScope)
047 */
048public final class QueryScope
049{
050    /**
051     * The query will be done only on the local node. This is the default scope.
052     */
053    public final static QueryScope QUERY_LOCAL = new QueryScope(Scope.LOCAL);
054
055    /**
056     * The query will be done on all active nodes in the cluster.
057     */
058    public final static QueryScope QUERY_CLUSTER = new QueryScope(Scope.CLUSTER);
059
060    /**
061     *  Possible audits that can be applied to the query.
062     */
063    public enum AuditMode
064    {
065        /**
066         * Default audit mode, distributed audits are disable. The node
067         * list is validated to insure one or more nodes are defined and
068         * that no node in the list is empty.
069         */
070        AUDIT_NODE_LIST,
071        /**
072         * Validate that the node list is valid and that distribution
073         * is active. Inactive nodes are skipped when the query is executed.
074         */
075        AUDIT_DISTRIBUTION,
076        /**
077         * Validate that the node list is valid, distribution is active,
078         * and that all nodes are active.
079         */
080        AUDIT_NODES_ACTIVE
081    }
082
083        /**
084         * Create a QueryScope instance with no nodes.
085         */
086    public QueryScope()
087        {
088                m_remoteNodes = new HashSet<String>();
089        m_scope = Scope.USER;
090        m_auditMode = AuditMode.AUDIT_NODE_LIST;
091        }
092
093        /**
094         * Create a QueryScope instance
095     *
096     * @param remoteNodes Array of remote nodes.
097         */
098        public QueryScope(String [] remoteNodes)
099        {
100                m_remoteNodes = new HashSet<String>();
101        Collections.addAll(m_remoteNodes, remoteNodes);
102        m_scope = Scope.USER;
103        m_auditMode = AuditMode.AUDIT_NODE_LIST;
104        }
105
106        /**
107     * Adds the specified node if not already present.
108     * @param nodeName Node to add.
109     * @exception UnsupportedOperationException
110     *  The operation was called on QUERY_LOCAL or QUERY_CLUSTER.
111         */
112    public void addNode(String nodeName)
113        throws UnsupportedOperationException
114    {
115        checkUser();
116        m_remoteNodes.add(nodeName);
117    }
118
119        /**
120     * Removed the specified node. If not present, the remove is ignored.
121     * @param nodeName Node to remove.
122     * @exception UnsupportedOperationException
123     *  The operation was called on QUERY_LOCAL or QUERY_CLUSTER.
124         */
125    public void removeNode(String nodeName)
126        throws UnsupportedOperationException
127    {
128        checkUser();
129        m_remoteNodes.remove(nodeName);
130    }
131
132        /**
133     * Get the nodes defined for this query scope.
134     * <p>
135     * If this method is called on QUERY_LOCAL or QUERY_CLUSTER
136     * instances, a zero length array is returned.
137     * @return Array of node names.
138         */
139    public String [] getNodes()
140    {
141        return m_remoteNodes.toArray(new String[0]);
142    }
143
144        /**
145     * Set audit mode when queries are executed.
146     * @param auditMode Audit mode to use.
147     * @exception UnsupportedOperationException
148     *  The operation was called on QUERY_LOCAL or QUERY_CLUSTER.
149         */
150    public void setAuditMode(AuditMode auditMode)
151        throws UnsupportedOperationException
152    {
153        checkUser();
154        m_auditMode = auditMode;
155    }
156
157        /**
158     * Return the current audit mode.
159     * @return Current audit mode.
160     * @exception UnsupportedOperationException
161     *  The operation was called on QUERY_LOCAL or QUERY_CLUSTER.
162         */
163    public AuditMode getAuditMode()
164        throws UnsupportedOperationException
165    {
166        checkUser();
167        return m_auditMode;
168    }
169
170    //
171    // Package private definitions.
172    //
173    enum Scope
174    {
175        LOCAL,
176        CLUSTER,
177        USER
178    }
179
180    Scope getScope()
181    {
182        return m_scope;
183    }
184
185    void checkUser() throws UnsupportedOperationException
186    {
187        if (m_scope != Scope.USER)
188        {
189            throw new UnsupportedOperationException();
190        }
191    }
192
193    void audit()
194    {
195        if (m_scope != Scope.USER)
196        {
197            assert( m_remoteNodes.isEmpty() );
198            return;
199        }
200
201        if (m_remoteNodes.isEmpty())
202        {
203            throw new IllegalArgumentException(
204                "The node array must have at least one entry");   
205        }
206        for (String node : m_remoteNodes)
207        {  
208            if (node.isEmpty())
209            {
210                throw new IllegalArgumentException("Empty node in list");
211            }
212        }
213
214        if (m_auditMode == AuditMode.AUDIT_DISTRIBUTION ||
215            m_auditMode == AuditMode.AUDIT_NODES_ACTIVE)
216        {
217            if (ManagedObject.m_distributedQuery == null)
218            {
219                throw new ResourceUnavailableException(
220                    "Distributed query processing not available on this node");
221            }
222            if (m_auditMode == AuditMode.AUDIT_NODES_ACTIVE)
223            {
224                ManagedObject.m_distributedQuery.validateNodesActive(this);
225            }
226        }
227    }
228
229    //
230    // Private constructor used for predefined scopes.
231    //
232    private QueryScope(Scope scope)
233    {
234                m_remoteNodes = new HashSet<String>();
235        m_scope = scope;
236        m_auditMode = AuditMode.AUDIT_NODE_LIST;
237    }
238
239    private HashSet<String>     m_remoteNodes;
240    private AuditMode           m_auditMode;
241    private Scope               m_scope;
242}