Author: [email protected]
Date: Mon Sep 19 14:35:42 2011
New Revision: 1398

Log:
[AMDATUCASSANDRA-70] Implemented OAuth nonce store, using expiring columns

Added:
   trunk/amdatu-cassandra/cassandra-store-nonce/
   trunk/amdatu-cassandra/cassandra-store-nonce/pom.xml
   trunk/amdatu-cassandra/cassandra-store-nonce/src/
   trunk/amdatu-cassandra/cassandra-store-nonce/src/main/
   trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/
   trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/
   trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/
   
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/
   
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/
   
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/nonce/
   
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/nonce/osgi/
   
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/nonce/osgi/Activator.java
   
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/nonce/service/
   
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/nonce/service/CassandraNonceStorageProviderImpl.java
   
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/nonce/service/NonceColumnFamilyProvider.java
   trunk/amdatu-cassandra/cassandra-store-nonce/src/test/
   trunk/amdatu-cassandra/cassandra-store-nonce/src/test/java/
   trunk/amdatu-cassandra/cassandra-store-nonce/src/test/java/org/
   trunk/amdatu-cassandra/cassandra-store-nonce/src/test/java/org/amdatu/
   
trunk/amdatu-cassandra/cassandra-store-nonce/src/test/java/org/amdatu/cassandra/
   
trunk/amdatu-cassandra/cassandra-store-nonce/src/test/java/org/amdatu/cassandra/test/
   
trunk/amdatu-cassandra/cassandra-store-nonce/src/test/java/org/amdatu/cassandra/test/unit/
   
trunk/amdatu-cassandra/cassandra-store-nonce/src/test/java/org/amdatu/cassandra/test/unit/CassandraNonceStoreTest.java
Modified:
   
trunk/amdatu-cassandra/cassandra-persistencemanager/src/main/java/org/amdatu/cassandra/persistencemanager/CassandraPersistenceManager.java
   
trunk/amdatu-cassandra/cassandra-persistencemanager/src/main/java/org/amdatu/cassandra/persistencemanager/service/HectorCassandraPersistenceManagerImpl.java
   trunk/amdatu-cassandra/pom.xml
   
trunk/amdatu-cassandra/test-unit/src/main/java/org/amdatu/cassandra/test/unit/framework/mock/CassandraPersistenceManagerMock.java
   
trunk/amdatu-cassandra/test-unit/src/main/java/org/amdatu/cassandra/test/unit/framework/mock/ColumnFamily.java

Modified: 
trunk/amdatu-cassandra/cassandra-persistencemanager/src/main/java/org/amdatu/cassandra/persistencemanager/CassandraPersistenceManager.java
==============================================================================
--- 
trunk/amdatu-cassandra/cassandra-persistencemanager/src/main/java/org/amdatu/cassandra/persistencemanager/CassandraPersistenceManager.java
  (original)
+++ 
trunk/amdatu-cassandra/cassandra-persistencemanager/src/main/java/org/amdatu/cassandra/persistencemanager/CassandraPersistenceManager.java
  Mon Sep 19 14:35:42 2011
@@ -173,6 +173,32 @@
      *        The value to set on the column
      */
     void setValue(String columnFamily, String rowKey, String superColumn, 
String column, Object value);
+    
+    /**
+     * Sets a single value on the specified column. The ColumnFamily may be 
either of type "Super" or
+     * of type "Standard". In case the ColumnFamily is of type "Super", the 
provided superColumn and column should both
+     * not be null. In case the ColumnFamily is of type "Standard", the 
superColumn should be null and the column should
+     * not be null. When illegal arguments are provided (as described above) 
or the specified ColumnFamily does not
+     * exist, an unchecked exception will be thrown.
+     * The provided value can be one of the following types: String, byte[], 
Double or double, java.util.Date,
+     * Long or long, int or Integer, java.util.UUID or java.lang.Object (must 
be Serializable though).
+     * 
+     * @param columnFamily
+     *        The name of the ColumnFamily to set the column for.
+     * @param rowKey
+     *        The key of the row to set the column for.
+     * @param superColumn
+     *        The name of the super column to set the column for (must be null 
in case the ColumnFamily is
+     *        of type "Standard").
+     * @param column
+     *        The name of the column to set the value.
+     * @param value
+     *        The value to set on the column
+     *  @param ttl
+     *         The time to live of the value, expressed in seconds. For 
example if ttl=60, the the value is removed
+     *         (expires) automatically after 60 seconds.
+     */
+    void setValue(String columnFamily, String rowKey, String superColumn, 
String column, Object value, int ttl);
 
     /**
      * Returns a single value from the specified column. The ColumnFamily may 
be either of type "Super" or

Modified: 
trunk/amdatu-cassandra/cassandra-persistencemanager/src/main/java/org/amdatu/cassandra/persistencemanager/service/HectorCassandraPersistenceManagerImpl.java
==============================================================================
--- 
trunk/amdatu-cassandra/cassandra-persistencemanager/src/main/java/org/amdatu/cassandra/persistencemanager/service/HectorCassandraPersistenceManagerImpl.java
        (original)
+++ 
trunk/amdatu-cassandra/cassandra-persistencemanager/src/main/java/org/amdatu/cassandra/persistencemanager/service/HectorCassandraPersistenceManagerImpl.java
        Mon Sep 19 14:35:42 2011
@@ -252,7 +252,11 @@
 
     public void setValue(final String columnFamily, final String rowKey, final 
String superColumn, final String column,
         final Object value) {
-        internalsetValue(columnFamily, rowKey, superColumn, column, value);
+        internalsetValue(columnFamily, rowKey, superColumn, column, value, 0);
+    }
+    
+    public void setValue(String columnFamily, String rowKey, String 
superColumn, String column, Object value, int ttl) {
+        internalsetValue(columnFamily, rowKey, superColumn, column, value, 
ttl);
     }
 
     public <T> T getValue(final String columnFamilyName, final String rowKey, 
final String superColumnName,
@@ -377,19 +381,25 @@
     }
 
     private <T extends Object> void internalsetValue(final String 
columnFamilyName, final String rowKey,
-        final String superColumnName, final String columnName, final T value) {
+        final String superColumnName, final String columnName, final T value, 
final int ttl) {
         StringSerializer stringSerializer = StringSerializer.get();
         Serializer<T> valueSerializer = getSerializer(value);
         Mutator<String> mutator = HFactory.createMutator(getHectorKeyspace(), 
stringSerializer);
         if (isSuper(columnFamilyName, superColumnName, columnName)) {
-            List<HColumn<String, T>> column =
-                toList(HFactory.createColumn(columnName, value, 
stringSerializer, valueSerializer));
-            mutator.insert(rowKey, columnFamilyName, 
HFactory.createSuperColumn(superColumnName, column,
+            HColumn<String, T> column = HFactory.createColumn(columnName, 
value, stringSerializer, valueSerializer);
+            if (ttl > 0) {
+                column.setTtl(ttl);
+            }
+            List<HColumn<String, T>> columnList = toList(column);
+            mutator.insert(rowKey, columnFamilyName, 
HFactory.createSuperColumn(superColumnName, columnList,
                 stringSerializer, stringSerializer, valueSerializer));
         }
         else {
-            mutator.insert(rowKey, columnFamilyName,
-                HFactory.createColumn(columnName, value, stringSerializer, 
valueSerializer));
+            HColumn<String, T> column = HFactory.createColumn(columnName, 
value, stringSerializer, valueSerializer);
+            if (ttl > 0) {
+                column.setTtl(ttl);
+            }
+            mutator.insert(rowKey, columnFamilyName, column);
         }
     }
 

Added: trunk/amdatu-cassandra/cassandra-store-nonce/pom.xml
==============================================================================
--- (empty file)
+++ trunk/amdatu-cassandra/cassandra-store-nonce/pom.xml        Mon Sep 19 
14:35:42 2011
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright (c) 2010, 2011 The Amdatu Foundation
+
+  Licensed 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.verning permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.amdatu.cassandra</groupId>
+    <artifactId>org.amdatu.cassandra</artifactId>
+    <version>0.2.1-SNAPSHOT</version>
+  </parent>
+  <artifactId>org.amdatu.cassandra.store.nonce</artifactId>
+  <packaging>bundle</packaging>
+  <name>Amdatu Cassandra - OAuth nonce Store</name>
+  <description>Provides an implementation of the OAuth nonce 
store</description>
+
+  <properties>
+    <amdatu.auth.version>0.2.0-RC1</amdatu.auth.version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.amdatu.cassandra</groupId>
+      <artifactId>org.amdatu.cassandra.listener</artifactId>
+      <type>bundle</type>
+    </dependency>
+    <dependency>
+      <groupId>org.amdatu.cassandra</groupId>
+      <artifactId>org.amdatu.cassandra.application</artifactId>
+      <type>bundle</type>
+    </dependency>
+    <dependency>
+      <groupId>org.amdatu.cassandra</groupId>
+      <artifactId>org.amdatu.cassandra.persistencemanager</artifactId>
+      <type>bundle</type>
+    </dependency>
+    <dependency>
+      <groupId>org.amdatu.core</groupId>
+      <artifactId>org.amdatu.core.tenant</artifactId>
+      <type>bundle</type>
+    </dependency>
+    <dependency>
+      <groupId>org.amdatu.auth</groupId>
+      <artifactId>org.amdatu.auth.oauth.api</artifactId>
+      <version>${amdatu.auth.version}</version>
+      <scope>provided</scope>
+      <type>bundle</type>
+    </dependency>
+    <dependency>
+      <groupId>org.amdatu.auth</groupId>
+      <artifactId>org.amdatu.auth.oauth.server</artifactId>
+      <version>${amdatu.auth.version}</version>
+      <scope>provided</scope>
+      <type>bundle</type>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-core-asl</artifactId>
+      <version>1.4.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-mapper-asl</artifactId>
+      <version>1.4.0</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.json</groupId>
+      <artifactId>json</artifactId>
+      <version>20090211</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.amdatu.cassandra</groupId>
+      <artifactId>org.amdatu.cassandra.test.unit</artifactId>
+      <type>jar</type>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-source-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            
<Bundle-Activator>org.amdatu.cassandra.store.nonce.osgi.Activator</Bundle-Activator>
+            
<Bundle-SymbolicName>org.amdatu.cassandra.store.nonce</Bundle-SymbolicName>
+            <Embed-Dependency>*;scope=compile</Embed-Dependency>
+            <Embed-Transitive>true</Embed-Transitive>
+            <Import-Package>
+              !org.joda.time,
+              org.amdatu.cassandra.application;version="[1.0,2.0)",
+              org.amdatu.cassandra.listener;version="[1.0,2.0)",
+              org.amdatu.cassandra.persistencemanager;version="[1.0,2.0)",
+              me.prettyprint.hector.api.beans;version="[1.0,2.0)",
+              *
+            </Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <!-- Profile that will copy the jar to the configured 
amdatu.deploy.directory when
+           the deploy profile is activated -->
+      <id>deploy</id>
+      <activation>
+        <activeByDefault>false</activeByDefault>
+      </activation>
+      <build>
+        <defaultGoal>install</defaultGoal>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-antrun-plugin</artifactId>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>

Added: 
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/nonce/osgi/Activator.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/nonce/osgi/Activator.java
     Mon Sep 19 14:35:42 2011
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2010, 2011 The Amdatu Foundation
+ *
+ * Licensed 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 org.amdatu.cassandra.store.nonce.osgi;
+
+import org.amdatu.auth.oauth.server.OAuthNonceStorageProvider;
+import org.amdatu.cassandra.listener.ColumnFamilyAvailable;
+import org.amdatu.cassandra.listener.ColumnFamilyProvider;
+import org.amdatu.cassandra.persistencemanager.CassandraPersistenceManager;
+import 
org.amdatu.cassandra.store.nonce.service.CassandraNonceStorageProviderImpl;
+import org.amdatu.cassandra.store.nonce.service.NonceColumnFamilyProvider;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This is the bundle activator for the Cassandra nonce store bundle.
+ */
+public class Activator extends DependencyActivatorBase {
+
+    public void init(final BundleContext context, final DependencyManager 
manager) {
+        // First define a service that provides the Service consumer 
ColumnFamily we need
+        manager.add(createComponent()
+            .setInterface(new String[]{ColumnFamilyProvider.class.getName()}, 
null)
+            .setImplementation(NonceColumnFamilyProvider.class));
+
+        // Create and register the Cassandra Service consumer storage provider
+        String keyspaceFilter = "(" + 
CassandraPersistenceManager.KEYSPACE_AWARE_KEY + "="
+        + CassandraPersistenceManager.DEFAULT_KEYSPACE + ")";
+        String nonceCFFilter =  "(&" + keyspaceFilter + "(" + 
ColumnFamilyAvailable.FILTER_NAME
+        + "=" + NonceColumnFamilyProvider.CF_NONCE + "))";
+        manager.add(
+            createComponent()
+            .setImplementation(CassandraNonceStorageProviderImpl.class)
+            .setInterface(OAuthNonceStorageProvider.class.getName(), null)
+            
.add(createServiceDependency().setService(CassandraPersistenceManager.class, 
keyspaceFilter).setRequired(true))
+            
.add(createServiceDependency().setService(ColumnFamilyAvailable.class, 
nonceCFFilter).setRequired(true)));
+    }
+
+    @Override
+    public void destroy(final BundleContext context, final DependencyManager 
manager) throws Exception {
+    }
+}

Added: 
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/nonce/service/CassandraNonceStorageProviderImpl.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/nonce/service/CassandraNonceStorageProviderImpl.java
  Mon Sep 19 14:35:42 2011
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2010, 2011 The Amdatu Foundation
+ *
+ * Licensed 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 org.amdatu.cassandra.store.nonce.service;
+
+import org.amdatu.auth.oauth.server.OAuthNonceStorageProvider;
+import org.amdatu.cassandra.persistencemanager.CassandraPersistenceManager;
+
+/**
+ * This class provides a Cassandra based implementation of a OAuth nonce store.
+ * The implementation is primarily focussed on performance. Nonces are created 
for each
+ * (OAuth protected) HTTP request so potentially, the nonce store may hold 
many nonces.
+ * 
+ * @author ivol
+ */
+public class CassandraNonceStorageProviderImpl implements 
OAuthNonceStorageProvider {
+    // NB: the time to live of nonces is up to the implementation of the Nonce 
store.
+    // We use time to live of 5 minutes
+    public static final int DEFAULT_NONCE_TTL = 5 * 60;
+
+    // Service dependencies, injected by the dependency manager
+    private volatile CassandraPersistenceManager m_pm;
+
+    // The time to live of nonces
+    private int m_ttl;
+    
+    // Column names
+    private static final String COLNAME = "n";
+
+    /**
+     * Default constructor.
+     */
+    public CassandraNonceStorageProviderImpl() {
+        m_ttl = DEFAULT_NONCE_TTL;
+    }
+
+    /**
+     * Constructor used by the Unit test.
+     * 
+     * @param pm
+     * @param logService
+     */
+    public CassandraNonceStorageProviderImpl(CassandraPersistenceManager pm, 
int ttl) {
+        m_pm = pm;
+        m_ttl = ttl;
+    }
+
+    public boolean addNonce(long timestamp, String nonce, String[] 
requestParams) {
+        String rowKey = getRowKey(timestamp, nonce, requestParams);
+        if (m_pm.getValue(NonceColumnFamilyProvider.CF_NONCE, rowKey, null, 
COLNAME, String.class) != null) {
+            return false;
+        }
+        m_pm.setValue(NonceColumnFamilyProvider.CF_NONCE, rowKey, null, 
COLNAME, "1", m_ttl);
+        return true;
+    }
+
+    // NB: this method has an empty implementation, since we handle nonce 
expiration in this
+    // Cassandra implementation in a different way, by using the "expiring 
columns" function.
+    public void removeExpiredNonces(long timestamp) {
+    }
+
+    // NB: there may be special cases in which two different 
timestamp/nonce/requestParams
+    // combinations are mapped to the same row key, but this store is 
primarily focused on
+    // performance rather then a 100% guarantee of unique nonces. In this very 
rare case
+    // a hacker could perform a replay attack with this specific request, but 
what would be
+    // the odds?
+    private String getRowKey(long timestamp, String nonce, String[] 
requestParams) {
+        String rowkey = nonce + "&";
+        if (requestParams != null) {
+            for (String param : requestParams) {
+                rowkey += param + "&";
+            }
+        }
+        rowkey += timestamp;
+        return rowkey;
+    }
+}

Added: 
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/nonce/service/NonceColumnFamilyProvider.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-cassandra/cassandra-store-nonce/src/main/java/org/amdatu/cassandra/store/nonce/service/NonceColumnFamilyProvider.java
  Mon Sep 19 14:35:42 2011
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010, 2011 The Amdatu Foundation
+ *
+ * Licensed 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 org.amdatu.cassandra.store.nonce.service;
+
+import org.amdatu.cassandra.listener.ColumnFamilyDefinition;
+import org.amdatu.cassandra.listener.ColumnFamilyDefinition.ColumnType;
+import org.amdatu.cassandra.listener.ColumnFamilyDefinition.CompareType;
+import org.amdatu.cassandra.listener.ColumnFamilyProvider;
+import org.amdatu.cassandra.persistencemanager.CassandraPersistenceManager;
+
+public class NonceColumnFamilyProvider implements ColumnFamilyProvider {
+    /**
+     * ColumnFamily that stores the nonces.
+     */
+    public static final String CF_NONCE = "OAuthNonce";
+
+    @Override
+    public ColumnFamilyDefinition[] getColumnFamilies() {
+        return new ColumnFamilyDefinition[] {
+            new ColumnFamilyDefinition(
+                CF_NONCE,
+                    new String[] 
{CassandraPersistenceManager.DEFAULT_KEYSPACE},
+                    ColumnType.STANDARD,
+                    CompareType.BYTESTYPE,
+                    CompareType.BYTESTYPE)};
+    }
+
+}

Added: 
trunk/amdatu-cassandra/cassandra-store-nonce/src/test/java/org/amdatu/cassandra/test/unit/CassandraNonceStoreTest.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-cassandra/cassandra-store-nonce/src/test/java/org/amdatu/cassandra/test/unit/CassandraNonceStoreTest.java
      Mon Sep 19 14:35:42 2011
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2010, 2011 The Amdatu Foundation
+ * 
+ * Licensed 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 org.amdatu.cassandra.test.unit;
+
+import junit.framework.Assert;
+
+import org.amdatu.auth.oauth.api.ConsumerAlreadyExistsException;
+import org.amdatu.auth.oauth.api.ConsumerNotFoundException;
+import org.amdatu.auth.oauth.api.ConsumerRegistryStorageException;
+import org.amdatu.cassandra.listener.ColumnFamilyDefinition;
+import 
org.amdatu.cassandra.store.nonce.service.CassandraNonceStorageProviderImpl;
+import org.amdatu.cassandra.store.nonce.service.NonceColumnFamilyProvider;
+import 
org.amdatu.cassandra.test.unit.framework.mock.CassandraPersistenceManagerMock;
+import org.amdatu.cassandra.test.unit.framework.mock.LogServiceMock;
+import org.junit.Test;
+
+public class CassandraNonceStoreTest {
+    @Test
+    public void test() throws ConsumerRegistryStorageException, 
ConsumerAlreadyExistsException,
+    ConsumerNotFoundException {
+        // Create the mock objects
+        LogServiceMock logService = new LogServiceMock();
+        CassandraPersistenceManagerMock<String, String, Object> pm = new 
CassandraPersistenceManagerMock<String, String, Object>("Default");
+        ColumnFamilyDefinition[] colDefs = new 
NonceColumnFamilyProvider().getColumnFamilies();
+        pm.addColumnFamily(colDefs);
+
+        // And the store under test itself, define a TTL of 5 seconds
+        int ttl = 1;
+        CassandraNonceStorageProviderImpl store = new 
CassandraNonceStorageProviderImpl(pm, ttl);
+
+        String nonce = "A";
+        long timestamp1 = 10000; // second 10
+        long timestamp2 = 10001;
+        long timestamp3 = 10002;
+        long timestamp4 = 20000;
+        long timestamp5 = 2435436;
+        
+        // Validate that the same nonce can be added only once
+        Assert.assertTrue(store.addNonce(timestamp1, nonce, null));
+        Assert.assertFalse(store.addNonce(timestamp1, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp2, nonce, null));
+        Assert.assertFalse(store.addNonce(timestamp2, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp3, nonce, new 
String[]{"arg1"}));
+        Assert.assertFalse(store.addNonce(timestamp3, nonce, new 
String[]{"arg1"}));
+        Assert.assertTrue(store.addNonce(timestamp4, nonce, new 
String[]{"arg2"}));
+        Assert.assertFalse(store.addNonce(timestamp4, nonce, new 
String[]{"arg2"}));
+        Assert.assertTrue(store.addNonce(timestamp5, nonce, new 
String[]{"arg1", "arg2"}));
+        Assert.assertFalse(store.addNonce(timestamp5, nonce, new 
String[]{"arg1", "arg2"}));
+
+        // Now wait for 5 seconds and verify that the nonces have been removed 
(TTL is 5 seconds)
+        try {
+            Thread.sleep(ttl*1000);
+        }
+        catch (InterruptedException e) {
+        }
+        Assert.assertTrue(store.addNonce(timestamp1, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp2, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp3, nonce, new 
String[]{"arg1"}));
+        Assert.assertTrue(store.addNonce(timestamp4, nonce, new 
String[]{"arg2"}));
+        Assert.assertTrue(store.addNonce(timestamp5, nonce, new 
String[]{"arg1", "arg2"}));
+        
+     
+    }
+}

Modified: trunk/amdatu-cassandra/pom.xml
==============================================================================
--- trunk/amdatu-cassandra/pom.xml      (original)
+++ trunk/amdatu-cassandra/pom.xml      Mon Sep 19 14:35:42 2011
@@ -274,6 +274,7 @@
     <module>cassandra-persistencemanager</module>
     <module>cassandra-store-consumer</module>
     <module>cassandra-store-gadget</module>
+    <module>cassandra-store-nonce</module>
     <module>cassandra-store-token</module>
     <module>cassandra-store-tenant</module>
     <module>cassandra-store-useradmin</module>

Modified: 
trunk/amdatu-cassandra/test-unit/src/main/java/org/amdatu/cassandra/test/unit/framework/mock/CassandraPersistenceManagerMock.java
==============================================================================
--- 
trunk/amdatu-cassandra/test-unit/src/main/java/org/amdatu/cassandra/test/unit/framework/mock/CassandraPersistenceManagerMock.java
   (original)
+++ 
trunk/amdatu-cassandra/test-unit/src/main/java/org/amdatu/cassandra/test/unit/framework/mock/CassandraPersistenceManagerMock.java
   Mon Sep 19 14:35:42 2011
@@ -163,6 +163,10 @@
 
     @SuppressWarnings("unchecked")
     public void setValue(String columnFamily, String rowKey, String 
superColumn, String column, Object value) {
+        setValue(columnFamily, rowKey, superColumn, column, value, 0);
+    }
+
+    public void setValue(String columnFamily, String rowKey, String 
superColumn, String column, Object value, int ttl) {
         if (!exists(columnFamily)) {
             throw new HInvalidRequestException("ColumnFamily '" + columnFamily 
+ "' does not exist");
         }
@@ -187,6 +191,9 @@
             else {
                 hColumn.setValue((V) value);
             }
+            if (ttl > 0) {
+                HectorUtil.getColumn(hSuperColumn, column).setTtl(ttl);
+            }
         }
         else {
             // Column case
@@ -203,6 +210,9 @@
             else {
                 col.setValue((V) value);
             }
+            if (ttl > 0) {
+                row.getColumn(column).setTtl(ttl);
+            }
         }
     }
 

Modified: 
trunk/amdatu-cassandra/test-unit/src/main/java/org/amdatu/cassandra/test/unit/framework/mock/ColumnFamily.java
==============================================================================
--- 
trunk/amdatu-cassandra/test-unit/src/main/java/org/amdatu/cassandra/test/unit/framework/mock/ColumnFamily.java
      (original)
+++ 
trunk/amdatu-cassandra/test-unit/src/main/java/org/amdatu/cassandra/test/unit/framework/mock/ColumnFamily.java
      Mon Sep 19 14:35:42 2011
@@ -182,7 +182,7 @@
         if (m_type == ColumnType.STANDARD) {
             for (RowImpl<SN, N, V> row : m_rows) {
                 if (row.getKey().equals(rowKey)) {
-                    return row.getColumns();
+                    return filterExpiredColumns(row.getColumns());
                 }
             }
             return null;
@@ -192,4 +192,14 @@
                 "Cannot retrieve columns from a ColumnFamily of type super, 
retrieve super columns instead");
         }
     }
+    
+    private List<HColumn<N, V>> filterExpiredColumns(List<HColumn<N, V>> 
columns) {
+        List<HColumn<N, V>> filteredColumns = new ArrayList<HColumn<N, V>>();
+        for (HColumn<N, V> column : columns) {
+            if (column.getTtl() == 0 || column.getClock() + 
1000*column.getTtl() > System.currentTimeMillis()) {
+                filteredColumns.add(column);
+            }
+        }
+        return filteredColumns;
+    }
 }
_______________________________________________
Amdatu-commits mailing list
[email protected]
http://lists.amdatu.org/mailman/listinfo/amdatu-commits

Reply via email to