You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by to...@apache.org on 2013/11/27 23:24:42 UTC
[1/3] Initial import of 2.0 core persistence code.
Updated Branches:
refs/heads/two-dot-o [created] ac634a1d0
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftest.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftest.java
new file mode 100644
index 0000000..7a71815
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftest.java
@@ -0,0 +1,54 @@
+/*
+ * 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 org.apache.usergrid.perftest;
+
+import org.apache.usergrid.perftest.logging.Log;
+import org.slf4j.Logger;
+
+/**
+ * A performance test that does nothing.
+ */
+public class NoopPerftest implements Perftest {
+ @Log Logger log;
+
+
+ @Override
+ public int getCallCount() {
+ return 1000;
+ }
+
+
+ @Override
+ public int getThreadCount() {
+ return 10;
+ }
+
+
+ @Override
+ public int getDelayBetweenCalls() {
+ return 0;
+ }
+
+
+ @Override
+ public void call() {
+ log.info( "This performance test tests nothing" );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftestModule.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftestModule.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftestModule.java
new file mode 100644
index 0000000..1dbe087
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftestModule.java
@@ -0,0 +1,34 @@
+/*
+ * 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 org.apache.usergrid.perftest;
+
+
+import com.google.inject.AbstractModule;
+
+
+/**
+ * A performance test module that does nothing.
+ */
+public class NoopPerftestModule extends AbstractModule {
+ @Override
+ protected void configure() {
+ bind( Perftest.class ).to( NoopPerftest.class );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/Perftest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/Perftest.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/Perftest.java
new file mode 100644
index 0000000..14388e3
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/Perftest.java
@@ -0,0 +1,11 @@
+package org.apache.usergrid.perftest;
+
+/**
+ * A performance test that will be run.
+ */
+public interface Perftest {
+ int getCallCount();
+ int getThreadCount();
+ int getDelayBetweenCalls();
+ void call();
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestModule.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestModule.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestModule.java
new file mode 100644
index 0000000..9f8581b
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestModule.java
@@ -0,0 +1,47 @@
+/*
+ * Created by IntelliJ IDEA.
+ * User: akarasulu
+ * Date: 11/22/13
+ * Time: 11:44 PM
+ */
+package org.apache.usergrid.perftest;
+
+import org.apache.usergrid.perftest.logging.Slf4jTypeListener;
+import org.apache.usergrid.perftest.rest.*;
+import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+import com.google.inject.matcher.Matchers;
+import com.google.inject.servlet.ServletModule;
+import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
+import org.apache.usergrid.perfteststats.CallStats;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class PerftestModule extends ServletModule {
+ public static final String PACKAGES_KEY = "com.sun.jersey.config.property.packages";
+
+
+ protected void configureServlets() {
+ bindListener( Matchers.any(), new Slf4jTypeListener() );
+
+ // Hook Jersey into Guice Servlet
+ bind( GuiceContainer.class );
+
+ // Hook Jackson into Jersey as the POJO <-> JSON mapper
+ bind( JacksonJsonProvider.class ).asEagerSingleton();
+
+ bind( CallStats.class );
+ bind( PerftestRunner.class );
+ bind( TestModuleLoader.class );
+ bind( PerftestResetResource.class ).asEagerSingleton();
+ bind( PerftestStopResource.class ).asEagerSingleton();
+ bind( PerftestStartResource.class ).asEagerSingleton();
+ bind( PerftestStatsResource.class ).asEagerSingleton();
+ bind( PerftestStatusResource.class ).asEagerSingleton();
+
+ Map<String, String> params = new HashMap<String, String>();
+ params.put( PACKAGES_KEY, getClass().getPackage().toString() );
+ serve("/*").with( GuiceContainer.class, params );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestRunner.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestRunner.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestRunner.java
new file mode 100644
index 0000000..a8bd831
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestRunner.java
@@ -0,0 +1,197 @@
+package org.apache.usergrid.perftest;
+
+
+import com.netflix.blitz4j.LoggingConfiguration;
+import org.apache.usergrid.perftest.logging.Log;
+import org.apache.usergrid.perftest.rest.CallStatsSnapshot;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Singleton;
+import com.netflix.config.DynamicLongProperty;
+import com.netflix.config.DynamicPropertyFactory;
+import org.apache.usergrid.perfteststats.CallStats;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Invokes a Perftest based on a CallSpec.
+ */
+@Singleton
+public class PerftestRunner {
+ @Log
+ Logger log;
+
+
+ private final TestModuleLoader loader;
+ private final Injector injector;
+ private final Object lock = new Object();
+ private DynamicLongProperty sleepToStop = DynamicPropertyFactory.getInstance().getLongProperty( "sleep.to.stop", 100 );
+ private CallStats stats;
+ private List<Thread> threads = new ArrayList<Thread>();
+ private boolean stopSignal = false;
+ private boolean running = false;
+ private boolean needsReset = false;
+ private long startTime;
+ private long stopTime;
+
+
+ @Inject
+ public PerftestRunner( Injector injector, TestModuleLoader loader )
+ {
+ this.loader = loader;
+ this.injector = injector;
+ setup();
+ }
+
+
+ public void setup() {
+ synchronized ( lock ) {
+ this.threads.clear();
+ this.stopSignal = false;
+ this.running = false;
+ this.startTime = 0;
+ this.stopTime = 0;
+
+ this.stats = injector.getInstance( CallStats.class );
+ final Perftest test = loader.getChildInjector().getInstance(Perftest.class);
+
+ final long delay = test.getDelayBetweenCalls();
+ threads = new ArrayList<Thread>( test.getThreadCount() );
+ for ( int ii = 0; ii < test.getThreadCount(); ii++ ) {
+ threads.add( new Thread( new Runnable() {
+ @Override
+ public void run() {
+ while( ( ! stopSignal ) && ( stats.getCallCount() < test.getCallCount() ) ) {
+ long startTime = System.nanoTime();
+ test.call();
+ long endTime = System.nanoTime();
+ stats.callOccurred( test, startTime, endTime, TimeUnit.NANOSECONDS );
+
+ if ( delay > 0 )
+ {
+ try {
+ Thread.sleep( delay );
+ } catch ( InterruptedException e ) {
+ log.error( "Thread was interrupted.", e );
+ }
+ }
+
+ synchronized ( lock ) {
+ lock.notifyAll();
+ }
+ }
+ }
+ }) );
+ }
+
+ this.needsReset = false;
+ }
+ }
+
+
+ public CallStatsSnapshot getCallStatsSnapshot() {
+ return stats.getStatsSnapshot( isRunning(), getStartTime(), getStopTime() );
+ }
+
+
+ public boolean isRunning() {
+ synchronized ( lock ) {
+ return running;
+ }
+ }
+
+
+ public boolean needsReset() {
+ synchronized ( lock )
+ {
+ return needsReset;
+ }
+ }
+
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+
+ public long getStopTime() {
+ return stopTime;
+ }
+
+
+ public void start() {
+ synchronized ( lock ) {
+ stopSignal = false;
+ startTime = System.nanoTime();
+ running = true;
+ for ( Thread t : threads ) {
+ t.start();
+ }
+ }
+
+ // launch a coordinator thread to detect when all others are done
+ new Thread( new Runnable() {
+ @Override
+ public void run() {
+ while ( threadsRunning() )
+ {
+ synchronized ( lock )
+ {
+ try {
+ lock.wait( sleepToStop.get() );
+ log.info( "woke up running = {}", PerftestRunner.this.running );
+ lock.notifyAll();
+ } catch (InterruptedException e) {
+ log.error( "Thread interrupted while sleeping", e );
+ }
+ }
+ }
+
+ log.info( "COORDINATOR THREAD: all threads have died." );
+ PerftestRunner.this.running = false;
+ PerftestRunner.this.needsReset = true;
+ stopTime = System.nanoTime();
+ }
+ } ).start();
+ }
+
+
+ private boolean threadsRunning()
+ {
+ boolean anyAlive = false;
+
+ try {
+ Thread.sleep( sleepToStop.get() );
+ }
+ catch ( InterruptedException e ) {
+ log.error( "Thread was interrupted.", e );
+ }
+
+ for ( Thread t : threads )
+ {
+ anyAlive |= t.isAlive();
+ }
+
+ return anyAlive;
+ }
+
+
+ public void stop() {
+ synchronized ( lock ) {
+ stopSignal = true;
+ boolean anyAlive = false;
+
+ do {
+ anyAlive |= threadsRunning();
+ } while( anyAlive );
+
+ running = false;
+ stopTime = System.nanoTime();
+ needsReset = true;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestServletConfig.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestServletConfig.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestServletConfig.java
new file mode 100644
index 0000000..7f664a9
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestServletConfig.java
@@ -0,0 +1,50 @@
+/*
+ * 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 org.apache.usergrid.perftest;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.servlet.GuiceServletContextListener;
+import com.netflix.blitz4j.LoggingConfiguration;
+
+import javax.servlet.ServletContextEvent;
+
+/**
+ * ...
+ */
+public class PerftestServletConfig extends GuiceServletContextListener {
+ @Override
+ protected Injector getInjector() {
+ return Guice.createInjector( new PerftestModule() );
+ }
+
+
+ @Override
+ public void contextInitialized( ServletContextEvent servletContextEvent ) {
+ LoggingConfiguration.getInstance().configure();
+ super.contextInitialized( servletContextEvent );
+ }
+
+ @Override
+ public void contextDestroyed( ServletContextEvent servletContextEvent ) {
+ LoggingConfiguration.getInstance().stop();
+ super.contextDestroyed( servletContextEvent );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/TestModuleLoader.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/TestModuleLoader.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/TestModuleLoader.java
new file mode 100644
index 0000000..45c7ebd
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/TestModuleLoader.java
@@ -0,0 +1,95 @@
+/*
+ * 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 org.apache.usergrid.perftest;
+
+
+import com.google.inject.*;
+import com.netflix.config.DynamicPropertyFactory;
+import com.netflix.config.DynamicStringProperty;
+
+
+/**
+ * Dynamically loads the Guice Module responsible for creating the Perftest.
+ */
+@Singleton
+public class TestModuleLoader implements Runnable {
+ public static final String MOCK_TEST_MODULE = "org.apache.usergrid.perftest.NoopPerftestModule";
+
+ private final Injector injector;
+ private Injector childInjector;
+ private DynamicStringProperty testModuleFqcn;
+ private Module testModule;
+
+
+ @Inject
+ public TestModuleLoader( Injector injector )
+ {
+ this.injector = injector;
+ testModuleFqcn = DynamicPropertyFactory.getInstance().getStringProperty( "test.module.fqcn", MOCK_TEST_MODULE );
+
+ if ( testModuleFqcn.get().equals( MOCK_TEST_MODULE ) ) {
+ testModule = new NoopPerftestModule();
+ }
+ else {
+ testModule = loadTestModule();
+ }
+
+ childInjector = injector.createChildInjector( testModule );
+ testModuleFqcn.addCallback( this );
+ }
+
+
+ public Module loadTestModule() {
+ // This is a crappy mechanism now - we need to use OSGi for this
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+ try {
+ Class clazz = cl.loadClass( testModuleFqcn.get() );
+ return ( Module ) clazz.newInstance();
+ } catch ( ClassNotFoundException e ) {
+ e.printStackTrace();
+ } catch ( InstantiationException e ) {
+ e.printStackTrace();
+ } catch ( IllegalAccessException e ) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+
+ public Module getTestModule()
+ {
+ return testModule;
+ }
+
+
+ public Injector getChildInjector()
+ {
+ return childInjector;
+ }
+
+
+ @Override
+ public void run() {
+ testModule = loadTestModule();
+ childInjector = injector.createChildInjector( testModule );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Log.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Log.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Log.java
new file mode 100644
index 0000000..32ddfee
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Log.java
@@ -0,0 +1,19 @@
+package org.apache.usergrid.perftest.logging;
+
+import javax.inject.Scope;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation to use to inject a SLF4J Logger backed by Log4j.
+ */
+@Scope
+@Documented
+@Retention( RUNTIME )
+@Target( FIELD )
+public @interface Log {
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jMembersInjector.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jMembersInjector.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jMembersInjector.java
new file mode 100644
index 0000000..7ccbe7f
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jMembersInjector.java
@@ -0,0 +1,46 @@
+/*
+ * 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 org.apache.usergrid.perftest.logging;
+
+
+import com.google.inject.MembersInjector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Field;
+
+public class Slf4jMembersInjector<T> implements MembersInjector<T> {
+ private final Field field;
+ private final Logger logger;
+
+ public Slf4jMembersInjector(Field field) {
+ this.field = field;
+ this.logger = LoggerFactory.getLogger(field.getDeclaringClass());
+ field.setAccessible(true);
+ }
+
+ public void injectMembers(T t) {
+ try {
+ field.set(t, logger);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jTypeListener.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jTypeListener.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jTypeListener.java
new file mode 100644
index 0000000..7c43bd9
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jTypeListener.java
@@ -0,0 +1,44 @@
+/*
+ * 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 org.apache.usergrid.perftest.logging;
+
+import com.google.inject.TypeLiteral;
+import com.google.inject.spi.TypeEncounter;
+import com.google.inject.spi.TypeListener;
+import org.slf4j.Logger;
+
+import java.lang.reflect.Field;
+
+/**
+ * ...
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class Slf4jTypeListener implements TypeListener {
+ public <T> void hear(TypeLiteral<T> typeLiteral, TypeEncounter<T> typeEncounter) {
+ for (Field field : typeLiteral.getRawType().getDeclaredFields()) {
+ if (field.getType() == Logger.class
+ && field.isAnnotationPresent(Log.class)) {
+ typeEncounter.register(new Slf4jMembersInjector<T>(field));
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/CallStatsSnapshot.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/CallStatsSnapshot.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/CallStatsSnapshot.java
new file mode 100644
index 0000000..f1fdd78
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/CallStatsSnapshot.java
@@ -0,0 +1,91 @@
+/*
+ * 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 org.apache.usergrid.perftest.rest;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * ...
+ */
+public class CallStatsSnapshot {
+ private final int callCount;
+ private final long maxTime;
+ private final long minTime;
+ private final long meanTime;
+ private final boolean running;
+ private final long startTime;
+ private final long stopTime;
+
+
+ public CallStatsSnapshot( int callCount, long maxTime, long minTime, long meanTime,
+ boolean running, long startTime, long stopTime ) {
+ this.callCount = callCount;
+ this.maxTime = maxTime;
+ this.minTime = minTime;
+ this.meanTime = meanTime;
+ this.running = running;
+ this.startTime = startTime;
+ this.stopTime = stopTime;
+ }
+
+
+ @JsonProperty
+ public int getCallCount() {
+ return callCount;
+ }
+
+
+ @JsonProperty
+ public long getMaxTime() {
+ return maxTime;
+ }
+
+
+ @JsonProperty
+ public long getMinTime() {
+ return minTime;
+ }
+
+
+ @JsonProperty
+ public long getMeanTime() {
+ return meanTime;
+ }
+
+
+ @JsonProperty
+ public boolean isRunning() {
+ return running;
+ }
+
+
+ @JsonProperty
+ public long getStartTime()
+ {
+ return startTime;
+ }
+
+
+ @JsonProperty
+ public long getStopTime()
+ {
+ return stopTime;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestResetResource.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestResetResource.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestResetResource.java
new file mode 100644
index 0000000..402945b
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestResetResource.java
@@ -0,0 +1,64 @@
+/*
+ * 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 org.apache.usergrid.perftest.rest;
+
+import org.apache.usergrid.perftest.PerftestRunner;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * ...
+ */
+@Singleton
+@Produces( MediaType.APPLICATION_JSON )
+@Path( "/perftest/reset" )
+public class PerftestResetResource {
+ private final PerftestRunner runner;
+
+
+ @Inject
+ public PerftestResetResource( PerftestRunner runner )
+ {
+ this.runner = runner;
+ }
+
+
+ @POST
+ public String reset()
+ {
+ if ( runner.isRunning() )
+ {
+ return "{ \"result\":\"still running stop before resetting\" }";
+ }
+
+ if ( runner.needsReset() )
+ {
+ runner.setup();
+ return "{ \"result\":\"reset complete\" }";
+ }
+
+ return "{ \"result\":\"reset not required\" }";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStartResource.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStartResource.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStartResource.java
new file mode 100644
index 0000000..0fb5705
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStartResource.java
@@ -0,0 +1,64 @@
+/*
+ * 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 org.apache.usergrid.perftest.rest;
+
+import org.apache.usergrid.perftest.PerftestRunner;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * ...
+ */
+@Singleton
+@Produces( MediaType.APPLICATION_JSON )
+@Path( "/perftest/start" )
+public class PerftestStartResource {
+ private final PerftestRunner runner;
+
+
+ @Inject
+ public PerftestStartResource( PerftestRunner runner )
+ {
+ this.runner = runner;
+ }
+
+
+ @POST
+ public String start()
+ {
+ if ( runner.isRunning() )
+ {
+ return "{ \"result\":\"already running\" }";
+ }
+
+ if ( runner.needsReset() )
+ {
+ return "{ \"result\":\"reset needed - but save the last run data first!\" }";
+ }
+
+ runner.start();
+ return "{ \"result\":\"successfully started\" }";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatsResource.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatsResource.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatsResource.java
new file mode 100644
index 0000000..93c2f20
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatsResource.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.apache.usergrid.perftest.rest;
+
+import org.apache.usergrid.perftest.PerftestRunner;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * ...
+ */
+@Singleton
+@Produces( MediaType.APPLICATION_JSON )
+@Path( "/perftest/perfteststats" )
+public class PerftestStatsResource {
+ private final PerftestRunner runner;
+
+
+ @Inject
+ public PerftestStatsResource( PerftestRunner runner )
+ {
+ this.runner = runner;
+ }
+
+
+ @GET
+ public CallStatsSnapshot getCallStatsSnapshot()
+ {
+ return runner.getCallStatsSnapshot();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatusResource.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatusResource.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatusResource.java
new file mode 100644
index 0000000..b146661
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatusResource.java
@@ -0,0 +1,62 @@
+/*
+ * 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 org.apache.usergrid.perftest.rest;
+
+import org.apache.usergrid.perftest.PerftestRunner;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * ...
+ */
+@Singleton
+@Produces( MediaType.APPLICATION_JSON )
+@Path( "/perftest/status" )
+public class PerftestStatusResource {
+ private final PerftestRunner runner;
+
+
+ @Inject
+ public PerftestStatusResource( PerftestRunner runner ) {
+ this.runner = runner;
+ }
+
+
+ @GET
+ public String status()
+ {
+ if ( runner.isRunning() )
+ {
+ return "{ \"status\":\"running\" }";
+ }
+
+ if ( runner.needsReset() )
+ {
+ return "{ \"status\":\"needs reset\" }";
+ }
+
+ return "{ \"status\":\"not running\" }";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStopResource.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStopResource.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStopResource.java
new file mode 100644
index 0000000..36d3590
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStopResource.java
@@ -0,0 +1,59 @@
+/*
+ * 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 org.apache.usergrid.perftest.rest;
+
+import org.apache.usergrid.perftest.PerftestRunner;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * ...
+ */
+@Singleton
+@Produces( MediaType.APPLICATION_JSON )
+@Path( "/perftest/stop" )
+public class PerftestStopResource {
+ private final PerftestRunner runner;
+
+
+ @Inject
+ public PerftestStopResource( PerftestRunner runner )
+ {
+ this.runner = runner;
+ }
+
+
+ @POST
+ public String stop()
+ {
+ if ( runner.isRunning() )
+ {
+ runner.stop();
+ return "{ \"result\":\"stopped\" }";
+ }
+
+ return "{ \"result\":\"already stopped\" }";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perfteststats/CallStats.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perfteststats/CallStats.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perfteststats/CallStats.java
new file mode 100644
index 0000000..e0a09a0
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perfteststats/CallStats.java
@@ -0,0 +1,90 @@
+/*
+ * 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 org.apache.usergrid.perfteststats;
+
+
+import org.apache.usergrid.perftest.Perftest;
+import org.apache.usergrid.perftest.logging.Log;
+import org.apache.usergrid.perftest.rest.CallStatsSnapshot;
+import org.slf4j.Logger;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+/**
+ * Atomically stores and updates call statistics on tests.
+ */
+public class CallStats {
+ @Log Logger log;
+
+ private final AtomicInteger callCount = new AtomicInteger();
+ private final Object lock = new Object();
+ private final TimeUnit units = TimeUnit.NANOSECONDS;
+
+ private long maxTime = Long.MIN_VALUE;
+ private long minTime = Long.MAX_VALUE;
+ private long meanTime = 0;
+ private long totalTime = 0;
+
+
+ public int getCallCount() {
+ synchronized ( lock ) {
+ return callCount.get();
+ }
+ }
+
+
+ public CallStatsSnapshot getStatsSnapshot( boolean isRunning, long startTime, long stopTime ) {
+ synchronized ( lock )
+ {
+ return new CallStatsSnapshot( callCount.get(), maxTime, minTime, meanTime, isRunning, startTime, stopTime );
+ }
+ }
+
+
+ public int callOccurred( Perftest test, long startTime, long endTime, TimeUnit units )
+ {
+ synchronized ( lock )
+ {
+ if ( callCount.get() > test.getCallCount() - 1 )
+ {
+ return callCount.get();
+ }
+
+ if ( this.units.equals( units ) ) {
+ long time = endTime - startTime;
+
+ totalTime += time;
+ maxTime = Math.max( maxTime, time );
+ minTime = Math.min( minTime, time );
+ int numCalls = callCount.incrementAndGet();
+ StringBuilder sb = new StringBuilder();
+ sb.append( numCalls ).append( " " ).append( startTime ).append( " " ).append( endTime );
+ log.debug(sb.toString());
+ meanTime = totalTime / numCalls;
+ return numCalls;
+ }
+ else {
+ throw new RuntimeException( "Time unit corrections have not been implemented." );
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/resources/config.properties
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/resources/config.properties b/stack/corepersistence/perftest/src/main/resources/config.properties
new file mode 100644
index 0000000..f411991
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/resources/config.properties
@@ -0,0 +1,22 @@
+default.call.iterations=10000
+default.thread.count=10
+default.configuration.override=simpledb
+
+archaius.deployment.environment=test
+archaius.deployment.region=us-east-1c
+archaius.deployment.datacenter=Virginia
+archaius.deployment.applicationId=corepersistence-perftests
+
+archaius.dynamicPropertyFactory.registerConfigWithJMX=true
+
+#com.netflix.config.dynamo.tableName=archaiusProperties
+#com.netflix.config.dynamo.keyAttributeName=key
+#com.netflix.config.dynamo.valueAttributeName=value
+#com.netflix.config.dynamo.contextKeyAttributeName=contextKey
+#com.netflix.config.dynamo.contextValueAttributeName=contextValue
+
+# Application Settings
+
+test.module.fqcn=org.apache.usergrid.perftest.NoopPerftestModule
+leep.to.stop=100
+
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/resources/log4j.properties b/stack/corepersistence/perftest/src/main/resources/log4j.properties
new file mode 100644
index 0000000..a114a72
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/resources/log4j.properties
@@ -0,0 +1,20 @@
+log4j.rootLogger=OFF
+log4j.rootCategory=OFF
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p %c{3}.%M(%L)<%t>- %m%n
+
+log4j.appender.results=org.apache.log4j.FileAppender
+log4j.appender.results.File=./results.log
+log4j.appender.results.layout=com.netflix.logging.log4jAdapter.NFPatternLayout
+log4j.appender.results.Append=true
+log4j.appender.results.layout.ConversionPattern=%m%n
+log4j.appender.results.Threshold=DEBUG
+
+log4j.logger.asyncAppenders=OFF,results,stdout
+batcher.com.netflix.logging.AsyncAppender.results.waitTimeinMillis=20000
+
+log4j.logger.org.apache.usergrid.perftest=INFO,stdout
+log4j.logger.org.apache.usergrid.perfteststats=DEBUG,results
+
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/webapp/WEB-INF/web.xml b/stack/corepersistence/perftest/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..310e6a3
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+ <display-name>Guice Servlet</display-name>
+
+ <session-config>
+ <session-timeout>30</session-timeout>
+ </session-config>
+
+ <listener>
+ <listener-class>
+ org.apache.usergrid.perftest.PerftestServletConfig
+ </listener-class>
+ </listener>
+
+ <filter>
+ <filter-name>Guice Filter</filter-name>
+ <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
+ </filter>
+
+ <filter-mapping>
+ <filter-name>Guice Filter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+</web-app>
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/pom.xml b/stack/corepersistence/pom.xml
new file mode 100644
index 0000000..f09e9b0
--- /dev/null
+++ b/stack/corepersistence/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.usergrid</groupId>
+ <artifactId>persistence</artifactId>
+ <packaging>pom</packaging>
+ <version>1.0-SNAPSHOT</version>
+
+ <description>Prorotype for refactoring persistence of usergrid</description>
+
+ <properties >
+ <astynax.version>1.56.44</astynax.version>
+ </properties>
+
+ <modules>
+ <module>model</module>
+ <module>collection</module>
+ <module>index</module>
+ <module>testutils</module>
+ <!-- Removed perfest until the jackson/guice is sorted
+ <module>perftest</module> -->
+
+ </modules>
+
+
+
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/testutils/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/testutils/pom.xml b/stack/corepersistence/testutils/pom.xml
new file mode 100644
index 0000000..f863cf5
--- /dev/null
+++ b/stack/corepersistence/testutils/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>persistence</artifactId>
+ <groupId>org.apache.usergrid</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>testutils</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.11</version>
+ </dependency>
+
+ <!-- snappy for cassandra -->
+ <dependency>
+ <groupId>org.xerial.snappy</groupId>
+ <artifactId>snappy-java</artifactId>
+ <version>1.1.0.1</version>
+ </dependency>
+
+ <!-- cassandra runtime -->
+ <dependency>
+ <groupId>org.apache.cassandra</groupId>
+ <artifactId>cassandra-all</artifactId>
+ <version>1.2.11</version>
+ </dependency>
+
+
+ <dependency>
+ <groupId>com.netflix.astyanax</groupId>
+ <artifactId>astyanax-cassandra</artifactId>
+ <version>${astynax.version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/AvailablePortFinder.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/AvailablePortFinder.java b/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/AvailablePortFinder.java
new file mode 100644
index 0000000..e2da676
--- /dev/null
+++ b/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/AvailablePortFinder.java
@@ -0,0 +1,187 @@
+/*
+ * 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 org.apache.usergrid.persistence.test;
+
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.ServerSocket;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.TreeSet;
+
+
+/**
+ * Finds currently available server ports.
+ *
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * @see <a href="http://www.iana.org/assignments/port-numbers">IANA.org</a>
+ */
+public class AvailablePortFinder {
+ /** The minimum number of server port number. */
+ public static final int MIN_PORT_NUMBER = 1;
+
+ /** The maximum number of server port number. */
+ public static final int MAX_PORT_NUMBER = 49151;
+
+
+ /** Creates a new instance. */
+ private AvailablePortFinder() {
+ // Do nothing
+ }
+
+
+ /**
+ * Returns the {@link java.util.Set} of currently available port numbers ({@link Integer}). This method is identical to
+ * <code>getAvailablePorts(MIN_PORT_NUMBER, MAX_PORT_NUMBER)</code>.
+ * <p/>
+ * WARNING: this can take a very long time.
+ */
+ public static Set<Integer> getAvailablePorts() {
+ return getAvailablePorts( MIN_PORT_NUMBER, MAX_PORT_NUMBER );
+ }
+
+
+ /**
+ * Gets an available port, selected by the system.
+ *
+ * @throws java.util.NoSuchElementException if there are no ports available
+ */
+ public static int getNextAvailable() {
+ ServerSocket serverSocket = null;
+
+ try {
+ // Here, we simply return an available port found by the system
+ serverSocket = new ServerSocket( 0 );
+ int port = serverSocket.getLocalPort();
+
+ // Don't forget to close the socket...
+ serverSocket.close();
+
+ return port;
+ }
+ catch ( IOException ioe ) {
+ throw new NoSuchElementException( ioe.getMessage() );
+ }
+ }
+
+
+ /**
+ * Gets the next available port starting at a port.
+ *
+ * @param fromPort the port to scan for availability
+ *
+ * @throws java.util.NoSuchElementException if there are no ports available
+ */
+ public static int getNextAvailable( int fromPort ) {
+ if ( fromPort < MIN_PORT_NUMBER || fromPort > MAX_PORT_NUMBER ) {
+ throw new IllegalArgumentException( "Invalid start port: " + fromPort );
+ }
+
+ for ( int i = fromPort; i <= MAX_PORT_NUMBER; i++ ) {
+ if ( available( i ) ) {
+ return i;
+ }
+ }
+
+ throw new NoSuchElementException( "Could not find an available port " + "above " + fromPort );
+ }
+
+
+ /**
+ * Checks to see if a specific port is available.
+ *
+ * @param port the port to check for availability
+ */
+ public static boolean available( int port ) {
+ if ( port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER ) {
+ throw new IllegalArgumentException( "Invalid start port: " + port );
+ }
+
+ ServerSocket ss = null;
+ DatagramSocket ds = null;
+
+ try {
+ ss = new ServerSocket( port );
+ ss.setReuseAddress( true );
+ ds = new DatagramSocket( port );
+ ds.setReuseAddress( true );
+ return true;
+ }
+ catch ( IOException e ) {
+ // Do nothing
+ }
+ finally {
+ if ( ds != null ) {
+ ds.close();
+ }
+
+ if ( ss != null ) {
+ try {
+ ss.close();
+ }
+ catch ( IOException e ) {
+ /* should not be thrown */
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Returns the {@link java.util.Set} of currently avaliable port numbers ({@link Integer}) between the specified port range.
+ *
+ * @throws IllegalArgumentException if port range is not between {@link #MIN_PORT_NUMBER} and {@link
+ * #MAX_PORT_NUMBER} or <code>fromPort</code> if greater than <code>toPort</code>.
+ */
+ public static Set<Integer> getAvailablePorts( int fromPort, int toPort ) {
+ if ( fromPort < MIN_PORT_NUMBER || toPort > MAX_PORT_NUMBER || fromPort > toPort ) {
+ throw new IllegalArgumentException( "Invalid port range: " + fromPort + " ~ " + toPort );
+ }
+
+ Set<Integer> result = new TreeSet<Integer>();
+
+ for ( int i = fromPort; i <= toPort; i++ ) {
+ ServerSocket s = null;
+
+ try {
+ s = new ServerSocket( i );
+ result.add( new Integer( i ) );
+ }
+ catch ( IOException e ) {
+ // Do nothing
+ }
+ finally {
+ if ( s != null ) {
+ try {
+ s.close();
+ }
+ catch ( IOException e ) {
+ /* should not be thrown */
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/CassandraRule.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/CassandraRule.java b/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/CassandraRule.java
new file mode 100644
index 0000000..989c00c
--- /dev/null
+++ b/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/CassandraRule.java
@@ -0,0 +1,86 @@
+package org.apache.usergrid.persistence.test;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.rules.ExternalResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.io.util.FileUtils;
+
+import com.google.common.io.Files;
+import com.netflix.astyanax.test.EmbeddedCassandra;
+
+
+/**
+ * TODO, not sure if this is a hack,
+ *
+ * @author tnine
+ */
+public class CassandraRule extends ExternalResource {
+
+ private static final Logger logger = LoggerFactory.getLogger( CassandraRule.class );
+
+ public static final int THRIFT_PORT = AvailablePortFinder.getNextAvailable();
+
+ public static final int GOSSIP_PORT = AvailablePortFinder.getNextAvailable();
+
+
+ private static final Object mutex = new Object();
+
+ private static EmbeddedCassandra cass;
+
+ private static boolean started = false;
+
+ @Override
+ protected void before() throws Throwable {
+
+ if ( started ) {
+ return;
+ }
+
+ synchronized ( mutex ) {
+
+ //we're late to the party, bail
+ if ( started ) {
+ return;
+ }
+
+
+ File dataDir = Files.createTempDir();
+ dataDir.deleteOnExit();
+
+
+
+ //cleanup before we run, shouldn't be necessary, but had the directory exist during JVM kill
+ if(dataDir.exists()){
+ FileUtils.deleteRecursive(dataDir);
+ }
+
+ try {
+ logger.info( "Starting cassandra" );
+
+ cass = new EmbeddedCassandra( dataDir, "Usergrid", THRIFT_PORT, GOSSIP_PORT );
+ cass.start();
+
+ logger.info( "Cassandra started" );
+
+ started = true;
+ }
+ catch ( IOException e ) {
+ throw new RuntimeException( "Unable to start cassandra", e );
+ }
+ }
+ }
+
+
+ @Override
+ protected void after() {
+
+ //TODO TN. this should only really happen when we shut down
+// cass.stop();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/testutils/src/main/resources/log4j-server.properties
----------------------------------------------------------------------
diff --git a/stack/corepersistence/testutils/src/main/resources/log4j-server.properties b/stack/corepersistence/testutils/src/main/resources/log4j-server.properties
new file mode 100644
index 0000000..cf22bca
--- /dev/null
+++ b/stack/corepersistence/testutils/src/main/resources/log4j-server.properties
@@ -0,0 +1,35 @@
+# 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.
+
+# for production, you should probably set pattern to %c instead of %l.
+# (%l is slower.)
+
+# output messages into a rolling log file as well as stdout
+log4j.rootLogger=INFO,stdout
+
+# stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%5p %d{HH:mm:ss,SSS} %m%n
+
+
+# Application logging options
+#log4j.logger.org.apache.cassandra=DEBUG
+#log4j.logger.org.apache.cassandra.db=DEBUG
+#log4j.logger.org.apache.cassandra.service.StorageProxy=DEBUG
+
+# Adding this to avoid thrift logging disconnect errors.
+log4j.logger.org.apache.thrift.server.TNonblockingServer=ERROR
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/testutils/src/main/resources/log4j.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/testutils/src/main/resources/log4j.xml b/stack/corepersistence/testutils/src/main/resources/log4j.xml
new file mode 100644
index 0000000..2db8103
--- /dev/null
+++ b/stack/corepersistence/testutils/src/main/resources/log4j.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <appender class="org.apache.log4j.ConsoleAppender" name="stdout">
+ <layout class="org.apache.log4j.PatternLayout">
+ <param value="%d %p (%t) [%c] - %m%n" name="ConversionPattern"/>
+ </layout>
+ </appender>
+ <logger name="org.apache">
+ <level value="info"/>
+ </logger>
+ <root>
+ <level value="debug"/>
+ <appender-ref ref="stdout"/>
+ </root>
+</log4j:configuration>
[2/3] Initial import of 2.0 core persistence code.
Posted by to...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImpl.java
new file mode 100644
index 0000000..f6217a2
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImpl.java
@@ -0,0 +1,219 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.cassandra.db.marshal.ReversedType;
+import org.apache.cassandra.db.marshal.UUIDType;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.collection.migration.CollectionColumnFamily;
+import org.apache.usergrid.persistence.collection.migration.Migration;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccLogEntry;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccLogEntryImpl;
+import org.apache.usergrid.persistence.collection.mvcc.entity.Stage;
+
+import com.google.common.base.Preconditions;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+import com.netflix.astyanax.ColumnListMutation;
+import com.netflix.astyanax.Keyspace;
+import com.netflix.astyanax.MutationBatch;
+import com.netflix.astyanax.connectionpool.OperationResult;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+import com.netflix.astyanax.connectionpool.exceptions.NotFoundException;
+import com.netflix.astyanax.model.Column;
+import com.netflix.astyanax.model.ColumnFamily;
+import com.netflix.astyanax.serializers.UUIDSerializer;
+
+
+/**
+ * Simple implementation for reading and writing log entries
+ *
+ * @author tnine
+ */
+@Singleton
+public class MvccLogEntrySerializationStrategyImpl implements MvccLogEntrySerializationStrategy, Migration {
+
+ public static final String TIMEOUT_PROP = "collection.stage.transient.timeout";
+
+ private static final ColumnFamily<UUID, UUID> CF_ENTITY_LOG =
+ new ColumnFamily<UUID, UUID>( "Entity_Log", UUIDSerializer.get(), UUIDSerializer.get() );
+
+ /**
+ * Used for caching the byte => stage mapping
+ */
+ private static final StageCache CACHE = new StageCache();
+
+
+ protected final Keyspace keyspace;
+ protected final int timeout;
+
+
+ @Inject
+ public MvccLogEntrySerializationStrategyImpl( final Keyspace keyspace, @Named( TIMEOUT_PROP ) final int timeout ) {
+ this.keyspace = keyspace;
+ this.timeout = timeout;
+ }
+
+
+ @Override
+ public MutationBatch write( final MvccLogEntry entry ) {
+
+ Preconditions.checkNotNull( entry, "entry is required" );
+
+
+ final Stage stage = entry.getStage();
+ final UUID colName = entry.getVersion();
+ final byte colValue = stage.getId();
+
+ return doWrite( entry.getContext(), entry.getEntityId(), new RowOp() {
+ @Override
+ public void doOp( final ColumnListMutation<UUID> colMutation ) {
+
+ //Write the stage with a timeout, it's set as transient
+ if ( stage.isTransient() ) {
+ colMutation.putColumn( colName, colValue, timeout );
+ return;
+ }
+
+ //otherwise it's persistent, write it with no expiration
+ colMutation.putColumn( colName, colValue );
+ }
+ } );
+ }
+
+
+ @Override
+ public MvccLogEntry load( final CollectionContext context, final UUID entityId, final UUID version )
+ throws ConnectionException {
+ Preconditions.checkNotNull( context, "context is required" );
+ Preconditions.checkNotNull( entityId, "entity id is required" );
+ Preconditions.checkNotNull( version, "version is required" );
+
+
+ Column<UUID> result = null;
+
+ try {
+ OperationResult<Column<UUID>>
+ foo = keyspace.prepareQuery( CF_ENTITY_LOG ).getKey( entityId ).getColumn( version ).execute();
+
+ result = foo.getResult();
+ }
+ catch ( NotFoundException nfe ) {
+ return null;
+ }
+
+ if ( result == null ) {
+ return null;
+ }
+
+ final byte stored = result.getByteValue();
+
+
+ final Stage stage = CACHE.getStage( stored );
+
+ Preconditions.checkNotNull( "No stage was found for byte value " + stored + ". This is a migration data bug" );
+
+ return new MvccLogEntryImpl( context, entityId, version, stage );
+ }
+
+
+ @Override
+ public List<MvccLogEntry> load( final CollectionContext context, final UUID entityId, final UUID version,
+ final int maxSize ) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+
+ @Override
+ public MutationBatch delete( final CollectionContext context, final UUID entityId, final UUID version ) {
+
+ Preconditions.checkNotNull( context, "context is required" );
+ Preconditions.checkNotNull( entityId, "entityId is required" );
+ Preconditions.checkNotNull( version, "version context is required" );
+
+ return doWrite( context, entityId, new RowOp() {
+ @Override
+ public void doOp( final ColumnListMutation<UUID> colMutation ) {
+ colMutation.deleteColumn( version );
+ }
+ } );
+ }
+
+
+ @Override
+ public Collection<CollectionColumnFamily> getColumnFamilies() {
+ //create the CF entity data. We want it reversed b/c we want the most recent version at the top of the
+ //row for fast seeks
+ CollectionColumnFamily cf = new CollectionColumnFamily( CF_ENTITY_LOG,
+ ReversedType.class.getName() + "(" + UUIDType.class.getName() + ")", true );
+
+
+ return Collections.singleton( cf );
+ }
+
+
+ /**
+ * Simple callback to perform puts and deletes with a common row setup code
+ */
+ private static interface RowOp {
+
+ /**
+ * The operation to perform on the row
+ */
+ void doOp( ColumnListMutation<UUID> colMutation );
+ }
+
+
+ /**
+ * Do the column update or delete for the given column and row key
+ *
+ * @param context We need to use this when getting the keyspace
+ */
+ private MutationBatch doWrite( CollectionContext context, UUID entityId, RowOp op ) {
+
+ final MutationBatch batch = keyspace.prepareMutationBatch();
+
+ op.doOp( batch.withRow( CF_ENTITY_LOG, entityId ) );
+
+ return batch;
+ }
+
+
+ /**
+ * Internal stage cache
+ */
+ private static class StageCache {
+ private Map<Byte, Stage> values = new HashMap<Byte, Stage>( Stage.values().length );
+
+
+ private StageCache() {
+ for ( Stage stage : Stage.values() ) {
+
+ final byte stageValue = stage.getId();
+
+ if ( values.containsKey( stageValue ) ) {
+ throw new RuntimeException(
+ "There are two Stages assigned to the byte " + stageValue + ". This is a bug" );
+ }
+
+ values.put( stageValue, stage );
+ }
+ }
+
+
+ /**
+ * Get the stage with the byte value
+ */
+ private Stage getStage( final byte value ) {
+ return values.get( value );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryTest.java b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryTest.java
new file mode 100644
index 0000000..4450efe
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryTest.java
@@ -0,0 +1,8 @@
+package org.apache.usergrid.persistence.collection;
+
+
+/**
+ * Basic tests
+ * @author tnine
+ */
+public class CollectionManagerFactoryTest {}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/guice/TestCollectionModule.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/guice/TestCollectionModule.java b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/guice/TestCollectionModule.java
new file mode 100644
index 0000000..3ea6e62
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/guice/TestCollectionModule.java
@@ -0,0 +1,103 @@
+package org.apache.usergrid.persistence.collection.guice;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.cassandra.locator.SimpleStrategy;
+
+import org.apache.usergrid.persistence.collection.astynax.AstynaxKeyspaceProvider;
+import org.apache.usergrid.persistence.collection.migration.MigrationException;
+import org.apache.usergrid.persistence.collection.migration.MigrationManager;
+import org.apache.usergrid.persistence.collection.migration.MigrationManagerImpl;
+import org.apache.usergrid.persistence.collection.serialization.MvccLogEntrySerializationStrategyImpl;
+import org.apache.usergrid.persistence.test.CassandraRule;
+
+import com.google.guiceberry.GuiceBerryEnvMain;
+import com.google.guiceberry.GuiceBerryModule;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.name.Names;
+
+
+/**
+ * Simple module for wiring our collection api
+ *
+ * @author tnine
+ */
+public class TestCollectionModule extends AbstractModule {
+
+
+ public TestCollectionModule() {
+ }
+
+
+ @Override
+ protected void configure() {
+
+
+ //import the guice berry module
+ install( new GuiceBerryModule() );
+
+ //now configure our db
+ bind( GuiceBerryEnvMain.class ).to( CassAppMain.class );
+
+ //import the runtime module
+ install( new CollectionModule() );
+
+
+ //configure our integration test properties. This should remain the same across all tests
+
+ Map<String, String> configProperties = new HashMap<String, String>();
+ configProperties.put( AstynaxKeyspaceProvider.CASSANDRA_HOSTS, "localhost" );
+ configProperties.put( AstynaxKeyspaceProvider.CASSANDRA_PORT, "" + CassandraRule.THRIFT_PORT );
+ configProperties.put( AstynaxKeyspaceProvider.CASSANDRA_CONNECTIONS, "10" );
+ configProperties.put( AstynaxKeyspaceProvider.CASSANDRA_CLUSTER_NAME, "Usergrid" );
+ configProperties.put( AstynaxKeyspaceProvider.CASSANDRA_VERSION, "1.2" );
+ configProperties.put( AstynaxKeyspaceProvider.COLLECTIONS_KEYSPACE_NAME, "Usergrid_Collections" );
+
+ configProperties.put( MigrationManagerImpl.REPLICATION_FACTOR, "1" );
+ configProperties.put( MigrationManagerImpl.STRATEGY_CLASS, SimpleStrategy.class.getName() );
+
+ /**
+ * Set the timeout to 60 seconds, no test should take that long for load+delete without a failure
+ */
+ configProperties.put( MvccLogEntrySerializationStrategyImpl.TIMEOUT_PROP, 60+"" );
+
+ Map<String, String> props = getOverrides();
+
+ if(props != null){
+ configProperties.putAll( props );
+ }
+
+ //bind to the props
+ Names.bindProperties( binder(), configProperties );
+ }
+
+
+ /**
+ * Get any overrides we need for system properties
+ */
+ public Map<String, String> getOverrides() {
+ return null;
+ }
+
+
+ static class CassAppMain implements GuiceBerryEnvMain {
+
+ @Inject
+ protected MigrationManager migrationManager;
+
+
+ public void run() {
+ try {
+ //run the injected migration manager to set up cassandra
+ migrationManager.migrate();
+ }
+ catch ( MigrationException e ) {
+ throw new RuntimeException( e );
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/mvcc/entity/StageTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/mvcc/entity/StageTest.java b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/mvcc/entity/StageTest.java
new file mode 100644
index 0000000..0090e64
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/mvcc/entity/StageTest.java
@@ -0,0 +1,91 @@
+package org.apache.usergrid.persistence.collection.mvcc.entity;
+
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author tnine
+ */
+public class StageTest {
+
+ @Test
+ public void active() {
+
+ assertTrue( Stage.ACTIVE.isTransient() );
+
+ assertEquals( ( byte ) 0, Stage.ACTIVE.getId() );
+
+ testUnique( Stage.ACTIVE );
+ }
+
+
+ @Test
+ public void rollback() {
+
+ assertTrue( Stage.ROLLBACK.isTransient() );
+
+ assertEquals( ( byte ) 1, Stage.ROLLBACK.getId() );
+
+ testUnique( Stage.ROLLBACK );
+ }
+
+
+ @Test
+ public void comitted() {
+
+ assertFalse( Stage.COMMITTED.isTransient() );
+
+ assertEquals( ( byte ) 2, Stage.COMMITTED.getId() );
+
+ testUnique( Stage.COMMITTED );
+ }
+
+
+
+
+ @Test
+ public void postProcess() {
+
+ assertFalse( Stage.POSTPROCESS.isTransient() );
+
+ assertEquals( ( byte ) 6, Stage.POSTPROCESS.getId() );
+
+ testUnique( Stage.POSTPROCESS );
+ }
+
+
+ @Test
+ public void complete() {
+
+ assertFalse( Stage.COMPLETE.isTransient() );
+
+ assertEquals( ( byte ) 14, Stage.COMPLETE.getId() );
+
+ testUnique( Stage.COMPLETE );
+ }
+
+
+ /**
+ * Test we don't have dups in the byte value
+ * @param test
+ */
+ private void testUnique( Stage test ) {
+
+ for ( Stage stage : Stage.values() ) {
+
+ //skip self
+ if ( stage == test ) {
+ continue;
+ }
+
+ assertFalse( stage.getId() == test.getId() );
+ }
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImplTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImplTest.java b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImplTest.java
new file mode 100644
index 0000000..a3bcead
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImplTest.java
@@ -0,0 +1,343 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.cassandra.db.marshal.UUIDType;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.collection.CollectionContextImpl;
+import org.apache.usergrid.persistence.collection.guice.TestCollectionModule;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntityImpl;
+import org.apache.usergrid.persistence.model.entity.Entity;
+import org.apache.usergrid.persistence.model.field.BooleanField;
+import org.apache.usergrid.persistence.model.field.DoubleField;
+import org.apache.usergrid.persistence.model.field.Field;
+import org.apache.usergrid.persistence.model.field.IntegerField;
+import org.apache.usergrid.persistence.model.field.LongField;
+import org.apache.usergrid.persistence.model.field.StringField;
+import org.apache.usergrid.persistence.model.field.UUIDField;
+import org.apache.usergrid.persistence.model.util.UUIDGenerator;
+import org.apache.usergrid.persistence.test.CassandraRule;
+
+import com.google.common.base.Optional;
+import com.google.guiceberry.junit4.GuiceBerryRule;
+import com.google.inject.Inject;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+import com.netflix.astyanax.serializers.UUIDSerializer;
+import com.netflix.astyanax.util.TimeUUIDUtils;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNull;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author tnine
+ */
+public class MvccEntitySerializationStrategyImplTest {
+
+ @Rule
+ public final GuiceBerryRule guiceBerry = new GuiceBerryRule( TestCollectionModule.class );
+
+ @Rule
+ public final CassandraRule rule = new CassandraRule();
+
+ @Inject
+ private MvccEntitySerializationStrategy serializationStrategy;
+
+
+ @Test
+ public void writeLoadDelete() throws ConnectionException {
+
+ final UUID applicationId = UUIDGenerator.newTimeUUID();
+ final String name = "test";
+
+ CollectionContext context = new CollectionContextImpl( applicationId, applicationId, name );
+
+
+ final UUID entityId = UUIDGenerator.newTimeUUID();
+ final UUID version = UUIDGenerator.newTimeUUID();
+ final String type = "test";
+ final long created = 1l;
+ final long updated = 2l;
+
+ Entity entity = new Entity( entityId, type );
+
+ entity.setVersion( version );
+ entity.setCreated( created );
+ entity.setUpdated( updated );
+
+
+ BooleanField boolField = new BooleanField( "boolean", false );
+ DoubleField doubleField = new DoubleField( "double", 1d );
+ IntegerField intField = new IntegerField( "long", 1 );
+ LongField longField = new LongField( "int", 1l );
+ StringField stringField = new StringField( "name", "test" );
+ UUIDField uuidField = new UUIDField( "uuid", UUIDGenerator.newTimeUUID() );
+
+ entity.setField( boolField );
+ entity.setField( doubleField );
+ entity.setField( intField );
+ entity.setField( longField );
+ entity.setField( stringField );
+ entity.setField( uuidField );
+
+
+ MvccEntity saved = new MvccEntityImpl( context, entityId, version, Optional.of( entity ) );
+
+
+ //persist the entity
+ serializationStrategy.write( saved ).execute();
+
+ //now read it back
+
+ MvccEntity returned = serializationStrategy.load( context, entityId, version );
+
+ assertEquals( "Mvcc entities are the same", saved, returned );
+
+
+ assertEquals( entityId, entity.getUuid() );
+ assertEquals( type, entity.getType() );
+ assertEquals( created, entity.getCreated() );
+ assertEquals( updated, entity.getUpdated() );
+
+
+ Field<Boolean> boolFieldReturned = entity.getField( boolField.getName() );
+
+ assertSame( boolField, boolFieldReturned );
+
+ Field<Double> doubleFieldReturned = entity.getField( doubleField.getName() );
+
+ assertSame( doubleField, doubleFieldReturned );
+
+ Field<Integer> intFieldReturned = entity.getField( intField.getName() );
+
+ assertSame( intField, intFieldReturned );
+
+ Field<Long> longFieldReturned = entity.getField( longField.getName() );
+
+ assertSame( longField, longFieldReturned );
+
+ Field<String> stringFieldReturned = entity.getField( stringField.getName() );
+
+ assertSame( stringField, stringFieldReturned );
+
+ Field<UUID> uuidFieldReturned = entity.getField( uuidField.getName() );
+
+ assertSame( uuidField, uuidFieldReturned );
+
+
+ Set<Field> results = new HashSet<Field>();
+ results.addAll( entity.getFields() );
+
+
+ assertTrue( results.contains( boolField ) );
+ assertTrue( results.contains( doubleField ) );
+ assertTrue( results.contains( intField ) );
+ assertTrue( results.contains( longField ) );
+ assertTrue( results.contains( stringField ) );
+ assertTrue( results.contains( uuidField ) );
+
+ assertEquals( 6, results.size() );
+
+
+ assertEquals( entityId, entity.getUuid() );
+ assertEquals( version, entity.getVersion() );
+
+
+ //now delete it
+ serializationStrategy.delete( context, entityId, version ).execute();
+
+ //now get it, should be gone
+
+ returned = serializationStrategy.load( context, entityId, version );
+
+ assertNull( returned );
+ }
+
+
+ @Test
+ public void writeLoadClearDelete() throws ConnectionException {
+
+ final UUID applicationId = UUIDGenerator.newTimeUUID();
+ final String name = "test";
+
+ CollectionContext context = new CollectionContextImpl( applicationId, applicationId, name );
+
+
+ final UUID entityId = UUIDGenerator.newTimeUUID();
+ final UUID version = UUIDGenerator.newTimeUUID();
+ final String type = "test";
+ final long created = 1l;
+ final long updated = 2l;
+
+ Entity entity = new Entity( entityId, type );
+
+ entity.setVersion( version );
+ entity.setCreated( created );
+ entity.setUpdated( updated );
+
+
+ MvccEntity saved = new MvccEntityImpl( context, entityId, version, Optional.of( entity ) );
+
+
+ //persist the entity
+ serializationStrategy.write( saved ).execute();
+
+ //now read it back
+
+ MvccEntity returned = serializationStrategy.load( context, entityId, version );
+
+ assertEquals( "Mvcc entities are the same", saved, returned );
+
+
+ assertEquals( entityId, entity.getUuid() );
+ assertEquals( type, entity.getType() );
+ assertEquals( created, entity.getCreated() );
+ assertEquals( updated, entity.getUpdated() );
+
+
+ //now clear it
+
+ serializationStrategy.clear( context, entityId, version ).execute();
+
+ returned = serializationStrategy.load( context, entityId, version );
+
+ assertEquals( context, returned.getContext() );
+ assertEquals( entityId, returned.getUuid() );
+ assertEquals( version, returned.getVersion() );
+ assertFalse( returned.getEntity().isPresent() );
+
+ //now delete it
+ serializationStrategy.delete( context, entityId, version ).execute();
+
+ //now get it, should be gone
+
+ returned = serializationStrategy.load( context, entityId, version );
+
+ assertNull( returned );
+ }
+
+
+ @Test
+ public void writeX2ClearDelete() throws ConnectionException {
+
+ final UUID applicationId = UUIDGenerator.newTimeUUID();
+ final String name = "test";
+
+ CollectionContext context = new CollectionContextImpl( applicationId, applicationId, name );
+
+
+ final UUID entityId = UUIDGenerator.newTimeUUID();
+ final UUID version1 = UUIDGenerator.newTimeUUID();
+ final String type = "test";
+
+ Entity entityv1 = new Entity( entityId, type );
+
+ entityv1.setVersion( version1 );
+
+
+ MvccEntity saved = new MvccEntityImpl( context, entityId, version1, Optional.of( entityv1 ) );
+
+
+ //persist the entity
+ serializationStrategy.write( saved ).execute();
+
+ //now read it back
+
+ MvccEntity returnedV1 = serializationStrategy.load( context, entityId, version1 );
+
+ assertEquals( "Mvcc entities are the same", saved, returnedV1 );
+
+
+ assertEquals( entityId, entityv1.getUuid() );
+ assertEquals( type, entityv1.getType() );
+
+
+ //now write a new version of it
+
+
+ Entity entityv2 = new Entity( entityId, type );
+
+ UUID version2 = UUIDGenerator.newTimeUUID();
+ entityv2.setVersion( version2 );
+
+
+ UUIDType comparator = UUIDType.instance;
+
+ int value = comparator.compare( UUIDSerializer.get().toByteBuffer( version1 ), UUIDSerializer.get().toByteBuffer( version2 ) );
+
+ assertTrue(value < 0);
+
+ value = comparator.compare( UUIDSerializer.get().toByteBuffer( version2 ), UUIDSerializer.get().toByteBuffer( version2 ) );
+
+ assertEquals(0, value);
+
+ MvccEntity savedV2 = new MvccEntityImpl( context, entityId, version2, Optional.of( entityv2 ) );
+
+ serializationStrategy.write( savedV2 ).execute();
+
+ MvccEntity returnedV2 = serializationStrategy.load( context, entityId, version2 );
+
+ assertEquals( "Mvcc entities are the same", savedV2, returnedV2 );
+
+
+ //now clear it at v3
+
+ UUID version3 = UUIDGenerator.newTimeUUID();
+
+ serializationStrategy.clear( context, entityId, version3 ).execute();
+
+
+ final Optional<Entity> empty = Optional.absent();
+
+ MvccEntity clearedV3 = new MvccEntityImpl( context, entityId, version3, empty );
+
+ MvccEntity returnedV3 = serializationStrategy.load( context, entityId, version3 );
+
+ assertEquals("entities are the same", clearedV3, returnedV3);
+
+ //now ask for up to 10 versions from the current version, we should get cleared, v2, v1
+ UUID current = UUIDGenerator.newTimeUUID();
+
+ List<MvccEntity> entities = serializationStrategy.load( context, entityId, current, 3 );
+
+ assertEquals( 3, entities.size() );
+
+ assertEquals( clearedV3, entities.get( 0 ) );
+
+ assertEquals( returnedV2, entities.get( 1 ) );
+
+ assertEquals( returnedV1, entities.get( 2 ) );
+
+
+ //now delete v2 and v1, we should still get v3
+ serializationStrategy.delete( context, entityId, version1 ).execute();
+ serializationStrategy.delete( context, entityId, version2 ).execute();
+
+ entities = serializationStrategy.load( context, entityId, current, 3 );
+
+ assertEquals( 1, entities.size() );
+
+ assertEquals( clearedV3, entities.get( 0 ) );
+
+
+ //now get it, should be gone
+ serializationStrategy.delete( context, entityId, version3 ).execute();
+
+
+ entities = serializationStrategy.load( context, entityId, current, 3 );
+
+ assertEquals( 0, entities.size() );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImplTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImplTest.java b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImplTest.java
new file mode 100644
index 0000000..c66fac2
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImplTest.java
@@ -0,0 +1,150 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.collection.CollectionContextImpl;
+import org.apache.usergrid.persistence.collection.guice.TestCollectionModule;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccLogEntry;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccLogEntryImpl;
+import org.apache.usergrid.persistence.collection.mvcc.entity.Stage;
+import org.apache.usergrid.persistence.model.util.UUIDGenerator;
+import org.apache.usergrid.persistence.test.CassandraRule;
+
+import com.google.guiceberry.GuiceBerryEnvSelector;
+import com.google.guiceberry.TestDescription;
+import com.google.guiceberry.junit4.GuiceBerryRule;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+
+/**
+ * @author tnine
+ */
+public class MvccLogEntrySerializationStrategyImplTest {
+
+
+ /**
+ * Set our timeout to 1 seconds. If it works for 1 seconds, we'll be good a any value
+ */
+ private static final int TIMEOUT = 1;
+
+
+ @Rule
+ public final GuiceBerryRule guiceBerry = new GuiceBerryRule( new TimeoutModMapper() );
+
+ @Rule
+ public final CassandraRule rule = new CassandraRule();
+
+ @Inject
+ private MvccLogEntrySerializationStrategy logEntryStrategy;
+
+
+ @Test
+ public void createAndDelete() throws ConnectionException {
+
+ final UUID applicationId = UUIDGenerator.newTimeUUID();
+ final String name = "test";
+
+
+ CollectionContext context = new CollectionContextImpl( applicationId, applicationId, name );
+
+
+ final UUID uuid = UUIDGenerator.newTimeUUID();
+ final UUID version = UUIDGenerator.newTimeUUID();
+
+ for ( Stage stage : Stage.values() ) {
+ MvccLogEntry saved = new MvccLogEntryImpl( context, uuid, version, stage );
+ logEntryStrategy.write( saved ).execute();
+
+ //Read it back
+
+ MvccLogEntry returned = logEntryStrategy.load( context, uuid, version );
+
+ assertNotNull( "Returned value should not be null", returned );
+
+ assertEquals( "Returned should equal the saved", saved, returned );
+ }
+ }
+
+
+ @Test
+ public void transientTimeout() throws ConnectionException, InterruptedException {
+
+ final UUID applicationId = UUIDGenerator.newTimeUUID();
+ final String name = "test";
+
+
+ CollectionContext context = new CollectionContextImpl( applicationId, applicationId, name );
+
+
+ final UUID uuid = UUIDGenerator.newTimeUUID();
+ final UUID version = UUIDGenerator.newTimeUUID();
+
+ for ( Stage stage : Stage.values() ) {
+
+ MvccLogEntry saved = new MvccLogEntryImpl( context, uuid, version, stage );
+ logEntryStrategy.write( saved ).execute();
+
+ //Read it back after the timeout
+
+ Thread.sleep( TIMEOUT * 1000 );
+
+ MvccLogEntry returned = logEntryStrategy.load( context, uuid, version );
+
+
+ if ( stage.isTransient() ) {
+
+ assertNull( "Active is transient and should time out", returned );
+ }
+ else {
+ assertNotNull( "Committed is not transient and should be returned", returned );
+
+ assertEquals( "Returned should equal the saved", saved, returned );
+ }
+ }
+ }
+
+
+ /**
+ * Mapper that will change which module we implement based on the test case
+ */
+ public static class TimeoutModMapper implements GuiceBerryEnvSelector {
+
+ @Override
+ public Class<? extends Module> guiceBerryEnvToUse( final TestDescription testDescription ) {
+
+ //in this edge case, we want to truncate the timeout to 1 second for this test, override the env to use
+ //this module setup
+ if ( (MvccLogEntrySerializationStrategyImplTest.class.getName()+".transientTimeout").equals( testDescription.getName() ) ) {
+ return TimeoutEnv.class;
+ }
+
+ //by default, we wnat to run the TestCollectionModule
+ return TestCollectionModule.class;
+ }
+ }
+
+
+ public static class TimeoutEnv extends TestCollectionModule {
+
+ @Override
+ public Map<String, String> getOverrides() {
+ Map<String, String> timeout = new HashMap<String, String>();
+ timeout.put( MvccLogEntrySerializationStrategyImpl.TIMEOUT_PROP, TIMEOUT + "" );
+ return timeout;
+ }
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/SerializationComparison.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/SerializationComparison.java b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/SerializationComparison.java
new file mode 100644
index 0000000..a78293b
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/SerializationComparison.java
@@ -0,0 +1,182 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.smile.SmileFactory;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.usergrid.persistence.model.entity.Entity;
+import org.apache.usergrid.persistence.model.field.ArrayField;
+import org.apache.usergrid.persistence.model.field.BooleanField;
+import org.apache.usergrid.persistence.model.field.ByteBufferField;
+import org.apache.usergrid.persistence.model.field.DoubleField;
+import org.apache.usergrid.persistence.model.field.EntityObjectField;
+import org.apache.usergrid.persistence.model.field.IntegerField;
+import org.apache.usergrid.persistence.model.field.ListField;
+import org.apache.usergrid.persistence.model.field.LocationField;
+import org.apache.usergrid.persistence.model.field.LongField;
+import org.apache.usergrid.persistence.model.field.SetField;
+import org.apache.usergrid.persistence.model.field.StringField;
+import org.apache.usergrid.persistence.model.field.UUIDField;
+import org.apache.usergrid.persistence.model.util.UUIDGenerator;
+import org.apache.usergrid.persistence.model.value.EntityObject;
+import org.apache.usergrid.persistence.model.value.Location;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.ByteBufferInputStream;
+import com.esotericsoftware.kryo.io.ByteBufferOutputStream;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+
+/**
+ * TODO We need to get both of these serialization methods working, and benchmark them for comparison
+ * Neither works out of the box for us without custom work.
+ * @author tnine
+ */
+public class SerializationComparison {
+
+ private static final Logger logger = LoggerFactory.getLogger( SerializationComparison.class );
+
+ private static final int count = 10000;
+
+
+ @Test
+ @Ignore
+ public void smileSerialization() throws IOException {
+ SmileFactory smile = new SmileFactory();
+
+ ObjectMapper smileMapper = new ObjectMapper( smile );
+
+
+ Entity entity = createEntity();
+
+ long writeTime = 0;
+ long readTime = 0;
+
+ for ( int i = 0; i < count; i++ ) {
+
+ //capture time in nannos for write
+ long writeStart = System.nanoTime();
+
+ byte[] smileData = smileMapper.writeValueAsBytes( entity );
+
+ writeTime += System.nanoTime() - writeStart;
+
+ long readStart = System.nanoTime();
+
+ Entity otherValue = smileMapper.readValue( smileData, Entity.class );
+
+ readTime += System.nanoTime() - readStart;
+ }
+
+ logger.info( "Smile took {} nanos for writing {} entities", writeTime, count );
+ logger.info( "Smile took {} nanos for reading {} entities", readTime, count );
+ }
+
+
+ @Test
+ @Ignore
+ public void kyroSerialization() {
+ Kryo kryo = new Kryo();
+
+ //container classes
+ kryo.register( Entity.class );
+
+ kryo.register( EntityObject.class );
+ kryo.register( Location.class );
+
+
+ //field classes
+ kryo.register( ArrayField.class );
+ kryo.register( BooleanField.class );
+ kryo.register( ByteBufferField.class );
+ kryo.register( DoubleField.class );
+ kryo.register( EntityObjectField.class );
+ kryo.register( IntegerField.class );
+ kryo.register( ListField.class );
+ kryo.register( LocationField.class );
+ kryo.register( LongField.class );
+ kryo.register( SetField.class );
+ kryo.register( StringField.class );
+ kryo.register( UUIDField.class, new de.javakaffee.kryoserializers.UUIDSerializer() );
+
+
+ long writeTime = 0;
+ long readTime = 0;
+
+ for ( int i = 0; i < count; i++ ) {
+
+ //capture time in nanos for write
+ long writeStart = System.nanoTime();
+
+ ByteBuffer data = ByteBuffer.allocate( 1024 );
+ ByteBufferOutputStream byteBuffOutputStream = new ByteBufferOutputStream(data);
+ Output output = new Output( byteBuffOutputStream );
+
+ Entity entity = createEntity();
+
+ kryo.writeObject( output, entity );
+ output.close();
+
+ writeTime += System.nanoTime() - writeStart;
+
+ data.rewind();
+
+ long readStart = System.nanoTime();
+
+
+ Input input = new Input( new ByteBufferInputStream( data ) );
+ Entity loaded = kryo.readObject( input, Entity.class );
+ input.close();
+
+ readTime += System.nanoTime() - readStart;
+ }
+
+ logger.info( "Smile took {} nanos for writing {} entities", writeTime, count );
+ logger.info( "Smile took {} nanos for reading {} entities", readTime, count );
+ }
+
+
+ private Entity createEntity() {
+
+ final UUID entityId = UUIDGenerator.newTimeUUID();
+
+ final UUID version = UUIDGenerator.newTimeUUID();
+
+ Entity entity = new Entity( entityId, "test" );
+ entity.setCreated( 1l );
+ entity.setUpdated( 2l );
+ entity.setVersion( version );
+
+
+ BooleanField boolField = new BooleanField( "boolean", false );
+ DoubleField doubleField = new DoubleField( "double", 1d );
+ IntegerField intField = new IntegerField( "long", 1 );
+ LongField longField = new LongField( "int", 1l );
+ StringField stringField = new StringField( "name", "test" );
+ UUIDField uuidField = new UUIDField( "uuid", UUIDGenerator.newTimeUUID() );
+
+ entity.setField( boolField );
+ entity.setField( doubleField );
+ entity.setField( intField );
+ entity.setField( longField );
+ entity.setField( stringField );
+ entity.setField( uuidField );
+
+ return entity;
+ }
+
+
+}
+
+
+
+
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/resources/cassandra.properties
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/resources/cassandra.properties b/stack/corepersistence/collection/src/test/resources/cassandra.properties
new file mode 100644
index 0000000..a69c771
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/resources/cassandra.properties
@@ -0,0 +1,5 @@
+#Purposefully left empty, we override this in our module code to KISS The runtime requires this file however
+#Boostrapping should fail fast if it's not present
+
+
+
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/pom.xml b/stack/corepersistence/index/pom.xml
new file mode 100644
index 0000000..85c2a0b
--- /dev/null
+++ b/stack/corepersistence/index/pom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>persistence</artifactId>
+ <groupId>org.apache.usergrid</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>index</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.usergrid</groupId>
+ <artifactId>collection</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Query.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Query.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Query.java
new file mode 100644
index 0000000..69fdb3f
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Query.java
@@ -0,0 +1,6 @@
+package org.apache.usergrid.persistence.index;
+
+
+/** Interface of our query implementation */
+public interface Query
+{}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngine.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngine.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngine.java
new file mode 100644
index 0000000..631c027
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngine.java
@@ -0,0 +1,22 @@
+package org.apache.usergrid.persistence.index;
+
+
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public interface QueryEngine
+{
+
+
+ /** Search and return the entities */
+ public Results<Entity> search( Query query );
+
+
+ /** Search the query, but parse the entities into the given class We may not need to implement this at first */
+ public <T> Results<T> search( Query query, Class<T> clazz );
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngineFactory.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngineFactory.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngineFactory.java
new file mode 100644
index 0000000..1358a4e
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngineFactory.java
@@ -0,0 +1,21 @@
+package org.apache.usergrid.persistence.index;
+
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public interface QueryEngineFactory
+{
+
+ /**
+ * Create an index manager for the collection context
+ *
+ * @param context The context to use when creating the index manager
+ */
+ public QueryEngineFactory createIndexManager( CollectionContext context );
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Results.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Results.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Results.java
new file mode 100644
index 0000000..bc4646e
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Results.java
@@ -0,0 +1,16 @@
+package org.apache.usergrid.persistence.index;
+
+
+import java.util.Iterator;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public interface Results<Entity> extends Iterable<Entity>, Iterator<Entity>
+{
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Complete.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Complete.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Complete.java
new file mode 100644
index 0000000..da051b6
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Complete.java
@@ -0,0 +1,21 @@
+package org.apache.usergrid.persistence.index.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+import org.apache.usergrid.persistence.collection.mvcc.stage.WriteStage;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public class Complete implements WriteStage
+{
+
+ @Override
+ public MvccEntity performStage( final MvccEntity entity ) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Start.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Start.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Start.java
new file mode 100644
index 0000000..a89e2af
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Start.java
@@ -0,0 +1,16 @@
+package org.apache.usergrid.persistence.index.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+import org.apache.usergrid.persistence.collection.mvcc.stage.WriteStage;
+
+
+/** This state should signal an index update has started */
+public class Start implements WriteStage
+{
+
+ @Override
+ public MvccEntity performStage( final MvccEntity entity ) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Write.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Write.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Write.java
new file mode 100644
index 0000000..8e75812
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Write.java
@@ -0,0 +1,17 @@
+package org.apache.usergrid.persistence.index.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+import org.apache.usergrid.persistence.collection.mvcc.stage.WriteStage;
+
+
+/** This state should perform an update of the index. */
+public class Write implements WriteStage
+{
+
+ @Override
+ public MvccEntity performStage( final MvccEntity entity ) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/pom.xml b/stack/corepersistence/model/pom.xml
new file mode 100644
index 0000000..a79af0b
--- /dev/null
+++ b/stack/corepersistence/model/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>persistence</artifactId>
+ <groupId>org.apache.usergrid</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>model</artifactId>
+
+ <!-- Runtime Dependencies -->
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>15.0</version>
+ </dependency>
+
+ <!-- Time UUID library -->
+ <dependency>
+ <groupId>com.fasterxml.uuid</groupId>
+ <artifactId>java-uuid-generator</artifactId>
+ <version>3.1.3</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.11</version>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/Entity.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/Entity.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/Entity.java
new file mode 100644
index 0000000..83cca40
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/Entity.java
@@ -0,0 +1,149 @@
+package org.apache.usergrid.persistence.model.entity;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.model.value.EntityObject;
+
+import com.google.common.base.Preconditions;
+
+
+/**
+ * Simple entity that is used for persistence. It has 4 required properties. <p/> uuid: The uuid of the entity type:
+ * The entity name (user, car, restaurant etc) created: The time the entity was created in millis since epoch updated:
+ * The time the entity was updated in millis since epoch;
+ */
+public class Entity extends EntityObject {
+
+
+ /**
+ * The entity type. This must be set
+ */
+ private String type;
+
+ /**
+ * The generated uuid. This should never be set by a user
+ */
+ private UUID uuid;
+
+ /**
+ * The version of this entity. Options, since it can be used for optimistic locking
+ */
+ private UUID version;
+
+ /**
+ * The time in milliseconds since epoch the entity was created
+ */
+ private long created;
+
+ /**
+ * The time in milliseconds since epoch the entity was updated
+ */
+ private long updated;
+
+
+ /**
+ * Create an entity with no uuid. This should be used for creating new entities
+ */
+ public Entity( String type ) {
+ Preconditions.checkNotNull( type, "Type must not be null" );
+ Preconditions.checkArgument( type.length() > 0, "Type must have a length" );
+ this.type = type;
+ }
+
+
+ /**
+ * Create an entity with the given type and uuid. Should be used for all update operations to an existing entity
+ */
+ public Entity( UUID uuid, String type ) {
+ this( type );
+
+ Preconditions.checkNotNull( uuid, "uuid must not be null" );
+
+ this.uuid = uuid;
+ }
+
+
+ /**
+ * Do not use! This is only for serialization.
+ */
+ public Entity() {
+
+ }
+
+
+ public UUID getUuid() {
+ return uuid;
+ }
+
+
+ public String getType() {
+ return type;
+ }
+
+
+ public UUID getVersion() {
+ return version;
+ }
+
+
+ public void setVersion( final UUID version ) {
+ this.version = version;
+ }
+
+
+ /**
+ * Should only be invoked by the persistence framework
+ */
+ public void setCreated( long created ) {
+ this.created = created;
+ }
+
+
+ /**
+ * Should only be invoked by the persistence framework
+ */
+ public void setUpdated( long updated ) {
+ this.updated = updated;
+ }
+
+
+ public long getCreated() {
+ return created;
+ }
+
+
+ public long getUpdated() {
+ return updated;
+ }
+
+
+ @Override
+ public boolean equals( Object o ) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ Entity entity = ( Entity ) o;
+
+ if ( type != null ? !type.equals( entity.type ) : entity.type != null ) {
+ return false;
+ }
+ if ( uuid != null ? !uuid.equals( entity.uuid ) : entity.uuid != null ) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ @Override
+ public int hashCode() {
+ int result = type != null ? type.hashCode() : 0;
+ result = 31 * result + ( uuid != null ? uuid.hashCode() : 0 );
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/AbstractField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/AbstractField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/AbstractField.java
new file mode 100644
index 0000000..674c7a7
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/AbstractField.java
@@ -0,0 +1,85 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import org.apache.usergrid.persistence.model.value.EntityObject;
+
+
+/** Base class for data information */
+public abstract class AbstractField<T> implements Field<T>
+{
+
+
+ /**
+ * Set the object this field belongs to
+ */
+ protected EntityObject parent;
+ protected String name;
+ protected T value;
+
+
+ /**
+ * Name and value must always be present.
+ *
+ * @param name The name of this field
+ * @param value The value to set. If value is null, this means that the value should be explicitly removed from
+ * the field storage
+ */
+ protected AbstractField( String name, T value )
+ {
+ this.name = name;
+ this.value = value;
+ }
+
+
+ /**
+ * Default constructor for serialization
+ */
+ protected AbstractField(){
+
+ }
+
+
+
+
+ public String getName()
+ {
+ return name;
+ }
+
+
+ @Override
+ public T getValue()
+ {
+ return value;
+ }
+
+
+ @Override
+ public boolean equals( Object o )
+ {
+ if ( this == o )
+ {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+
+ AbstractField that = ( AbstractField ) o;
+
+ if ( !name.equals( that.name ) )
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ @Override
+ public int hashCode()
+ {
+ return name.hashCode();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ArrayField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ArrayField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ArrayField.java
new file mode 100644
index 0000000..cb5f45d
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ArrayField.java
@@ -0,0 +1,27 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/** A marker to signal array handling. Just delegates to list field for easier handling internally */
+public class ArrayField extends ListField
+{
+
+ /** Contructor that intializes with an empty set for adding to later */
+ public ArrayField( String name )
+ {
+ super(name);
+ }
+
+ public ArrayField(){
+ super();
+ }
+
+ /** Add the value to the list */
+ public void add( Field field )
+ {
+ value.add( field );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/BooleanField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/BooleanField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/BooleanField.java
new file mode 100644
index 0000000..3948b27
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/BooleanField.java
@@ -0,0 +1,19 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public class BooleanField extends AbstractField<Boolean> {
+
+ public BooleanField( String name, Boolean value ) {
+ super( name, value );
+ }
+
+
+ public BooleanField() {
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ByteBufferField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ByteBufferField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ByteBufferField.java
new file mode 100644
index 0000000..2dbc193
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ByteBufferField.java
@@ -0,0 +1,29 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.nio.ByteBuffer;
+
+
+/** A field for storing byte buffers */
+public class ByteBufferField extends AbstractField<ByteBuffer>
+{
+
+
+ /** Creates an immutable copy of the byte buffer */
+ public ByteBufferField( String name, ByteBuffer value )
+ {
+ //always return a duplicate so we don't mess with the markers
+ super( name, value.duplicate() );
+ }
+
+ public ByteBufferField() {
+
+ }
+
+ @Override
+ public ByteBuffer getValue()
+ {
+ //always return a duplicate so we don't mess with the markers
+ return value.duplicate();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/DoubleField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/DoubleField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/DoubleField.java
new file mode 100644
index 0000000..729560c
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/DoubleField.java
@@ -0,0 +1,20 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public class DoubleField extends AbstractField<Double>
+{
+
+ public DoubleField( String name, Double value )
+ {
+ super( name, value );
+ }
+
+ public DoubleField() {
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/EntityObjectField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/EntityObjectField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/EntityObjectField.java
new file mode 100644
index 0000000..4031a81
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/EntityObjectField.java
@@ -0,0 +1,20 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import org.apache.usergrid.persistence.model.value.EntityObject;
+
+
+/** An object field */
+public class EntityObjectField extends AbstractField<EntityObject>
+{
+
+
+ public EntityObjectField( String name, EntityObject value )
+ {
+ super( name, value );
+ }
+
+ public EntityObjectField() {
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/Field.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/Field.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/Field.java
new file mode 100644
index 0000000..5061d49
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/Field.java
@@ -0,0 +1,28 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.io.Serializable;
+
+import org.apache.usergrid.persistence.model.value.EntityObject;
+
+
+/**
+ * Interface for fields. All fields must implement this method The T is the type of field (in the java runtime) The V
+ * is the value of the field
+ */
+public interface Field<T> extends Serializable {
+
+ /**
+ * Get the name of the field
+ * @return
+ */
+ public String getName();
+
+ /**
+ * Get the value of the field
+ * @return
+ */
+ public T getValue();
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/IntegerField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/IntegerField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/IntegerField.java
new file mode 100644
index 0000000..9c807e0
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/IntegerField.java
@@ -0,0 +1,20 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public class IntegerField extends AbstractField<Integer>
+{
+
+ public IntegerField( String name, Integer value )
+ {
+ super( name, value );
+ }
+
+ public IntegerField() {
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ListField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ListField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ListField.java
new file mode 100644
index 0000000..0795201
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ListField.java
@@ -0,0 +1,28 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/** An object field that represents a list of objects. This can also be used to represent arrays */
+public class ListField extends AbstractField<List<Field>>
+{
+
+ /** Contructor that intializes with an empty set for adding to later */
+ public ListField( String name )
+ {
+ super( name, new ArrayList<Field>() );
+ }
+
+ public ListField(){
+ super();
+ }
+
+
+ /** Add the value to the list */
+ public void add( Field field )
+ {
+ value.add( field );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LocationField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LocationField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LocationField.java
new file mode 100644
index 0000000..5d9fd57
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LocationField.java
@@ -0,0 +1,20 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import org.apache.usergrid.persistence.model.value.Location;
+
+
+/** Basic field for storing location data */
+public class LocationField extends AbstractField<Location>
+{
+
+ /** Create a location field with the given point */
+ public LocationField( String name, Location value )
+ {
+ super( name, value );
+ }
+
+ public LocationField() {
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LongField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LongField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LongField.java
new file mode 100644
index 0000000..c56b514
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LongField.java
@@ -0,0 +1,23 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public class LongField extends AbstractField<Long>
+{
+
+ public LongField( String name, Long value )
+ {
+ super( name, value );
+ }
+
+ public LongField() {
+
+ }
+
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/SetField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/SetField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/SetField.java
new file mode 100644
index 0000000..f28b5ec
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/SetField.java
@@ -0,0 +1,28 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+
+/** An object field that represents a set of objects */
+public class SetField extends AbstractField<Set<Field>>
+{
+
+
+ /** Contructor that intializes with an empty set for adding to later */
+ public SetField( String name )
+ {
+ super( name, new LinkedHashSet<Field>() );
+ }
+
+ public SetField() {
+
+ }
+
+ /** Add an entry to the set */
+ public void addEntry( Field field )
+ {
+ value.add( field );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/StringField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/StringField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/StringField.java
new file mode 100644
index 0000000..bcb8b2d
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/StringField.java
@@ -0,0 +1,17 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+/** A String field */
+public class StringField extends AbstractField<String>
+{
+
+
+ public StringField( String name, String value )
+ {
+ super( name, value );
+ }
+
+ public StringField() {
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/UUIDField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/UUIDField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/UUIDField.java
new file mode 100644
index 0000000..11dfc22
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/UUIDField.java
@@ -0,0 +1,20 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.util.UUID;
+
+
+/** A String field */
+public class UUIDField extends AbstractField<UUID>
+{
+
+
+ public UUIDField( String name, UUID value )
+ {
+ super( name, value );
+ }
+
+ public UUIDField() {
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/util/UUIDGenerator.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/util/UUIDGenerator.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/util/UUIDGenerator.java
new file mode 100644
index 0000000..687e27b
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/util/UUIDGenerator.java
@@ -0,0 +1,106 @@
+package org.apache.usergrid.persistence.model.util;
+
+
+import java.io.IOException;
+import java.util.Random;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.fasterxml.uuid.EthernetAddress;
+import com.fasterxml.uuid.TimestampSynchronizer;
+import com.fasterxml.uuid.UUIDTimer;
+import com.fasterxml.uuid.impl.TimeBasedGenerator;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public class UUIDGenerator
+{
+
+
+ private static final TimestampSynchronizer synchronizer = new TimestampSynchronizer()
+ {
+
+ /**
+ * Pointer to the last value we returned
+ */
+ private long last = 0;
+
+ /**
+ * The number of ticks that can be used in the millisecond. In a time UUID a tick is divided into 1/10000 of
+ * a millisecond
+ *
+ */
+ private AtomicInteger ticks = new AtomicInteger();
+
+
+ @Override
+ protected long initialize() throws IOException
+ {
+
+ last = System.currentTimeMillis();
+ return last;
+ }
+
+
+ @Override
+ protected void deactivate() throws IOException
+ {
+ //no op
+ }
+
+
+ @Override
+ protected long update( long now ) throws IOException
+ {
+ /**
+ * It's greater
+ */
+ if ( now > last )
+ {
+ last = now;
+ ticks.set( 0 );
+ return last;
+ }
+
+ //we have the same value (since now should always be increasing) increment a tick
+ last = now + ticks.incrementAndGet();
+
+ return last;
+ }
+ };
+
+
+ private static final Random random = new Random();
+ private static final UUIDTimer timer;
+
+
+ /**
+ * Lame, but required
+ */
+ static
+ {
+ try
+ {
+ timer = new UUIDTimer( random, synchronizer );
+ }
+ catch ( IOException e )
+ {
+ throw new RuntimeException( "Couldn't intialize timer", e );
+ }
+ }
+
+
+ private static final TimeBasedGenerator generator =
+ new TimeBasedGenerator( EthernetAddress.fromInterface(), timer );
+
+
+ /** Create a new time uuid */
+ public static UUID newTimeUUID()
+ {
+ return generator.generate();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/EntityObject.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/EntityObject.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/EntityObject.java
new file mode 100644
index 0000000..5f02053
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/EntityObject.java
@@ -0,0 +1,40 @@
+package org.apache.usergrid.persistence.model.value;
+
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.usergrid.persistence.model.field.Field;
+
+
+/** Simple wrapper for holding nested objects */
+public class EntityObject implements Serializable
+{
+
+
+ /** Fields the users can set */
+ private Map<String, Field> fields = new HashMap<String, Field>();
+
+
+ /** Add the field, return the old one if it existed */
+ public <T extends java.lang.Object> Field<T> setField( Field<T> value )
+ {
+ return fields.put( value.getName(), value );
+ }
+
+
+ /** Get the field by name the user has set into the entity */
+ public <T extends java.lang.Object> Field<T> getField( String name )
+ {
+ return fields.get( name );
+ }
+
+
+ /** Get all fields in the entity */
+ public Collection<Field> getFields()
+ {
+ return fields.values();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/Location.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/Location.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/Location.java
new file mode 100644
index 0000000..4370aa1
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/Location.java
@@ -0,0 +1,32 @@
+package org.apache.usergrid.persistence.model.value;
+
+
+import java.io.Serializable;
+
+
+/** Geographic point. Should be used when we want to store geo information */
+public class Location implements Serializable
+{
+
+ private final double latitude;
+ private final double longtitude;
+
+
+ public Location( double latitude, double longtitude )
+ {
+ this.latitude = latitude;
+ this.longtitude = longtitude;
+ }
+
+
+ public double getLatitude()
+ {
+ return latitude;
+ }
+
+
+ public double getLongtitude()
+ {
+ return longtitude;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/test/java/org/apache/usergrid/persistence/model/field/EntityTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/test/java/org/apache/usergrid/persistence/model/field/EntityTest.java b/stack/corepersistence/model/src/test/java/org/apache/usergrid/persistence/model/field/EntityTest.java
new file mode 100644
index 0000000..9f94b18
--- /dev/null
+++ b/stack/corepersistence/model/src/test/java/org/apache/usergrid/persistence/model/field/EntityTest.java
@@ -0,0 +1,117 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+import org.junit.Test;
+
+import org.apache.usergrid.persistence.model.entity.Entity;
+import org.apache.usergrid.persistence.model.util.UUIDGenerator;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+
+/** Simple test for validating stored entities */
+public class EntityTest
+{
+
+ @Test
+ public void testPutAndGet()
+ {
+
+
+ final UUID uuid = UUIDGenerator.newTimeUUID();
+ final UUID version = UUIDGenerator.newTimeUUID();
+ final String type = "test";
+ final long created = 1l;
+ final long updated = 2l;
+
+ Entity entity = new Entity( uuid, type );
+
+ entity.setVersion( version );
+ entity.setCreated( created );
+ entity.setUpdated( updated );
+
+ assertEquals( uuid, entity.getUuid() );
+ assertEquals( type, entity.getType() );
+ assertEquals( created, entity.getCreated() );
+ assertEquals( updated, entity.getUpdated() );
+
+
+ BooleanField boolField = new BooleanField( "boolean", false );
+ DoubleField doubleField = new DoubleField( "double", 1d );
+ IntegerField intField = new IntegerField( "long", 1 );
+ LongField longField = new LongField( "int", 1l );
+ StringField stringField = new StringField( "name", "test" );
+ UUIDField uuidField = new UUIDField( "uuid", UUIDGenerator.newTimeUUID() );
+
+ entity.setField( boolField );
+ entity.setField( doubleField );
+ entity.setField( intField );
+ entity.setField( longField );
+ entity.setField( stringField );
+ entity.setField( uuidField );
+
+ Field<Boolean> boolFieldReturned = entity.getField( boolField.getName() );
+
+ assertSame( boolField, boolFieldReturned );
+
+ Field<Double> doubleFieldReturned = entity.getField( doubleField.getName() );
+
+ assertSame( doubleField, doubleFieldReturned );
+
+ Field<Integer> intFieldReturned = entity.getField( intField.getName() );
+
+ assertSame( intField, intFieldReturned );
+
+ Field<Long> longFieldReturned = entity.getField( longField.getName() );
+
+ assertSame( longField, longFieldReturned );
+
+ Field<String> stringFieldReturned = entity.getField( stringField.getName() );
+
+ assertSame( stringField, stringFieldReturned );
+
+ Field<UUID> uuidFieldReturned = entity.getField( uuidField.getName() );
+
+ assertSame( uuidField, uuidFieldReturned );
+
+
+ Set<Field> results = new HashSet<Field>();
+ results.addAll( entity.getFields() );
+
+
+ assertTrue( results.contains( boolField ) );
+ assertTrue( results.contains( doubleField ) );
+ assertTrue( results.contains( intField ) );
+ assertTrue( results.contains( longField ) );
+ assertTrue( results.contains( stringField ) );
+ assertTrue( results.contains( uuidField ) );
+
+ assertEquals( 6, results.size() );
+
+
+ assertEquals( uuid, entity.getUuid() );
+ assertEquals( version, entity.getVersion() );
+ }
+
+
+ @Test( expected = NullPointerException.class )
+ public void uuidRequired()
+ {
+ new Entity( null, "test" );
+ }
+
+
+ @Test( expected = NullPointerException.class )
+ public void versionRequired()
+ {
+ final UUID uuid = UUIDGenerator.newTimeUUID();
+
+ new Entity( uuid, null );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/Readme.md
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/Readme.md b/stack/corepersistence/perftest/Readme.md
new file mode 100644
index 0000000..8061dda
--- /dev/null
+++ b/stack/corepersistence/perftest/Readme.md
@@ -0,0 +1,52 @@
+# A Simple Performance Testing Framework on AWS
+
+This is a simple performance testing framework designed to pound the heck out
+of a clustered persistence teir. It is designed as a web application that
+can be run on several tomcat or jetty servers to bombard in unison an in JVM
+API that operates against a clustered data storage layer like Cassandra.
+
+## Setting up a Perftest
+
+The framework simply executes a number of calls which you specify using a
+Perftest implementation class. This class specifies all the parameters as
+methods and is construct by the framework using a TestModule (a guice module)
+which you also provide.
+
+The framework simply loads your TestModule and uses its Guice Injector to
+create the Perftest instance. It coordinates executing calls against the
+Perftest instance across the set of servers containing the framework. SimpleDB
+is used to communicate presence to all the nodes of the workers so they can
+find each other and coordinate their calls at the same time against the
+Perftest instance.
+
+The following endpoints are used to control the framework:
+
+ * /perftest/start
+ * /perftest/stop
+ * /perftest/reset
+ * /perftest/stats
+
+The following ascii text shows the states of the framework which one can
+go through while issuing POSTs to the end points above:
+
+ start stop
+ +-----+ +-------+ +-------+
+ ---+ready+-------+running+-----+stopped|
+ +--+--+ +-------+ +---+---+
+ | |
+ |____________________________|
+ reset
+
+A post to a single node issues the same POST to all the nodes in the cluster.
+
+## Dependencies
+
+It uses the following libraries to do what it does:
+
+* Jersey - for REST services
+* Jackson - for JSON <--> Java marshalling
+* Guice - for the DI container
+* Archaius - for dynamic properties
+* Blitz4j - for asynchronous logging
+* Slf4j - API for binding to Blitz4j
+
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/pom.xml b/stack/corepersistence/perftest/pom.xml
new file mode 100644
index 0000000..b5c70ee
--- /dev/null
+++ b/stack/corepersistence/perftest/pom.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>persistence</artifactId>
+ <groupId>org.apache.usergrid</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>perftest</artifactId>
+ <packaging>war</packaging>
+ <description>Simple Performance Testing Framework</description>
+
+ <properties>
+ <slf4j.version>1.6.1</slf4j.version>
+ <blitz4j.version>1.31</blitz4j.version>
+ <archaius.version>0.4.1</archaius.version>
+ <servo.version>0.4.36</servo.version>
+ <jersey.version>1.9.1</jersey.version>
+ <jackson.version>2.1.5</jackson.version>
+ <fastxml.version>2.3.0-SNAPSHOT</fastxml.version>
+ <jetty.plugin.version>8.1.14.v20131031</jetty.plugin.version>
+ <jetty.version>9.1.0.M0</jetty.version>
+ <log4j.version>1.2.17</log4j.version>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <guice.version>3.0</guice.version>
+ </properties>
+
+ <build>
+ <finalName>${pom.artifactId}</finalName>
+
+ <plugins>
+ <plugin>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty-maven-plugin</artifactId>
+ <version>${jetty.plugin.version}</version>
+ <configuration>
+ <scanIntervalSeconds>5</scanIntervalSeconds>
+ <webApp>
+ <contextPath>/</contextPath>
+ </webApp>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-server</artifactId>
+ <version>${jersey.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.sun.jersey.contribs</groupId>
+ <artifactId>jersey-guice</artifactId>
+ <version>${jersey.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-json-provider</artifactId>
+ <version>${jackson.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.module</groupId>
+ <artifactId>jackson-module-guice</artifactId>
+ <version>${fastxml.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.inject</groupId>
+ <artifactId>guice</artifactId>
+ <version>${guice.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.inject.extensions</groupId>
+ <artifactId>guice-multibindings</artifactId>
+ <version>${guice.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.inject.extensions</groupId>
+ <artifactId>guice-servlet</artifactId>
+ <version>${guice.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlet</artifactId>
+ <version>${jetty.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>${jetty.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.netflix.archaius</groupId>
+ <artifactId>archaius-core</artifactId>
+ <version>${archaius.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.netflix.blitz4j</groupId>
+ <artifactId>blitz4j</artifactId>
+ <version>${blitz4j.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.netflix.archaius</groupId>
+ <artifactId>archaius-aws</artifactId>
+ <version>${archaius.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>${slf4j.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <!-- Test RelatedDependencies -->
+
+ <dependency>
+ <groupId>com.google.guiceberry</groupId>
+ <artifactId>guiceberry</artifactId>
+ <version>3.3.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
+
[3/3] git commit: Initial import of 2.0 core persistence code.
Posted by to...@apache.org.
Initial import of 2.0 core persistence code.
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/ac634a1d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/ac634a1d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/ac634a1d
Branch: refs/heads/two-dot-o
Commit: ac634a1d05e3f8eedc99eb06f5d7b97c3b437d23
Parents: a30bf50
Author: Todd Nine <tn...@apigee.com>
Authored: Wed Nov 27 15:08:46 2013 -0700
Committer: Todd Nine <tn...@apigee.com>
Committed: Wed Nov 27 15:08:46 2013 -0700
----------------------------------------------------------------------
stack/corepersistence/.gitignore | 7 +
stack/corepersistence/README.md | 73 ++++
stack/corepersistence/collection/pom.xml | 113 ++++++
.../collection/CollecitonManagerImpl.java | 15 +
.../collection/CollectionContext.java | 25 ++
.../collection/CollectionContextImpl.java | 82 +++++
.../collection/CollectionManager.java | 41 +++
.../collection/CollectionManagerFactory.java | 18 +
.../CollectionManagerFactoryImpl.java | 15 +
.../collection/CollectionManagerImpl.java | 45 +++
.../astynax/AstynaxKeyspaceProvider.java | 99 ++++++
.../collection/guice/CollectionModule.java | 64 ++++
.../collection/guice/PropertyUtils.java | 60 ++++
.../migration/CollectionColumnFamily.java | 50 +++
.../collection/migration/Migration.java | 16 +
.../migration/MigrationException.java | 18 +
.../collection/migration/MigrationManager.java | 15 +
.../migration/MigrationManagerImpl.java | 198 +++++++++++
.../collection/mvcc/entity/MvccEntity.java | 38 ++
.../collection/mvcc/entity/MvccEntityImpl.java | 95 +++++
.../collection/mvcc/entity/MvccLogEntry.java | 38 ++
.../mvcc/entity/MvccLogEntryImpl.java | 90 +++++
.../collection/mvcc/entity/Stage.java | 67 ++++
.../mvcc/event/PostProcessListener.java | 24 ++
.../collection/mvcc/stage/Commit.java | 18 +
.../collection/mvcc/stage/MvccStrategy.java | 42 +++
.../collection/mvcc/stage/Start.java | 24 ++
.../collection/mvcc/stage/Write.java | 25 ++
.../collection/mvcc/stage/WriteStage.java | 20 ++
.../collection/mvcc/verify/AtomicUpdate.java | 26 ++
.../mvcc/verify/OptimisticUpdate.java | 22 ++
.../collection/mvcc/verify/UniqueUpdate.java | 27 ++
.../MvccEntitySerializationStrategy.java | 77 +++++
.../MvccEntitySerializationStrategyImpl.java | 252 ++++++++++++++
.../MvccLogEntrySerializationStrategy.java | 59 ++++
.../MvccLogEntrySerializationStrategyImpl.java | 219 ++++++++++++
.../CollectionManagerFactoryTest.java | 8 +
.../collection/guice/TestCollectionModule.java | 103 ++++++
.../collection/mvcc/entity/StageTest.java | 91 +++++
...MvccEntitySerializationStrategyImplTest.java | 343 +++++++++++++++++++
...ccLogEntrySerializationStrategyImplTest.java | 150 ++++++++
.../serialization/SerializationComparison.java | 182 ++++++++++
.../src/test/resources/cassandra.properties | 5 +
stack/corepersistence/index/pom.xml | 22 ++
.../usergrid/persistence/index/Query.java | 6 +
.../usergrid/persistence/index/QueryEngine.java | 22 ++
.../persistence/index/QueryEngineFactory.java | 21 ++
.../usergrid/persistence/index/Results.java | 16 +
.../persistence/index/stage/Complete.java | 21 ++
.../usergrid/persistence/index/stage/Start.java | 16 +
.../usergrid/persistence/index/stage/Write.java | 17 +
stack/corepersistence/model/pom.xml | 38 ++
.../persistence/model/entity/Entity.java | 149 ++++++++
.../persistence/model/field/AbstractField.java | 85 +++++
.../persistence/model/field/ArrayField.java | 27 ++
.../persistence/model/field/BooleanField.java | 19 +
.../model/field/ByteBufferField.java | 29 ++
.../persistence/model/field/DoubleField.java | 20 ++
.../model/field/EntityObjectField.java | 20 ++
.../usergrid/persistence/model/field/Field.java | 28 ++
.../persistence/model/field/IntegerField.java | 20 ++
.../persistence/model/field/ListField.java | 28 ++
.../persistence/model/field/LocationField.java | 20 ++
.../persistence/model/field/LongField.java | 23 ++
.../persistence/model/field/SetField.java | 28 ++
.../persistence/model/field/StringField.java | 17 +
.../persistence/model/field/UUIDField.java | 20 ++
.../persistence/model/util/UUIDGenerator.java | 106 ++++++
.../persistence/model/value/EntityObject.java | 40 +++
.../persistence/model/value/Location.java | 32 ++
.../persistence/model/field/EntityTest.java | 117 +++++++
stack/corepersistence/perftest/Readme.md | 52 +++
stack/corepersistence/perftest/pom.xml | 150 ++++++++
.../apache/usergrid/perftest/NoopPerftest.java | 54 +++
.../usergrid/perftest/NoopPerftestModule.java | 34 ++
.../org/apache/usergrid/perftest/Perftest.java | 11 +
.../usergrid/perftest/PerftestModule.java | 47 +++
.../usergrid/perftest/PerftestRunner.java | 197 +++++++++++
.../perftest/PerftestServletConfig.java | 50 +++
.../usergrid/perftest/TestModuleLoader.java | 95 +++++
.../apache/usergrid/perftest/logging/Log.java | 19 +
.../perftest/logging/Slf4jMembersInjector.java | 46 +++
.../perftest/logging/Slf4jTypeListener.java | 44 +++
.../perftest/rest/CallStatsSnapshot.java | 91 +++++
.../perftest/rest/PerftestResetResource.java | 64 ++++
.../perftest/rest/PerftestStartResource.java | 64 ++++
.../perftest/rest/PerftestStatsResource.java | 53 +++
.../perftest/rest/PerftestStatusResource.java | 62 ++++
.../perftest/rest/PerftestStopResource.java | 59 ++++
.../usergrid/perfteststats/CallStats.java | 90 +++++
.../src/main/resources/config.properties | 22 ++
.../src/main/resources/log4j.properties | 20 ++
.../perftest/src/main/webapp/WEB-INF/web.xml | 27 ++
stack/corepersistence/pom.xml | 30 ++
stack/corepersistence/testutils/pom.xml | 43 +++
.../persistence/test/AvailablePortFinder.java | 187 ++++++++++
.../persistence/test/CassandraRule.java | 86 +++++
.../src/main/resources/log4j-server.properties | 35 ++
.../testutils/src/main/resources/log4j.xml | 16 +
99 files changed, 5754 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/.gitignore
----------------------------------------------------------------------
diff --git a/stack/corepersistence/.gitignore b/stack/corepersistence/.gitignore
new file mode 100644
index 0000000..7e23415
--- /dev/null
+++ b/stack/corepersistence/.gitignore
@@ -0,0 +1,7 @@
+.idea
+atlassian-ide-plugin.xml
+target
+**/target
+*.iml
+*.swp
+*.log
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/README.md
----------------------------------------------------------------------
diff --git a/stack/corepersistence/README.md b/stack/corepersistence/README.md
new file mode 100644
index 0000000..5f9a44b
--- /dev/null
+++ b/stack/corepersistence/README.md
@@ -0,0 +1,73 @@
+Core Persistence
+===============
+
+A Framework to provide basic component services for persistence frameworks
+
+
+Data Templates
+==============
+
+Below are the basic data templates this system should support
+
+
+Collections
+-----------
+
+A collection storage and indexing framework. Properties should be secondary indexed, and should be able to be queried efficiently.
+
+
+*MVCC Semantics*
+
+Transaction/Checkpoint logging on indexing.
+Consistent data view. Can potentially be for long running jobs.
+Optimistic Locking (maybe)
+Atomic updates (maybe)
+
+*Operation Chaining* (maybe)
+
+Possible ability to define an operation context where a set of all writes must either succeed or fail as a group
+(can probably be done with MVCC)
+
+
+
+
+Graphs
+-----------
+
+A system for creating relationships between collection entities. The directed edges can be named (a type) and
+an index query can be executed on those edges.
+
+
+
+Maps
+-----------
+
+A map that can store hierarchical keys. Shorter keys are better. This should allow for range "scanning". I.E.
+
+key1: => org1/app1/env1/version1
+
+key2: => org1/app1/env2/version1
+
+Operations:
+
+ Put by key
+ Get by key
+ Iterate by scan
+ Delete by key
+
+
+Get me all keys present in org1/app1.
+
+Start => org1/app1
+
+End => org1/app1 inclusive
+
+-----------
+===========
+
+A write through distributed cache backed by the cassandra map implementation for persistence
+
+
+
+
+
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/pom.xml b/stack/corepersistence/collection/pom.xml
new file mode 100644
index 0000000..47875bc
--- /dev/null
+++ b/stack/corepersistence/collection/pom.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>persistence</artifactId>
+ <groupId>org.apache.usergrid</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <description>The module for handling all collection I/O</description>
+ <properties>
+ <guice.version>3.0</guice.version>
+ </properties>
+
+ <artifactId>collection</artifactId>
+
+ <dependencies>
+
+ <!-- Depends on the basic models -->
+ <dependency>
+ <groupId>${project.parent.groupId}</groupId>
+ <artifactId>model</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+
+ <!-- include our cassandra client -->
+ <dependency>
+ <groupId>com.netflix.astyanax</groupId>
+ <artifactId>astyanax-core</artifactId>
+ <version>${astynax.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.netflix.astyanax</groupId>
+ <artifactId>astyanax-thrift</artifactId>
+ <version>${astynax.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.netflix.astyanax</groupId>
+ <artifactId>astyanax-cassandra</artifactId>
+ <version>${astynax.version}</version>
+ </dependency>
+
+ <!-- Serialization libraries -->
+
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-smile</artifactId>
+ <version>1.9.13</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.esotericsoftware.kryo</groupId>
+ <artifactId>kryo</artifactId>
+ <version>2.22</version>
+ </dependency>
+
+ <!-- helper serializers for kryo -->
+ <dependency>
+ <groupId>de.javakaffee</groupId>
+ <artifactId>kryo-serializers</artifactId>
+ <version>0.26</version>
+ </dependency>
+
+ <!-- Google Guice -->
+ <dependency>
+ <groupId>com.google.inject</groupId>
+ <artifactId>guice</artifactId>
+ <version>${guice.version}</version>
+ </dependency>
+
+ <!-- guice helper removed for now, investigate using this later, it's getting in the way right now while learning guice -->
+ <!--<dependency>
+ <groupId>com.netflix.governator</groupId>
+ <artifactId>governator</artifactId>
+ <version>1.2.3</version>
+ </dependency> -->
+
+ <dependency>
+ <groupId>com.google.inject.extensions</groupId>
+ <artifactId>guice-multibindings</artifactId>
+ <version>${guice.version}</version>
+ </dependency>
+
+ <!-- Google Guice Integration Test Injectors -->
+
+ <dependency>
+ <groupId>com.google.guiceberry</groupId>
+ <artifactId>guiceberry</artifactId>
+ <version>3.3.1</version>
+ <scope>test</scope>
+ </dependency>
+
+
+ <dependency>
+ <groupId>${project.parent.groupId}</groupId>
+ <artifactId>testutils</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollecitonManagerImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollecitonManagerImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollecitonManagerImpl.java
new file mode 100644
index 0000000..5a2f708
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollecitonManagerImpl.java
@@ -0,0 +1,15 @@
+package org.apache.usergrid.persistence.collection;
+
+
+/**
+ * @author tnine
+ */
+public class CollecitonManagerImpl {
+
+ private final CollectionContext context;
+
+
+ public CollecitonManagerImpl( final CollectionContext context ) {
+ this.context = context;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContext.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContext.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContext.java
new file mode 100644
index 0000000..dd30d3a
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContext.java
@@ -0,0 +1,25 @@
+package org.apache.usergrid.persistence.collection;
+
+
+import java.util.UUID;
+
+
+/**
+ * A context to use when creating the collection manager. Typically, this would be something like an application, or an
+ * organization. Some context that "owns" the collection
+ */
+public interface CollectionContext
+{
+
+ /** @return The application that will contain this collection */
+ public UUID getApplication();
+
+ /**
+ * @return A uuid that is unique to this context. It can be any uuid (time uuid preferred). Usually an application
+ * Id, but could be an entity Id that is the parent of another collection
+ */
+ public UUID getOwner();
+
+ /** @return The name of the collection. This should be singular, NO PLURALIZATION!!!!!! */
+ public String getName();
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContextImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContextImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContextImpl.java
new file mode 100644
index 0000000..371bde9
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContextImpl.java
@@ -0,0 +1,82 @@
+package org.apache.usergrid.persistence.collection;
+
+
+import java.util.UUID;
+
+import com.google.common.base.Preconditions;
+
+
+/**
+ * Simple impl of hte collection context
+ * @author tnine
+ */
+public class CollectionContextImpl implements CollectionContext {
+
+ private final UUID applicationId;
+ private final UUID ownerId;
+ private final String name;
+
+
+ public CollectionContextImpl( final UUID applicationId, final UUID ownerId, final String name ) {
+ Preconditions.checkNotNull( applicationId , "applicationId is required");
+ Preconditions.checkNotNull( ownerId , "ownerId is required");
+ Preconditions.checkNotNull( name , "name is required");
+
+
+ this.applicationId = applicationId;
+ this.ownerId = ownerId;
+ this.name = name;
+ }
+
+
+ @Override
+ public UUID getApplication() {
+ return applicationId;
+ }
+
+
+ @Override
+ public UUID getOwner() {
+ return ownerId;
+ }
+
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+
+ @Override
+ public boolean equals( final Object o ) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ final CollectionContextImpl that = ( CollectionContextImpl ) o;
+
+ if ( !applicationId.equals( that.applicationId ) ) {
+ return false;
+ }
+ if ( !name.equals( that.name ) ) {
+ return false;
+ }
+ if ( !ownerId.equals( that.ownerId ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ @Override
+ public int hashCode() {
+ int result = applicationId.hashCode();
+ result = 31 * result + ownerId.hashCode();
+ result = 31 * result + name.hashCode();
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManager.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManager.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManager.java
new file mode 100644
index 0000000..570a1e4
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManager.java
@@ -0,0 +1,41 @@
+package org.apache.usergrid.persistence.collection;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+
+/**
+ *
+ *
+ * @author: tnine
+ *
+ */
+public interface CollectionManager
+{
+
+ /**
+ * Create the entity in the collection. Only use for entities your are sure are new.
+ *
+ * @param entity The entity to update
+ */
+ public void create( Entity entity );
+
+ /**
+ * Update the entity with the given fields.
+ *
+ * @param entity The entity properties to update
+ */
+ public void update( Entity entity );
+
+ /** Delete the entity and remove it's indexes with the given entity id */
+ public void delete( UUID entityId );
+
+ /**
+ * Load the entity with the given entity Id
+ * @param entityId
+ * @return
+ */
+ public Entity load(UUID entityId);
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactory.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactory.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactory.java
new file mode 100644
index 0000000..7791a0b
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactory.java
@@ -0,0 +1,18 @@
+package org.apache.usergrid.persistence.collection;
+
+
+/** A basic factory that creates a collection manager with the given context */
+public interface CollectionManagerFactory
+{
+
+ /**
+ * Create a new CollectionManager for the given context. The CollectionManager can safely be used on the current
+ * thread and will cache responses. The returned instance should not be shared among threads it will not be
+ * guaranteed to be thread safe
+ *
+ * @param context The context to use when creating the collection manager
+ *
+ * @return The collection manager to perform operations within the provided context
+ */
+ public CollectionManager createCollectionManager( CollectionContext context );
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryImpl.java
new file mode 100644
index 0000000..a6631e6
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryImpl.java
@@ -0,0 +1,15 @@
+package org.apache.usergrid.persistence.collection;
+
+
+/**
+ * Basic Imple
+ * @author tnine
+ */
+public class CollectionManagerFactoryImpl implements CollectionManagerFactory {
+
+
+ @Override
+ public CollectionManager createCollectionManager( final CollectionContext context ) {
+ return new CollectionManagerImpl( context );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerImpl.java
new file mode 100644
index 0000000..15ef3ff
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerImpl.java
@@ -0,0 +1,45 @@
+package org.apache.usergrid.persistence.collection;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+
+/**
+ * Simple implementation. Should perform
+ * @author tnine
+ */
+public class CollectionManagerImpl implements CollectionManager {
+
+ private final CollectionContext context;
+
+
+ public CollectionManagerImpl( final CollectionContext context ) {
+ this.context = context;
+ }
+
+
+ @Override
+ public void create( final Entity entity ) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+
+ @Override
+ public void update( final Entity entity ) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+
+ @Override
+ public void delete( final UUID entityId ) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+
+ @Override
+ public Entity load( final UUID entityId ) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/astynax/AstynaxKeyspaceProvider.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/astynax/AstynaxKeyspaceProvider.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/astynax/AstynaxKeyspaceProvider.java
new file mode 100644
index 0000000..150f221
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/astynax/AstynaxKeyspaceProvider.java
@@ -0,0 +1,99 @@
+package org.apache.usergrid.persistence.collection.astynax;
+
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.name.Named;
+import com.netflix.astyanax.AstyanaxConfiguration;
+import com.netflix.astyanax.AstyanaxContext;
+import com.netflix.astyanax.Keyspace;
+import com.netflix.astyanax.connectionpool.ConnectionPoolConfiguration;
+import com.netflix.astyanax.connectionpool.NodeDiscoveryType;
+import com.netflix.astyanax.connectionpool.impl.ConnectionPoolConfigurationImpl;
+import com.netflix.astyanax.connectionpool.impl.Slf4jConnectionPoolMonitorImpl;
+import com.netflix.astyanax.impl.AstyanaxConfigurationImpl;
+import com.netflix.astyanax.thrift.ThriftFamilyFactory;
+
+
+/**
+ * TODO. Provide the ability to do a service hook for realtime tuning without the need of a JVM restart
+ * This could be done with governator and service discovery
+ * @author tnine
+ */
+public class AstynaxKeyspaceProvider implements Provider<Keyspace> {
+
+ /**
+ * The cassandra URL property
+ */
+ public static final String CASSANDRA_HOSTS = "cassandra.hosts";
+ public static final String CASSANDRA_PORT = "cassandra.port";
+ public static final String CASSANDRA_CONNECTIONS = "cassandra.connections";
+ public static final String CASSANDRA_CLUSTER_NAME = "cassandra.cluster_name";
+ public static final String CASSANDRA_VERSION = "cassandra.version";
+ public static final String COLLECTIONS_KEYSPACE_NAME = "collections.keyspace";
+
+ protected final String cassandraHosts;
+ protected final int cassandraPort;
+ protected final int cassandraConnections;
+ protected final String clusterName;
+ protected final String keyspaceName;
+ protected final String cassandraVersion;
+
+
+ @Inject
+ public AstynaxKeyspaceProvider( @Named( CASSANDRA_HOSTS ) final String cassandraHosts,
+ @Named( CASSANDRA_PORT ) final int cassandraPort,
+ @Named( CASSANDRA_CONNECTIONS ) final int cassandraConnections,
+ @Named( CASSANDRA_CLUSTER_NAME ) final String clusterName,
+ @Named( CASSANDRA_VERSION ) final String cassandraVersion,
+ @Named( COLLECTIONS_KEYSPACE_NAME ) final String keyspaceName ) {
+ this.cassandraHosts = cassandraHosts;
+ this.cassandraPort = cassandraPort;
+ this.cassandraConnections = cassandraConnections;
+ this.clusterName = clusterName;
+ this.keyspaceName = keyspaceName;
+ this.cassandraVersion = cassandraVersion;
+ }
+
+
+ @Override
+ public Keyspace get() {
+ AstyanaxConfiguration config = new AstyanaxConfigurationImpl().setDiscoveryType( NodeDiscoveryType.TOKEN_AWARE )
+ .setTargetCassandraVersion( cassandraVersion );
+
+ ConnectionPoolConfiguration connectionPoolConfiguration =
+ new ConnectionPoolConfigurationImpl( "UsergridConnectionPool" ).setPort( cassandraPort )
+ .setMaxConnsPerHost(
+ cassandraConnections )
+ .setSeeds( cassandraHosts );
+
+ AstyanaxContext<Keyspace> context =
+ new AstyanaxContext.Builder().forCluster( clusterName ).forKeyspace( keyspaceName )
+ /**
+ *TODO tnine Filter this by adding a host supplier. We will get token discovery from cassandra
+ * but only connect
+ * to nodes that have been specified. Good for real time updates of the cass system without adding
+ * load to them during runtime
+ */.withAstyanaxConfiguration( config )
+ .withConnectionPoolConfiguration( connectionPoolConfiguration )
+ .withConnectionPoolMonitor( new Slf4jConnectionPoolMonitorImpl() )
+ .buildKeyspace( ThriftFamilyFactory.getInstance() );
+
+ context.start();
+
+
+ return context.getClient();
+ }
+
+
+ /**
+ * Get runtime options that can be overridden. TODO: Make this an interface and somehow hook it into Guice auotmagically
+ * @return
+ */
+ public static String[] getRuntimeOptions() {
+ return new String[] {
+ CASSANDRA_HOSTS, CASSANDRA_PORT, CASSANDRA_CONNECTIONS, CASSANDRA_CLUSTER_NAME, CASSANDRA_VERSION,
+ COLLECTIONS_KEYSPACE_NAME
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/CollectionModule.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/CollectionModule.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/CollectionModule.java
new file mode 100644
index 0000000..c83fe4f
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/CollectionModule.java
@@ -0,0 +1,64 @@
+package org.apache.usergrid.persistence.collection.guice;
+
+
+import org.apache.usergrid.persistence.collection.astynax.AstynaxKeyspaceProvider;
+import org.apache.usergrid.persistence.collection.migration.Migration;
+import org.apache.usergrid.persistence.collection.migration.MigrationManager;
+import org.apache.usergrid.persistence.collection.migration.MigrationManagerImpl;
+import org.apache.usergrid.persistence.collection.serialization.MvccEntitySerializationStrategy;
+import org.apache.usergrid.persistence.collection.serialization.MvccEntitySerializationStrategyImpl;
+import org.apache.usergrid.persistence.collection.serialization.MvccLogEntrySerializationStrategy;
+import org.apache.usergrid.persistence.collection.serialization.MvccLogEntrySerializationStrategyImpl;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.multibindings.Multibinder;
+import com.google.inject.name.Names;
+import com.netflix.astyanax.Keyspace;
+
+
+/**
+ * Simple module for wiring our collection api
+ *
+ * @author tnine
+ */
+public class CollectionModule extends AbstractModule {
+
+ /**
+ * The location of the properties file
+ */
+ private static final String CASS_PROPS = "cassandra.properties";
+
+
+ @Override
+ protected void configure() {
+
+
+ //bind our cassandra properties
+ Names.bindProperties( binder(), PropertyUtils.loadFromClassPath( CASS_PROPS ) );
+
+ //Load the cassandra url if set on the system properties
+ Names.bindProperties( binder(),
+ PropertyUtils.loadSystemProperties( AstynaxKeyspaceProvider.getRuntimeOptions() ) );
+
+ //bind our keyspace to the AstynaxKeyspaceProvider
+ bind( Keyspace.class ).toProvider( AstynaxKeyspaceProvider.class );
+
+ //bind our migration manager
+ bind(MigrationManager.class).to( MigrationManagerImpl.class );
+
+
+ //bind the serialization strategies
+
+ bind( MvccEntitySerializationStrategy.class ).to( MvccEntitySerializationStrategyImpl.class );
+
+
+ bind( MvccLogEntrySerializationStrategy.class ).to( MvccLogEntrySerializationStrategyImpl.class );
+
+
+ //do multibindings for migrations
+ Multibinder<Migration> uriBinder = Multibinder.newSetBinder( binder(), Migration.class );
+
+ uriBinder.addBinding().to( MvccEntitySerializationStrategyImpl.class );
+ uriBinder.addBinding().to( MvccLogEntrySerializationStrategyImpl.class );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/PropertyUtils.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/PropertyUtils.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/PropertyUtils.java
new file mode 100644
index 0000000..a427135
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/PropertyUtils.java
@@ -0,0 +1,60 @@
+package org.apache.usergrid.persistence.collection.guice;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+
+/**
+ * Simple Utility class to get properties
+ *
+ * @author tnine
+ */
+public class PropertyUtils {
+
+
+ /**
+ * Load the properties file from the classpath. Throws IOException if they cannot be loaded
+ */
+ public static Properties loadFromClassPath( String propsFile ) {
+ InputStream in = PropertyUtils.class.getClassLoader().getResourceAsStream( propsFile );
+
+ if ( in == null ) {
+ throw new RuntimeException( new IOException(
+ String.format( "Could not find properties file on the classpath at location %s", propsFile ) ) );
+ }
+
+ Properties props = new Properties();
+
+ try {
+ props.load( in );
+ }
+ catch ( IOException e ) {
+ throw new RuntimeException( e );
+ }
+
+ return props;
+ }
+
+
+ /**
+ * Load each of the defined properties into a system property and return them. If a system property is not found,
+ * it will be ignored
+ */
+ public static Properties loadSystemProperties( String... properties ) {
+
+ Properties props = new Properties();
+
+ for ( String propName : properties ) {
+ String propValue = System.getProperty( propName );
+
+ if ( propValue != null ) {
+ props.put( propName, propValue );
+ }
+ }
+
+
+ return props;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/CollectionColumnFamily.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/CollectionColumnFamily.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/CollectionColumnFamily.java
new file mode 100644
index 0000000..a8036bd
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/CollectionColumnFamily.java
@@ -0,0 +1,50 @@
+package org.apache.usergrid.persistence.collection.migration;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.netflix.astyanax.model.ColumnFamily;
+
+
+/**
+ * Bean wrapper for column family information
+ *
+ * @author tnine
+ */
+public class CollectionColumnFamily {
+
+ public static final String COMPARATOR_TYPE = "comparator_type";
+ public static final String REVERSED = "reversed";
+ public static final String READ_REPAIR_CHANCE = "read_repair_chance";
+
+
+ private final ColumnFamily columnFamily;
+ private final String comparator;
+ private final boolean reversed;
+
+
+ public CollectionColumnFamily( final ColumnFamily columnFamily, final String comparator, final boolean reversed ) {
+ this.columnFamily = columnFamily;
+ this.comparator = comparator;
+ this.reversed = reversed;
+ }
+
+
+ public Map<String, Object> getOptions(){
+
+ Map<String, Object> options = new HashMap<String, Object>();
+ options.put( COMPARATOR_TYPE, comparator );
+ options.put( REVERSED, reversed );
+
+ //always use 10% read repair chance!
+ options.put( READ_REPAIR_CHANCE, 0.1d );
+
+ return options;
+ }
+
+
+ public ColumnFamily getColumnFamily() {
+ return columnFamily;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/Migration.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/Migration.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/Migration.java
new file mode 100644
index 0000000..f446031
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/Migration.java
@@ -0,0 +1,16 @@
+package org.apache.usergrid.persistence.collection.migration;
+
+
+import java.util.Collection;
+
+
+/**
+ * @author tnine
+ */
+public interface Migration {
+
+ /**
+ * Get the column families required for this implementation. If one does not exist it will be created.
+ */
+ public Collection<CollectionColumnFamily> getColumnFamilies();
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationException.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationException.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationException.java
new file mode 100644
index 0000000..5eed4fd
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationException.java
@@ -0,0 +1,18 @@
+package org.apache.usergrid.persistence.collection.migration;
+
+
+/**
+ * Thrown when a migration cannot be performed
+ * @author tnine
+ */
+public class MigrationException extends Exception {
+
+ public MigrationException( final String message ) {
+ super( message );
+ }
+
+
+ public MigrationException( final String message, final Throwable cause ) {
+ super( message, cause );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManager.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManager.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManager.java
new file mode 100644
index 0000000..d3d7038
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManager.java
@@ -0,0 +1,15 @@
+package org.apache.usergrid.persistence.collection.migration;
+
+
+/**
+ * A manager that will perform any migrations necessary. Setup code should invoke the implementation of this interface
+ *
+ * @author tnine
+ */
+public interface MigrationManager {
+
+ /**
+ * Perform any migration necessary in the application. Will only create keyspaces and column families if they do not exist
+ */
+ public void migrate() throws MigrationException;
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManagerImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManagerImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManagerImpl.java
new file mode 100644
index 0000000..a57dcef
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManagerImpl.java
@@ -0,0 +1,198 @@
+package org.apache.usergrid.persistence.collection.migration;
+
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+import com.netflix.astyanax.Keyspace;
+import com.netflix.astyanax.connectionpool.exceptions.BadRequestException;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+import com.netflix.astyanax.ddl.ColumnFamilyDefinition;
+import com.netflix.astyanax.ddl.KeyspaceDefinition;
+
+
+/**
+ * Implementation of the migration manager to set up keyspace
+ *
+ * @author tnine
+ */
+@Singleton
+public class MigrationManagerImpl implements MigrationManager {
+
+
+ private static final Logger logger = LoggerFactory.getLogger( MigrationManagerImpl.class );
+
+ public static final String STRATEGY_CLASS = "collections.keyspace.strategy.class";
+ public static final String STRATEGY_OPTIONS = "collections.keyspace.strategy.options";
+ public static final String REPLICATION_FACTOR = "collections.keyspace.replicationfactor";
+
+
+ private final String strategyClass;
+ private final String replicationFactor;
+
+
+ private final Set<Migration> migrations;
+ private final Keyspace keyspace;
+ private final Properties props;
+
+
+ @Inject
+ public MigrationManagerImpl( final Keyspace keyspace, final Set<Migration> migrations, final Properties props,
+ @Named( STRATEGY_CLASS ) final String strategyClass,
+ @Named( REPLICATION_FACTOR ) final String replicationFactor ) {
+ this.keyspace = keyspace;
+ this.migrations = migrations;
+ this.props = props;
+ this.strategyClass = strategyClass;
+ this.replicationFactor = replicationFactor;
+ }
+
+
+ @Override
+ public void migrate() throws MigrationException {
+
+
+ try {
+
+ testAndCreateKeyspace();
+
+ for ( Migration migration : migrations ) {
+
+ final Collection<CollectionColumnFamily> columnFamilies = migration.getColumnFamilies();
+
+
+ if(columnFamilies == null){
+ logger.warn( "Class {} implements {} but returns null column families for migration. Either implement this method or remove the interface from the class", migration.getClass(), Migration.class );
+ continue;
+ }
+
+ for ( CollectionColumnFamily cf : columnFamilies) {
+ testAndCreateColumnFamilyDef( cf );
+ }
+ }
+ }
+ catch ( Throwable t ) {
+ logger.error( "Unable to perform migration", t );
+ throw new MigrationException( "Unable to perform migration", t );
+ }
+ }
+
+
+ /**
+ * Check if the column family exists. If it dosn't create it
+ */
+ private void testAndCreateColumnFamilyDef( CollectionColumnFamily columnFamily ) throws ConnectionException {
+ final KeyspaceDefinition keyspaceDefinition = keyspace.describeKeyspace();
+
+ final ColumnFamilyDefinition existing =
+ keyspaceDefinition.getColumnFamily( columnFamily.getColumnFamily().getName() );
+
+ if ( existing != null ) {
+ return;
+ }
+
+ keyspace.createColumnFamily( columnFamily.getColumnFamily(), columnFamily.getOptions() );
+
+ waitForMigration();
+ }
+
+
+ /**
+ * Check if they keyspace exists. If it doesn't create it
+ */
+ private void testAndCreateKeyspace() throws ConnectionException {
+
+
+ KeyspaceDefinition keyspaceDefinition = null;
+
+ try {
+ keyspaceDefinition = keyspace.describeKeyspace();
+ }
+ catch ( BadRequestException badRequestException ) {
+
+ //check if it's b/c the keyspace is missing, if so
+ final String message = badRequestException.getMessage();
+
+ boolean missingKeyspace = message.contains( "why:Keyspace" ) && message.contains( "does not exist" );
+
+ if ( !missingKeyspace ) {
+ throw badRequestException;
+ }
+ }
+
+
+ if ( keyspaceDefinition != null ) {
+ return;
+ }
+
+
+ ImmutableMap.Builder<String, Object> strategyOptions =
+ ImmutableMap.<String, Object>builder().put( "replication_factor", replicationFactor );
+
+ strategyOptions.putAll( getKeySpaceProps() );
+
+
+ ImmutableMap<String, Object> options =
+ ImmutableMap.<String, Object>builder().put( "strategy_class", strategyClass )
+ .put( "strategy_options", strategyOptions.build() ).build();
+
+
+ keyspace.createKeyspace( options );
+
+ waitForMigration();
+ }
+
+
+ /**
+ * Get keyspace properties
+ */
+ private Map<String, String> getKeySpaceProps() {
+ Map<String, String> keyspaceProps = new HashMap<String, String>();
+
+ for ( Map.Entry<Object, Object> entry : props.entrySet() ) {
+ final String key = entry.getKey().toString();
+
+ if ( !key.startsWith( STRATEGY_OPTIONS ) ) {
+ continue;
+ }
+
+ final String optionKey = key.substring( STRATEGY_OPTIONS.length() + 1 );
+
+ keyspaceProps.put( optionKey, entry.getValue().toString() );
+ }
+
+ return keyspaceProps;
+ }
+
+
+ private void waitForMigration() throws ConnectionException {
+
+ while ( true ) {
+
+ final Map<String, List<String>> versions = keyspace.describeSchemaVersions();
+
+ if ( versions != null && versions.size() == 1 ) {
+ return;
+ }
+
+ //sleep and try it again
+ try {
+ Thread.sleep( 100 );
+ }
+ catch ( InterruptedException e ) {
+ //swallow
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntity.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntity.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntity.java
new file mode 100644
index 0000000..3e544ad
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntity.java
@@ -0,0 +1,38 @@
+package org.apache.usergrid.persistence.collection.mvcc.entity;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+import com.google.common.base.Optional;
+
+
+/**
+ * A Marker interface for an in flight update to allow context information to be passed between states
+ */
+public interface MvccEntity {
+
+
+ /**
+ * Get the entity for this context.
+ * @return This will return absent if no data is present. Otherwise the entity will be contained within the optional
+ */
+ Optional<Entity> getEntity();
+
+ /**
+ * Return the version of this entityId we are attempting to write used in the current context
+ */
+ UUID getVersion();
+
+ /**
+ * Get the UUID of the entity
+ */
+ UUID getUuid();
+
+ /**
+ * Get the collection context this entity belongs i
+ */
+ CollectionContext getContext();
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntityImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntityImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntityImpl.java
new file mode 100644
index 0000000..5c01cb5
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntityImpl.java
@@ -0,0 +1,95 @@
+package org.apache.usergrid.persistence.collection.mvcc.entity;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+
+/**
+ * @author tnine
+ */
+public class MvccEntityImpl implements MvccEntity {
+
+ private final CollectionContext context;
+ private final UUID entityId;
+ private final UUID version;
+ private final Optional<Entity> entity;
+
+
+ public MvccEntityImpl( final CollectionContext context, final UUID entityId, final UUID version,
+ final Optional<Entity> entity ) {
+ Preconditions.checkNotNull( context, "context is required" );
+ Preconditions.checkNotNull( entityId, "entity id is required" );
+ Preconditions.checkNotNull( version, "version id is required" );
+ Preconditions.checkNotNull( entity, "entity is required" );
+
+ this.context = context;
+ this.entityId = entityId;
+ this.version = version;
+ this.entity = entity;
+ }
+
+
+ @Override
+ public Optional<Entity> getEntity() {
+ return entity;
+ }
+
+
+ @Override
+ public UUID getVersion() {
+ return version;
+ }
+
+
+ @Override
+ public UUID getUuid() {
+ return entityId;
+ }
+
+
+ @Override
+ public CollectionContext getContext() {
+ return context;
+ }
+
+
+ @Override
+ public boolean equals( final Object o ) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ final MvccEntityImpl that = ( MvccEntityImpl ) o;
+
+ if ( !context.equals( that.context ) ) {
+ return false;
+ }
+ if ( !getUuid().equals( that.getUuid() ) ) {
+ return false;
+ }
+
+ if ( !getVersion().equals( that.getVersion() ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ @Override
+ public int hashCode() {
+ int result = context.hashCode();
+ result = 31 * result + getUuid().hashCode();
+ result = 31 * result + getVersion().hashCode();
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntry.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntry.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntry.java
new file mode 100644
index 0000000..40ff498
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntry.java
@@ -0,0 +1,38 @@
+package org.apache.usergrid.persistence.collection.mvcc.entity;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+
+
+/**
+ * A Marker interface for an in flight update to allow context information to be passed between states
+ */
+public interface MvccLogEntry {
+
+
+ /**
+ * Get the stage for the current version
+ */
+ Stage getStage();
+
+ /**
+ * Get the entity to add info to the log
+ * @return
+ */
+ UUID getEntityId();
+
+ /**
+ * Get the version of the entity
+ * @return
+ */
+ UUID getVersion();
+
+ /**
+ * Get the context of the entity
+ * @return
+ */
+ CollectionContext getContext();
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntryImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntryImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntryImpl.java
new file mode 100644
index 0000000..f0f803b
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntryImpl.java
@@ -0,0 +1,90 @@
+package org.apache.usergrid.persistence.collection.mvcc.entity;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+
+
+/**
+ * The simple implementation of a log entry
+ * @author tnine
+ */
+public class MvccLogEntryImpl implements MvccLogEntry {
+
+ private final CollectionContext context;
+ private final UUID entityId;
+ private final UUID version;
+ private final Stage stage;
+
+
+ public MvccLogEntryImpl(final CollectionContext context, final UUID entityId, final UUID version,
+ final Stage stage ) {
+ this.context = context;
+ this.entityId = entityId;
+ this.version = version;
+ this.stage = stage;
+ }
+
+
+ @Override
+ public Stage getStage() {
+ return stage;
+ }
+
+
+ @Override
+ public UUID getEntityId() {
+ return entityId;
+ }
+
+
+ @Override
+ public UUID getVersion() {
+ return version;
+ }
+
+
+ @Override
+ public CollectionContext getContext() {
+ return context;
+ }
+
+
+ @Override
+ public boolean equals( final Object o ) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ final MvccLogEntryImpl that = ( MvccLogEntryImpl ) o;
+
+ if ( !context.equals( that.context ) ) {
+ return false;
+ }
+ if ( !entityId.equals( that.entityId ) ) {
+ return false;
+ }
+ if ( stage != that.stage ) {
+ return false;
+ }
+ if ( !version.equals( that.version ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ @Override
+ public int hashCode() {
+ int result = context.hashCode();
+ result = 31 * result + entityId.hashCode();
+ result = 31 * result + version.hashCode();
+ result = 31 * result + stage.hashCode();
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/Stage.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/Stage.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/Stage.java
new file mode 100644
index 0000000..96ca3a6
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/Stage.java
@@ -0,0 +1,67 @@
+package org.apache.usergrid.persistence.collection.mvcc.entity;
+
+
+/**
+ * The different stages that can exist in the commit log
+ */
+public enum Stage {
+
+ /**
+ * These are bitmasks that represent the state's we've been through
+ *
+ * Active => 0000
+ * RollBack => 1000
+ * COMMITTED => 1100
+ * POSTPROCESSOR => 1110
+ * ACTIVE => 1111
+ */
+
+ /**
+ * The entity has started writing but is not yet committed
+ */
+ ACTIVE(true, (byte)0),
+
+ /**
+ * The entity has started writing but not yet committed.
+ */
+ ROLLBACK(true, (byte)1),
+ /**
+ * We have applied enough writes to be able to recover via writeahead logging. The system can recover from a
+ * crash without data loss at this point
+ */
+ COMMITTED(false, (byte)2),
+ /**
+ * The entity is going through post processing
+ */
+ POSTPROCESS(false, (byte)6),
+
+ /**
+ * The entity has completed all post processing
+ */
+ COMPLETE(false, (byte)14);
+
+
+ private final boolean transientStage;
+ private final byte id;
+
+
+ private Stage(final boolean transientStage, final byte id){
+ this.transientStage = transientStage;
+ this.id = id;
+ }
+
+
+ /**
+ * Returns true if this stage is transient and should not be retained in the datastore permanently
+ * Stages such as start and write don't need to be retained, but can be used to signal "in flight"
+ * updates
+ */
+ public boolean isTransient() {
+ return transientStage;
+ }
+
+ public byte getId(){
+ return this.id;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/event/PostProcessListener.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/event/PostProcessListener.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/event/PostProcessListener.java
new file mode 100644
index 0000000..47ad997
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/event/PostProcessListener.java
@@ -0,0 +1,24 @@
+package org.apache.usergrid.persistence.collection.mvcc.event;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public interface PostProcessListener<T extends MvccEntity>
+{
+
+
+ /**
+ * The entity was rejected by the MVCC system and will be removed
+ *
+ * @param mvccEntity The mvcc entity to perform post processing on
+ * @return the MvccEntity to use during this stage
+ */
+ public MvccEntity doPostProcessing(MvccEntity mvccEntity );
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Commit.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Commit.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Commit.java
new file mode 100644
index 0000000..64d3c5d
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Commit.java
@@ -0,0 +1,18 @@
+package org.apache.usergrid.persistence.collection.mvcc.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/**
+ * This phase should invoke any finalization, and mark the entity as committed in the data store before returning
+ */
+public class Commit implements WriteStage {
+
+
+
+ @Override
+ public MvccEntity performStage( final MvccEntity entity ) {
+ return entity;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/MvccStrategy.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/MvccStrategy.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/MvccStrategy.java
new file mode 100644
index 0000000..c11f420
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/MvccStrategy.java
@@ -0,0 +1,42 @@
+package org.apache.usergrid.persistence.collection.mvcc.stage;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+
+/**
+ * Interface to define mvcc operations
+ *
+ * TODO: Not sure we need this any more
+ */
+public interface MvccStrategy {
+
+ /**
+ * Start progress through states for this entity
+ *
+ * @param context The context this entity belongs in
+ * @param entityId The entity id to assign to this entity
+ * @param entity The entity values to write
+ */
+ public WriteStage beingWrite( CollectionContext context, UUID entityId, Entity entity );
+
+
+ /**
+ * Get the current stage of the entity in the context at the current version. Should be used for write verification
+ * on resume
+ *
+ * @param context The context this entity belongs in
+ * @param entityId The entity Id to search for in the context
+ * @param version The version of the entityId to review
+ */
+ public WriteStage getCurrentState( CollectionContext context, UUID entityId, UUID version );
+
+
+ /**
+ * Get the write stage of the entity in the context with a version <= the current version and a stage of Comitted
+ */
+ public WriteStage getCurrentStateOfEntity( CollectionContext context, UUID entityId, UUID maxVersion );
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Start.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Start.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Start.java
new file mode 100644
index 0000000..d7f85ae
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Start.java
@@ -0,0 +1,24 @@
+package org.apache.usergrid.persistence.collection.mvcc.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/**
+ * This is the first stage and should be invoked immediately when a write is started. It should persist the start of a
+ * new write in the data store for a checkpoint and recovery
+ */
+public class Start implements WriteStage {
+
+ /**
+ * Create a new stage with the current context
+ */
+ protected Start( ){
+ }
+
+
+ @Override
+ public MvccEntity performStage( final MvccEntity entity ) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Write.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Write.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Write.java
new file mode 100644
index 0000000..b67ca31
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Write.java
@@ -0,0 +1,25 @@
+package org.apache.usergrid.persistence.collection.mvcc.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/**
+ * This phase should execute the serialization to the data store.
+ */
+public class Write implements WriteStage {
+
+ /**
+ * Create a new stage with the current context
+ */
+ protected Write( ){
+ }
+
+
+ @Override
+ public MvccEntity performStage( final MvccEntity entity) {
+
+
+ return entity;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/WriteStage.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/WriteStage.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/WriteStage.java
new file mode 100644
index 0000000..cda707f
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/WriteStage.java
@@ -0,0 +1,20 @@
+package org.apache.usergrid.persistence.collection.mvcc.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/**
+ * The possible stages in our write flow.
+ */
+public interface WriteStage {
+
+ /**
+ * Run this stage. This will return the MvccEntity that should be returned or passed to the next stage
+ * @param entity The entity to use in this stage
+ *
+ * @return The MvccEntity to use for the next sgage
+ *
+ */
+ public MvccEntity performStage( MvccEntity entity);
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/AtomicUpdate.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/AtomicUpdate.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/AtomicUpdate.java
new file mode 100644
index 0000000..d844f3b
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/AtomicUpdate.java
@@ -0,0 +1,26 @@
+package org.apache.usergrid.persistence.collection.mvcc.verify;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/**
+ * Interface to test if we can perform atomic operations
+ * <p/>
+ * Note This will probably require a new WriteStage that is after start, which is rollback
+ */
+public interface AtomicUpdate
+{
+
+ /** Signal that we are starting update */
+ public void startUpdate( MvccEntity context );
+
+ /**
+ * Try the commit.
+ *
+ * @return true if we can proceed. False if we cannot
+ */
+ public boolean tryCommit( MvccEntity context );
+}
+
+
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/OptimisticUpdate.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/OptimisticUpdate.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/OptimisticUpdate.java
new file mode 100644
index 0000000..cef476f
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/OptimisticUpdate.java
@@ -0,0 +1,22 @@
+package org.apache.usergrid.persistence.collection.mvcc.verify;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/** Interface to define how optimistic updates should be performed */
+public interface OptimisticUpdate
+{
+
+ /**
+ * Verify the entity we're trying to write in our current context has the correct most current version
+ *
+ * @param context The mvcc context
+ * @param optimisticVersion The optimistic version the caller provider as the most up to date
+ *
+ * @return True if the optimisticVersion is the most current >= Comitted stage, false otherwise
+ */
+ public boolean verifyCurrent( MvccEntity context, UUID optimisticVersion );
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/UniqueUpdate.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/UniqueUpdate.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/UniqueUpdate.java
new file mode 100644
index 0000000..2df987f
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/UniqueUpdate.java
@@ -0,0 +1,27 @@
+package org.apache.usergrid.persistence.collection.mvcc.verify;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+import org.apache.usergrid.persistence.model.field.Field;
+
+
+/** Interface to define how unique updates should be performed */
+public interface UniqueUpdate
+{
+
+ /**
+ * Verify the entity we're trying to write in our current context has the correct most current version
+ *
+ * @param context The mvcc context
+ * @param uniqueField The field to check for uniqueness
+ *
+ * @return True if the value in the uniqueField is unique in the collection context
+ */
+ public boolean verifyUnique( MvccEntity context, Field<?> uniqueField );
+
+ /**
+ * During the commit phase, ensure this entity is committed as a unique value. This may release locks or overwrite
+ * expiring timeout values since we are at the final commit phase
+ */
+ public void commitUnique( MvccEntity entity, Field<?> uniqueField );
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategy.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategy.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategy.java
new file mode 100644
index 0000000..0d3d112
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategy.java
@@ -0,0 +1,77 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+import com.netflix.astyanax.MutationBatch;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+
+
+/** The interface that allows us to serialize an entity to disk */
+public interface MvccEntitySerializationStrategy
+{
+
+ /**
+ * Serialize the entity to the data store with the given collection context
+ *
+ * @param entity The entity to persist
+ * @return The MutationBatch operations for this update
+ */
+ public MutationBatch write( MvccEntity entity );
+
+
+ /**
+ * Load and return the entity with the given id and a version that is <= the version provided
+ *
+ * @param context The context to persist the entity into
+ * @param entityId The entity id to load
+ * @param version The version to load. This will return the version <= the given version
+ *
+ * @return The deserialized version of the entity. Null if no version == to version exists.
+ * If the entity version has been cleared, the MvccEntity will be returned, but the optional entity
+ * will not be set
+ */
+ public MvccEntity load( CollectionContext context, UUID entityId, UUID version ) throws ConnectionException;
+
+ /**
+ * Load a list, from highest to lowest of the entity with versions <= version up to maxSize elements
+ *
+ * @param context The context to persist the entity into
+ * @param entityId The entity id to load
+ * @param version The max version to seek from. I.E a stored version <= this argument
+ * @param maxSize The maximum size to return. If you receive this size, there may be more versions to load.
+ *
+ * @return A list of entities up to max size ordered from max(UUID)=> min(UUID). The return value should be null safe
+ * and return an empty list when there are no matches
+ */
+ public List<MvccEntity> load( CollectionContext context, UUID entityId, UUID version, int maxSize );
+
+
+ /**
+ * Clear this version from the persistence store, but keep the version to mark that is has been cleared
+ * This can be used in a mark+sweep system. The entity with the given version will exist in the context,
+ * but no data will be stored
+ *
+ * @param context
+ * @param entityId
+ * @param version
+ * @return
+ */
+ public MutationBatch clear(CollectionContext context, UUID entityId, UUID version);
+
+
+ /**
+ * Delete the entity from the context with the given entityId and version
+ *
+ * @param context The context that contains the entity
+ * @param entityId The entity id to delete
+ * @param version The version to delete
+ */
+ public MutationBatch delete( CollectionContext context, UUID entityId, UUID version );
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImpl.java
new file mode 100644
index 0000000..4b8840d
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImpl.java
@@ -0,0 +1,252 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.cassandra.db.marshal.ReversedType;
+import org.apache.cassandra.db.marshal.UUIDType;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.collection.migration.CollectionColumnFamily;
+import org.apache.usergrid.persistence.collection.migration.Migration;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntityImpl;
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.netflix.astyanax.ColumnListMutation;
+import com.netflix.astyanax.Keyspace;
+import com.netflix.astyanax.MutationBatch;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+import com.netflix.astyanax.connectionpool.exceptions.NotFoundException;
+import com.netflix.astyanax.model.Column;
+import com.netflix.astyanax.model.ColumnFamily;
+import com.netflix.astyanax.model.ColumnList;
+import com.netflix.astyanax.model.ColumnSlice;
+import com.netflix.astyanax.serializers.AbstractSerializer;
+import com.netflix.astyanax.serializers.ObjectSerializer;
+import com.netflix.astyanax.serializers.UUIDSerializer;
+
+
+/**
+ * @author tnine
+ */
+@Singleton
+public class MvccEntitySerializationStrategyImpl implements MvccEntitySerializationStrategy, Migration {
+
+
+ private static final EntitySerializer SER = new EntitySerializer();
+
+
+ private static final ColumnFamily<UUID, UUID> CF_ENTITY_DATA =
+ new ColumnFamily<UUID, UUID>( "Entity_Version_Data", UUIDSerializer.get(), UUIDSerializer.get() );
+
+
+ protected final Keyspace keyspace;
+
+
+ @Inject
+ public MvccEntitySerializationStrategyImpl( final Keyspace keyspace ) {
+ this.keyspace = keyspace;
+ }
+
+
+ @Override
+ public MutationBatch write( final MvccEntity entity ) {
+ Preconditions.checkNotNull( entity, "entity is required" );
+
+ final UUID colName = entity.getVersion();
+ final UUID entityId = entity.getUuid();
+
+ final Optional<Entity> colValue = entity.getEntity();
+
+ return doWrite( entityId, new RowOp() {
+ @Override
+ public void doOp( final ColumnListMutation<UUID> colMutation ) {
+ colMutation.putColumn( colName, SER.toByteBuffer( colValue ) );
+ }
+ } );
+ }
+
+
+ @Override
+ public MvccEntity load( final CollectionContext context, final UUID entityId, final UUID version )
+ throws ConnectionException {
+ Preconditions.checkNotNull( context, "context is required" );
+ Preconditions.checkNotNull( entityId, "entity id is required" );
+ Preconditions.checkNotNull( version, "version is required" );
+
+
+ final UUID start = null;
+ final UUID end = null;
+
+ Column<UUID> column;
+
+ try {
+ column = keyspace.prepareQuery( CF_ENTITY_DATA ).getKey( entityId )
+ .getColumn( version ).execute().getResult();
+ }
+
+ catch ( NotFoundException e ) {
+ //swallow, there's just no column
+ return null;
+ }
+
+ if ( column == null ) {
+ return null;
+ }
+
+
+ return new MvccEntityImpl( context, entityId, version, column.getValue(SER) );
+
+ }
+
+
+ @Override
+ public List<MvccEntity> load( final CollectionContext context, final UUID entityId, final UUID version,
+ final int maxSize ) {
+
+ Preconditions.checkNotNull( context, "context is required" );
+ Preconditions.checkNotNull( entityId, "entity id is required" );
+ Preconditions.checkNotNull( version, "version is required" );
+ Preconditions.checkArgument( maxSize > 0, "max Size must be greater than 0" );
+
+
+ ColumnList<UUID> columns;
+
+ try {
+
+
+ columns = keyspace.prepareQuery( CF_ENTITY_DATA ).getKey( entityId )
+ .withColumnRange( version, null, false, maxSize ).execute().getResult();
+ }
+
+ catch ( ConnectionException e ) {
+ throw new RuntimeException( "Unable to load column data", e );
+ }
+
+ if ( columns == null ) {
+ return Collections.EMPTY_LIST;
+ }
+
+ List<MvccEntity> results = new ArrayList<MvccEntity>( columns.size() );
+
+ for ( Column<UUID> col : columns ) {
+ results.add( new MvccEntityImpl( context, entityId, col.getName(), col.getValue( SER ) ) );
+ }
+
+ return results;
+ }
+
+
+ @Override
+ public MutationBatch clear( final CollectionContext context, final UUID entityId, final UUID version ) {
+ Preconditions.checkNotNull( context, "context is required" );
+ Preconditions.checkNotNull( entityId, "entity id is required" );
+ Preconditions.checkNotNull( version, "version is required" );
+
+ final Optional<Entity> value = Optional.absent();
+
+ return doWrite( entityId, new RowOp() {
+ @Override
+ public void doOp( final ColumnListMutation<UUID> colMutation ) {
+ colMutation.putColumn( version, SER.toByteBuffer( value ) );
+ }
+ } );
+ }
+
+
+ @Override
+ public MutationBatch delete( final CollectionContext context, final UUID entityId, final UUID version ) {
+
+ return doWrite( entityId, new RowOp() {
+ @Override
+ public void doOp( final ColumnListMutation<UUID> colMutation ) {
+ colMutation.deleteColumn( version );
+ }
+ } );
+ }
+
+
+ @Override
+ public Collection<CollectionColumnFamily> getColumnFamilies() {
+
+ //create the CF entity data. We want it reversed b/c we want the most recent version at the top of the
+ //row for fast seeks
+ CollectionColumnFamily cf = new CollectionColumnFamily( CF_ENTITY_DATA,
+ ReversedType.class.getName() + "(" + UUIDType.class.getName() + ")", true );
+
+
+ return Collections.singleton( cf );
+ }
+
+
+ /**
+ * Do the write on the correct row for the entity id with the operation
+ */
+ private MutationBatch doWrite( UUID entityId, RowOp op ) {
+ final MutationBatch batch = keyspace.prepareMutationBatch();
+
+ op.doOp( batch.withRow( CF_ENTITY_DATA, entityId ) );
+
+ return batch;
+ }
+
+
+ /**
+ * Simple callback to perform puts and deletes with a common row setup code
+ */
+ private static interface RowOp {
+
+ /**
+ * The operation to perform on the row
+ */
+ void doOp( ColumnListMutation<UUID> colMutation );
+ }
+
+
+ /**
+ * TODO: Serializer for the entity. This just uses object serialization, change this to use SMILE before production!
+ * We want to retain the Optional wrapper. It helps us easily mark something as cleaned without removing the data
+ */
+ private static class EntitySerializer extends AbstractSerializer<Optional<Entity>> {
+
+ private static final ObjectSerializer SER = ObjectSerializer.get();
+
+ //the marker for when we're passed a "null" value
+ private static final byte[] EMPTY = new byte[] { 0x0 };
+
+
+ @Override
+ public ByteBuffer toByteBuffer( final Optional<Entity> obj ) {
+
+ //mark this version as empty
+ if ( !obj.isPresent() ) {
+ return ByteBuffer.wrap( EMPTY );
+ }
+
+ return SER.toByteBuffer( obj.get() );
+ }
+
+
+ @Override
+ public Optional<Entity> fromByteBuffer( final ByteBuffer byteBuffer ) {
+
+ final ByteBuffer check = byteBuffer.duplicate();
+
+ if ( check.remaining() == 1 && check.get() == EMPTY[0] ) {
+ return Optional.absent();
+ }
+
+ return Optional.of( ( Entity ) SER.fromByteBuffer( byteBuffer ) );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategy.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategy.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategy.java
new file mode 100644
index 0000000..a249522
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategy.java
@@ -0,0 +1,59 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccLogEntry;
+
+import com.netflix.astyanax.MutationBatch;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+
+
+/** The interface that allows us to serialize a log entry to disk */
+public interface MvccLogEntrySerializationStrategy
+{
+
+ /**
+ * Serialize the entity to the data store with the given collection context
+ *
+ * @param entry the entry to write
+ * @return The mutation batch with the mutation operations for this write.
+ */
+ public MutationBatch write( MvccLogEntry entry );
+
+ /**
+ * Load and return the stage with the given id and a version that is <= the version provided
+ *
+ *
+ * @param context The context to persist the entity into
+ * @param entityId The entity id to load
+ * @param version The version to load. This will return the version <= the given version
+ *
+ * @return The deserialized version of the entity. Null if no version <= the current version exists, or the entity does not exist
+ */
+ public MvccLogEntry load( final CollectionContext context, final UUID entityId, final UUID version )
+ throws ConnectionException;
+
+ /**
+ * Load a list, from highest to lowest of the stage with versions <= version up to maxSize elements
+ *
+ * @param context The context to persist the entity into
+ * @param entityId The entity id to load
+ * @param version The max version to seek from
+ * @param maxSize The maximum size to return. If you receive this size, there may be more versions to load.
+ *
+ * @return A list of entities up to max size ordered from max(UUID)=> min(UUID)
+ */
+ public List<MvccLogEntry> load( CollectionContext context, UUID entityId, UUID version, int maxSize );
+
+ /**
+ * Delete the stage from the context with the given entityId and version
+ *
+ * @param context The context that contains the entity
+ * @param entityId The entity id to delete
+ * @param version The version to delete
+ */
+ public MutationBatch delete( CollectionContext context, UUID entityId, UUID version );
+}