From 6ae8f8f6a875830748f6b422ce242ea2d88e0882 Mon Sep 17 00:00:00 2001 From: Rui Gu Date: Wed, 8 Mar 2017 13:36:41 +0000 Subject: [PATCH] Redisson's Spring custom namespace support --- redisson/pom.xml | 1 + .../src/main/java/org/redisson/Redisson.java | 6 + .../java/org/redisson/api/RedissonClient.java | 16 + .../spring/misc/BeanMethodInvoker.java | 44 + ...ractRedissonNamespaceDefinitionParser.java | 84 + .../spring/support/DelayedQueueDecorator.java | 44 + .../LocalCachedMapOptionsDecorator.java | 95 + .../support/ReadWriteLockDecorator.java | 48 + .../spring/support/RedisDefinitionParser.java | 78 + .../support/RedissonDefinitionParser.java | 203 ++ ...RedissonGenericObjectDefinitionParser.java | 90 + .../RedissonLiveObjectDefinitionParser.java | 87 + ...iveObjectRegistrationDefinitionParser.java | 62 + .../RedissonMultiLockDefinitionParser.java | 108 + .../support/RedissonNamespaceDecorator.java | 29 + .../RedissonNamespaceDefaultDecorator.java | 33 + .../RedissonNamespaceHandlerSupport.java | 144 ++ .../RedissonNamespaceParserSupport.java | 300 +++ .../RedissonNestedElementAwareDecorator.java | 62 + .../RedissonRPCClientDefinitionParser.java | 63 + .../RedissonRPCServerDefinitionParser.java | 94 + ...issonReadAndWriteLockDefinitionParser.java | 55 + .../RemoteInvocationOptionDecorator.java | 116 + .../main/resources/META-INF/spring.handlers | 1 + .../main/resources/META-INF/spring.schemas | 2 + .../main/resources/META-INF/spring.tooling | 4 + .../redisson/spring/support/redisson-1.0.xsd | 2231 +++++++++++++++++ .../org/redisson/spring/support/redisson.gif | Bin 0 -> 1059 bytes .../src/test/java/org/redisson/BaseTest.java | 7 +- .../test/java/org/redisson/ClusterRunner.java | 99 + .../test/java/org/redisson/RedisRunner.java | 187 +- .../redisson/RedissonRemoteServiceTest.java | 4 +- .../support/SpringNamespaceObjectTest.java | 282 +++ .../spring/support/SpringNamespaceTest.java | 368 +++ .../org/redisson/spring/support/namespace.xml | 77 + .../spring/support/redisson_objects.xml | 186 ++ 36 files changed, 5287 insertions(+), 23 deletions(-) create mode 100644 redisson/src/main/java/org/redisson/spring/misc/BeanMethodInvoker.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/AbstractRedissonNamespaceDefinitionParser.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/DelayedQueueDecorator.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/LocalCachedMapOptionsDecorator.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/ReadWriteLockDecorator.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedisDefinitionParser.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonDefinitionParser.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonGenericObjectDefinitionParser.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectDefinitionParser.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectRegistrationDefinitionParser.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonMultiLockDefinitionParser.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDecorator.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDefaultDecorator.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceHandlerSupport.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceParserSupport.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonNestedElementAwareDecorator.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonRPCClientDefinitionParser.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonRPCServerDefinitionParser.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RedissonReadAndWriteLockDefinitionParser.java create mode 100644 redisson/src/main/java/org/redisson/spring/support/RemoteInvocationOptionDecorator.java create mode 100644 redisson/src/main/resources/META-INF/spring.handlers create mode 100644 redisson/src/main/resources/META-INF/spring.schemas create mode 100644 redisson/src/main/resources/META-INF/spring.tooling create mode 100644 redisson/src/main/resources/org/redisson/spring/support/redisson-1.0.xsd create mode 100644 redisson/src/main/resources/org/redisson/spring/support/redisson.gif create mode 100644 redisson/src/test/java/org/redisson/ClusterRunner.java create mode 100644 redisson/src/test/java/org/redisson/spring/support/SpringNamespaceObjectTest.java create mode 100644 redisson/src/test/java/org/redisson/spring/support/SpringNamespaceTest.java create mode 100644 redisson/src/test/resources/org/redisson/spring/support/namespace.xml create mode 100644 redisson/src/test/resources/org/redisson/spring/support/redisson_objects.xml diff --git a/redisson/pom.xml b/redisson/pom.xml index 32222c03e4a..a216922ef20 100644 --- a/redisson/pom.xml +++ b/redisson/pom.xml @@ -428,6 +428,7 @@ true JAVADOC_STYLE + XML_STYLE true true diff --git a/redisson/src/main/java/org/redisson/Redisson.java b/redisson/src/main/java/org/redisson/Redisson.java index 7594e69e72e..2fc4a4c2dba 100755 --- a/redisson/src/main/java/org/redisson/Redisson.java +++ b/redisson/src/main/java/org/redisson/Redisson.java @@ -357,7 +357,13 @@ public RScheduledExecutorService getExecutorService(String name) { } @Override + @Deprecated public RScheduledExecutorService getExecutorService(Codec codec, String name) { + return getExecutorService(name, codec); + } + + @Override + public RScheduledExecutorService getExecutorService(String name, Codec codec) { return new RedissonExecutorService(codec, connectionManager.getCommandExecutor(), this, name); } diff --git a/redisson/src/main/java/org/redisson/api/RedissonClient.java b/redisson/src/main/java/org/redisson/api/RedissonClient.java index 2845c503524..b38746061fe 100755 --- a/redisson/src/main/java/org/redisson/api/RedissonClient.java +++ b/redisson/src/main/java/org/redisson/api/RedissonClient.java @@ -742,12 +742,28 @@ public interface RedissonClient { * Returns ScheduledExecutorService by name * using provided codec for task, response and request serialization * + * Please use getExecutorService(String name, Codec codec) method instead. + * + * @deprecated - use {@link #getExecutorService(String, Codec)} instead. + * * @param name - name of object * @param codec - codec for task, response and request * @return ScheduledExecutorService object */ + @Deprecated RScheduledExecutorService getExecutorService(Codec codec, String name); + /** + * Returns ScheduledExecutorService by name + * using provided codec for task, response and request serialization + * + * @param name - name of object + * @param codec - codec for task, response and request + * @return ScheduledExecutorService object + * @since 2.8.2 + */ + RScheduledExecutorService getExecutorService(String name, Codec codec); + /** * Returns object for remote operations prefixed with the default name (redisson_remote_service) * diff --git a/redisson/src/main/java/org/redisson/spring/misc/BeanMethodInvoker.java b/redisson/src/main/java/org/redisson/spring/misc/BeanMethodInvoker.java new file mode 100644 index 00000000000..fe6d8c7e003 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/misc/BeanMethodInvoker.java @@ -0,0 +1,44 @@ +/** + * 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.spring.misc; + +import java.lang.reflect.InvocationTargetException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.support.ArgumentConvertingMethodInvoker; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class BeanMethodInvoker extends ArgumentConvertingMethodInvoker + implements InitializingBean { + + @Override + public void afterPropertiesSet() throws Exception { + prepare(); + try { + invoke(); + } catch (InvocationTargetException ex) { + if (ex.getTargetException() instanceof Exception) { + throw (Exception) ex.getTargetException(); + } + if (ex.getTargetException() instanceof Error) { + throw (Error) ex.getTargetException(); + } + throw ex; + } + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/AbstractRedissonNamespaceDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/AbstractRedissonNamespaceDefinitionParser.java new file mode 100644 index 00000000000..313333612a6 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/AbstractRedissonNamespaceDefinitionParser.java @@ -0,0 +1,84 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.Assert; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public abstract class AbstractRedissonNamespaceDefinitionParser + extends AbstractSingleBeanDefinitionParser { + + protected final RedissonNamespaceParserSupport helper; + private final RedissonNamespaceDecorator decorator; + private final String parentRefAttribute; + + protected AbstractRedissonNamespaceDefinitionParser(RedissonNamespaceParserSupport helper, String parentRefAttribute) { + this.helper = helper; + this.parentRefAttribute = parentRefAttribute; + this.decorator = new RedissonNamespaceDefaultDecorator(); + } + + public AbstractRedissonNamespaceDefinitionParser(RedissonNamespaceParserSupport helper, String parentRefAttribute, RedissonNamespaceDecorator decorator) { + this.helper = helper; + this.parentRefAttribute = parentRefAttribute; + this.decorator = decorator; + } + + @Override + protected final void doParse(Element element, BeanDefinitionBuilder builder) { + } + + @Override + protected final void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + Assert.state(helper.isRedissonNS(element), + "Illegal state. " + + this.getClass().getName() + + " can only parse " + + RedissonNamespaceParserSupport.REDISSON_NAMESPACE + + " namespace elements"); + Assert.state(element.hasAttribute(parentRefAttribute), + "Illegal state. property \"" + parentRefAttribute + + "\" is required in the \"" + + helper.getName(element) + + "\" element."); + + helper.populateIdAttribute(element, builder, parserContext); + AbstractBeanDefinition bd = builder.getRawBeanDefinition(); + parseNested(element, parserContext, builder, bd); + decorator.decorate(element, parserContext, builder, helper); + parserContext.getDelegate().parseQualifierElements(element, bd); + if (parserContext.isNested()) { + helper.registerBeanDefinition(builder, element, parserContext); + } + } + + protected abstract void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd); + + @Override + protected final boolean shouldGenerateIdAsFallback() { + return true; + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/DelayedQueueDecorator.java b/redisson/src/main/java/org/redisson/spring/support/DelayedQueueDecorator.java new file mode 100644 index 00000000000..452d4ab5e8d --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/DelayedQueueDecorator.java @@ -0,0 +1,44 @@ +/** + * 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.spring.support; + +import org.redisson.api.RQueue; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.Assert; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class DelayedQueueDecorator implements RedissonNamespaceDecorator { + + private static final String DESTINATION_QUEUE_REF = "destination-queue-ref"; + + @Override + public void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + Assert.state(element.hasAttribute(DESTINATION_QUEUE_REF), + "Illegal state. property \"" + DESTINATION_QUEUE_REF + + "\" is required in the \"" + + helper.getName(element) + + "\" element."); + helper.addConstructorArgs(new RuntimeBeanReference( + helper.getAttribute(element, DESTINATION_QUEUE_REF)), + RQueue.class, builder); + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/LocalCachedMapOptionsDecorator.java b/redisson/src/main/java/org/redisson/spring/support/LocalCachedMapOptionsDecorator.java new file mode 100644 index 00000000000..16d5c4635d3 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/LocalCachedMapOptionsDecorator.java @@ -0,0 +1,95 @@ +/** + * 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.spring.support; + +import org.redisson.api.LocalCachedMapOptions; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.Assert; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class LocalCachedMapOptionsDecorator implements RedissonNamespaceDecorator { + + @Override + public void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + NodeList list = element.getElementsByTagNameNS( + RedissonNamespaceParserSupport.REDISSON_NAMESPACE, + RedissonNamespaceParserSupport.LOCAL_CACHED_MAP_OPTIONS_ELEMENT); + Element options = null; + String id; + if (list.getLength() == 1) { + options = (Element) list.item(0); + id = invokeOptions(options, parserContext, helper); + for (int i = 0; i < options.getAttributes().getLength(); i++) { + Attr item = (Attr) options.getAttributes().item(i); + if (helper.isEligibleAttribute(item) + && !RedissonNamespaceParserSupport.TIME_TO_LIVE_UNIT_ATTRIBUTE + .equals(item.getLocalName()) + && !RedissonNamespaceParserSupport.MAX_IDLE_UNIT_ATTRIBUTE + .equals(item.getLocalName())) { + helper.invoker(id, + helper.getName(item), + new Object[]{item.getValue()}, + parserContext); + } + } + invokeTimeUnitOptions(options, id, parserContext, helper, + RedissonNamespaceParserSupport.TIME_TO_LIVE_ATTRIBUTE, + RedissonNamespaceParserSupport.TIME_TO_LIVE_UNIT_ATTRIBUTE); + + invokeTimeUnitOptions(options, id, parserContext, helper, + RedissonNamespaceParserSupport.MAX_IDLE_ATTRIBUTE, + RedissonNamespaceParserSupport.MAX_IDLE_UNIT_ATTRIBUTE); + } else { + id = invokeOptions(options, parserContext, helper); + } + helper.addConstructorArgs(new RuntimeBeanReference(id), + LocalCachedMapOptions.class, builder); + } + + private String invokeOptions(Element element, ParserContext parserContext, RedissonNamespaceParserSupport helper) { + BeanComponentDefinition defaultOption + = helper.factoryInvoker(element, LocalCachedMapOptions.class, + "defaults", null, parserContext); + return defaultOption.getName(); + } + + private void invokeTimeUnitOptions(Element element, String id, ParserContext parserContext, RedissonNamespaceParserSupport helper, String timeAttrubute, String timeUnitAttribute) { + if (helper.hasAttribute(element, timeUnitAttribute)) { + Assert.state( + helper.hasAttribute(element, timeAttrubute), + "Missing \"" + timeAttrubute + "\" attribute in \"" + + RedissonNamespaceParserSupport.LOCAL_CACHED_MAP_OPTIONS_ELEMENT + + "\" element."); + helper.invoker(id, + Conventions.attributeNameToPropertyName(timeAttrubute), + new Object[]{ + Integer.parseInt( + helper.getAttribute(element, timeAttrubute)), + helper.getAttribute(element, timeUnitAttribute)}, + parserContext); + } + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/ReadWriteLockDecorator.java b/redisson/src/main/java/org/redisson/spring/support/ReadWriteLockDecorator.java new file mode 100644 index 00000000000..6f36c8c594d --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/ReadWriteLockDecorator.java @@ -0,0 +1,48 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class ReadWriteLockDecorator implements RedissonNamespaceDecorator { + + + @Override + public void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + parseNested(element, RedissonNamespaceParserSupport.READ_LOCK_ELEMENT, parserContext, builder, helper); + parseNested(element, RedissonNamespaceParserSupport.WRITE_LOCK_ELEMENT, parserContext, builder, helper); + } + + private void parseNested(Element element, String eltType, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + NodeList list = element.getElementsByTagNameNS( + RedissonNamespaceParserSupport.REDISSON_NAMESPACE, eltType); + if (list.getLength() == 1) { + Element elt = (Element) list.item(0); + helper.setAttribute(elt, RedissonNamespaceParserSupport.READ_WRITE_LOCK_REF_ATTRIBUTE, + helper.getAttribute(element, + RedissonNamespaceParserSupport.ID_ATTRIBUTE)); + parserContext.getDelegate() + .parseCustomElement(elt, builder.getRawBeanDefinition()); + } + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedisDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedisDefinitionParser.java new file mode 100644 index 00000000000..6c02eee29bb --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedisDefinitionParser.java @@ -0,0 +1,78 @@ +/** + * 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.spring.support; + +import org.redisson.client.RedisClient; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public final class RedisDefinitionParser + extends AbstractSimpleBeanDefinitionParser { + + private static final String HOST_ATTRIBUTE = "host"; + private static final String PORT_ATTRIBUTE = "port"; + private static final String CONNECTION_TIMEOUT_ATTRIBUTE = "connectionTimeout"; + private static final String COMMAND_TIMEOUT_ATTRIBUTE = "commandTimeout"; + + private final RedissonNamespaceParserSupport helper; + + public RedisDefinitionParser(RedissonNamespaceParserSupport helper) { + this.helper = helper; + } + + @Override + protected Class getBeanClass(Element element) { + return RedisClient.class; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + builder.getRawBeanDefinition().setBeanClass(RedisClient.class); + helper.addConstructorArgs(element, + HOST_ATTRIBUTE, String.class, builder); + helper.addConstructorArgs(element, + PORT_ATTRIBUTE, int.class, builder); + helper.addConstructorArgs(element, + CONNECTION_TIMEOUT_ATTRIBUTE, int.class, builder); + helper.addConstructorArgs(element, + COMMAND_TIMEOUT_ATTRIBUTE, int.class, builder); + builder.setDestroyMethodName("shutdown"); + parserContext.getDelegate().parseQualifierElements(element, + builder.getRawBeanDefinition()); + } + + @Override + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + @Override + protected boolean isEligibleAttribute(String attributeName) { + return helper.isEligibleAttribute(attributeName); + } + + @Override + protected boolean isEligibleAttribute(Attr attribute, ParserContext parserContext) { + return helper.isEligibleAttribute(attribute); + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonDefinitionParser.java new file mode 100644 index 00000000000..b1e64be1d64 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonDefinitionParser.java @@ -0,0 +1,203 @@ +/** + * 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.spring.support; + +import java.util.List; +import org.redisson.Redisson; +import org.redisson.config.Config; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.parsing.CompositeComponentDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public final class RedissonDefinitionParser + implements BeanDefinitionParser { + + public static final String ID_ATTRIBUTE = "id"; + public static final String NAME_ATTRIBUTE = "name"; + private static final String REF_SUFFIX = "-ref"; + private static final String REDISSON_REF = "redisson-ref"; + + static enum ConfigType { + singleServer, + sentinelServers, + replicatedServers, + masterSlaveServers, + clusterServers; + + public static boolean contains(String type) { + try { + valueOf(type); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + } + + static enum AddressType { + slaveAddress, + sentinelAddress, + nodeAddress; + + public static boolean contains(String type) { + try { + valueOf(type); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + } + + private final RedissonNamespaceParserSupport helper; + + RedissonDefinitionParser(RedissonNamespaceParserSupport helper) { + this.helper = helper; + } + + private void parseChildElements(Element element, String parentId, String redissonRef, ParserContext parserContext) { + if (element.hasChildNodes()) { + CompositeComponentDefinition compositeDef + = new CompositeComponentDefinition(parentId, + parserContext.extractSource(element)); + parserContext.pushContainingComponent(compositeDef); + List childElts = DomUtils.getChildElements(element); + for (Element elt : childElts) { + if(BeanDefinitionParserDelegate + .QUALIFIER_ELEMENT.equals(elt.getLocalName())) { + continue;//parsed elsewhere + } + String localName = parserContext.getDelegate().getLocalName(elt); + localName = Conventions.attributeNameToPropertyName(localName); + if (ConfigType.contains(localName)) { + parseConfigTypes(elt, localName, parserContext); + } else if (AddressType.contains(localName)) { + parseAddressTypes(elt, localName, parserContext); + } else if (helper.isRedissonNS(elt)) { + elt.setAttribute(REDISSON_REF, redissonRef); + parserContext.getDelegate().parseCustomElement(elt); + } + } + parserContext.popContainingComponent(); + } + } + + private void parseConfigTypes(Element element, String configType, ParserContext parserContext) { + BeanDefinitionBuilder builder + = helper.createBeanDefinitionBuilder(element, + parserContext, null); + //Use factory method on the Config bean + AbstractBeanDefinition bd = builder.getRawBeanDefinition(); + bd.setFactoryMethodName("use" + StringUtils.capitalize(configType)); + bd.setFactoryBeanName(parserContext.getContainingComponent().getName()); + String id = parserContext.getReaderContext().generateBeanName(bd); + helper.registerBeanDefinition(builder, id, + helper.parseAliase(element), parserContext); + parseAttributes(element, parserContext, builder); + parseChildElements(element, id, null, parserContext); + parserContext.getDelegate().parseQualifierElements(element, bd); + } + + private void parseAddressTypes(Element element, String addressType, ParserContext parserContext) { + helper.invoker(element, + parserContext.getContainingComponent().getName(), + "add" + StringUtils.capitalize(addressType), + new String[]{element.getAttribute("value")}, + parserContext); + } + + private void parseAttributes(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + NamedNodeMap attributes = element.getAttributes(); + for (int x = 0; x < attributes.getLength(); x++) { + Attr attribute = (Attr) attributes.item(x); + if (helper.isEligibleAttribute(attribute)) { + String propertyName + = attribute.getLocalName().endsWith(REF_SUFFIX) + ? attribute.getLocalName() + .substring(0, attribute.getLocalName() + .length() - REF_SUFFIX.length()) + : attribute.getLocalName(); + propertyName = Conventions + .attributeNameToPropertyName(propertyName); + Assert.state(StringUtils.hasText(propertyName), + "Illegal property name returned from" + + " 'extractPropertyName(String)': cannot be" + + " null or empty."); + if (attribute.getLocalName().endsWith(REF_SUFFIX)) { + builder.addPropertyReference(propertyName, + attribute.getValue()); + } else { + String value = attribute.getValue(); + String localName = parserContext.getDelegate() + .getLocalName(element); + localName = Conventions + .attributeNameToPropertyName(localName); + if ("masterAddress".equals(propertyName) + && ConfigType.masterSlaveServers.name() + .equals(localName)) { + value = "redis://" + value; + } + builder.addPropertyValue(propertyName, value); + } + } + } + } + + @Override + public BeanDefinition parse(Element element, ParserContext parserContext) { + //Sort out the Config Class + BeanDefinitionBuilder configBuilder + = helper.createBeanDefinitionBuilder(element, parserContext, + Config.class); + String configId = helper.getId(null, configBuilder, parserContext); + parseAttributes(element, parserContext, configBuilder); + helper.registerBeanDefinition(configBuilder, configId, + null, parserContext); + + //Do the main Redisson bean + BeanDefinitionBuilder builder + = helper.createBeanDefinitionBuilder(element, parserContext, + Redisson.class); + builder.setFactoryMethod("create"); + builder.setDestroyMethodName("shutdown"); + builder.addConstructorArgReference(configId); + parserContext.getDelegate().parseQualifierElements(element, + builder.getRawBeanDefinition()); + String id = helper.getId(element, builder, parserContext); + parseAttributes(element, parserContext, configBuilder); + //Sort out all the nested elements + parseChildElements(element, configId, id, parserContext); + + helper.registerBeanDefinition(builder, id, + helper.parseAliase(element), parserContext); + return builder.getBeanDefinition(); + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonGenericObjectDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonGenericObjectDefinitionParser.java new file mode 100644 index 00000000000..681e26a3d2a --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonGenericObjectDefinitionParser.java @@ -0,0 +1,90 @@ +/** + * 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.spring.support; + +import org.redisson.api.RDestroyable; +import org.redisson.client.codec.Codec; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonGenericObjectDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + private final static String KEY_ATTRIBUTE = "key"; + private final static String TOPIC_ATTRIBUTE = "topic"; + private final static String PATTERN_ATTRIBUTE = "pattern"; + private final static String SERVICE_ATTRIBUTE = "service"; + private final static String CODEC_REF_ATTRIBUTE = "codec-ref"; + private final static String FAIL_LOCK = "fairLock"; + + RedissonGenericObjectDefinitionParser(RedissonNamespaceParserSupport helper) { + super(helper, RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE); + } + + RedissonGenericObjectDefinitionParser(RedissonNamespaceParserSupport helper, RedissonNamespaceDecorator decorator) { + super(helper, + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE, + decorator); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + bd.setFactoryBeanName(element.getAttribute( + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE)); + String typeName + = Conventions.attributeNameToPropertyName(element.getLocalName()); + bd.setFactoryMethodName("get" + StringUtils.capitalize(typeName)); + + helper.addConstructorArgs(element, KEY_ATTRIBUTE, + String.class, builder); + helper.addConstructorArgs(element, TOPIC_ATTRIBUTE, + String.class, builder); + helper.addConstructorArgs(element, PATTERN_ATTRIBUTE, + String.class, builder); + helper.addConstructorArgs(element, SERVICE_ATTRIBUTE, + String.class, builder); + helper.addConstructorArgs(element, CODEC_REF_ATTRIBUTE, + Codec.class, builder); + if (RDestroyable.class.isAssignableFrom(getBeanClass(element))) { + ((AbstractBeanDefinition) bd).setDestroyMethodName("destroy"); + } + } + + @Override + protected Class getBeanClass(Element element) { + String elementName + = Conventions.attributeNameToPropertyName( + element.getLocalName()); + try { + return Class.forName(RedissonNamespaceParserSupport.API_CLASS_PATH_PREFIX + + (StringUtils.capitalize(FAIL_LOCK.equals(elementName) + ? "lock" + : elementName))); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException(ex); + } + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectDefinitionParser.java new file mode 100644 index 00000000000..38ec7f64497 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectDefinitionParser.java @@ -0,0 +1,87 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.MethodInvokingFactoryBean; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; +import reactor.core.support.Assert; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonLiveObjectDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + public RedissonLiveObjectDefinitionParser(RedissonNamespaceParserSupport helper) { + super(helper, RedissonNamespaceParserSupport.LIVE_OBJECT_SERVICE_REF_ATTRIBUTE); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + Class apiClass; + try { + apiClass = Class.forName(helper.getAttribute(element, + RedissonNamespaceParserSupport.CLASS_ATTRIBUTE)); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException( + "The class [" + helper.getAttribute(element, + RedissonNamespaceParserSupport.CLASS_ATTRIBUTE) + + "] specified in \"api-class\" attribute has not " + + "found. Please check the class path.", ex); + } + Assert.state(helper.hasAttribute(element, + RedissonNamespaceParserSupport.OBJECT_ID_ATTRIBUTE) + || helper.hasAttribute(element, + RedissonNamespaceParserSupport.OBJECT_ID_REF_ATTRIBUTE), + "One of \"" + + RedissonNamespaceParserSupport.OBJECT_ID_ATTRIBUTE + + "\" or \"" + + RedissonNamespaceParserSupport.OBJECT_ID_REF_ATTRIBUTE + + "\" attribute is required in the \"" + + RedissonNamespaceParserSupport.LIVE_OBJECT_ELEMENT + + "\" element."); + builder.addPropertyValue("targetObject", new RuntimeBeanReference( + helper.getAttribute(element, + RedissonNamespaceParserSupport.LIVE_OBJECT_SERVICE_REF_ATTRIBUTE))); + builder.addPropertyValue("targetMethod", "get"); + ManagedList args = new ManagedList(); + args.add(apiClass); + if (helper.hasAttribute(element, + RedissonNamespaceParserSupport.OBJECT_ID_ATTRIBUTE)) { + args.add(helper.getAttribute(element, + RedissonNamespaceParserSupport.OBJECT_ID_ATTRIBUTE)); + } + if (helper.hasAttribute(element, + RedissonNamespaceParserSupport.OBJECT_ID_REF_ATTRIBUTE)) { + args.add(new RuntimeBeanReference( + helper.getAttribute(element, + RedissonNamespaceParserSupport.OBJECT_ID_REF_ATTRIBUTE))); + } + builder.addPropertyValue("arguments", args); + } + + @Override + protected Class getBeanClass(Element element) { + return MethodInvokingFactoryBean.class; + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectRegistrationDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectRegistrationDefinitionParser.java new file mode 100644 index 00000000000..4090cc0ec21 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonLiveObjectRegistrationDefinitionParser.java @@ -0,0 +1,62 @@ +/** + * 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.spring.support; + +import org.redisson.spring.misc.BeanMethodInvoker; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonLiveObjectRegistrationDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + public RedissonLiveObjectRegistrationDefinitionParser(RedissonNamespaceParserSupport helper) { + super(helper, + RedissonNamespaceParserSupport.LIVE_OBJECT_SERVICE_REF_ATTRIBUTE); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + Class apiClass; + try { + apiClass = Class.forName(helper.getAttribute(element, + RedissonNamespaceParserSupport.CLASS_ATTRIBUTE)); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException( + "The class [" + helper.getAttribute(element, + RedissonNamespaceParserSupport.CLASS_ATTRIBUTE) + + "] specified in \"api-class\" attribute has not " + + "found. Please check the class path.", ex); + } + builder.addPropertyValue("targetObject", new RuntimeBeanReference( + helper.getAttribute(element, + RedissonNamespaceParserSupport.LIVE_OBJECT_SERVICE_REF_ATTRIBUTE))); + builder.addPropertyValue("targetMethod", "registerClass"); + builder.addPropertyValue("arguments", new Object[] {apiClass}); + } + + @Override + protected Class getBeanClass(Element element) { + return BeanMethodInvoker.class; + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonMultiLockDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonMultiLockDefinitionParser.java new file mode 100644 index 00000000000..063784ebfea --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonMultiLockDefinitionParser.java @@ -0,0 +1,108 @@ +/** + * 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.spring.support; + +import java.util.List; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonMultiLockDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + +// static enum supportedTypes { +// redissonMultiLock, +// redissonRedLock +// } + + public RedissonMultiLockDefinitionParser(RedissonNamespaceParserSupport helper) { + super(helper, + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + bd.setDependsOn(element.getAttribute( + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE)); + bd.setLazyInit(true); + List childElements = DomUtils.getChildElements(element); + for (Element elt : childElements) { + String localName = elt.getLocalName(); + if (BeanDefinitionParserDelegate + .QUALIFIER_ELEMENT.equals(localName)) { + continue;//parsed elsewhere + } + String id; + if (BeanDefinitionParserDelegate.REF_ELEMENT.equals(localName)){ + id = elt.getAttribute( + BeanDefinitionParserDelegate.BEAN_REF_ATTRIBUTE); + } else { + if (!elt.hasAttribute( + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE)) { + helper.setAttribute(elt, + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE, + element.getAttribute( + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE)); + } + helper.populateIdAttribute(elt, builder, parserContext); + parserContext.getDelegate().parseCustomElement(elt, bd); + id = elt.getAttribute( + RedissonNamespaceParserSupport.ID_ATTRIBUTE); + } + ConstructorArgumentValues args + = builder.getRawBeanDefinition() + .getConstructorArgumentValues(); + if (args.getArgumentCount() > 0) { + ConstructorArgumentValues.ValueHolder value + = args.getIndexedArgumentValues().get(0); + ManagedList list; + if (value.getValue() instanceof ManagedList) { + list = (ManagedList) value.getValue(); + } else { + list = new ManagedList(); + list.add(value.getValue()); + value.setValue(list); + value.setType(ManagedList.class.getName()); + } + list.add(new RuntimeBeanReference(id)); + } else { + builder.addConstructorArgReference(id); + } + } + } + + @Override + protected String getBeanClassName(Element element) { + String elementName + = Conventions.attributeNameToPropertyName( + element.getLocalName()); + return RedissonNamespaceParserSupport.IMPL_CLASS_PATH_PREFIX + + StringUtils.capitalize(elementName); + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDecorator.java b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDecorator.java new file mode 100644 index 00000000000..b04c29824d7 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDecorator.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.spring.support; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public interface RedissonNamespaceDecorator { + + void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper); +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDefaultDecorator.java b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDefaultDecorator.java new file mode 100644 index 00000000000..7aa9bf96c87 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceDefaultDecorator.java @@ -0,0 +1,33 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonNamespaceDefaultDecorator implements RedissonNamespaceDecorator { + + @Override + public void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + //default is no decoration; + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceHandlerSupport.java b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceHandlerSupport.java new file mode 100644 index 00000000000..cf14811a013 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceHandlerSupport.java @@ -0,0 +1,144 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonNamespaceHandlerSupport extends NamespaceHandlerSupport { + + @Override + public void init() { + RedissonNamespaceParserSupport helper + = new RedissonNamespaceParserSupport(); + + RedissonGenericObjectDefinitionParser defaultParser + = new RedissonGenericObjectDefinitionParser(helper); + + RedissonReadAndWriteLockDefinitionParser readAndWriteLockParser + = new RedissonReadAndWriteLockDefinitionParser(helper); + + RedissonMultiLockDefinitionParser nestedParser + = new RedissonMultiLockDefinitionParser(helper); + + RedissonNestedElementAwareDecorator readWriteLockDecorator + = new RedissonNestedElementAwareDecorator( + new String[]{ + RedissonNamespaceParserSupport.READ_LOCK_ELEMENT, + RedissonNamespaceParserSupport.WRITE_LOCK_ELEMENT + }, + RedissonNamespaceParserSupport.READ_WRITE_LOCK_REF_ATTRIBUTE); + + RedissonGenericObjectDefinitionParser readWriteLockParser + = new RedissonGenericObjectDefinitionParser(helper, + readWriteLockDecorator); + + RedissonNestedElementAwareDecorator remoteServiceDecorator + = new RedissonNestedElementAwareDecorator( + new String[]{ + RedissonNamespaceParserSupport.RPC_SERVER_ELEMENT, + RedissonNamespaceParserSupport.RPC_CLIENT_ELEMENT + }, + RedissonNamespaceParserSupport.REMOTE_SERVICE_REF_ATTRIBUTE); + + RedissonGenericObjectDefinitionParser remoteServiceParser + = new RedissonGenericObjectDefinitionParser(helper, + remoteServiceDecorator); + + RedissonNestedElementAwareDecorator liveObjectServiceDecorator + = new RedissonNestedElementAwareDecorator( + new String[]{ + RedissonNamespaceParserSupport.LIVE_OBJECT_ELEMENT, + RedissonNamespaceParserSupport.LIVE_OBJECT_REGISTRATION_ELEMENT + }, + RedissonNamespaceParserSupport.LIVE_OBJECT_SERVICE_REF_ATTRIBUTE); + + RedissonGenericObjectDefinitionParser liveObjectServiceParser + = new RedissonGenericObjectDefinitionParser(helper, + liveObjectServiceDecorator); + + //root beans + registerBeanDefinitionParser("client", + new RedissonDefinitionParser(helper)); + registerBeanDefinitionParser("redis", new RedisDefinitionParser(helper)); + //object parsers + registerBeanDefinitionParser("binary-stream", defaultParser); + registerBeanDefinitionParser("geo", defaultParser); + registerBeanDefinitionParser("set-cache", defaultParser); + registerBeanDefinitionParser("map-cache", defaultParser); + registerBeanDefinitionParser("bucket", defaultParser); + registerBeanDefinitionParser("buckets", defaultParser); + registerBeanDefinitionParser("hyper-log-log", defaultParser); + registerBeanDefinitionParser("list", defaultParser); + registerBeanDefinitionParser("list-multimap", defaultParser); + registerBeanDefinitionParser("list-multimap-cache", defaultParser); + registerBeanDefinitionParser("local-cached-map", + new RedissonGenericObjectDefinitionParser(helper, + new LocalCachedMapOptionsDecorator())); + registerBeanDefinitionParser("map", defaultParser); + registerBeanDefinitionParser("set-multimap", defaultParser); + registerBeanDefinitionParser("set-multimap-cache", defaultParser); + registerBeanDefinitionParser("semaphore", defaultParser); + registerBeanDefinitionParser("permit-expirable-semaphore", defaultParser); + registerBeanDefinitionParser("lock", defaultParser); + registerBeanDefinitionParser("fair-lock", defaultParser); + registerBeanDefinitionParser("read-write-lock",readWriteLockParser); + registerBeanDefinitionParser("read-lock", readAndWriteLockParser); + registerBeanDefinitionParser("write-lock", readAndWriteLockParser); + registerBeanDefinitionParser("multi-lock", nestedParser); + registerBeanDefinitionParser("red-lock", nestedParser); + registerBeanDefinitionParser("set", defaultParser); + registerBeanDefinitionParser("sorted-set", defaultParser); + registerBeanDefinitionParser("scored-sorted-set", defaultParser); + registerBeanDefinitionParser("lex-sorted-set", defaultParser); + registerBeanDefinitionParser("topic", defaultParser); + registerBeanDefinitionParser("pattern-topic", defaultParser); + registerBeanDefinitionParser("blocking-fair-queue", defaultParser); + registerBeanDefinitionParser("queue", defaultParser); + registerBeanDefinitionParser("delayed-queue", + new RedissonGenericObjectDefinitionParser(helper, + new DelayedQueueDecorator())); + registerBeanDefinitionParser("priority-queue", defaultParser); + registerBeanDefinitionParser("priority-deque", defaultParser); + registerBeanDefinitionParser("blocking-queue", defaultParser); + registerBeanDefinitionParser("bounded-blocking-queue", defaultParser); + registerBeanDefinitionParser("deque", defaultParser); + registerBeanDefinitionParser("blocking-deque", defaultParser); + registerBeanDefinitionParser("atomic-long", defaultParser); + registerBeanDefinitionParser("atomic-double", defaultParser); + registerBeanDefinitionParser("count-down-latch", defaultParser); + registerBeanDefinitionParser("bit-set", defaultParser); + registerBeanDefinitionParser("bloom-filter", defaultParser); + registerBeanDefinitionParser("script", defaultParser); + registerBeanDefinitionParser("executor-service", defaultParser);//nested unfinished + registerBeanDefinitionParser("remote-service", remoteServiceParser); + registerBeanDefinitionParser("rpc-server", + new RedissonRPCServerDefinitionParser(helper)); + registerBeanDefinitionParser("rpc-client", + new RedissonRPCClientDefinitionParser(helper, + new RemoteInvocationOptionDecorator())); + registerBeanDefinitionParser("keys", defaultParser); + registerBeanDefinitionParser("live-object-service", liveObjectServiceParser); + registerBeanDefinitionParser("live-object", + new RedissonLiveObjectDefinitionParser(helper)); + registerBeanDefinitionParser("live-object-registration", + new RedissonLiveObjectRegistrationDefinitionParser(helper)); + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceParserSupport.java b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceParserSupport.java new file mode 100644 index 00000000000..d7b2804aa9d --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonNamespaceParserSupport.java @@ -0,0 +1,300 @@ +/** + * 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.spring.support; + +import org.redisson.spring.misc.BeanMethodInvoker; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.config.MethodInvokingFactoryBean; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.StringUtils; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonNamespaceParserSupport { + + public final static String REDISSON_NAMESPACE + = "http://redisson.org/schema/redisson"; + + static final String API_CLASS_PATH_PREFIX = "org.redisson.api.R"; + static final String IMPL_CLASS_PATH_PREFIX = "org.redisson.Redisson"; + + static final String ID_ATTRIBUTE = "id"; + static final String NAME_ATTRIBUTE = "name"; + static final String REDISSON_REF_ATTRIBUTE = "redisson-ref"; + static final String READ_WRITE_LOCK_REF_ATTRIBUTE = "read-write-lock-ref"; + static final String EXECUTOR_REF_ATTRIBUTE = "executor-ref"; + static final String REMOTE_SERVICE_REF_ATTRIBUTE = "remote-service-ref"; + static final String LIVE_OBJECT_SERVICE_REF_ATTRIBUTE + = "live-object-service-ref"; + static final String OBJECT_ID_REF_ATTRIBUTE = "object-id-ref"; + + static final String MAX_IDLE_ATTRIBUTE = "max-idle"; + static final String TIME_TO_LIVE_ATTRIBUTE = "time-to-live"; + static final String MAX_IDLE_UNIT_ATTRIBUTE = "max-idle-unit"; + static final String TIME_TO_LIVE_UNIT_ATTRIBUTE = "time-to-live-unit"; + static final String CONCURRENT_WORKERS_ATTRIBUTE = "concurrent-workers"; + static final String WITHIN_ATTRIBUTE = "within"; + static final String TIME_UNIT_ATTRIBUTE = "time-unit"; + static final String API_CLASS_ATTRIBUTE = "api-class"; + static final String CLASS_ATTRIBUTE = "class"; + static final String OBJECT_ID_ATTRIBUTE = "object-id"; + + static final String READ_LOCK_ELEMENT = "read-lock"; + static final String WRITE_LOCK_ELEMENT = "write-lock"; + static final String RPC_SERVER_ELEMENT = "rpc-server"; + static final String RPC_CLIENT_ELEMENT = "rpc-client"; + static final String REMOTE_INVOCATION_OPTIONS_ELEMENT + = "remote-invocation-options"; + static final String REMOTE_NO_ACK_ELEMENT = "remote-no-ack"; + static final String REMOTE_ACK_ELEMENT = "remote-ack"; + static final String REMOTE_NO_RESULT_ELEMENT = "remote-no-result"; + static final String REMOTE_RESULT_ELEMENT = "remote-result"; + static final String LOCAL_CACHED_MAP_OPTIONS_ELEMENT + = "local-cached-map-options"; + static final String LIVE_OBJECT_ELEMENT + = "live-object"; + static final String LIVE_OBJECT_REGISTRATION_ELEMENT + = "live-object-registration"; + + public String[] parseAliase(Element element) { + if (element == null) { + return null; + } + String[] aliases = null; + String name = element.getAttribute(NAME_ATTRIBUTE); + if (StringUtils.hasLength(name)) { + aliases = StringUtils.trimArrayElements( + StringUtils.commaDelimitedListToStringArray(name)); + } + return aliases; + } + + public BeanDefinitionBuilder createBeanDefinitionBuilder(Element element, ParserContext parserContext, Class cls) { + BeanDefinitionBuilder builder + = BeanDefinitionBuilder.genericBeanDefinition(); + builder.getRawBeanDefinition().setBeanClass(cls); + builder.getRawBeanDefinition() + .setSource(parserContext.extractSource(element)); + if (parserContext.isNested()) { + builder.setScope(parserContext.getContainingBeanDefinition() + .getScope()); + } + if (parserContext.isDefaultLazyInit()) { + builder.setLazyInit(true); + } + return builder; + } + + public BeanComponentDefinition registerBeanDefinition(BeanDefinitionBuilder builder, String id, String[] aliases, ParserContext parserContext) { + BeanDefinitionHolder holder + = new BeanDefinitionHolder(builder.getBeanDefinition(), id, + aliases); + BeanDefinitionReaderUtils + .registerBeanDefinition(holder, parserContext.getRegistry()); + BeanComponentDefinition componentDefinition + = new BeanComponentDefinition(holder); + parserContext.registerComponent(componentDefinition); + return componentDefinition; + } + + public BeanComponentDefinition registerBeanDefinition(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) { + BeanDefinitionHolder holder + = new BeanDefinitionHolder(builder.getBeanDefinition(), + getId(element, builder, parserContext), + parseAliase(element)); + BeanDefinitionReaderUtils + .registerBeanDefinition(holder, parserContext.getRegistry()); + BeanComponentDefinition componentDefinition + = new BeanComponentDefinition(holder); + parserContext.registerComponent(componentDefinition); + return componentDefinition; + } + + public void addConstructorArgs(Element element, String attribute, Class type, BeanDefinition bd) { + if (element.hasAttribute(attribute)) { + addConstructorArgs(element.getAttribute(attribute), type, bd); + } + } + + public void addConstructorArgs(Object value, Class type, BeanDefinition bd) { + ConstructorArgumentValues.ValueHolder vHolder + = new ConstructorArgumentValues.ValueHolder(value, type.getName()); + ConstructorArgumentValues args + = bd.getConstructorArgumentValues(); + args.addIndexedArgumentValue(args.getArgumentCount(), vHolder); + } + + public void addConstructorArgs(Element element, String attribute, Class type, BeanDefinitionBuilder builder) { + addConstructorArgs(element, attribute, type, builder.getRawBeanDefinition()); + } + + public void addConstructorArgs(Object value, Class type, BeanDefinitionBuilder builder) { + addConstructorArgs(value, type, builder.getRawBeanDefinition()); + } + + public String getName(Node node) { + return Conventions.attributeNameToPropertyName(node.getLocalName()); + } + + public String getId(Element element, BeanDefinitionBuilder builder, ParserContext parserContext) { + String id = element != null + ? element.getAttribute(ID_ATTRIBUTE) + : null; + if (!StringUtils.hasText(id)) { + id = generateId(builder, parserContext); + } + return id; + } + + public String generateId(BeanDefinitionBuilder builder, ParserContext parserContext) { + return parserContext.getReaderContext() + .generateBeanName(builder.getRawBeanDefinition()); + } + + public void populateIdAttribute(Element element, BeanDefinitionBuilder builder, ParserContext parserContext) { + if (element == null) { + return; + } + if (!StringUtils.hasText(element.getAttribute(ID_ATTRIBUTE))) { + element.setAttribute(ID_ATTRIBUTE, + generateId(builder, parserContext)); + } + } + + public BeanComponentDefinition factoryInvoker(Element element, String bean, String method, Object[] args, ParserContext parserContext) { + BeanDefinitionBuilder builder + = preInvoke(element, bean, method, args, parserContext, true); + builder.addPropertyReference("targetObject", bean); + return doInvoke(element, builder, parserContext); + } + + public BeanComponentDefinition factoryInvoker(Element element, Object obj, String method, Object[] args, ParserContext parserContext) { + BeanDefinitionBuilder builder + = preInvoke(element, obj, method, args, parserContext, true); + builder.addPropertyValue("targetObject", obj); + return doInvoke(element, builder, parserContext); + } + + + public BeanComponentDefinition factoryInvoker(String bean, String method, Object[] args, ParserContext parserContext) { + return factoryInvoker(null, bean, method, args, parserContext); + } + + public BeanComponentDefinition factoryInvoker(Object obj, String method, Object[] args, ParserContext parserContext) { + return factoryInvoker(null, obj, method, args, parserContext); + } + + + public BeanComponentDefinition invoker(Element element, String bean, String method, Object[] args, ParserContext parserContext) { + BeanDefinitionBuilder builder + = preInvoke(element, bean, method, args, parserContext, false); + builder.addPropertyReference("targetObject", bean); + return doInvoke(element, builder, parserContext); + } + + public BeanComponentDefinition invoker(Element element, Object obj, String method, Object[] args, ParserContext parserContext) { + BeanDefinitionBuilder builder + = preInvoke(element, obj, method, args, parserContext, false); + builder.addPropertyValue("targetObject", obj); + return doInvoke(element, builder, parserContext); + } + + + public BeanComponentDefinition invoker(String bean, String method, Object[] args, ParserContext parserContext) { + return invoker(null, bean, method, args, parserContext); + } + + public BeanComponentDefinition invoker(Object obj, String method, Object[] args, ParserContext parserContext) { + return invoker(null, obj, method, args, parserContext); + } + + private BeanDefinitionBuilder preInvoke(Element element, Object obj, String method, Object[] args, ParserContext parserContext, boolean factory) { + BeanDefinitionBuilder builder + = createBeanDefinitionBuilder(element, parserContext, + factory + ? MethodInvokingFactoryBean.class + : BeanMethodInvoker.class); + if (obj instanceof Class) { + builder.addPropertyValue("staticMethod", + ((Class) obj).getName() + "." + method); + } else { + builder.addPropertyValue("targetMethod", method); + } + builder.addPropertyValue("arguments", args); + if (element != null) { + parserContext.getDelegate().parseQualifierElements(element, + builder.getRawBeanDefinition()); + } + return builder; + } + + private BeanComponentDefinition doInvoke(Element element, BeanDefinitionBuilder builder, ParserContext parserContext) { + String id = getId(element, builder, parserContext); + return registerBeanDefinition(builder, id, + parseAliase(element), parserContext); + } + + public boolean isEligibleAttribute(String attributeName) { + return !attributeName.equals("xmlns") + && !attributeName.startsWith("xmlns:") + && !ID_ATTRIBUTE.equals(attributeName) + && !NAME_ATTRIBUTE.equals(attributeName); + } + + public boolean isEligibleAttribute(Attr attribute) { + return isEligibleAttribute(attribute.getName()); + } + + public boolean isRedissonNS(Node node) { + return node != null + && REDISSON_NAMESPACE.equals(node.getNamespaceURI()); + } + + public String getAttribute(Element element, String attribute) { + return element.getAttribute(attribute); + } + + public void setAttribute(Element element, String attribute, String value) { + element.setAttribute(attribute, value); + } + + public boolean hasAttribute(Element element, String attribute) { + return element.hasAttribute(attribute); + } + + public boolean hasElement(Element element, String tagName) { + return element.getElementsByTagNameNS( + RedissonNamespaceParserSupport.REDISSON_NAMESPACE, tagName) + .getLength() > 0; + } + + public Element getSingleElement(Element element, String tagName) { + return (Element) element.getElementsByTagNameNS( + RedissonNamespaceParserSupport.REDISSON_NAMESPACE, tagName) + .item(0); + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonNestedElementAwareDecorator.java b/redisson/src/main/java/org/redisson/spring/support/RedissonNestedElementAwareDecorator.java new file mode 100644 index 00000000000..7ff45b5624e --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonNestedElementAwareDecorator.java @@ -0,0 +1,62 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonNestedElementAwareDecorator implements RedissonNamespaceDecorator { + + private final String[] nestedElements; + private final String referenceAttribute; + + public RedissonNestedElementAwareDecorator(String[] nestedElements, String referenceAttribute) { + this.nestedElements = nestedElements; + this.referenceAttribute = referenceAttribute; + } + + @Override + public void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + for (String nestedElement : nestedElements) { + parseNested(element, nestedElement, parserContext, builder, helper); + } + } + + private void parseNested(Element element, String eltType, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + NodeList list = element.getElementsByTagNameNS( + RedissonNamespaceParserSupport.REDISSON_NAMESPACE, eltType); + if (list.getLength() == 1) { + Element elt = (Element) list.item(0); + if (StringUtils.hasText(referenceAttribute)) { + helper.setAttribute(elt, referenceAttribute, + helper.getAttribute(element, + RedissonNamespaceParserSupport.ID_ATTRIBUTE)); + helper.setAttribute(elt, RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE, + helper.getAttribute(element, + RedissonNamespaceParserSupport.REDISSON_REF_ATTRIBUTE)); + } + parserContext.getDelegate() + .parseCustomElement(elt, builder.getRawBeanDefinition()); + } + } +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonRPCClientDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonRPCClientDefinitionParser.java new file mode 100644 index 00000000000..b97b1994c96 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonRPCClientDefinitionParser.java @@ -0,0 +1,63 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.MethodInvokingFactoryBean; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonRPCClientDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + public RedissonRPCClientDefinitionParser(RedissonNamespaceParserSupport helper, RedissonNamespaceDecorator decorator) { + super(helper, + RedissonNamespaceParserSupport.REMOTE_SERVICE_REF_ATTRIBUTE, + decorator); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + Class apiClass; + try { + apiClass = Class.forName(helper.getAttribute(element, + RedissonNamespaceParserSupport.API_CLASS_ATTRIBUTE)); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException( + "The class [" + helper.getAttribute(element, + RedissonNamespaceParserSupport.API_CLASS_ATTRIBUTE) + + "] specified in \"api-class\" attribute has not " + + "found. Please check the class path.", ex); + } + builder.addPropertyValue("targetObject", new RuntimeBeanReference( + helper.getAttribute(element, + RedissonNamespaceParserSupport.REMOTE_SERVICE_REF_ATTRIBUTE))); + builder.addPropertyValue("targetMethod", "get"); + builder.addPropertyValue("arguments", new Object[] {apiClass}); + } + + @Override + protected Class getBeanClass(Element element) { + return MethodInvokingFactoryBean.class; + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonRPCServerDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonRPCServerDefinitionParser.java new file mode 100644 index 00000000000..ef7c93fce48 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonRPCServerDefinitionParser.java @@ -0,0 +1,94 @@ +/** + * 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.spring.support; + +import org.redisson.spring.misc.BeanMethodInvoker; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; +import reactor.core.support.Assert; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonRPCServerDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + public RedissonRPCServerDefinitionParser(RedissonNamespaceParserSupport helper) { + super(helper, RedissonNamespaceParserSupport.REMOTE_SERVICE_REF_ATTRIBUTE); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + Class apiClass; + try { + apiClass = Class.forName(helper.getAttribute(element, + RedissonNamespaceParserSupport.API_CLASS_ATTRIBUTE)); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException( + "The class [" + helper.getAttribute(element, + RedissonNamespaceParserSupport.API_CLASS_ATTRIBUTE) + + "] specified in \"api-class\" attribute has not " + + "found. Please check the class path.", ex); + } + builder.addPropertyValue("targetObject", new RuntimeBeanReference( + helper.getAttribute(element, + RedissonNamespaceParserSupport.REMOTE_SERVICE_REF_ATTRIBUTE))); + builder.addPropertyValue("targetMethod", "register"); + ManagedList args = new ManagedList(); + args.add(apiClass); + args.add(new RuntimeBeanReference( + helper.getAttribute(element, + BeanDefinitionParserDelegate.BEAN_REF_ATTRIBUTE))); + String workers = null; + if (helper.hasAttribute(element, + RedissonNamespaceParserSupport.CONCURRENT_WORKERS_ATTRIBUTE)) { + workers = helper.getAttribute(element, + RedissonNamespaceParserSupport.CONCURRENT_WORKERS_ATTRIBUTE); + } + if (StringUtils.hasText(workers)) { + args.add(Integer.parseInt(workers)); + } + if (helper.hasAttribute(element, + RedissonNamespaceParserSupport.EXECUTOR_REF_ATTRIBUTE)) { + Assert.state(helper.hasAttribute(element, + RedissonNamespaceParserSupport.CONCURRENT_WORKERS_ATTRIBUTE), + "The \"" + + RedissonNamespaceParserSupport.CONCURRENT_WORKERS_ATTRIBUTE + + "\" attribute in \"" + + RedissonNamespaceParserSupport.RPC_SERVER_ELEMENT + + "\" element is required when \"" + + RedissonNamespaceParserSupport.EXECUTOR_REF_ATTRIBUTE + + "\" attribute is specified."); + args.add(new RuntimeBeanReference( + helper.getAttribute(element, + RedissonNamespaceParserSupport.EXECUTOR_REF_ATTRIBUTE))); + } + builder.addPropertyValue("arguments", args); + } + + @Override + protected Class getBeanClass(Element element) { + return BeanMethodInvoker.class; + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RedissonReadAndWriteLockDefinitionParser.java b/redisson/src/main/java/org/redisson/spring/support/RedissonReadAndWriteLockDefinitionParser.java new file mode 100644 index 00000000000..248af5430d4 --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RedissonReadAndWriteLockDefinitionParser.java @@ -0,0 +1,55 @@ +/** + * 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.spring.support; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.w3c.dom.Element; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RedissonReadAndWriteLockDefinitionParser + extends AbstractRedissonNamespaceDefinitionParser { + + public RedissonReadAndWriteLockDefinitionParser(RedissonNamespaceParserSupport helper) { + super(helper, + RedissonNamespaceParserSupport.READ_WRITE_LOCK_REF_ATTRIBUTE); + } + + @Override + protected void parseNested(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, BeanDefinition bd) { + bd.setFactoryBeanName(element.getAttribute( + RedissonNamespaceParserSupport.READ_WRITE_LOCK_REF_ATTRIBUTE)); + String typeName + = Conventions.attributeNameToPropertyName(element.getLocalName()); + bd.setFactoryMethodName(typeName); + } + + @Override + protected Class getBeanClass(Element element) { + try { + return Class.forName(RedissonNamespaceParserSupport.API_CLASS_PATH_PREFIX + + "Lock"); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException(ex); + } + } + +} diff --git a/redisson/src/main/java/org/redisson/spring/support/RemoteInvocationOptionDecorator.java b/redisson/src/main/java/org/redisson/spring/support/RemoteInvocationOptionDecorator.java new file mode 100644 index 00000000000..a622f8e6f4c --- /dev/null +++ b/redisson/src/main/java/org/redisson/spring/support/RemoteInvocationOptionDecorator.java @@ -0,0 +1,116 @@ +/** + * 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.spring.support; + +import java.util.ArrayList; +import java.util.Arrays; +import org.redisson.api.RemoteInvocationOptions; +import org.springframework.beans.MutablePropertyValues; +import org.springframework.beans.PropertyValue; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; +import reactor.core.support.Assert; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class RemoteInvocationOptionDecorator implements RedissonNamespaceDecorator { + + @Override + public void decorate(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, RedissonNamespaceParserSupport helper) { + if (helper.hasElement(element, + RedissonNamespaceParserSupport.REMOTE_INVOCATION_OPTIONS_ELEMENT)) { + Element options = helper.getSingleElement(element, + RedissonNamespaceParserSupport.REMOTE_INVOCATION_OPTIONS_ELEMENT); + String optionBeanId = invokeOptions(options, parserContext, helper); + if (helper.hasElement(element, + RedissonNamespaceParserSupport.REMOTE_NO_ACK_ELEMENT)) { + helper.invoker(optionBeanId, "noAck", null, parserContext); + } + if (helper.hasElement(element, + RedissonNamespaceParserSupport.REMOTE_ACK_ELEMENT)) { + Element remoteAck = helper.getSingleElement(element, + RedissonNamespaceParserSupport.REMOTE_ACK_ELEMENT); + Assert.state(helper.hasAttribute(remoteAck, + RedissonNamespaceParserSupport.WITHIN_ATTRIBUTE), + "Missing \"" + + RedissonNamespaceParserSupport.WITHIN_ATTRIBUTE + + "\" attribute in \"" + + RedissonNamespaceParserSupport.REMOTE_ACK_ELEMENT + + "\" element."); + ArrayList args = new ArrayList(2); + args.add(helper.getAttribute(remoteAck, + RedissonNamespaceParserSupport.WITHIN_ATTRIBUTE)); + if (helper.hasAttribute(remoteAck, + RedissonNamespaceParserSupport.TIME_UNIT_ATTRIBUTE)) { + args.add(helper.getAttribute(remoteAck, + RedissonNamespaceParserSupport.TIME_UNIT_ATTRIBUTE)); + } + helper.invoker(optionBeanId, "expectAckWithin", args.toArray(), + parserContext); + } + if (helper.hasElement(element, + RedissonNamespaceParserSupport.REMOTE_NO_RESULT_ELEMENT)) { + helper.invoker(optionBeanId, "noResult", null, parserContext); + } + if (helper.hasElement(element, + RedissonNamespaceParserSupport.REMOTE_RESULT_ELEMENT)) { + Element remoteResult = helper.getSingleElement(element, + RedissonNamespaceParserSupport.REMOTE_RESULT_ELEMENT); + Assert.state(helper.hasAttribute(remoteResult, + RedissonNamespaceParserSupport.WITHIN_ATTRIBUTE), + "Missing \"" + + RedissonNamespaceParserSupport.WITHIN_ATTRIBUTE + + "\" attribute in \"" + + RedissonNamespaceParserSupport.REMOTE_RESULT_ELEMENT + + "\" element."); + ArrayList args = new ArrayList(2); + args.add(helper.getAttribute(remoteResult, + RedissonNamespaceParserSupport.WITHIN_ATTRIBUTE)); + if (helper.hasAttribute(remoteResult, + RedissonNamespaceParserSupport.TIME_UNIT_ATTRIBUTE)) { + args.add(helper.getAttribute(remoteResult, + RedissonNamespaceParserSupport.TIME_UNIT_ATTRIBUTE)); + } + helper.invoker(optionBeanId, "expectResultWithin", args.toArray(), + parserContext); + } + MutablePropertyValues properties = builder.getRawBeanDefinition() + .getPropertyValues(); + PropertyValue propertyValue + = properties.getPropertyValue("arguments"); + ManagedList args = new ManagedList(); + args.addAll(Arrays.asList( + (Object[]) propertyValue.getValue())); + args.add(new RuntimeBeanReference(optionBeanId)); + properties.removePropertyValue("arguments"); + properties.addPropertyValue("arguments", args); + } + } + + private String invokeOptions(Element element, ParserContext parserContext, RedissonNamespaceParserSupport helper) { + BeanComponentDefinition defaultOption + = helper.factoryInvoker(element, RemoteInvocationOptions.class, + "defaults", null, parserContext); + return defaultOption.getName(); + } + +} diff --git a/redisson/src/main/resources/META-INF/spring.handlers b/redisson/src/main/resources/META-INF/spring.handlers new file mode 100644 index 00000000000..dd43eb54cfa --- /dev/null +++ b/redisson/src/main/resources/META-INF/spring.handlers @@ -0,0 +1 @@ +http\://redisson.org/schema/redisson=org.redisson.spring.support.RedissonNamespaceHandlerSupport diff --git a/redisson/src/main/resources/META-INF/spring.schemas b/redisson/src/main/resources/META-INF/spring.schemas new file mode 100644 index 00000000000..1753d1003e1 --- /dev/null +++ b/redisson/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,2 @@ +http\://redisson.org/schema/redisson.xsd=org/redisson/spring/support/redisson-1.0.xsd +http\://redisson.org/schema/redisson-1.0.xsd=org/redisson/spring/support/redisson-1.0.xsd diff --git a/redisson/src/main/resources/META-INF/spring.tooling b/redisson/src/main/resources/META-INF/spring.tooling new file mode 100644 index 00000000000..f57ce295c9a --- /dev/null +++ b/redisson/src/main/resources/META-INF/spring.tooling @@ -0,0 +1,4 @@ +# Tooling related information for the redisson namespace +http\://redisson.org/schema/redisson@name=redisson Namespace +http\://redisson.org/schema/redisson@prefix=redisson +http\://redisson.org/schema/redisson@icon=org/redisson/spring/support/redisson.gif diff --git a/redisson/src/main/resources/org/redisson/spring/support/redisson-1.0.xsd b/redisson/src/main/resources/org/redisson/spring/support/redisson-1.0.xsd new file mode 100644 index 00000000000..097feb15d18 --- /dev/null +++ b/redisson/src/main/resources/org/redisson/spring/support/redisson-1.0.xsd @@ -0,0 +1,2231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <qualifier> is not used. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + timeout time + and current connections amount bigger than minimum idle connections pool + size, then it will closed and removed from pool. + Value in milliseconds. + + Default: 10000 + ]]> + + + + + Node.ping and Node.pingAll + operation. Value in milliseconds. + + Default: 1000 + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + failedAttempts. + + Default: 3 + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + each slave node. + + Default: 1 + ]]> + + + + + each slave node. + + Default: 50 + ]]> + + + + + each slave + node. + + Default: 10 + ]]> + + + + + each slave + node. + + Default: 64 + ]]> + + + + + each slave + node. + + Default: 10 + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NB: applications must ensure the JVM DNS cache TTL is low enough to + support this. e.g., http://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/java-dg-jvm-ttl.html + + Default: false + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true then invalidation + message which removes corresponding entry from cache will be sent to all + other RLocalCachedMap instances on each entry update/remove operation. + if false then invalidation message won't be sent. + ]]> + + + + + LRU - uses cache with LRU (least recently used) eviction + policy. +

LFU - uses cache with LFU (least frequently used) + eviction policy. +

SOFT - uses cache with soft references. The garbage + collector will evict items from the cache when the JVM is + running out of memory. JVM flag -XX:SoftRefLRUPolicyMSPerMB=??? + is required to function. +

NONE - doesn't use eviction policy, but timeToLive and + maxIdleTime params are still working. + ]]> + + + + + 0 then local cache is unbounded. + ]]> + + + + + 0 then timeout is not applied. + + Default unit is MILLISECONDS + ]]> + + + + + + + + + + 0 then timeout is not applied. + + Default unit is MILLISECONDS + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOT mandatory + since the class will also be registered lazily when it is first used. + + All classed registered with the service is stored in a class cache. + + The cache is independent between different RedissonClient instances. When + a class is registered in one RLiveObjectService instance it is also + accessible in another RLiveObjectService instance so long as they are + created by the same RedissonClient instance. + ]]> + + + + + + + + + + + + + + + + + + NOT mandatory + since the class will also be registered lazily when it is first used. + + All classed registered with the service is stored in a class cache. + + The cache is independent between different RedissonClient instances. When + a class is registered in one RLiveObjectService instance it is also + accessible in another RLiveObjectService instance so long as they are + created by the same RedissonClient instance. + + One of "object-id" or "object-id-ref" attribute is required. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Set eviction + + Redisson distributed Set for Java with eviction support implemented by + separate RSetCache object which extends RSet interface. It also + implements java.util.Set interface. + + Current redis implementation doesn't has set value eviction + functionality. Therefore expired values are cleaned by + org.redisson.EvictionScheduler. It removes 100 expired values at once. + Task launch time tuned automatically and depends on expired entries + amount deleted in previous time and varies between 1 second to 2 hours. + Thus if clean task deletes 100 values each time it will be executed + every second (minimum execution delay). But if current expired values + amount is lower than previous one then execution delay will be increased + by 1.5 times. + ]]> + + + + + + + + + + + Map eviction + + Redisson distributed Map for Java with eviction support implemented by + separate RMapCache object which extends RMap interface. It keeps + elements in insertion order and implements + java.util.concurrent.ConcurrentMap and java.util.Map interfaces. + Redisson has a Spring Cache integration which based on Map and MapCache + objects. + + Current redis implementation doesn't has map entry eviction + functionality. Therefore expired entries are cleaned by + org.redisson.EvictionScheduler. It removes 100 expired entries at once. + Task launch time tuned automatically and depends on expired entries + amount deleted in previous time and varies between 1 second to 2 hours. + Thus if clean task deletes 100 entries each time it will be executed + every second (minimum execution delay). But if current expired entries + amount is lower than previous one then execution delay will be increased + by 1.5 times. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Map local cache + + In case when a Map is used mostly for read operations and/or network + roundtrips are undesirable. Redisson offers RLocalCachedMap object which + caches Map entries on Redisson side. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + interface. + Keeps elements uniqueness via element state comparison. + ]]> + + + + + + + + + + + interface. + Keeps elements uniqueness via element state comparison. + ]]> + + + + + + + + + + + + + + + + + interface. + Keeps elements uniqueness via element state comparison. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Live distributed object (also abbreviated as live object) refers to a + running instance of a distributed multi-party (or peer-to-peer) protocol, + viewed from the object-oriented perspective, as an entity that has a + distinct identity, may encapsulate internal state and threads of + execution, and that exhibits a well-defined externally visible behavior. + + + Redisson Live Object (RLO) realised this idea by mapping all the fields + inside a Java class to a redis hash through a runtime-constructed proxy + class. All the get/set methods of each field are translated to hget/hset + commands operated on the redis hash, making it accessable to/from any + clients connected to the same redis server. As we all know, the field + values of an object represent its state; having them stored in a remote + repository, redis, makes it a distributed object. This object is a + Redisson Live Object. + + By using RLO, sharing an object between applications and/or servers is + the same as sharing one in a standalone application. This removes the + need for serialization and deserialization, and at the same time reduces + the complexity of the programming model: Changes made to one field + is (almost^) immediately accessable to other processes, applications and + servers. (^Redis' eventual consistant replication rule still applies + when connected to slave nodes) + + Since the redis server is a single-threaded application, all field + access to the live object is automatically executed in atomic fashion: a + value will not be changed when you are reading it. + + With RLO, you can treat the redis server as a shared Heap space for all + connected JVMs. + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Define and create a Redisson instance. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Define and create a RedisClient instance. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/redisson/src/main/resources/org/redisson/spring/support/redisson.gif b/redisson/src/main/resources/org/redisson/spring/support/redisson.gif new file mode 100644 index 0000000000000000000000000000000000000000..0eae52d5f3db8eb8a2bb37a47248d5802f5ca204 GIT binary patch literal 1059 zcmeH`>rYz+7{;G+6GlOpW@Ch&oDDMG*spw343ooOT--n2l0Op)Ixi1^xrR`Q?}A`SN~v zllK*EnI_Mage2^afh8oxfELgJM&JTa3ltL)6L1M=2HJoQ-~-^Ja6KZ{lF|~A)KXM= zM!HtcRLQ8fsr2_$sy2 z<_X#%rJZ3D3{Nlu!8i%VMKF)TNac1$xt5vDDY6%HSbGjTclrgZnte#h*Gaj9RPY&D zJgsyxDnYJt(aL`)#S==gMP)u_^SbrA&vZJoPCua6UpE*=bh;^HJmGeU9c%brnbVwaoX%ZAXiTPo>0=KdPHc$%=s#!-#7(Syp)bn7Fs4@7i}?YTh~8 z`qZ(UkV9|2IZ}0dNjLg#+WYQt*L*zNxcc?0vYti5%O_q+%M+aQpZ*{(YqqqtU5$D6 zwGVw)T6>J?CH-R4Pu7OWk7b4Cs!8`(mBxx;S9@$rZ@gJ%eB*9sR7%~A#EK~679ZI^ zc)YV&ppvKiilpZi{;mk;;zDWt73GUdbsIyeO~)1z+I#rqlQp+$y0_{RNAtzcd>ick LCPv&R5+VOz$16;h literal 0 HcmV?d00001 diff --git a/redisson/src/test/java/org/redisson/BaseTest.java b/redisson/src/test/java/org/redisson/BaseTest.java index 2caa0e612c4..ac6ba763d24 100644 --- a/redisson/src/test/java/org/redisson/BaseTest.java +++ b/redisson/src/test/java/org/redisson/BaseTest.java @@ -38,7 +38,9 @@ public void before() throws IOException, InterruptedException { if (redisson == null) { redisson = defaultRedisson; } - redisson.getKeys().flushall(); + if (flushBetweenTests()) { + redisson.getKeys().flushall(); + } } } @@ -74,4 +76,7 @@ public static RedissonClient createInstance() { return Redisson.create(config); } + protected boolean flushBetweenTests() { + return true; + } } diff --git a/redisson/src/test/java/org/redisson/ClusterRunner.java b/redisson/src/test/java/org/redisson/ClusterRunner.java new file mode 100644 index 00000000000..b1c11228b98 --- /dev/null +++ b/redisson/src/test/java/org/redisson/ClusterRunner.java @@ -0,0 +1,99 @@ +package org.redisson; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class ClusterRunner { + + private final LinkedHashMap nodes = new LinkedHashMap<>(); + + public ClusterRunner addNode(RedisRunner runner) { + nodes.put(runner, getRandomId()); + if (!runner.hasOption(RedisRunner.REDIS_OPTIONS.CLUSTER_ENABLED)) { + runner.clusterEnabled(true); + } + if (!runner.hasOption(RedisRunner.REDIS_OPTIONS.CLUSTER_NODE_TIMEOUT)) { + runner.clusterNodeTimeout(5000); + } + if (!runner.hasOption(RedisRunner.REDIS_OPTIONS.PORT)) { + runner.randomPort(1); + runner.port(RedisRunner.findFreePort()); + } + if (!runner.hasOption(RedisRunner.REDIS_OPTIONS.BIND)) { + runner.bind("127.0.0.1"); + } + return this; + } + + public List run() throws IOException, InterruptedException, RedisRunner.FailedToStartRedisException { + ArrayList processes = new ArrayList<>(); + for (RedisRunner runner : nodes.keySet()) { + List options = getClusterConfig(runner); + String confFile = runner.defaultDir() + File.pathSeparator + nodes.get(runner) + ".conf"; + System.out.println("WRITING CONFIG: for " + nodes.get(runner)); + try (PrintWriter printer = new PrintWriter(new FileWriter(confFile))) { + options.stream().forEach((line) -> { + printer.println(line); + System.out.println(line); + }); + } + processes.add(runner.clusterConfigFile(confFile).run()); + } + Thread.sleep(1000); + for (RedisRunner.RedisProcess process : processes) { + if (!process.isAlive()) { + throw new RedisRunner.FailedToStartRedisException(); + } + } + return processes; + } + + private List getClusterConfig(RedisRunner runner) { + String me = runner.getInitialBindAddr() + ":" + runner.getPort(); + List nodeConfig = new ArrayList<>(); + int c = 0; + for (RedisRunner node : nodes.keySet()) { + StringBuilder sb = new StringBuilder(); + String nodeAddr = node.getInitialBindAddr() + ":" + node.getPort(); + sb.append(nodes.get(node)).append(" "); + sb.append(nodeAddr).append(" "); + sb.append(me.equals(nodeAddr) + ? "myself," + : "").append("master -").append(" "); + sb.append("0").append(" "); + sb.append(me.equals(nodeAddr) + ? "0" + : "1").append(" "); + sb.append(c + 1).append(" "); + sb.append("connected "); + sb.append(getSlots(c, nodes.size())); + c++; + nodeConfig.add(sb.toString()); + } + nodeConfig.add("vars currentEpoch 0 lastVoteEpoch 0"); + return nodeConfig; + } + + private String getSlots(int index, int groupNum) { + final double t = 16383; + int start = index == 0 ? 0 : (int) (t / groupNum * index); + int end = index == groupNum - 1 ? (int) t : (int) (t / groupNum * (index + 1)) - 1; + return start + "-" + end; + } + + private String getRandomId() { + final SecureRandom r = new SecureRandom(); + return new BigInteger(160, r).toString(16); + } +} diff --git a/redisson/src/test/java/org/redisson/RedisRunner.java b/redisson/src/test/java/org/redisson/RedisRunner.java index c7bfd0ddefd..7012abbe96c 100644 --- a/redisson/src/test/java/org/redisson/RedisRunner.java +++ b/redisson/src/test/java/org/redisson/RedisRunner.java @@ -2,8 +2,10 @@ import java.io.BufferedReader; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; +import java.io.PrintWriter; import java.net.Inet4Address; import java.net.ServerSocket; import java.net.URL; @@ -103,7 +105,19 @@ public enum REDIS_OPTIONS { CLIENT_OUTPUT_BUFFER_LIMIT$SLAVE, CLIENT_OUTPUT_BUFFER_LIMIT$PUBSUB, HZ, - AOF_REWRITE_INCREMENTAL_FSYNC; + AOF_REWRITE_INCREMENTAL_FSYNC, + PROTECTED_MODE, + SENTINEL, + SENTINEL$ANNOUNCE_IP, + SENTINEL$ANNOUNCE_PORT, + SENTINEL$MONITOR(true), + SENTINEL$AUTH_PASS(true), + SENTINEL$DOWN_AFTER_MILLISECONDS(true), + SENTINEL$PARALLEL_SYNCS(true), + SENTINEL$FAILOVER_TIMEOUT(true), + SENTINEL$NOTIFICATION_SCRIPT(true), + SENTINEL$CLIENT_RECONFIG_SCRIPT(true) + ; private final boolean allowMutiple; @@ -172,7 +186,7 @@ public enum KEYSPACE_EVENTS_OPTIONS { e, A } - + private final LinkedHashMap options = new LinkedHashMap<>(); protected static RedisRunner.RedisProcess defaultRedisInstance; private static int defaultRedisInstanceExitCode; @@ -184,6 +198,8 @@ public enum KEYSPACE_EVENTS_OPTIONS { private int port = 6379; private int retryCount = Integer.MAX_VALUE; private boolean randomPort = false; + private String sentinelFile; + private String clusterFile; { this.options.put(REDIS_OPTIONS.BINARY_PATH, RedissonRuntimeEnvironment.redisBinaryPath); @@ -253,13 +269,38 @@ public RedisProcess run() throws IOException, InterruptedException, FailedToStar } public RedisProcess runAndCheck() throws IOException, InterruptedException, FailedToStartRedisException { - RedisProcess rp = runWithOptions(this, options.values().toArray(new String[0])); - if (rp.redisProcess.waitFor(1000, TimeUnit.MILLISECONDS)) { + List args = new ArrayList(options.values()); + if (sentinelFile != null && sentinelFile.length() > 0) { + String confFile = defaultDir + File.pathSeparator + sentinelFile; + try (PrintWriter printer = new PrintWriter(new FileWriter(confFile))) { + args.stream().forEach((arg) -> { + if (arg.contains("--")) { + printer.println(arg.replace("--", "\n\r")); + } + }); + } + args = args.subList(0, 1); + args.add(confFile); + args.add("--sentinel"); + } + RedisProcess rp = runWithOptions(this, args.toArray(new String[0])); + if (!isCluster() + && rp.redisProcess.waitFor(1000, TimeUnit.MILLISECONDS)) { throw new FailedToStartRedisException(); } + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + rp.stop(); + } catch (InterruptedException ex) { + } + })); return rp; } + public boolean hasOption(REDIS_OPTIONS option) { + return options.containsKey(option); + } + private void addConfigOption(REDIS_OPTIONS option, Object... args) { StringBuilder sb = new StringBuilder("--") .append(option.toString() @@ -306,7 +347,7 @@ public RedisRunner randomPort(int retryCount) { options.remove(REDIS_OPTIONS.PORT); return this; } - + public int getPort() { return this.port; } @@ -440,6 +481,11 @@ public RedisRunner slaveof(Inet4Address masterip, int port) { return this; } + public RedisRunner slaveof(String masterip, int port) { + addConfigOption(REDIS_OPTIONS.SLAVEOF, masterip, port); + return this; + } + public RedisRunner masterauth(String masterauth) { addConfigOption(REDIS_OPTIONS.MASTERAUTH, masterauth); return this; @@ -582,6 +628,7 @@ public RedisRunner clusterEnabled(boolean clusterEnabled) { public RedisRunner clusterConfigFile(String clusterConfigFile) { addConfigOption(REDIS_OPTIONS.CLUSTER_CONFIG_FILE, clusterConfigFile); + this.clusterFile = clusterConfigFile; return this; } @@ -702,7 +749,70 @@ public RedisRunner aofRewriteIncrementalFsync(boolean aofRewriteIncrementalFsync addConfigOption(REDIS_OPTIONS.AOF_REWRITE_INCREMENTAL_FSYNC, convertBoolean(aofRewriteIncrementalFsync)); return this; } + + public RedisRunner protectedMode(boolean protectedMode) { + addConfigOption(REDIS_OPTIONS.PROTECTED_MODE, convertBoolean(protectedMode)); + return this; + } + + public RedisRunner sentinel() { + sentinelFile = "sentinel_conf_" + UUID.randomUUID() + ".conf"; + return this; + } + + public RedisRunner sentinelAnnounceIP(String sentinelAnnounceIP) { + addConfigOption(REDIS_OPTIONS.SENTINEL$ANNOUNCE_IP, sentinelAnnounceIP); + return this; + } + + public RedisRunner sentinelAnnouncePort(int sentinelAnnouncePort) { + addConfigOption(REDIS_OPTIONS.SENTINEL$ANNOUNCE_PORT, sentinelAnnouncePort); + return this; + } + + public RedisRunner sentinelMonitor(String masterName, String ip, int port, int quorum) { + addConfigOption(REDIS_OPTIONS.SENTINEL$MONITOR, masterName, ip, port, quorum); + return this; + } + + public RedisRunner sentinelAuthPass(String masterName, String password) { + addConfigOption(REDIS_OPTIONS.SENTINEL$AUTH_PASS, masterName, password); + return this; + } + + public RedisRunner sentinelDownAfterMilliseconds(String masterName, long downAfterMilliseconds) { + addConfigOption(REDIS_OPTIONS.SENTINEL$DOWN_AFTER_MILLISECONDS, masterName, downAfterMilliseconds); + return this; + } + + public RedisRunner sentinelParallelSyncs(String masterName, int numSlaves) { + addConfigOption(REDIS_OPTIONS.SENTINEL$PARALLEL_SYNCS, masterName, numSlaves); + return this; + } + + public RedisRunner sentinelFailoverTimeout(String masterName, long failoverTimeout) { + addConfigOption(REDIS_OPTIONS.SENTINEL$FAILOVER_TIMEOUT, masterName, failoverTimeout); + return this; + } + + public RedisRunner sentinelNotificationScript(String masterName, String scriptPath) { + addConfigOption(REDIS_OPTIONS.SENTINEL$NOTIFICATION_SCRIPT, masterName, scriptPath); + return this; + } + + public RedisRunner sentinelClientReconfigScript(String masterName, String scriptPath) { + addConfigOption(REDIS_OPTIONS.SENTINEL$CLIENT_RECONFIG_SCRIPT, masterName, scriptPath); + return this; + } + + public boolean isSentinel() { + return this.sentinelFile != null; + } + public boolean isCluster() { + return this.clusterFile != null; + } + public boolean isRandomDir() { return this.randomDir; } @@ -722,14 +832,32 @@ public String getInitialBindAddr() { public boolean deleteDBfileDir() { File f = new File(defaultDir); if (f.exists()) { - System.out.println("REDIS RUNNER: Deleting directory " + defaultDir); + System.out.println("REDIS RUNNER: Deleting directory " + f.getAbsolutePath()); + return f.delete(); + } + return false; + } + + public boolean deleteSentinelFile() { + File f = new File(defaultDir + File.pathSeparator + sentinelFile); + if (f.exists()) { + System.out.println("REDIS RUNNER: Deleting sentinel config file " + f.getAbsolutePath()); + return f.delete(); + } + return false; + } + + public boolean deleteClusterFile() { + File f = new File(clusterFile); + if (f.exists()) { + System.out.println("REDIS RUNNER: Deleting cluster config file " + f.getAbsolutePath()); return f.delete(); } return false; } private void makeRandomDefaultDir() { - File f = new File(RedissonRuntimeEnvironment.tempDir + "/" + UUID.randomUUID()); + File f = new File(RedissonRuntimeEnvironment.tempDir + File.pathSeparator + UUID.randomUUID()); if (f.exists()) { makeRandomDefaultDir(); } else { @@ -761,6 +889,12 @@ public int stop() throws InterruptedException { } redisProcess.destroy(); int exitCode = redisProcess.isAlive() ? redisProcess.waitFor() : redisProcess.exitValue(); + if (runner.isSentinel()) { + runner.deleteSentinelFile(); + } + if (runner.isCluster()) { + runner.deleteClusterFile(); + } if (runner.isRandomDir()) { runner.deleteDBfileDir(); } @@ -799,18 +933,16 @@ public String getRedisServerBindAddress() { public String getRedisServerAddressAndPort() { return getRedisServerBindAddress() + ":" + getRedisServerPort(); } + + public boolean isAlive() { + return redisProcess.isAlive(); + } } public static RedisRunner.RedisProcess startDefaultRedisServerInstance() throws IOException, InterruptedException, FailedToStartRedisException { if (defaultRedisInstance == null) { System.out.println("REDIS RUNNER: Starting up default instance..."); defaultRedisInstance = new RedisRunner().nosave().randomDir().randomPort().run(); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - shutDownDefaultRedisServerInstance(); - } catch (InterruptedException ex) { - } - })); } return defaultRedisInstance; } @@ -853,12 +985,11 @@ public static int findFreePort() { socket = new ServerSocket(0); socket.setReuseAddress(true); int port = socket.getLocalPort(); - try { - socket.close(); - } catch (IOException e) { - // Ignore IOException on close() + if (port > 55535 && isFreePort(port - 10000)) { + return port - 10000; + } else { + return port; } - return port; } catch (IOException e) { } finally { if (socket != null) { @@ -871,9 +1002,27 @@ public static int findFreePort() { throw new IllegalStateException("Could not find a free TCP/IP port."); } + public static boolean isFreePort(int port) { + ServerSocket socket = null; + try { + socket = new ServerSocket(port); + socket.setReuseAddress(true); + return true; + } catch (IOException e) { + } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + } + } + } + return false; + } + public static class FailedToStartRedisException extends RuntimeException { - private FailedToStartRedisException() { + public FailedToStartRedisException() { } } diff --git a/redisson/src/test/java/org/redisson/RedissonRemoteServiceTest.java b/redisson/src/test/java/org/redisson/RedissonRemoteServiceTest.java index e80c18ffa9e..c9dec60aec0 100644 --- a/redisson/src/test/java/org/redisson/RedissonRemoteServiceTest.java +++ b/redisson/src/test/java/org/redisson/RedissonRemoteServiceTest.java @@ -1,7 +1,5 @@ package org.redisson; -import static org.assertj.core.api.Assertions.assertThat; - import java.io.IOException; import java.io.NotSerializableException; import java.io.Serializable; @@ -133,7 +131,7 @@ public interface RemoteInterface { } - public class RemoteImpl implements RemoteInterface { + public static class RemoteImpl implements RemoteInterface { private AtomicInteger iterations; diff --git a/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceObjectTest.java b/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceObjectTest.java new file mode 100644 index 00000000000..59f92dd46bf --- /dev/null +++ b/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceObjectTest.java @@ -0,0 +1,282 @@ +package org.redisson.spring.support; + +import java.util.Arrays; +import java.util.Collection; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; +import org.redisson.BaseTest; +import org.redisson.RedisRunner; +import org.redisson.RedissonFairLock; +import org.redisson.RedissonLiveObjectServiceTest.TestREntity; +import org.redisson.RedissonMultiLock; +import org.redisson.RedissonReadLock; +import org.redisson.RedissonRedLock; +import org.redisson.RedissonRuntimeEnvironment; +import org.redisson.RedissonWriteLock; +import org.redisson.api.LocalCachedMapOptions; +import org.redisson.api.RAtomicDouble; +import org.redisson.api.RAtomicLong; +import org.redisson.api.RBinaryStream; +import org.redisson.api.RBitSet; +import org.redisson.api.RBlockingDeque; +import org.redisson.api.RBlockingFairQueue; +import org.redisson.api.RBlockingQueue; +import org.redisson.api.RBloomFilter; +import org.redisson.api.RBoundedBlockingQueue; +import org.redisson.api.RBucket; +import org.redisson.api.RBuckets; +import org.redisson.api.RCountDownLatch; +import org.redisson.api.RDelayedQueue; +import org.redisson.api.RDeque; +import org.redisson.api.RExecutorService; +import org.redisson.api.RGeo; +import org.redisson.api.RHyperLogLog; +import org.redisson.api.RKeys; +import org.redisson.api.RLexSortedSet; +import org.redisson.api.RList; +import org.redisson.api.RListMultimap; +import org.redisson.api.RLiveObject; +import org.redisson.api.RLiveObjectService; +import org.redisson.api.RLocalCachedMap; +import org.redisson.api.RLock; +import org.redisson.api.RMap; +import org.redisson.api.RMapCache; +import org.redisson.api.RObject; +import org.redisson.api.RPatternTopic; +import org.redisson.api.RPermitExpirableSemaphore; +import org.redisson.api.RPriorityDeque; +import org.redisson.api.RPriorityQueue; +import org.redisson.api.RQueue; +import org.redisson.api.RReadWriteLock; +import org.redisson.api.RRemoteService; +import org.redisson.api.RScoredSortedSet; +import org.redisson.api.RScript; +import org.redisson.api.RSemaphore; +import org.redisson.api.RSet; +import org.redisson.api.RSetCache; +import org.redisson.api.RSetMultimap; +import org.redisson.api.RSetMultimapCache; +import org.redisson.api.RSortedSet; +import org.redisson.api.RTopic; +import org.redisson.api.RemoteInvocationOptions; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +@RunWith(Parameterized.class) +public class SpringNamespaceObjectTest extends BaseTest { + + private static ApplicationContext context; + + @BeforeClass + public static void setupClass() throws Exception { + if (!RedissonRuntimeEnvironment.isTravis) { + startContext(); + } + } + + @AfterClass + public static void shutDownClass() { + if (!RedissonRuntimeEnvironment.isTravis) { + stopContext(); + } + } + + @Before + public void setup() throws Exception { + if (RedissonRuntimeEnvironment.isTravis) { + startContext(); + } + } + + @After + public void shutDown() { + if (RedissonRuntimeEnvironment.isTravis) { + stopContext(); + } + } + + @Override + protected boolean flushBetweenTests() { + return false; + } + + public static void startContext() { + TestREntity entity = new TestREntity("live-object"); + entity.setValue("1"); + defaultRedisson.getLiveObjectService().merge(entity); + entity = new TestREntity("live-object-ext"); + entity.setValue("1"); + defaultRedisson.getLiveObjectService().merge(entity); + + System.setProperty("redisAddress", RedisRunner.getDefaultRedisServerBindAddressAndPort()); + context = new ClassPathXmlApplicationContext("classpath:org/redisson/spring/support/redisson_objects.xml"); + } + + public static void stopContext() { + ((ConfigurableApplicationContext) context).close(); + } + + @Parameters(name = "{index}: key=[{0}], class=[{1}], parent=[{2}]") + public static Collection tests() { + return Arrays.asList(new Object[][]{ + {"binary-stream", RBinaryStream.class, null}, + {"geo", RGeo.class, null}, + {"set-cache", RSetCache.class, null}, + {"map-cache", RMapCache.class, null}, + {"bucket", RBucket.class, null}, + {"buckets", RBuckets.class, null}, + {"hyper-log-log", RHyperLogLog.class, null}, + {"list", RList.class, null}, + {"list-multimap", RListMultimap.class, null}, + {"local-cached-map", RLocalCachedMap.class, null}, + {"local-options", LocalCachedMapOptions.class, null}, + {"map", RMap.class, null}, + {"set-multimap", RSetMultimap.class, null}, + {"set-multimap-cache", RSetMultimapCache.class, null}, + {"semaphore", RSemaphore.class, null}, + {"permit-expirable-semaphore", RPermitExpirableSemaphore.class, null}, + {"lock", RLock.class, null}, + {"fair-lock", RedissonFairLock.class, null}, + {"read-write-lock", RReadWriteLock.class, null}, + {"read-lock", RedissonReadLock.class, "read-write-lock"}, + {"write-lock", RedissonWriteLock.class, "read-write-lock"}, + {"multi-lock", RedissonMultiLock.class, null}, + {"lock-1", RLock.class, null}, + {"fair-lock-1", RedissonFairLock.class, null}, + {"read-lock-1", RedissonReadLock.class, "read-write-lock"}, + {"write-lock-1", RedissonWriteLock.class, "read-write-lock"}, + {"red-lock", RedissonRedLock.class, null}, + {"lock-2", RLock.class, null}, + {"fair-lock-2", RedissonFairLock.class, null}, + {"read-lock-2", RedissonReadLock.class, "read-write-lock"}, + {"write-lock-2", RedissonWriteLock.class, "read-write-lock"}, + {"set", RSet.class, null}, + {"sorted-set", RSortedSet.class, null}, + {"scored-sorted-set", RScoredSortedSet.class, null}, + {"lex-sorted-set", RLexSortedSet.class, null}, + {"topic", RTopic.class, null}, + {"pattern-topic", RPatternTopic.class, null}, + {"blocking-fair-queue", RBlockingFairQueue.class, null}, + {"queue", RQueue.class, null}, + {"delayed-queue", RDelayedQueue.class, "queue"}, + {"priority-queue", RPriorityQueue.class, null}, + {"priority-deque", RPriorityDeque.class, null}, + {"blocking-queue", RBlockingQueue.class, null}, + {"bounded-blocking-queue", RBoundedBlockingQueue.class, null}, + {"deque", RDeque.class, null}, + {"blocking-deque", RBlockingDeque.class, null}, + {"atomic-long", RAtomicLong.class, null}, + {"atomic-double", RAtomicDouble.class, null}, + {"count-down-latch", RCountDownLatch.class, null}, + {"bit-set", RBitSet.class, null}, + {"bloom-filter", RBloomFilter.class, null}, + {"script", RScript.class, null}, + {"executor-service", RExecutorService.class, null}, + {"remote-service", RRemoteService.class, null}, + {"rpc-client", org.redisson.RedissonRemoteServiceTest.RemoteInterface.class, null}, + {"options", RemoteInvocationOptions.class, null}, + {"keys", RKeys.class, null}, + {"live-object-service", RLiveObjectService.class, null}, + {"live-object", RLiveObject.class, null}, + {"binary-stream-ext", RBinaryStream.class, null}, + {"geo-ext", RGeo.class, null}, + {"set-cache-ext", RSetCache.class, null}, + {"map-cache-ext", RMapCache.class, null}, + {"bucket-ext", RBucket.class, null}, + {"buckets-ext", RBuckets.class, null}, + {"hyper-log-log-ext", RHyperLogLog.class, null}, + {"list-ext", RList.class, null}, + {"list-multimap-ext", RListMultimap.class, null}, + {"local-cached-map-ext", RLocalCachedMap.class, null}, + {"local-options-ext", LocalCachedMapOptions.class, null}, + {"map-ext", RMap.class, null}, + {"set-multimap-ext", RSetMultimap.class, null}, + {"set-multimap-cache-ext", RSetMultimapCache.class, null}, + {"semaphore-ext", RSemaphore.class, null}, + {"permit-expirable-semaphore-ext", RPermitExpirableSemaphore.class, null}, + {"lock-ext", RLock.class, null}, + {"fair-lock-ext", RedissonFairLock.class, null}, + {"read-write-lock-ext", RReadWriteLock.class, null}, + {"read-lock-ext", RedissonReadLock.class, "read-write-lock-ext"}, + {"write-lock-ext", RedissonWriteLock.class, "read-write-lock-ext"}, + {"multi-lock-ext", RedissonMultiLock.class, null}, + {"lock-1-ext", RLock.class, null}, + {"fair-lock-1-ext", RedissonFairLock.class, null}, + {"read-lock-1-ext", RedissonReadLock.class, "read-write-lock-ext"}, + {"write-lock-1-ext", RedissonWriteLock.class, "read-write-lock-ext"}, + {"red-lock-ext", RedissonRedLock.class, null}, + {"lock-2-ext", RLock.class, null}, + {"fair-lock-2-ext", RedissonFairLock.class, null}, + {"read-lock-2-ext", RedissonReadLock.class, "read-write-lock-ext"}, + {"write-lock-2-ext", RedissonWriteLock.class, "read-write-lock-ext"}, + {"set-ext", RSet.class, null}, + {"sorted-set-ext", RSortedSet.class, null}, + {"scored-sorted-set-ext", RScoredSortedSet.class, null}, + {"lex-sorted-set-ext", RLexSortedSet.class, null}, + {"topic-ext", RTopic.class, null}, + {"pattern-topic-ext", RPatternTopic.class, null}, + {"blocking-fair-queue-ext", RBlockingFairQueue.class, null}, + {"queue-ext", RQueue.class, null}, + {"delayed-queue-ext", RDelayedQueue.class, "queue-ext"}, + {"priority-queue-ext", RPriorityQueue.class, null}, + {"priority-deque-ext", RPriorityDeque.class, null}, + {"blocking-queue-ext", RBlockingQueue.class, null}, + {"bounded-blocking-queue-ext", RBoundedBlockingQueue.class, null}, + {"deque-ext", RDeque.class, null}, + {"blocking-deque-ext", RBlockingDeque.class, null}, + {"atomic-long-ext", RAtomicLong.class, null}, + {"atomic-double-ext", RAtomicDouble.class, null}, + {"count-down-latch-ext", RCountDownLatch.class, null}, + {"bit-set-ext", RBitSet.class, null}, + {"bloom-filter-ext", RBloomFilter.class, null}, + {"script-ext", RScript.class, null}, + {"executor-service-ext", RExecutorService.class, null}, + {"remote-service-ext", RRemoteService.class, null}, + {"rpc-client-ext", org.redisson.RedissonRemoteServiceTest.RemoteInterface.class, null}, + {"options-ext", RemoteInvocationOptions.class, null}, + {"keys-ext", RKeys.class, null}, + {"live-object-service-ext", RLiveObjectService.class, null}, + {"live-object-ext", RLiveObject.class, null}, + }); + } + + @Parameter + public String key; + + @Parameter(1) + public Class cls; + + @Parameter(2) + public String parentKey; + + @Test + public void testRObjects() { + Object bean = context.getBean(key); + assertTrue(cls.isInstance(bean)); + if (RObject.class.isAssignableFrom(cls)) { + assertEquals(parentKey == null ? key : parentKey, RObject.class.cast(bean).getName()); + } + if (RTopic.class.isAssignableFrom(cls)) { + assertEquals(key, RTopic.class.cast(bean).getChannelNames().get(0)); + } + if (RPatternTopic.class.isAssignableFrom(cls)) { + assertEquals(key, RPatternTopic.class.cast(bean).getPatternNames().get(0)); + } + if (RLiveObject.class.isAssignableFrom(cls)) { + assertEquals(key, RLiveObject.class.cast(bean).getLiveObjectId()); + } + } +} diff --git a/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceTest.java b/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceTest.java new file mode 100644 index 00000000000..a2e36842c7c --- /dev/null +++ b/redisson/src/test/java/org/redisson/spring/support/SpringNamespaceTest.java @@ -0,0 +1,368 @@ +package org.redisson.spring.support; + +import java.util.List; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.redisson.BaseTest; +import org.redisson.ClusterRunner; +import org.redisson.RedisRunner; +import org.redisson.Redisson; +import org.redisson.RedissonRuntimeEnvironment; +import org.redisson.api.RedissonClient; +import org.redisson.client.RedisClient; +import org.redisson.client.RedisConnection; +import org.redisson.codec.MsgPackJacksonCodec; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * + * @author Rui Gu (https://github.com/jackygurui) + */ +public class SpringNamespaceTest extends BaseTest { + + private static ApplicationContext context; + + @BeforeClass + public static void setupClass() throws Exception { + if (!RedissonRuntimeEnvironment.isTravis) { + startContext(); + } + } + + @AfterClass + public static void shutDownClass() throws Exception { + if (!RedissonRuntimeEnvironment.isTravis) { + stopContext(); + } + } + + @Before + public void setup() throws Exception { + if (RedissonRuntimeEnvironment.isTravis) { + startContext(); + } + } + + @After + public void shutDown() throws Exception { + if (RedissonRuntimeEnvironment.isTravis) { + stopContext(); + } + } + + public static void startContext() throws Exception { + System.setProperty("redisAddress", RedisRunner.getDefaultRedisServerBindAddressAndPort()); + + //Needs a instance running on the default port, launch it if there isn't one already + if (RedisRunner.isFreePort(6379)) { + new RedisRunner() + .nosave() + .randomDir() + .run(); + } + + RedisRunner.RedisProcess slave1 = new RedisRunner() + .nosave() + .randomDir() + .randomPort() + .slaveof( + RedisRunner.getDefaultRedisServerInstance().getRedisServerBindAddress(), + RedisRunner.getDefaultRedisServerInstance().getRedisServerPort()) + .run(); + System.setProperty("slave1Address", slave1.getRedisServerAddressAndPort()); + + RedisRunner.RedisProcess slave2 = new RedisRunner() + .nosave() + .randomDir() + .randomPort() + .slaveof( + RedisRunner.getDefaultRedisServerInstance().getRedisServerBindAddress(), + RedisRunner.getDefaultRedisServerInstance().getRedisServerPort()) + .run(); + System.setProperty("slave2Address", slave2.getRedisServerAddressAndPort()); + + RedisRunner.RedisProcess sentinel1 = new RedisRunner() + .nosave() + .randomDir() + .randomPort() + .sentinel() + .sentinelMonitor( + "myMaster", + RedisRunner.getDefaultRedisServerInstance().getRedisServerBindAddress(), + RedisRunner.getDefaultRedisServerInstance().getRedisServerPort(), + 2).run(); + System.setProperty("sentinel1Address", sentinel1.getRedisServerAddressAndPort()); + + RedisRunner.RedisProcess sentinel2 = new RedisRunner() + .nosave() + .randomDir() + .randomPort() + .sentinel() + .sentinelMonitor( + "myMaster", + RedisRunner.getDefaultRedisServerInstance().getRedisServerBindAddress(), + RedisRunner.getDefaultRedisServerInstance().getRedisServerPort(), + 2).run(); + System.setProperty("sentinel2Address", sentinel2.getRedisServerAddressAndPort()); + + RedisRunner.RedisProcess sentinel3 = new RedisRunner() + .nosave() + .randomDir() + .randomPort() + .sentinel() + .sentinelMonitor( + "myMaster", + RedisRunner.getDefaultRedisServerInstance().getRedisServerBindAddress(), + RedisRunner.getDefaultRedisServerInstance().getRedisServerPort(), + 2).run(); + System.setProperty("sentinel3Address", sentinel3.getRedisServerAddressAndPort()); + + ClusterRunner clusterRunner = new ClusterRunner() + .addNode(new RedisRunner().randomPort().randomDir().nosave()) + .addNode(new RedisRunner().randomPort().randomDir().nosave()) + .addNode(new RedisRunner().randomPort().randomDir().nosave()); + List nodes = clusterRunner.run(); + nodes.stream().forEach((node) -> { + System.setProperty("node" + (nodes.indexOf(node) + 1) + "Address", node.getRedisServerAddressAndPort()); + }); + + context = new ClassPathXmlApplicationContext("classpath:org/redisson/spring/support/namespace.xml"); + } + + public static void stopContext() throws Exception { + ((ConfigurableApplicationContext) context).close(); + } + + public static class AutowireRedisson { + + @Autowired + @Qualifier("redisson1") + private Redisson redisson1; + + @Autowired + @Qualifier("redisson2") + private RedissonClient redisson2; + + @Autowired + @Qualifier("redisson3") + private Redisson redisson3; + + @Autowired + @Qualifier("redisson4") + private RedissonClient redisson4; + + @Autowired + @Qualifier("myRedisson1") + private Redisson redisson5; + + @Autowired + @Qualifier("myRedisson2") + private Redisson redisson6; + + @Autowired + @Qualifier("qualifier1") + private RedissonClient redisson7; + + @Autowired + @Qualifier("qualifier2") + private RedissonClient redisson8; + + /** + * @return the redisson1 + */ + public Redisson getRedisson1() { + return redisson1; + } + + /** + * @param redisson1 the redisson1 to set + */ + public void setRedisson1(Redisson redisson1) { + this.redisson1 = redisson1; + } + + /** + * @return the redisson2 + */ + public RedissonClient getRedisson2() { + return redisson2; + } + + /** + * @param redisson2 the redisson2 to set + */ + public void setRedisson2(RedissonClient redisson2) { + this.redisson2 = redisson2; + } + + /** + * @return the redisson3 + */ + public Redisson getRedisson3() { + return redisson3; + } + + /** + * @param redisson3 the redisson3 to set + */ + public void setRedisson3(Redisson redisson3) { + this.redisson3 = redisson3; + } + + /** + * @return the redisson4 + */ + public RedissonClient getRedisson4() { + return redisson4; + } + + /** + * @param redisson4 the redisson4 to set + */ + public void setRedisson4(RedissonClient redisson4) { + this.redisson4 = redisson4; + } + + /** + * @return the redisson5 + */ + public Redisson getRedisson5() { + return redisson5; + } + + /** + * @param redisson5 the redisson5 to set + */ + public void setRedisson5(Redisson redisson5) { + this.redisson5 = redisson5; + } + + /** + * @return the redisson6 + */ + public Redisson getRedisson6() { + return redisson6; + } + + /** + * @param redisson6 the redisson6 to set + */ + public void setRedisson6(Redisson redisson6) { + this.redisson6 = redisson6; + } + + /** + * @return the redisson7 + */ + public RedissonClient getRedisson7() { + return redisson7; + } + + /** + * @param redisson7 the redisson7 to set + */ + public void setRedisson7(RedissonClient redisson7) { + this.redisson7 = redisson7; + } + + /** + * @return the redisson8 + */ + public RedissonClient getRedisson8() { + return redisson8; + } + + /** + * @param redisson8 the redisson8 to set + */ + public void setRedisson8(RedissonClient redisson8) { + this.redisson8 = redisson8; + } + + } + + @Test + public void testNamespace() { + Object bean = context.getBean("myRedisson1"); + assertTrue(bean instanceof Redisson); + } + + @Test + public void testAlias() { + Object origin = context.getBean("myRedisson1"); + assertTrue(origin instanceof Redisson); + Object bean = context.getBean("redisson1"); + assertTrue(bean instanceof Redisson); + assertEquals(origin, bean); + bean = context.getBean("redisson2"); + assertTrue(bean instanceof Redisson); + assertEquals(origin, bean); + } + + @Test + public void testAutowire() { + AutowireRedisson bean = context.getAutowireCapableBeanFactory().getBean(AutowireRedisson.class); + assertNotNull(bean.getRedisson1()); + assertNotNull(bean.getRedisson2()); + assertNotNull(bean.getRedisson3()); + assertNotNull(bean.getRedisson4()); + assertNotNull(bean.getRedisson5()); + assertNotNull(bean.getRedisson6()); + assertNotNull(bean.getRedisson7()); + assertNotNull(bean.getRedisson8()); + assertEquals(bean.getRedisson1(), bean.getRedisson2()); + assertEquals(bean.getRedisson1(), bean.getRedisson5()); + assertNotEquals(bean.getRedisson1(), bean.getRedisson7()); + assertNotEquals(bean.getRedisson1(), bean.getRedisson8()); + assertEquals(bean.getRedisson3(), bean.getRedisson4()); + assertEquals(bean.getRedisson3(), bean.getRedisson6()); + assertNotEquals(bean.getRedisson3(), bean.getRedisson7()); + assertNotEquals(bean.getRedisson3(), bean.getRedisson8()); + assertNotEquals(bean.getRedisson7(), bean.getRedisson8()); + } + + @Test + public void testBeanRef() { + AutowireRedisson bean = context.getAutowireCapableBeanFactory().getBean(AutowireRedisson.class); + assertTrue(bean.getRedisson1().getConfig().getCodec() instanceof MsgPackJacksonCodec); + assertFalse(bean.getRedisson3().getConfig().getCodec() instanceof MsgPackJacksonCodec); + assertFalse(bean.getRedisson7().getConfig().getCodec() instanceof MsgPackJacksonCodec); + assertFalse(bean.getRedisson8().getConfig().getCodec() instanceof MsgPackJacksonCodec); + } + + public static class AutowireRedis { + + @Autowired + private RedisClient redisClient; + + /** + * @return the redisClient + */ + public RedisClient getRedisClient() { + return redisClient; + } + + /** + * @param redisClient the redisClient to set + */ + public void setRedisClient(RedisClient redisClient) { + this.redisClient = redisClient; + } + } + + @Test + public void testAutowireRedis() { + AutowireRedis bean = context.getAutowireCapableBeanFactory().getBean(AutowireRedis.class); + RedisConnection connection = bean.getRedisClient().connect(); + assertTrue(connection.isActive()); + connection.closeAsync().awaitUninterruptibly(); + } +} diff --git a/redisson/src/test/resources/org/redisson/spring/support/namespace.xml b/redisson/src/test/resources/org/redisson/spring/support/namespace.xml new file mode 100644 index 00000000000..928ee1847e5 --- /dev/null +++ b/redisson/src/test/resources/org/redisson/spring/support/namespace.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/redisson/src/test/resources/org/redisson/spring/support/redisson_objects.xml b/redisson/src/test/resources/org/redisson/spring/support/redisson_objects.xml new file mode 100644 index 00000000000..3e3b6450489 --- /dev/null +++ b/redisson/src/test/resources/org/redisson/spring/support/redisson_objects.xml @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +