diff --git a/redisson/src/main/java/org/redisson/RedissonLiveObjectService.java b/redisson/src/main/java/org/redisson/RedissonLiveObjectService.java index 9c1264f4a..fa9c21f85 100644 --- a/redisson/src/main/java/org/redisson/RedissonLiveObjectService.java +++ b/redisson/src/main/java/org/redisson/RedissonLiveObjectService.java @@ -49,6 +49,8 @@ import org.redisson.api.RExpirableAsync; import org.redisson.api.RMap; import org.redisson.api.RMapAsync; import org.redisson.api.RObjectAsync; +import org.redisson.api.annotation.RFieldAccessor; +import org.redisson.liveobject.core.FieldAccessorInterceptor; import org.redisson.liveobject.core.RExpirableInterceptor; import org.redisson.liveobject.core.RMapInterceptor; import org.redisson.liveobject.core.RObjectInterceptor; @@ -328,6 +330,10 @@ public class RedissonLiveObjectService implements RLiveObjectService { .install(LiveObjectInterceptor.Getter.class, LiveObjectInterceptor.Setter.class))) .implement(RLiveObject.class) + .method(ElementMatchers.isAnnotatedWith(RFieldAccessor.class) + .and(ElementMatchers.named("get") + .or(ElementMatchers.named("set")))) + .intercept(MethodDelegation.to(FieldAccessorInterceptor.class)) .method(ElementMatchers.isDeclaredBy(RObject.class) .or(ElementMatchers.isDeclaredBy(RObjectAsync.class))) .intercept(MethodDelegation.to(RObjectInterceptor.class)) diff --git a/redisson/src/main/java/org/redisson/api/annotation/RFieldAccessor.java b/redisson/src/main/java/org/redisson/api/annotation/RFieldAccessor.java new file mode 100644 index 000000000..dc9bcd573 --- /dev/null +++ b/redisson/src/main/java/org/redisson/api/annotation/RFieldAccessor.java @@ -0,0 +1,29 @@ +/** + * Copyright 2016 Nikita Koksharov + * + * 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.api.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface RFieldAccessor {} diff --git a/redisson/src/main/java/org/redisson/liveobject/core/FieldAccessorInterceptor.java b/redisson/src/main/java/org/redisson/liveobject/core/FieldAccessorInterceptor.java new file mode 100644 index 000000000..61b01b818 --- /dev/null +++ b/redisson/src/main/java/org/redisson/liveobject/core/FieldAccessorInterceptor.java @@ -0,0 +1,61 @@ +/** + * Copyright 2016 Nikita Koksharov + * + * 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.liveobject.core; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import net.bytebuddy.implementation.bind.annotation.AllArguments; +import net.bytebuddy.implementation.bind.annotation.FieldValue; +import net.bytebuddy.implementation.bind.annotation.Origin; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.This; +import org.redisson.api.RMap; +import org.redisson.liveobject.misc.ClassUtils; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class FieldAccessorInterceptor { + + @RuntimeType + public static Object intercept( + @Origin Method method, + @AllArguments Object[] args, + @This Object me, + @FieldValue("liveObjectLiveMap") RMap map + ) throws Exception { + if (args.length >= 1 && String.class.isAssignableFrom(args[0].getClass())) { + String name = ((String) args[0]).substring(0, 1).toUpperCase() + ((String) args[0]).substring(1); + if ("get".equals(method.getName()) && args.length == 1) { + try { + return me.getClass().getMethod("get" + name).invoke(me); + } catch (NoSuchMethodException noSuchMethodException) { + throw new NoSuchFieldException((String) args[0]); + } + } else if ("set".equals(method.getName()) && args.length == 2) { + Method m = ClassUtils.searchForMethod(me.getClass(), "set" + name, new Class[]{args[1].getClass()}); + if (m != null) { + return m.invoke(me, args[1]); + } else { + throw new NoSuchFieldException((String) args[0]); + } + } + } + throw new NoSuchMethodException(method.getName() + " called with wrong signature"); + + } +} diff --git a/redisson/src/test/java/org/redisson/RedissonLiveObjectServiceTest.java b/redisson/src/test/java/org/redisson/RedissonLiveObjectServiceTest.java index 6816e5444..03981d8d8 100644 --- a/redisson/src/test/java/org/redisson/RedissonLiveObjectServiceTest.java +++ b/redisson/src/test/java/org/redisson/RedissonLiveObjectServiceTest.java @@ -40,6 +40,7 @@ import org.redisson.api.RSet; import org.redisson.api.RSortedSet; import org.redisson.api.RedissonClient; import org.redisson.api.annotation.REntity; +import org.redisson.api.annotation.RFieldAccessor; import org.redisson.api.annotation.RId; import org.redisson.liveobject.resolver.DefaultNamingScheme; import org.redisson.liveobject.resolver.DistributedAtomicLongIdGenerator; @@ -391,6 +392,15 @@ public class RedissonLiveObjectServiceTest extends BaseTest { public void setContent(Object content) { this.content = content; } + + @RFieldAccessor + public void set(String field, T value) { + } + + @RFieldAccessor + public T get(String field) { + return null; + } @Override public boolean equals(Object obj) { @@ -965,5 +975,26 @@ public class RedissonLiveObjectServiceTest extends BaseTest { assertEquals("Please use RLiveObjectService instance for this type of functions", e.getMessage()); } } + + @Test + public void testFieldAccessor() { + RLiveObjectService service = redisson.getLiveObjectService(); + TestClass myObject = service.create(TestClass.class); + myObject.setValue("123345"); + assertEquals("123345", myObject.get("value")); + myObject.set("value", "9999"); + assertEquals("9999", myObject.get("value")); + assertEquals("9999", myObject.getValue()); + try { + myObject.get("555555"); + } catch (Exception e) { + assertTrue(e instanceof NoSuchFieldException); + } + try { + myObject.set("555555", "999"); + } catch (Exception e) { + assertTrue(e instanceof NoSuchFieldException); + } + } }