Fixed DefaultNamingScheme not serialising RId value properly

RId values are now serialised into hex string in DefaultNamingScheme
It now throws exception when trying to set RId value as an array.
pull/527/head
jackygurui 9 years ago
parent 5fd2ba5876
commit 708d049145

@ -1,3 +1,18 @@
/**
* Copyright 2014 Nikita Koksharov, Nickolay Borbit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson;
import org.redisson.client.codec.Codec;

@ -17,6 +17,7 @@ package org.redisson.liveobject;
import org.redisson.client.codec.Codec;
import org.redisson.core.RObject;
import org.redisson.liveobject.annotation.REntity;
/**
*
@ -26,12 +27,16 @@ public interface CodecProvider {
Codec getCodec(Class<? extends Codec> codecClass);
Codec getCodec(REntity anno, Class cls);
Codec getCodec(Class<? extends Codec> codecClass, Class<? extends RObject> rObjectClass, String name);
Codec getCodec(Class<? extends Codec> codecClass, RObject rObject, String name);
void registerCodec(Class<? extends Codec> codecClass, Codec codec);
void registerCodec(REntity anno, Class cls, Codec codec);
void registerCodec(Class<? extends Codec> codecClass, Class<? extends RObject> rObjectClass, String name, Codec codec);
void registerCodec(Class<? extends Codec> codecClass, RObject rObject, String name, Codec codec);

@ -19,6 +19,7 @@ import io.netty.util.internal.PlatformDependent;
import java.util.concurrent.ConcurrentMap;
import org.redisson.client.codec.Codec;
import org.redisson.core.RObject;
import org.redisson.liveobject.annotation.REntity;
/**
*
@ -40,10 +41,18 @@ public class DefaultCodecProvider implements CodecProvider {
return codecCache.get(codecClass);
}
@Override
public Codec getCodec(REntity anno, Class cls) {
if (!cls.isAnnotationPresent(anno.annotationType())) {
throw new IllegalArgumentException("Annotation REntity does not present on type [" + cls.getCanonicalName() + "]");
}
return getCodec(anno.codec());
}
@Override
public Codec getCodec(Class<? extends Codec> codecClass, Class<? extends RObject> rObjectClass, String name) {
if (rObjectClass.isInterface()) {
throw new IllegalArgumentException("Cannot lookup an interface class of RObject " + rObjectClass.getCanonicalName() + ". Concrete class only.");
throw new IllegalArgumentException("Cannot lookup an interface class of RObject [" + rObjectClass.getCanonicalName() + "]. Concrete class only.");
}
return getCodec(codecClass);
}
@ -58,10 +67,18 @@ public class DefaultCodecProvider implements CodecProvider {
codecCache.putIfAbsent(cls, codec);
}
@Override
public void registerCodec(REntity anno, Class cls, Codec codec) {
if (!cls.isAnnotationPresent(anno.getClass())) {
throw new IllegalArgumentException("Annotation REntity does not present on type [" + cls.getCanonicalName() + "]");
}
registerCodec(anno.codec(), codec);
}
@Override
public void registerCodec(Class<? extends Codec> codecClass, Class<? extends RObject> rObjectClass, String name, Codec codec) {
if (rObjectClass.isInterface()) {
throw new IllegalArgumentException("Cannot register an interface class of RObject " + rObjectClass.getCanonicalName() + ". Concrete class only.");
throw new IllegalArgumentException("Cannot register an interface class of RObject [" + rObjectClass.getCanonicalName() + "]. Concrete class only.");
}
registerCodec(codecClass, codec);
}

@ -15,11 +15,14 @@
*/
package org.redisson.liveobject.annotation;
import io.netty.buffer.Unpooled;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.redisson.client.codec.Codec;
import org.redisson.client.handler.State;
import org.redisson.codec.JsonJacksonCodec;
/**
@ -46,13 +49,33 @@ public @interface REntity {
}
public class DefaultNamingScheme implements NamingScheme {
public abstract class AbstractNamingScheme implements NamingScheme {
public static final DefaultNamingScheme INSTANCE = new DefaultNamingScheme();
protected final Codec codec;
public AbstractNamingScheme(Codec codec) {
this.codec = codec;
}
}
public class DefaultNamingScheme extends AbstractNamingScheme implements NamingScheme {
public static final DefaultNamingScheme INSTANCE = new DefaultNamingScheme(new JsonJacksonCodec());
private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
public DefaultNamingScheme(Codec codec) {
super(codec);
}
@Override
public String getName(Class cls, String idFieldName, Object id) {
return "redisson_live_object:{class=" + cls.getName() + ", " + idFieldName + "=" + id.toString() + "}";
try {
String encode = bytesToHex(codec.getMapKeyEncoder().encode(id));
return "redisson_live_object:{class=" + cls.getName() + ", " + idFieldName + "=" + encode + "}";
} catch (IOException ex) {
throw new IllegalArgumentException("Unable to encode id [" + id + "] into byte[]", ex);
}
}
@Override
@ -67,8 +90,32 @@ public @interface REntity {
@Override
public Object resolveId(String name) {
return name.substring(name.indexOf("=", name.indexOf("=") + 1) + 1, name.length() - 1);
String decode = name.substring(name.indexOf("=", name.indexOf("=") + 1) + 1, name.length() - 1);
try {
return codec.getMapKeyDecoder().decode(Unpooled.wrappedBuffer(hexToBytes(decode)), new State(false));
} catch (IOException ex) {
throw new IllegalStateException("Unable to decode [" + decode + "] into object", ex);
}
}
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
public static byte[] hexToBytes(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
}
}

@ -73,7 +73,10 @@ public class AccessorInterceptor {
if (isSetter(method, fieldName)) {
if (args[0].getClass().getSuperclass().isAnnotationPresent(REntity.class)) {
Class<? extends Object> rEntity = args[0].getClass().getSuperclass();
REntity.NamingScheme ns = rEntity.getAnnotation(REntity.class).namingScheme().newInstance();
REntity anno = rEntity.getAnnotation(REntity.class);
REntity.NamingScheme ns = anno.namingScheme()
.getDeclaredConstructor(Codec.class)
.newInstance(codecProvider.getCodec(anno, (Class) rEntity));
return liveMap.put(fieldName, new RedissonReference(rEntity, ns.getName(rEntity, getREntityIdFieldName(args[0]), ((RLiveObject) args[0]).getLiveObjectId())));
} else if (args[0] instanceof RObject) {
RObject ar = (RObject) args[0];
@ -115,7 +118,10 @@ public class AccessorInterceptor {
Class<? extends Object> type = rr.getType();
if (type != null) {
if (type.isAnnotationPresent(REntity.class)) {
REntity.NamingScheme ns = type.getAnnotation(REntity.class).namingScheme().newInstance();
REntity anno = type.getAnnotation(REntity.class);
REntity.NamingScheme ns = anno.namingScheme()
.getDeclaredConstructor(Codec.class)
.newInstance(codecProvider.getCodec(anno, rr.getType()));
return (RLiveObject) redisson.getLiveObjectService(codecProvider).get(type, ns.resolveId(rr.getKeyName()));
}
for (Method method : RedissonClient.class.getDeclaredMethods()) {

@ -58,9 +58,9 @@ public class LiveObjectInterceptor {
this.codecProvider = codecProvider;
this.originalClass = entityClass;
this.idFieldName = idFieldName;
REntity anno = ((REntity) entityClass.getAnnotation(REntity.class));
this.namingScheme = anno.namingScheme().newInstance();
REntity anno = (REntity) entityClass.getAnnotation(REntity.class);
this.codecClass = anno.codec();
this.namingScheme = anno.namingScheme().getDeclaredConstructor(Codec.class).newInstance(codecProvider.getCodec(anno, originalClass));
}
@RuntimeType
@ -76,6 +76,9 @@ public class LiveObjectInterceptor {
@FieldProxy("liveObjectLiveMap") Getter mapGetter
) throws Exception {
if ("setLiveObjectId".equals(method.getName())) {
if (args[0].getClass().isArray()) {
throw new UnsupportedOperationException("RId value cannot be an array.");
}
//TODO: distributed locking maybe required.
String idKey = getMapKey(args[0]);
if (map != null) {

@ -1,8 +1,12 @@
package org.redisson;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.junit.Assert.*;
import org.junit.Test;
import org.redisson.core.RMap;
@ -316,4 +320,48 @@ public class RedissonAttachedLiveObjectServiceTest extends BaseTest {
s.<TestREntityValueNested, String>get(TestREntityValueNested.class, "122")
.getValue().getValue().get("field"));
}
@REntity
public static class TestSerializable {
@RId
private Serializable id;
private String value;
public Serializable getId() {
return id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
@Test
public void testSerializerable() {
RLiveObjectService liveObjectService = redisson.getLiveObjectService();
TestSerializable t = liveObjectService.get(TestSerializable.class, "55555");
assertTrue(Objects.equals("55555", t.getId()));
t = liveObjectService.get(TestSerializable.class, 90909l);
assertTrue(Objects.equals(90909l, t.getId()));
t = liveObjectService.get(TestSerializable.class, Arrays.asList(1, 2, 3, 4));
List<Integer> l = new ArrayList();
l.addAll(Arrays.asList(1, 2, 3, 4));
assertTrue(l.removeAll((List) t.getId()));
assertTrue(l.isEmpty());
try {
liveObjectService.get(TestSerializable.class, new int[]{1, 2, 3, 4, 5});
} catch (Exception e) {
assertEquals("RId value cannot be an array.", e.getCause().getMessage());
}
}
}

Loading…
Cancel
Save