/*
* Copyright 2014 Basho Technologies Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.basho.riak.client.api.commands.itest;
import com.basho.riak.client.api.cap.ConflictResolver;
import com.basho.riak.client.api.cap.ConflictResolverFactory;
import com.basho.riak.client.api.cap.UnresolvedConflictException;
import com.basho.riak.client.api.convert.ConversionException;
import com.basho.riak.client.api.convert.Converter;
import com.basho.riak.client.api.convert.ConverterFactory;
import com.basho.riak.client.core.operations.itest.ITestBase;
import com.basho.riak.client.api.commands.kv.FetchValue;
import com.basho.riak.client.api.RiakClient;
import com.basho.riak.client.api.annotations.RiakVClock;
import com.basho.riak.client.api.cap.VClock;
import com.basho.riak.client.api.convert.RiakJacksonModule;
import com.basho.riak.client.core.operations.StoreBucketPropsOperation;
import com.basho.riak.client.api.commands.kv.StoreValue.Option;
import com.basho.riak.client.api.commands.kv.StoreValue;
import com.basho.riak.client.api.commands.kv.UpdateValue;
import com.basho.riak.client.api.commands.kv.UpdateValue.Update;
import com.basho.riak.client.core.query.Location;
import com.basho.riak.client.core.query.Namespace;
import com.basho.riak.client.core.query.RiakObject;
import com.basho.riak.client.core.util.BinaryValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author Brian Roach <roach at basho dot com>
*/
public class ITestORM extends ITestBase
{
@Before
public void resetFactory()
{
TypeReference<GenericPojo<Integer>> tr =
new TypeReference<GenericPojo<Integer>>(){};
ConverterFactory.getInstance().unregisterConverterForClass(tr);
ConflictResolverFactory.getInstance().unregisterConflictResolver(tr);
}
@Test
public void storeParamterizedTypeJSON() throws ExecutionException, InterruptedException, JsonProcessingException
{
RiakClient client = new RiakClient(cluster);
Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString());
Location loc = new Location(ns, "test_ORM_key1");
GenericPojo<Foo> gpf = new GenericPojo<Foo>();
List<Foo> fooList = new ArrayList<Foo>();
fooList.add(new Foo("Foo in list value"));
gpf.value = new Foo("Foo in gp value");
gpf.list = fooList;
StoreValue sv =
new StoreValue.Builder(gpf)
.withLocation(loc)
.withOption(Option.RETURN_BODY, true)
.build();
StoreValue.Response resp = client.execute(sv);
TypeReference<GenericPojo<Foo>> tr =
new TypeReference<GenericPojo<Foo>>(){};
GenericPojo<Foo> gpf2 = resp.getValue(tr);
assertNotNull(gpf2);
assertNotNull(gpf2.value);
assertEquals(gpf.value, gpf2.value);
assertNotNull(gpf2.list);
assertTrue(gpf.list.containsAll(gpf2.list));
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new RiakJacksonModule());
String json = mapper.writeValueAsString(gpf2);
RiakObject ro = resp.getValue(RiakObject.class);
assertEquals(json, ro.getValue().toString());
}
@Test
public void storeAndFetchParamterizedTypeJSON() throws ExecutionException, InterruptedException, JsonProcessingException
{
RiakClient client = new RiakClient(cluster);
Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString());
Location loc = new Location(ns, "test_ORM_key2");
GenericPojo<Foo> gpf = new GenericPojo<Foo>();
List<Foo> fooList = new ArrayList<Foo>();
fooList.add(new Foo("Foo in list value"));
gpf.value = new Foo("Foo in gp value");
gpf.list = fooList;
StoreValue sv =
new StoreValue.Builder(gpf)
.withLocation(loc)
.build();
client.execute(sv);
FetchValue fv = new FetchValue.Builder(loc).build();
FetchValue.Response resp = client.execute(fv);
TypeReference<GenericPojo<Foo>> tr =
new TypeReference<GenericPojo<Foo>>(){};
GenericPojo<Foo> gpf2 = resp.getValue(tr);
assertNotNull(gpf2);
assertNotNull(gpf2.value);
assertEquals(gpf.value, gpf2.value);
assertNotNull(gpf2.list);
assertTrue(gpf.list.containsAll(gpf2.list));
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new RiakJacksonModule());
String json = mapper.writeValueAsString(gpf2);
RiakObject ro = resp.getValue(RiakObject.class);
assertEquals(json, ro.getValue().toString());
}
@Test
public void updateParameterizedTypeJSON() throws ExecutionException, InterruptedException, JsonProcessingException
{
RiakClient client = new RiakClient(cluster);
Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString());
Location loc = new Location(ns, "test_ORM_key3");
MyUpdate update = new MyUpdate();
TypeReference<GenericPojo<Integer>> tr =
new TypeReference<GenericPojo<Integer>>(){};
UpdateValue uv = new UpdateValue.Builder(loc)
.withUpdate(update, tr)
.withStoreOption(Option.RETURN_BODY, true)
.build();
UpdateValue.Response resp = client.execute(uv);
GenericPojo<Integer> gpi = resp.getValue(tr);
assertNotNull(gpi);
assertNotNull(gpi.value);
assertEquals(1, gpi.value.intValue());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new RiakJacksonModule());
String json = mapper.writeValueAsString(gpi);
RiakObject ro = resp.getValue(RiakObject.class);
assertEquals(json, ro.getValue().toString());
resp = client.execute(uv);
gpi = resp.getValue(tr);
assertNotNull(gpi);
assertNotNull(gpi.value);
assertEquals(2, gpi.value.intValue());
}
@Test
public void updateAndResolveParameterizedTypeJSON() throws ExecutionException, InterruptedException
{
// We're back to allow_mult=false as default
Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString());
StoreBucketPropsOperation op =
new StoreBucketPropsOperation.Builder(ns)
.withAllowMulti(true)
.build();
cluster.execute(op);
op.get();
RiakClient client = new RiakClient(cluster);
Location loc = new Location(ns, "test_ORM_key4");
MyUpdate update = new MyUpdate();
TypeReference<GenericPojo<Integer>> tr =
new TypeReference<GenericPojo<Integer>>(){};
UpdateValue uv = new UpdateValue.Builder(loc)
.withUpdate(update, tr)
.build();
client.execute(uv);
// Create a sibling
GenericPojo<Integer> gpi = update.apply(null);
StoreValue sv =
new StoreValue.Builder(gpi)
.withLocation(loc)
.build();
client.execute(sv);
ConflictResolverFactory.getInstance().registerConflictResolver(tr, new MyResolver());
uv = new UpdateValue.Builder(loc)
.withUpdate(update, tr)
.withStoreOption(Option.RETURN_BODY, true)
.build();
UpdateValue.Response uvResp = client.execute(uv);
gpi = uvResp.getValue(tr);
assertNotNull(gpi);
assertNotNull(gpi.value);
assertEquals(3, gpi.value.intValue());
}
@Test
public void updateAndResolveParameterizedTypeCustom() throws ExecutionException, InterruptedException
{
// We're back to allow_mult=false as default
Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString());
StoreBucketPropsOperation op =
new StoreBucketPropsOperation.Builder(ns)
.withAllowMulti(true)
.build();
cluster.execute(op);
op.get();
RiakClient client = new RiakClient(cluster);
Location loc = new Location(ns, "test_ORM_key5");
TypeReference<GenericPojo<Integer>> tr =
new TypeReference<GenericPojo<Integer>>(){};
ConflictResolverFactory.getInstance().registerConflictResolver(tr, new MyResolver());
ConverterFactory.getInstance().registerConverterForClass(tr, new MyConverter(tr.getType()));
MyUpdate update = new MyUpdate();
UpdateValue uv = new UpdateValue.Builder(loc)
.withUpdate(update, tr)
.build();
client.execute(uv);
// Create a sibling
GenericPojo<Integer> gpi = update.apply(null);
StoreValue sv =
new StoreValue.Builder(gpi, tr)
.withLocation(loc)
.build();
client.execute(sv);
uv = new UpdateValue.Builder(loc)
.withUpdate(update, tr)
.withStoreOption(Option.RETURN_BODY, true)
.build();
UpdateValue.Response uvResp = client.execute(uv);
gpi = uvResp.getValue(tr);
assertNotNull(gpi);
assertNotNull(gpi.value);
assertEquals(3, gpi.value.intValue());
// Check to see that the custom conversion is right
RiakObject ro = uvResp.getValue(RiakObject.class);
assertEquals("3", ro.getValue().toString());
}
@Test
public void updateAndResolveRawTypeJSON() throws ExecutionException, InterruptedException, JsonProcessingException
{
RiakClient client = new RiakClient(cluster);
Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString());
Location loc = new Location(ns, "test_ORM_key6");
ConflictResolverFactory.getInstance().registerConflictResolver(Foo.class, new MyFooResolver());
MyFooUpdate update = new MyFooUpdate();
UpdateValue uv = new UpdateValue.Builder(loc)
.withUpdate(update)
.build();
client.execute(uv);
// Create a sibling
Foo f = update.apply(null);
StoreValue sv =
new StoreValue.Builder(f)
.withLocation(loc)
.build();
client.execute(sv);
uv = new UpdateValue.Builder(loc)
.withUpdate(update)
.withStoreOption(Option.RETURN_BODY, true)
.build();
UpdateValue.Response uvResp = client.execute(uv);
f = uvResp.getValue(Foo.class);
assertNotNull(f);
assertEquals("Little bunny", f.fooValue);
uv = new UpdateValue.Builder(loc)
.withUpdate(update)
.withStoreOption(Option.RETURN_BODY, true)
.build();
uvResp = client.execute(uv);
f = uvResp.getValue(Foo.class);
assertNotNull(f);
assertEquals("Little bunny foo foo.", f.fooValue);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new RiakJacksonModule());
String json = mapper.writeValueAsString(f);
RiakObject ro = uvResp.getValue(RiakObject.class);
assertEquals(json, ro.getValue().toString());
}
@Test
public void updateAndResolveRawTypeCustom() throws ExecutionException, InterruptedException
{
RiakClient client = new RiakClient(cluster);
Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString());
Location loc = new Location(ns, "test_ORM_key7");
ConflictResolverFactory.getInstance().registerConflictResolver(Foo.class, new MyFooResolver());
ConverterFactory.getInstance().registerConverterForClass(Foo.class, new MyFooConverter());
MyFooUpdate update = new MyFooUpdate();
UpdateValue uv = new UpdateValue.Builder(loc)
.withUpdate(update)
.build();
client.execute(uv);
// Create a sibling
Foo f = update.apply(null);
StoreValue sv =
new StoreValue.Builder(f)
.withLocation(loc)
.build();
client.execute(sv);
uv = new UpdateValue.Builder(loc)
.withUpdate(update)
.withStoreOption(Option.RETURN_BODY, true)
.build();
UpdateValue.Response uvResp = client.execute(uv);
f = uvResp.getValue(Foo.class);
assertNotNull(f);
assertEquals("Little bunny", f.fooValue);
uv = new UpdateValue.Builder(loc)
.withUpdate(update)
.withStoreOption(Option.RETURN_BODY, true)
.build();
uvResp = client.execute(uv);
f = uvResp.getValue(Foo.class);
assertNotNull(f);
assertEquals("Little bunny foo foo.", f.fooValue);
RiakObject ro = uvResp.getValue(RiakObject.class);
assertEquals("Little bunny foo foo.", ro.getValue().toString());
}
@Test
public void updateRiakObject() throws ExecutionException, InterruptedException
{
RiakClient client = new RiakClient(cluster);
Namespace ns = new Namespace(Namespace.DEFAULT_BUCKET_TYPE, bucketName.toString());
Location loc = new Location(ns, "test_ORM_key9");
UpdateValue uv = new UpdateValue.Builder(loc)
.withUpdate(new Update<RiakObject>(){
@Override
public RiakObject apply(RiakObject original)
{
return new RiakObject().setValue(BinaryValue.create("value"));
}
})
.withStoreOption(Option.RETURN_BODY, true)
.build();
UpdateValue.Response response = client.execute(uv);
RiakObject ro = response.getValues().get(0);
assertNotNull(ro.getVClock());
}
public static class GenericPojo<T>
{
public T value;
public List<T> list;
@RiakVClock
public VClock vclock;
}
public static class Foo
{
public String fooValue;
@RiakVClock
public VClock vclock;
public Foo() {}
public Foo(String v)
{
this.fooValue = v;
}
@Override
public int hashCode()
{
int hash = 7;
hash = 89 * hash + (this.fooValue != null ? this.fooValue.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object o)
{
Foo other = (Foo)o;
return fooValue.equals(other.fooValue);
}
}
public static class MyUpdate extends Update<GenericPojo<Integer>>
{
@Override
public GenericPojo<Integer> apply(GenericPojo<Integer> original)
{
if (original == null)
{
original = new GenericPojo<Integer>();
original.value = 1;
}
else
{
original.value++;
}
return original;
}
}
public static class MyResolver implements ConflictResolver<GenericPojo<Integer>>
{
@Override
public GenericPojo<Integer> resolve(List<GenericPojo<Integer>> objectList) throws UnresolvedConflictException
{
int total = 0;
for (GenericPojo<Integer> gpi : objectList)
{
total += gpi.value;
}
GenericPojo<Integer> newObj = new GenericPojo<Integer>();
newObj.value = total;
return newObj;
}
}
public static class MyConverter extends Converter<GenericPojo<Integer>>
{
public MyConverter(Type t)
{
super(t);
}
@Override
public GenericPojo<Integer> toDomain(BinaryValue value, String contentType) throws ConversionException
{
GenericPojo<Integer> gpi = new GenericPojo<Integer>();
gpi.value = Integer.valueOf(value.toString());
return gpi;
}
@Override
public ContentAndType fromDomain(GenericPojo<Integer> domainObject) throws ConversionException
{
return new ContentAndType(BinaryValue.create(String.valueOf(domainObject.value)), "text/plain");
}
}
public class MyFooUpdate extends Update<Foo>
{
@Override
public Foo apply(Foo original)
{
if (original == null)
{
original = new Foo("Little");
}
else
{
if (original.fooValue.endsWith("Little"))
{
original.fooValue = original.fooValue + " bunny";
}
else if (original.fooValue.endsWith("bunny"))
{
original.fooValue = original.fooValue + " foo foo.";
}
}
return original;
}
}
public class MyFooResolver implements ConflictResolver<Foo>
{
@Override
public Foo resolve(List<Foo> objectList) throws UnresolvedConflictException
{
if (objectList.isEmpty())
{
return null;
}
else
{
int longest = 0;
for (int i = 0; i < objectList.size(); i++)
{
if (objectList.get(i).fooValue.length() > longest)
{
longest = i;
}
}
return objectList.get(longest);
}
}
}
public static class MyFooConverter extends Converter<Foo>
{
public MyFooConverter()
{
super(Foo.class);
}
@Override
public Foo toDomain(BinaryValue value, String contentType) throws ConversionException
{
return new Foo(value.toString());
}
@Override
public ContentAndType fromDomain(Foo domainObject) throws ConversionException
{
return new ContentAndType(BinaryValue.create(domainObject.fooValue), "text/plain");
}
}
}