geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From upthewatersp...@apache.org
Subject [1/2] incubator-geode git commit: GEODE-1742: Fixing synchronization of cqListeners in CqAttributesImpl
Date Thu, 11 Aug 2016 21:02:16 GMT
Repository: incubator-geode
Updated Branches:
  refs/heads/develop b5e4fd0e1 -> dba71fd5e


GEODE-1742: Fixing synchronization of cqListeners in CqAttributesImpl

Extracted CQAttributesImpl to it's own class, and fixed the
synchronization of cqListeners so that we grab a snapshot of the
listeners to check to avoid races where the listeners can change after
we check for null.


Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/083160ed
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/083160ed
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/083160ed

Branch: refs/heads/develop
Commit: 083160ed6ff213b1780fbcc226dea02cce87795e
Parents: b5e4fd0
Author: Dan Smith <upthewaterspout@apache.org>
Authored: Wed Aug 10 11:00:07 2016 -0700
Committer: Dan Smith <upthewaterspout@apache.org>
Committed: Thu Aug 11 13:53:03 2016 -0700

----------------------------------------------------------------------
 .../cache/query/CqAttributesFactory.java        | 232 +-----------------
 .../query/internal/cq/CqAttributesImpl.java     | 237 +++++++++++++++++++
 .../internal/cq/CqAttributesImplJUnitTest.java  |  45 ++++
 3 files changed, 293 insertions(+), 221 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/083160ed/geode-core/src/main/java/com/gemstone/gemfire/cache/query/CqAttributesFactory.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/com/gemstone/gemfire/cache/query/CqAttributesFactory.java
b/geode-core/src/main/java/com/gemstone/gemfire/cache/query/CqAttributesFactory.java
index 101c9e6..1e0c88a 100644
--- a/geode-core/src/main/java/com/gemstone/gemfire/cache/query/CqAttributesFactory.java
+++ b/geode-core/src/main/java/com/gemstone/gemfire/cache/query/CqAttributesFactory.java
@@ -17,19 +17,12 @@
 
 package com.gemstone.gemfire.cache.query;
 
-import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Iterator;
 import java.util.List;
 
-import org.apache.logging.log4j.Logger;
-
-import com.gemstone.gemfire.SystemFailure;
-import com.gemstone.gemfire.cache.CacheCallback;
+import com.gemstone.gemfire.cache.query.internal.cq.CqAttributesImpl;
 import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
-import com.gemstone.gemfire.internal.logging.LogService;
-import com.gemstone.gemfire.internal.logging.log4j.LocalizedMessage;
 
 /**
  * The factory class for the CqAttributes instance. This provides the CqListener 
@@ -48,8 +41,7 @@ import com.gemstone.gemfire.internal.logging.log4j.LocalizedMessage;
  * @since GemFire 5.5
  */
 public class CqAttributesFactory {
-  private static final Logger logger = LogService.getLogger();
-  
+
   /* Handle for CqAttributes. */
   private final CqAttributesImpl cqAttributes = new CqAttributesImpl();
   
@@ -70,9 +62,7 @@ public class CqAttributesFactory {
    *          AttributesFactory
    */
   public CqAttributesFactory(CqAttributes cqAttributes) {
-    synchronized (this.cqAttributes) {
-      this.cqAttributes.cqListeners = new ArrayList(Arrays.asList(cqAttributes.getCqListeners()));
-    }
+    this.cqAttributes.setCqListeners(new ArrayList(Arrays.asList(cqAttributes.getCqListeners())));
   }
     
   /**
@@ -84,9 +74,7 @@ public class CqAttributesFactory {
     if (cqListener == null) {
       throw new IllegalArgumentException(LocalizedStrings.CqAttributesFactory_ADDCQLISTENER_PARAMETER_WAS_NULL.toLocalizedString());
     }
-    synchronized (this.cqAttributes) {
-      this.cqAttributes.addCqListener(cqListener);
-    }
+    this.cqAttributes.addCqListener(cqListener);
   }
   
   /**
@@ -97,16 +85,14 @@ public class CqAttributesFactory {
    * null element
    */
   public void initCqListeners(CqListener[] cqListeners) {
-    synchronized (this.cqAttributes) {
-      if (cqListeners == null || cqListeners.length == 0) {
-        this.cqAttributes.cqListeners = null;
-      } else {
-        List nl = Arrays.asList(cqListeners);
-        if (nl.contains(null)) {
-          throw new IllegalArgumentException(LocalizedStrings.CqAttributesFactory_INITCQLISTENERS_PARAMETER_HAD_A_NULL_ELEMENT.toLocalizedString());
-        }
-        this.cqAttributes.cqListeners = new ArrayList(nl);
+    if (cqListeners == null || cqListeners.length == 0) {
+      this.cqAttributes.setCqListeners(null);
+    } else {
+      List nl = Arrays.asList(cqListeners);
+      if (nl.contains(null)) {
+        throw new IllegalArgumentException(LocalizedStrings.CqAttributesFactory_INITCQLISTENERS_PARAMETER_HAD_A_NULL_ELEMENT.toLocalizedString());
       }
+      this.cqAttributes.setCqListeners(new ArrayList(nl));
     }
   }
       
@@ -117,202 +103,6 @@ public class CqAttributesFactory {
   public CqAttributes create() {
     return (CqAttributes)this.cqAttributes.clone();
   }
-  
-  
-  protected static class CqAttributesImpl implements CqAttributes, CqAttributesMutator, Cloneable,
Serializable {
-    private static final long serialVersionUID = -959395592883099100L;
-    
-    ArrayList cqListeners = null;
-    
-    boolean dataPolicyHasBeenSet = false;
-    
-    private static final CqListener[] EMPTY_LISTENERS = new CqListener[0];
-    
-    /**
-     * Used to synchronize access to cqListeners
-     */
-    private final Object clSync = new Object();
-    
-    /**
-     * Returns the CqListeners set with the CQ
-     * @return CqListener[]
-     */
-    public CqListener[] getCqListeners() {
-      if (this.cqListeners == null){
-        return CqAttributesImpl.EMPTY_LISTENERS;
-      }
-      
-      CqListener[] result = null;
-      synchronized(this.clSync){   
-        result = new CqListener[cqListeners.size()];
-        cqListeners.toArray(result);
-      }
-      return result;
 
-    }
 
-    /**
-     * Returns the CqListener set with the CQ
-     * @return CqListener
-     */
-    public CqListener getCqListener() {
-      ArrayList listeners = this.cqListeners;
-      if (listeners == null) {
-        return null;
-      }
-      synchronized (this.clSync) {
-        if (listeners.size() == 0) {
-          return null;
-        }
-        if (listeners.size() == 1) {
-          return (CqListener)this.cqListeners.get(0);
-        }
-      }
-      throw new IllegalStateException(LocalizedStrings.CqAttributesFactory_MORE_THAN_ONE_CQLISTENER_EXISTS.toLocalizedString());
-    }
-    
-    @Override
-    public Object clone() {
-      try {
-        return super.clone();
-      }
-      catch (CloneNotSupportedException e) {
-        throw new InternalError(LocalizedStrings.CqAttributesFactory_CLONENOTSUPPORTEDEXCEPTION_THROWN_IN_CLASS_THAT_IMPLEMENTS_CLONEABLE.toLocalizedString());
-      }
-    }
-    
-    /**
-     * Adds a Cqlistener to the end of the list of Cqlisteners on this CqQuery.
-     * @param cql the user defined cq listener to add to the CqQuery.
-     * @throws IllegalArgumentException if <code>aListener</code> is null
-     */ 
-    public void addCqListener(CqListener cql)
-    {
-      if (cql == null) {
-        throw new IllegalArgumentException(LocalizedStrings.CqAttributesFactory_ADDCQLISTENER_PARAMETER_WAS_NULL.toLocalizedString());
-      }
-      synchronized (this.clSync) {
-        ArrayList oldListeners = this.cqListeners;
-        if (oldListeners == null || oldListeners.size() == 0) {
-          ArrayList al = new ArrayList(1);
-          al.add(cql);
-          this.cqListeners = al;
-        }
-        else {
-          if (!oldListeners.contains(cql)) {
-            oldListeners.add(cql);
-          }
-        }
-      }
-    }
-    
-    /**
-     * Removes all Cqlisteners, calling on each of them, and then adds each listener in the
specified array.
-     * @param addedListeners a possibly null or empty array of listeners to add to this CqQuery.
-     * @throws IllegalArgumentException if the <code>newListeners</code> array
has a null element
-     */ 
-    public void initCqListeners(CqListener[] addedListeners)
-    {
-      ArrayList oldListeners = null;
-      synchronized (this.clSync) {
-        oldListeners = this.cqListeners;
-        if (addedListeners == null || addedListeners.length == 0) {
-          this.cqListeners = null;
-        }
-        else { // we have some listeners to add
-          List nl = Arrays.asList(addedListeners);
-          if (nl.contains(null)) {
-            throw new IllegalArgumentException(LocalizedStrings.CqAttributesFactory_INITCQLISTENERS_PARAMETER_HAD_A_NULL_ELEMENT.toLocalizedString());
-          }
-          this.cqListeners = new ArrayList(nl);      
-        }
-      }
-      
-      if (oldListeners != null) {
-        CqListener cql = null;
-        for (Iterator iter = oldListeners.iterator(); iter.hasNext();) {
-          try {
-            cql = (CqListener)iter.next();
-            cql.close();
-            // Handle client side exceptions.
-          } catch (Exception ex) {
-            logger.warn(LocalizedMessage.create(LocalizedStrings.CqAttributesFactory_EXCEPTION_OCCURED_WHILE_CLOSING_CQ_LISTENER_ERROR_0,
ex.getLocalizedMessage()));
-            if (logger.isDebugEnabled()) {
-              logger.debug(ex.getMessage(), ex);
-            }
-          } 
-          catch (VirtualMachineError err) {
-            SystemFailure.initiateFailure(err);
-            // If this ever returns, rethrow the error.  We're poisoned
-            // now, so don't let this thread continue.
-            throw err;
-          }
-          catch (Throwable t) {
-            // Whenever you catch Error or Throwable, you must also
-            // catch VirtualMachineError (see above).  However, there is
-            // _still_ a possibility that you are dealing with a cascading
-            // error condition, so you also need to check to see if the JVM
-            // is still usable:
-            SystemFailure.checkFailure();
-            logger.warn(LocalizedMessage.create(LocalizedStrings.CqAttributesFactory_RUNTIME_EXCEPTION_OCCURED_WHILE_CLOSING_CQ_LISTENER_ERROR_0,
t.getLocalizedMessage()));
-            if (logger.isDebugEnabled()) {
-              logger.debug(t.getMessage(), t);
-            }
-          }        
-        }
-      }
-    }
-    
-    /**
-     * Removes a Cqlistener from the list of Cqlisteners on this CqQuery.
-     * Does nothing if the specified listener has not been added.
-     * If the specified listener has been added then {@link CacheCallback#close()} will
-     * be called on it; otherwise does nothing.
-     * @param cql the Cqlistener to remove from the CqQuery.
-     * @throws IllegalArgumentException if <code>cl</code> is null
-     */ 
-    public void removeCqListener(CqListener cql)
-    {
-      if (cql == null) {
-        throw new IllegalArgumentException(LocalizedStrings.CqAttributesFactory_REMOVECQLISTENER_PARAMETER_WAS_NULL.toLocalizedString());
-      }
-      synchronized (this.clSync) {
-        ArrayList oldListeners = this.cqListeners;
-        if (oldListeners != null) {
-          if (oldListeners.remove(cql)) {
-            if (oldListeners.isEmpty()) {
-              this.cqListeners = null;
-            }
-            try {
-              cql.close();
-              // Handle client side exceptions.
-            } catch (Exception ex) {
-              logger.warn(LocalizedMessage.create(LocalizedStrings.CqAttributesFactory_EXCEPTION_CLOSING_CQ_LISTENER_ERROR_0,
ex.getLocalizedMessage()));
-              if (logger.isDebugEnabled()) {
-                logger.debug(ex.getMessage(), ex);
-              }
-            } 
-            catch (VirtualMachineError err) {
-              SystemFailure.initiateFailure(err);
-              // If this ever returns, rethrow the error.  We're poisoned
-              // now, so don't let this thread continue.
-              throw err;
-            }
-            catch (Throwable t) {
-              // Whenever you catch Error or Throwable, you must also
-              // catch VirtualMachineError (see above).  However, there is
-              // _still_ a possibility that you are dealing with a cascading
-              // error condition, so you also need to check to see if the JVM
-              // is still usable:
-              SystemFailure.checkFailure();
-              logger.warn(LocalizedMessage.create(LocalizedStrings.CqAttributesFactory_RUNTIME_EXCEPTION_OCCURED_CLOSING_CQ_LISTENER_ERROR_0,
t.getLocalizedMessage()));
-              if (logger.isDebugEnabled()) {
-                logger.debug(t.getMessage(), t);
-              }
-              }        
-          }
-        }
-      }
-    }
-  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/083160ed/geode-core/src/main/java/com/gemstone/gemfire/cache/query/internal/cq/CqAttributesImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/com/gemstone/gemfire/cache/query/internal/cq/CqAttributesImpl.java
b/geode-core/src/main/java/com/gemstone/gemfire/cache/query/internal/cq/CqAttributesImpl.java
new file mode 100644
index 0000000..cab855e
--- /dev/null
+++ b/geode-core/src/main/java/com/gemstone/gemfire/cache/query/internal/cq/CqAttributesImpl.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gemstone.gemfire.cache.query.internal.cq;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import com.gemstone.gemfire.SystemFailure;
+import com.gemstone.gemfire.cache.CacheCallback;
+import com.gemstone.gemfire.cache.query.CqAttributes;
+import com.gemstone.gemfire.cache.query.CqAttributesMutator;
+import com.gemstone.gemfire.cache.query.CqListener;
+import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
+import com.gemstone.gemfire.internal.logging.LogService;
+import com.gemstone.gemfire.internal.logging.log4j.LocalizedMessage;
+
+import org.apache.logging.log4j.Logger;
+
+public class CqAttributesImpl implements CqAttributes, CqAttributesMutator, Cloneable {
+  private static final Logger logger = LogService.getLogger();
+
+  private volatile ArrayList<CqListener> cqListeners = null;
+
+  private static final CqListener[] EMPTY_LISTENERS = new CqListener[0];
+
+  /**
+   * Used to synchronize access to cqListeners
+   */
+  private final Object clSync = new Object();
+
+  /**
+   * Returns the CqListeners set with the CQ
+   * @return CqListener[]
+   */
+  public CqListener[] getCqListeners() {
+    final ArrayList listeners = this.cqListeners;
+    if (listeners == null){
+      return CqAttributesImpl.EMPTY_LISTENERS;
+    }
+
+    CqListener[] result = null;
+    synchronized(this.clSync){
+      result = new CqListener[listeners.size()];
+      listeners.toArray(result);
+    }
+    return result;
+
+  }
+
+  /**
+   * Returns the CqListener set with the CQ
+   * @return CqListener
+   */
+  public CqListener getCqListener() {
+    ArrayList<CqListener> listeners = this.cqListeners;
+    if (listeners == null) {
+      return null;
+    }
+    synchronized (this.clSync) {
+      if (listeners.size() == 0) {
+        return null;
+      }
+      if (listeners.size() == 1) {
+        return listeners.get(0);
+      }
+    }
+    throw new IllegalStateException(LocalizedStrings.CqAttributesFactory_MORE_THAN_ONE_CQLISTENER_EXISTS.toLocalizedString());
+  }
+
+  @Override
+  public Object clone() {
+    try {
+      return super.clone();
+    }
+    catch (CloneNotSupportedException e) {
+      throw new InternalError(LocalizedStrings.CqAttributesFactory_CLONENOTSUPPORTEDEXCEPTION_THROWN_IN_CLASS_THAT_IMPLEMENTS_CLONEABLE.toLocalizedString());
+    }
+  }
+
+  /**
+   * Adds a Cqlistener to the end of the list of Cqlisteners on this CqQuery.
+   * @param cql the user defined cq listener to add to the CqQuery.
+   * @throws IllegalArgumentException if <code>aListener</code> is null
+   */
+  public void addCqListener(CqListener cql)
+  {
+    if (cql == null) {
+      throw new IllegalArgumentException(LocalizedStrings.CqAttributesFactory_ADDCQLISTENER_PARAMETER_WAS_NULL.toLocalizedString());
+    }
+    synchronized (this.clSync) {
+      ArrayList<CqListener> oldListeners = this.cqListeners;
+      if (oldListeners == null || oldListeners.size() == 0) {
+        ArrayList<CqListener> al = new ArrayList<CqListener>(1);
+        al.add(cql);
+        this.setCqListeners(al);
+      }
+      else {
+        if (!oldListeners.contains(cql)) {
+          oldListeners.add(cql);
+        }
+      }
+    }
+  }
+
+  /**
+   * Removes all Cqlisteners, calling on each of them, and then adds each listener in the
specified array.
+   * @param addedListeners a possibly null or empty array of listeners to add to this CqQuery.
+   * @throws IllegalArgumentException if the <code>newListeners</code> array
has a null element
+   */
+  public void initCqListeners(CqListener[] addedListeners)
+  {
+    ArrayList<CqListener> oldListeners;
+    synchronized (this.clSync) {
+      oldListeners = this.cqListeners;
+      if (addedListeners == null || addedListeners.length == 0) {
+        this.setCqListeners(null);
+      }
+      else { // we have some listeners to add
+        List nl = Arrays.asList(addedListeners);
+        if (nl.contains(null)) {
+          throw new IllegalArgumentException(LocalizedStrings.CqAttributesFactory_INITCQLISTENERS_PARAMETER_HAD_A_NULL_ELEMENT.toLocalizedString());
+        }
+        this.setCqListeners(new ArrayList(nl));
+      }
+    }
+
+    if (oldListeners != null) {
+      CqListener cql = null;
+      for (Iterator<CqListener> iter = oldListeners.iterator(); iter.hasNext();) {
+        try {
+          cql = iter.next();
+          cql.close();
+          // Handle client side exceptions.
+        } catch (Exception ex) {
+          logger.warn(LocalizedMessage
+            .create(LocalizedStrings.CqAttributesFactory_EXCEPTION_OCCURED_WHILE_CLOSING_CQ_LISTENER_ERROR_0,
ex.getLocalizedMessage()));
+          if (logger.isDebugEnabled()) {
+            logger.debug(ex.getMessage(), ex);
+          }
+        }
+        catch (VirtualMachineError err) {
+          SystemFailure.initiateFailure(err);
+          // If this ever returns, rethrow the error.  We're poisoned
+          // now, so don't let this thread continue.
+          throw err;
+        }
+        catch (Throwable t) {
+          // Whenever you catch Error or Throwable, you must also
+          // catch VirtualMachineError (see above).  However, there is
+          // _still_ a possibility that you are dealing with a cascading
+          // error condition, so you also need to check to see if the JVM
+          // is still usable:
+          SystemFailure.checkFailure();
+          logger.warn(LocalizedMessage.create(LocalizedStrings.CqAttributesFactory_RUNTIME_EXCEPTION_OCCURED_WHILE_CLOSING_CQ_LISTENER_ERROR_0,
t.getLocalizedMessage()));
+          if (logger.isDebugEnabled()) {
+            logger.debug(t.getMessage(), t);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Removes a Cqlistener from the list of Cqlisteners on this CqQuery.
+   * Does nothing if the specified listener has not been added.
+   * If the specified listener has been added then {@link CacheCallback#close()} will
+   * be called on it; otherwise does nothing.
+   * @param cql the Cqlistener to remove from the CqQuery.
+   * @throws IllegalArgumentException if <code>cl</code> is null
+   */
+  public void removeCqListener(CqListener cql)
+  {
+    if (cql == null) {
+      throw new IllegalArgumentException(LocalizedStrings.CqAttributesFactory_REMOVECQLISTENER_PARAMETER_WAS_NULL.toLocalizedString());
+    }
+    synchronized (this.clSync) {
+      ArrayList<CqListener> oldListeners = this.cqListeners;
+      if (oldListeners != null) {
+        if (oldListeners.remove(cql)) {
+          if (oldListeners.isEmpty()) {
+            this.setCqListeners(null);
+          }
+          try {
+            cql.close();
+            // Handle client side exceptions.
+          } catch (Exception ex) {
+            logger.warn(LocalizedMessage.create(LocalizedStrings.CqAttributesFactory_EXCEPTION_CLOSING_CQ_LISTENER_ERROR_0,
ex.getLocalizedMessage()));
+            if (logger.isDebugEnabled()) {
+              logger.debug(ex.getMessage(), ex);
+            }
+          }
+          catch (VirtualMachineError err) {
+            SystemFailure.initiateFailure(err);
+            // If this ever returns, rethrow the error.  We're poisoned
+            // now, so don't let this thread continue.
+            throw err;
+          }
+          catch (Throwable t) {
+            // Whenever you catch Error or Throwable, you must also
+            // catch VirtualMachineError (see above).  However, there is
+            // _still_ a possibility that you are dealing with a cascading
+            // error condition, so you also need to check to see if the JVM
+            // is still usable:
+            SystemFailure.checkFailure();
+            logger.warn(LocalizedMessage.create(LocalizedStrings.CqAttributesFactory_RUNTIME_EXCEPTION_OCCURED_CLOSING_CQ_LISTENER_ERROR_0,
t.getLocalizedMessage()));
+            if (logger.isDebugEnabled()) {
+              logger.debug(t.getMessage(), t);
+            }
+            }
+        }
+      }
+    }
+  }
+
+  public void setCqListeners(ArrayList<CqListener> cqListeners) {
+    synchronized(this.clSync) {
+      this.cqListeners = cqListeners;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/083160ed/geode-core/src/test/java/com/gemstone/gemfire/cache/query/internal/cq/CqAttributesImplJUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/cache/query/internal/cq/CqAttributesImplJUnitTest.java
b/geode-core/src/test/java/com/gemstone/gemfire/cache/query/internal/cq/CqAttributesImplJUnitTest.java
new file mode 100644
index 0000000..7371bc7
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/cache/query/internal/cq/CqAttributesImplJUnitTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gemstone.gemfire.cache.query.internal.cq;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+
+import com.gemstone.gemfire.cache.query.CqAttributes;
+import com.gemstone.gemfire.cache.query.CqListener;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(UnitTest.class)
+public class CqAttributesImplJUnitTest {
+
+  @Test
+  public void getCQListenersReturnsEmptyListWhenNoCqListeners() {
+    CqAttributesImpl attributes = new CqAttributesImpl();
+    assertEquals(0, attributes.getCqListeners().length);
+  }
+
+  @Test
+  public void getCQListenersReturnsAddedCqListeners() {
+    CqAttributesImpl attributes = new CqAttributesImpl();
+    CqListener listener = mock(CqListener.class);
+    attributes.addCqListener(listener);
+    assertArrayEquals(new CqListener[] {listener} ,attributes.getCqListeners());
+  }
+}
\ No newline at end of file


Mime
View raw message