DATACASS-164: app & test code done

This commit is contained in:
Matthew Adams
2014-09-17 09:38:10 -05:00
parent 347accc8d6
commit 5f98a05d8b
4 changed files with 487 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
package org.springframework.data.cassandra.repository.support;
import java.lang.reflect.Proxy;
import org.springframework.data.cassandra.repository.MapId;
import org.springframework.util.Assert;
@SuppressWarnings("unchecked")
public class MapIdFactory {
public static <T extends MapId> T id(Class<T> idInterface) {
Assert.notNull(idInterface);
return id(idInterface, idInterface.getClassLoader());
}
public static <T extends MapId> T id(Class<T> idInterface, ClassLoader loader) {
return (T) Proxy.newProxyInstance(loader, new Class<?>[] { idInterface }, new MapIdProxyDelegate(idInterface));
}
}

View File

@@ -0,0 +1,160 @@
package org.springframework.data.cassandra.repository.support;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.springframework.data.cassandra.repository.MapId;
import org.springframework.util.StringUtils;
class MapIdProxyDelegate implements InvocationHandler {
static class Signature {
String name;
Class<?>[] argTypes;
Class<?> returnType;
Signature(Method method, boolean includeReturnType) {
this(method.getName(), method.getParameterTypes(), includeReturnType ? method.getReturnType() : null);
}
Signature(String name, Class<?>[] argTypes, Class<?> returnType) {
this.name = name;
this.argTypes = argTypes;
this.returnType = returnType;
}
@Override
public String toString() {
return "Signature [name=" + name + ", argTypes=" + Arrays.toString(argTypes) + ", returnType=" + returnType + "]";
}
@Override
public boolean equals(Object that) {
if (that == null) {
return false;
}
if (this == that) {
return true;
}
if (!(that instanceof Signature)) {
return false;
}
Signature that_ = (Signature) that;
if (!this.name.equals(that_.name)) {
return false;
}
if ((this.argTypes == null && that_.argTypes != null) || (this.argTypes != null && that_.argTypes == null)) {
return false;
}
if (this.argTypes != null) {
if (this.argTypes.length != that_.argTypes.length) {
return false;
}
for (int i = 0; i < this.argTypes.length; i++) {
if (!this.argTypes[i].equals(that_.argTypes[i])) {
return false;
}
}
}
if (this.returnType == null) {
return that_.returnType == null;
}
return this.returnType.equals(that_.returnType);
}
@Override
public int hashCode() {
return name.hashCode() ^ (argTypes != null ? argTypes.hashCode() : 0)
^ (returnType != null ? returnType.hashCode() : 0);
}
}
private static final Signature[] MAP_ID_SIGNATURES;
static {
Method[] mapIdMethods = MapId.class.getMethods();
MAP_ID_SIGNATURES = new Signature[mapIdMethods.length];
int i = 0;
for (Method m : mapIdMethods) {
MAP_ID_SIGNATURES[i++] = new Signature(m, true);
}
}
MapId delegate = new BasicMapId();
Class<?> idInterface;
MapIdProxyDelegate(Class<?> idInterface) {
this.idInterface = idInterface;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (isMapIdMethod(method)) {
return method.invoke(delegate, args);
}
if (args != null && args.length > 1) {
throw new IllegalArgumentException(String.format("Method [%s] on interface [%s] must take zero or one argument",
method, idInterface));
}
boolean isSetter = args != null && args.length == 1;
String name = method.getName();
if (isSetter) {
handleSetter(name, args[0]);
return void.class.equals(method.getReturnType()) ? null : proxy;
}
return handleGetter(name);
}
private boolean isMapIdMethod(Method method) {
for (Signature mapIdSignature : MAP_ID_SIGNATURES) {
if (mapIdSignature.equals(new Signature(method, true))) {
return true;
}
}
return false;
}
private Serializable handleGetter(String name) {
if (name.startsWith("get")) {
if (name.length() == 3) {
throw new IllegalArgumentException(String.format("Method [%s] on interface [%s] must be of form "
+ "'<PropertyType> get<PropertyName>()' or " + "'<PropertyType> <propertyName>()'", name, idInterface));
}
name = StringUtils.uncapitalize(name.substring(3));
}
return delegate.get(name);
}
private void handleSetter(String name, Object value) {
int minLength = 1;
boolean isSet = name.startsWith("set");
boolean isWith = name.startsWith("with");
minLength += isSet ? 3 : isWith ? 4 : 0;
int length = name.length();
if (isSet || isWith) {
if (length < minLength) {
throw new IllegalArgumentException(String.format("Method [%s] on interface [%s] must be of form "
+ "'<IdType|void> set<PropertyName>(<PropertyType>)', "
+ "'<IdType|void> with<PropertyName>(<PropertyType>)' or "
+ "'<IdType|void> <propertyName>(<PropertyType>)'", name, idInterface));
}
name = StringUtils.uncapitalize(name.substring(minLength - 1));
}
if (value == null) {
delegate.put(name, null);
return;
}
if (!(value instanceof Serializable)) {
throw new IllegalArgumentException(String.format("Given object [%s] must implement %s", value,
Serializable.class.getName()));
}
delegate.put(name, (Serializable) value);
}
}

View File

@@ -0,0 +1,210 @@
package org.springframework.data.cassandra.test.integration.mapping.mapid.proxy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.springframework.data.cassandra.repository.support.MapIdFactory.id;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.mapping.Column;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Table;
import org.springframework.data.cassandra.repository.MapId;
import org.springframework.data.cassandra.test.integration.support.AbstractSpringDataEmbeddedCassandraIntegrationTest;
import org.springframework.data.cassandra.test.integration.support.IntegrationTestConfig;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class CassandraTemplateMapIdProxyDelegateIntegrationTest extends
AbstractSpringDataEmbeddedCassandraIntegrationTest {
@Configuration
public static class Config extends IntegrationTestConfig {
@Override
public String[] getEntityBasePackages() {
return new String[] { SinglePkc.class.getPackage().getName() };
}
}
@Autowired
CassandraOperations t;
@Before
public void before() {
assertNotNull(t);
}
@Test
public void testSinglePkc() {
// insert
SinglePkc inserted = new SinglePkc(uuid());
inserted.setValue(uuid());
SinglePkc saved = t.insert(inserted);
assertSame(saved, inserted);
// select
SinglePkcId id = id(SinglePkcId.class).key(saved.getKey());
SinglePkc selected = t.selectOneById(SinglePkc.class, id);
assertNotSame(selected, saved);
assertEquals(saved.getKey(), selected.getKey());
assertEquals(saved.getValue(), selected.getValue());
// update
selected.setValue(uuid());
SinglePkc updated = t.update(selected);
assertSame(updated, selected);
selected = t.selectOneById(SinglePkc.class, id);
assertNotSame(selected, updated);
assertEquals(updated.getValue(), selected.getValue());
// delete
t.delete(selected);
assertNull(t.selectOneById(SinglePkc.class, id));
}
public interface SinglePkcId extends MapId {
SinglePkcId key(String key);
String key();
}
@Table
public static class SinglePkc {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED)
String key;
@Column
String value;
/**
* @deprecated for persistence use only
*/
@Deprecated
@SuppressWarnings("unused")
private SinglePkc() {}
public SinglePkc(String key) {
setKey(key);
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
@Test
public void testMultiPkc() {
// insert
MultiPkc inserted = new MultiPkc(uuid(), uuid());
inserted.setValue(uuid());
MultiPkc saved = t.insert(inserted);
assertSame(saved, inserted);
// select
MultiPkcId id = id(MultiPkcId.class).key0(saved.getKey0()).key1(saved.getKey1());
MultiPkc selected = t.selectOneById(MultiPkc.class, id);
assertNotSame(selected, saved);
assertEquals(saved.getKey0(), selected.getKey0());
assertEquals(saved.getKey1(), selected.getKey1());
assertEquals(saved.getValue(), selected.getValue());
// update
selected.setValue(uuid());
MultiPkc updated = t.update(selected);
assertSame(updated, selected);
selected = t.selectOneById(MultiPkc.class, id);
assertNotSame(selected, updated);
assertEquals(updated.getValue(), selected.getValue());
// delete
t.delete(selected);
assertNull(t.selectOneById(MultiPkc.class, id));
}
public interface MultiPkcId extends MapId {
MultiPkcId key0(String key0);
String key0();
MultiPkcId key1(String key1);
String key1();
}
@Table
public static class MultiPkc {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED)
String key0;
@PrimaryKeyColumn(ordinal = 1)
String key1;
@Column
String value;
/**
* @deprecated for persistence use only
*/
@Deprecated
@SuppressWarnings("unused")
private MultiPkc() {}
public MultiPkc(String key0, String key1) {
setKey0(key0);
setKey1(key1);
}
public String getKey0() {
return key0;
}
public void setKey0(String key0) {
this.key0 = key0;
}
public String getKey1() {
return key1;
}
public void setKey1(String key1) {
this.key1 = key1;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}

View File

@@ -0,0 +1,98 @@
package org.springframework.data.cassandra.test.unit.mapidfactory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.springframework.data.cassandra.repository.support.MapIdFactory.id;
import java.util.Random;
import org.junit.Test;
import org.springframework.data.cassandra.repository.MapId;
public class MapIdFactoryTest {
static interface MyId extends MapId {
MyId string(String s);
void setString(String s);
MyId withString(String s);
String string();
String getString();
MyId number(Integer i);
void setNumber(Integer i);
Integer number();
Integer getNumber();
}
@Test
public void test() {
Random r = new Random();
String s = "" + r.nextInt();
Integer i = new Integer(r.nextInt());
MyId id = id(MyId.class);
assertNull(id.string());
assertNull(id.number());
assertNull(id.getString());
assertNull(id.getNumber());
id.setNumber(i);
assertEquals(i, id.getNumber());
assertEquals(i, id.number());
assertEquals(i, id.get("number"));
MyId returned = null;
returned = id.number(i = r.nextInt());
assertSame(returned, id);
assertEquals(i, id.getNumber());
assertEquals(i, id.number());
assertEquals(i, id.get("number"));
id.put("number", i = r.nextInt());
assertEquals(i, id.getNumber());
assertEquals(i, id.number());
assertEquals(i, id.get("number"));
id.setString(s);
assertEquals(s, id.getString());
assertEquals(s, id.string());
assertEquals(s, id.get("string"));
returned = id.string(s = "" + r.nextInt());
assertSame(returned, id);
assertEquals(s, id.getString());
assertEquals(s, id.string());
assertEquals(s, id.get("string"));
returned = id.withString(s = "" + r.nextInt());
assertSame(returned, id);
assertEquals(s, id.getString());
assertEquals(s, id.string());
assertEquals(s, id.get("string"));
id.put("string", s = "" + r.nextInt());
assertEquals(s, id.getString());
assertEquals(s, id.string());
assertEquals(s, id.get("string"));
id.setString(null);
assertNull(id.getString());
assertNull(id.string());
assertNull(id.get("string"));
id.setNumber(null);
assertNull(id.getNumber());
assertNull(id.number());
assertNull(id.get("number"));
}
}