├── .tgitconfig ├── src ├── main │ ├── resources │ │ └── META-INF │ │ │ ├── spring-autoconfigure-metadata.properties │ │ │ └── spring.factories │ └── java │ │ └── com │ │ └── lmax │ │ └── disruptor │ │ └── spring │ │ └── boot │ │ ├── event │ │ ├── handler │ │ │ ├── Nameable.java │ │ │ ├── chain │ │ │ │ ├── HandlerChain.java │ │ │ │ ├── HandlerChainResolver.java │ │ │ │ ├── HandlerChainManager.java │ │ │ │ ├── ProxiedHandlerChain.java │ │ │ │ └── def │ │ │ │ │ ├── PathMatchingHandlerChainResolver.java │ │ │ │ │ ├── DefaultNamedHandlerList.java │ │ │ │ │ └── DefaultHandlerChainManager.java │ │ │ ├── PathProcessor.java │ │ │ ├── DisruptorHandler.java │ │ │ ├── AbstractNameableEventHandler.java │ │ │ ├── NamedHandlerList.java │ │ │ ├── DisruptorEventDispatcher.java │ │ │ ├── AbstractEnabledEventHandler.java │ │ │ ├── AbstractRouteableEventHandler.java │ │ │ ├── AbstractAdviceEventHandler.java │ │ │ └── AbstractPathMatchEventHandler.java │ │ ├── factory │ │ │ ├── DisruptorEventThreadFactory.java │ │ │ ├── DisruptorEventDaemonThreadFactory.java │ │ │ ├── DisruptorEventMaxPriorityThreadFactory.java │ │ │ ├── DisruptorBindEventFactory.java │ │ │ ├── DisruptorEventWorkerThreadFactory.java │ │ │ └── DisruptorEventLoggerThreadFactory.java │ │ ├── DisruptorStartedEvent.java │ │ ├── DisruptorStoppedEvent.java │ │ ├── DisruptorRefreshedEvent.java │ │ ├── translator │ │ │ ├── DisruptorEventTwoArgTranslator.java │ │ │ ├── DisruptorEventThreeArgTranslator.java │ │ │ └── DisruptorEventOneArgTranslator.java │ │ ├── DisruptorBindEvent.java │ │ ├── DisruptorApplicationEvent.java │ │ └── DisruptorEvent.java │ │ ├── hooks │ │ └── DisruptorShutdownHook.java │ │ ├── exception │ │ └── EventHandleException.java │ │ ├── annotation │ │ └── EventRule.java │ │ ├── context │ │ ├── event │ │ │ ├── DisruptorEventPublisherAware.java │ │ │ ├── DisruptorEventPublisher.java │ │ │ └── EventPublicationInterceptor.java │ │ ├── DisruptorApplicationContext.java │ │ └── DisruptorEventAwareProcessor.java │ │ ├── config │ │ ├── EventHandlerDefinition.java │ │ └── Ini.java │ │ ├── util │ │ ├── WaitStrategys.java │ │ └── StringUtils.java │ │ ├── DisruptorTemplate.java │ │ ├── DisruptorProperties.java │ │ ├── RingBufferAutoConfiguration.java │ │ └── DisruptorAutoConfiguration.java └── test │ ├── java │ └── org │ │ └── apache │ │ └── rocketmq │ │ └── spring │ │ └── boot │ │ └── DemoApplicationTests.java │ └── resources │ └── application.yml ├── .gitignore ├── README.md ├── mvnw.cmd ├── mvnw ├── LICENSE └── pom.xml /.tgitconfig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring-autoconfigure-metadata.properties: -------------------------------------------------------------------------------- 1 | com.lmax.disruptor.spring.boot.DisruptorAutoConfiguration= 2 | com.lmax.disruptor.spring.boot.RingBufferAutoConfiguration= -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | # Auto Configure 2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 3 | com.lmax.disruptor.spring.boot.DisruptorAutoConfiguration,\ 4 | com.lmax.disruptor.spring.boot.RingBufferAutoConfiguration -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/Nameable.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler; 2 | 3 | /** 4 | * 设置一个唯一的名称 5 | */ 6 | public interface Nameable { 7 | 8 | void setName(String name); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/chain/HandlerChain.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler.chain; 2 | 3 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 4 | 5 | public interface HandlerChain{ 6 | 7 | void doHandler(T event) throws Exception; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/PathProcessor.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler; 2 | 3 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 4 | 5 | /** 6 | * 给Handler设置路径 7 | */ 8 | public interface PathProcessor { 9 | 10 | DisruptorHandler processPath(String path); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/chain/HandlerChainResolver.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler.chain; 2 | 3 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 4 | 5 | public interface HandlerChainResolver { 6 | 7 | HandlerChain getChain(T event , HandlerChain originalChain); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/factory/DisruptorEventThreadFactory.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.factory; 2 | 3 | import java.util.concurrent.ThreadFactory; 4 | 5 | public class DisruptorEventThreadFactory implements ThreadFactory { 6 | 7 | @Override 8 | public Thread newThread(Runnable r) { 9 | return new Thread(r); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/DisruptorHandler.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler; 2 | 3 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 4 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChain; 5 | 6 | public interface DisruptorHandler { 7 | 8 | public void doHandler(T event, HandlerChain handlerChain) throws Exception; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/org/apache/rocketmq/spring/boot/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.apache.rocketmq.spring.boot; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.test.context.junit4.SpringRunner; 8 | 9 | @Configuration 10 | @RunWith(SpringRunner.class) 11 | @SpringBootTest 12 | public class DemoApplicationTests { 13 | 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/hooks/DisruptorShutdownHook.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.hooks; 2 | 3 | import com.lmax.disruptor.dsl.Disruptor; 4 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 5 | 6 | public class DisruptorShutdownHook extends Thread{ 7 | 8 | private Disruptor disruptor; 9 | 10 | public DisruptorShutdownHook(Disruptor disruptor) { 11 | this.disruptor = disruptor; 12 | } 13 | 14 | @Override 15 | public void run() { 16 | disruptor.shutdown(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/AbstractNameableEventHandler.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler; 2 | 3 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 4 | 5 | public abstract class AbstractNameableEventHandler implements DisruptorHandler, Nameable { 6 | 7 | /** 8 | * 过滤器名称 9 | */ 10 | protected String name; 11 | 12 | protected String getName() { 13 | return this.name; 14 | } 15 | 16 | @Override 17 | public void setName(String name) { 18 | this.name = name; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *# 2 | *.iml 3 | *.ipr 4 | *.iws 5 | *.jar 6 | *.sw? 7 | *~ 8 | .#* 9 | .*.md.html 10 | .apt_generated 11 | .DS_Store 12 | .classpath 13 | .factorypath 14 | .gradle 15 | .idea 16 | .metadata 17 | .project 18 | .recommenders 19 | .settings 20 | .springBeans 21 | .sts4-cache 22 | /build 23 | /code 24 | MANIFEST.MF 25 | _site/ 26 | activemq-data 27 | bin 28 | build 29 | build.log 30 | dependency-reduced-pom.xml 31 | dump.rdb 32 | interpolated*.xml 33 | lib/ 34 | manifest.yml 35 | overridedb.* 36 | settings.xml 37 | target 38 | transaction-logs 39 | .flattened-pom.xml 40 | secrets.yml 41 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/exception/EventHandleException.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.exception; 2 | 3 | @SuppressWarnings("serial") 4 | public class EventHandleException extends RuntimeException { 5 | 6 | public EventHandleException(Exception e) { 7 | super(e.getMessage(), null); 8 | } 9 | 10 | public EventHandleException(String errorMessage) { 11 | super(errorMessage, null); 12 | } 13 | 14 | public EventHandleException(String errorMessage, Throwable cause) { 15 | super(errorMessage, cause); 16 | } 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/annotation/EventRule.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Inherited; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | @Target(ElementType.TYPE) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Documented 13 | @Inherited 14 | public @interface EventRule { 15 | 16 | /** 17 | * Ant风格的事件分发规则表达式,格式为:/event/tags/keys,如:/Event-DC-Output/TagA-Output/** 18 | * @return 规则表达式 19 | */ 20 | String value() default "*"; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | tomcat: 3 | max-threads: 100 4 | min-spare-threads: 20 5 | connection-timeout: 5000 6 | ssl: 7 | key-store: classpath:.keystore 8 | key-store-type: JKS 9 | key-password: qq123456 10 | key-alias: tomcat 11 | port: 8080 12 | spring: 13 | disruptor: 14 | enabled: true 15 | ring-buffer: false 16 | ring-buffer-size: 1024 17 | ring-thread-numbers: 4 18 | multi-producer: true 19 | handler-definitions: 20 | - order : 1 21 | definitions: /Event-DC-Output/TagA-Output/** = inDbPreHandler 22 | /Event-DC-Output/TagB-Output/** = smsPreHandler 23 | /** = allPreHandler 24 | - order: 2 25 | definitions: /Event-DC-Output/TagA-Output/** = inDbPostHandler 26 | /Event-DC-Output/TagB-Output/** = smsPostHandler 27 | /** = allPostHandler 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/context/event/DisruptorEventPublisherAware.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.context.event; 17 | 18 | public interface DisruptorEventPublisherAware { 19 | 20 | void setDisruptorEventPublisher(DisruptorEventPublisher disruptorEventPublisher); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/context/event/DisruptorEventPublisher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.context.event; 17 | 18 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 19 | 20 | public interface DisruptorEventPublisher { 21 | 22 | void publishEvent(DisruptorEvent event); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/DisruptorStartedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event; 17 | 18 | @SuppressWarnings("serial") 19 | public class DisruptorStartedEvent extends DisruptorEvent { 20 | 21 | public DisruptorStartedEvent(Object source) { 22 | super(source); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/DisruptorStoppedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event; 17 | 18 | @SuppressWarnings("serial") 19 | public class DisruptorStoppedEvent extends DisruptorEvent { 20 | 21 | public DisruptorStoppedEvent(Object source) { 22 | super(source); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/DisruptorRefreshedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event; 17 | 18 | @SuppressWarnings("serial") 19 | public class DisruptorRefreshedEvent extends DisruptorEvent { 20 | 21 | public DisruptorRefreshedEvent(Object source) { 22 | super(source); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/NamedHandlerList.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler; 2 | 3 | import java.util.List; 4 | 5 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 6 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChain; 7 | 8 | 9 | public interface NamedHandlerList extends List> { 10 | 11 | /** 12 | * Returns the configuration-unique name assigned to this {@code Handler} list. 13 | * @return configuration-unique name 14 | */ 15 | String getName(); 16 | 17 | /** 18 | * Returns a new {@code HandlerChain} instance that will first execute this list's {@code Handler}s (in list order) 19 | * and end with the execution of the given {@code handlerChain} instance. 20 | * @param handlerChain {@code HandlerChain} instance 21 | * @return {@code HandlerChain} instance 22 | */ 23 | HandlerChain proxy(HandlerChain handlerChain); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/config/EventHandlerDefinition.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.config; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | public class EventHandlerDefinition { 7 | 8 | /** 9 | * 当前处理器所在位置 10 | */ 11 | private int order = 0; 12 | 13 | /** 14 | * 处理器链定义 15 | */ 16 | private String definitions = null; 17 | private Map definitionMap = new LinkedHashMap(); 18 | 19 | public int getOrder() { 20 | return order; 21 | } 22 | 23 | public void setOrder(int order) { 24 | this.order = order; 25 | } 26 | 27 | public String getDefinitions() { 28 | return definitions; 29 | } 30 | 31 | public void setDefinitions(String definitions) { 32 | this.definitions = definitions; 33 | } 34 | 35 | public Map getDefinitionMap() { 36 | return definitionMap; 37 | } 38 | 39 | public void setDefinitionMap(Map definitionMap) { 40 | this.definitionMap = definitionMap; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/factory/DisruptorEventDaemonThreadFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event.factory; 17 | 18 | import java.util.concurrent.ThreadFactory; 19 | 20 | public class DisruptorEventDaemonThreadFactory implements ThreadFactory { 21 | 22 | public Thread newThread(Runnable r) { 23 | Thread t = new Thread(r); 24 | t.setDaemon(true); 25 | return t; 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/factory/DisruptorEventMaxPriorityThreadFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event.factory; 17 | 18 | import java.util.concurrent.ThreadFactory; 19 | 20 | public class DisruptorEventMaxPriorityThreadFactory implements ThreadFactory { 21 | 22 | public Thread newThread(Runnable r) { 23 | Thread t = new Thread(r); 24 | t.setPriority(Thread.MAX_PRIORITY); 25 | return t; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/factory/DisruptorBindEventFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event.factory; 17 | 18 | import com.lmax.disruptor.EventFactory; 19 | import com.lmax.disruptor.spring.boot.event.DisruptorBindEvent; 20 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 21 | 22 | public class DisruptorBindEventFactory implements EventFactory { 23 | 24 | @Override 25 | public DisruptorEvent newInstance() { 26 | return new DisruptorBindEvent(this); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/util/WaitStrategys.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.util; 2 | 3 | import com.lmax.disruptor.BlockingWaitStrategy; 4 | import com.lmax.disruptor.BusySpinWaitStrategy; 5 | import com.lmax.disruptor.SleepingWaitStrategy; 6 | import com.lmax.disruptor.WaitStrategy; 7 | import com.lmax.disruptor.YieldingWaitStrategy; 8 | 9 | public class WaitStrategys { 10 | 11 | /**BlockingWaitStrategy 是最低效的策略,但其对CPU的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现*/ 12 | public static WaitStrategy BLOCKING_WAIT = new BlockingWaitStrategy(); 13 | 14 | /**SleepingWaitStrategy 的性能表现跟BlockingWaitStrategy差不多,对CPU的消耗也类似,但其对生产者线程的影响最小,适合用于异步日志类似的场景*/ 15 | public static WaitStrategy SLEEPING_WAIT = new SleepingWaitStrategy(); 16 | 17 | /**YieldingWaitStrategy是可以被用在低延迟系统中的两个策略之一,这种策略在减低系统延迟的同时也会增加CPU运算量。YieldingWaitStrategy策略会循环等待sequence增加到合适的值。循环中调用Thread.yield()允许其他准备好的线程执行。如果需要高性能而且事件消费者线程比逻辑内核少的时候,推荐使用YieldingWaitStrategy策略。例如:在开启超线程的时候。*/ 18 | public static WaitStrategy YIELDING_WAIT = new YieldingWaitStrategy(); 19 | 20 | /**BusySpinWaitStrategy是性能最高的等待策略,同时也是对部署环境要求最高的策略。这个性能最好用在事件处理线程比物理内核数目还要小的时候。例如:在禁用超线程技术的时候。*/ 21 | public static WaitStrategy BUSYSPIN_WAIT = new BusySpinWaitStrategy(); 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/factory/DisruptorEventWorkerThreadFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event.factory; 17 | 18 | import java.util.concurrent.ThreadFactory; 19 | 20 | public class DisruptorEventWorkerThreadFactory implements ThreadFactory { 21 | 22 | private int counter = 0; 23 | private String prefix = ""; 24 | 25 | public DisruptorEventWorkerThreadFactory(String prefix) { 26 | this.prefix = prefix; 27 | } 28 | 29 | public Thread newThread(Runnable r) { 30 | return new Thread(r, prefix + "-" + counter++); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/translator/DisruptorEventTwoArgTranslator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event.translator; 17 | 18 | import com.lmax.disruptor.EventTranslatorTwoArg; 19 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 20 | 21 | public class DisruptorEventTwoArgTranslator implements EventTranslatorTwoArg { 22 | 23 | @Override 24 | public void translateTo(DisruptorEvent dtEevent, long sequence, String event, String tag) { 25 | dtEevent.setEvent(event); 26 | dtEevent.setTag(tag); 27 | dtEevent.setKey(String.valueOf(sequence)); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/translator/DisruptorEventThreeArgTranslator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event.translator; 17 | 18 | import com.lmax.disruptor.EventTranslatorThreeArg; 19 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 20 | 21 | public class DisruptorEventThreeArgTranslator implements EventTranslatorThreeArg { 22 | 23 | @Override 24 | public void translateTo(DisruptorEvent dtEevent, long sequence, String event, String tag, String key) { 25 | dtEevent.setEvent(event); 26 | dtEevent.setTag(tag); 27 | dtEevent.setKey(key); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/factory/DisruptorEventLoggerThreadFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event.factory; 17 | 18 | import java.util.concurrent.ThreadFactory; 19 | 20 | import org.slf4j.LoggerFactory; 21 | 22 | public class DisruptorEventLoggerThreadFactory implements ThreadFactory { 23 | 24 | @Override 25 | public Thread newThread(Runnable r) { 26 | Thread t = new Thread(r); 27 | t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 28 | @Override 29 | public void uncaughtException(Thread t, Throwable e) { 30 | LoggerFactory.getLogger(t.getName()).error(e.getMessage(), e); 31 | } 32 | }); 33 | return t; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/DisruptorEventDispatcher.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler; 2 | 3 | import org.springframework.core.Ordered; 4 | 5 | import com.lmax.disruptor.EventHandler; 6 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 7 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChain; 8 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChainResolver; 9 | import com.lmax.disruptor.spring.boot.event.handler.chain.ProxiedHandlerChain; 10 | 11 | /** 12 | * Disruptor 事件分发实现 13 | */ 14 | public class DisruptorEventDispatcher extends AbstractRouteableEventHandler implements EventHandler, Ordered { 15 | 16 | private int order = 0; 17 | 18 | public DisruptorEventDispatcher(HandlerChainResolver filterChainResolver,int order) { 19 | super(filterChainResolver); 20 | this.order = order; 21 | } 22 | 23 | /* 24 | * 责任链入口 25 | */ 26 | @Override 27 | public void onEvent(DisruptorEvent event, long sequence, boolean endOfBatch) throws Exception { 28 | 29 | //构造原始链对象 30 | HandlerChain originalChain = new ProxiedHandlerChain(); 31 | //执行事件处理链 32 | this.doHandler(event, originalChain); 33 | 34 | } 35 | 36 | @Override 37 | public int getOrder() { 38 | return order; 39 | } 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/DisruptorBindEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event; 17 | 18 | @SuppressWarnings("serial") 19 | public class DisruptorBindEvent extends DisruptorEvent { 20 | 21 | /** 22 | * 当前事件绑定的数据对象 23 | */ 24 | protected Object bind; 25 | 26 | public DisruptorBindEvent() { 27 | super(null); 28 | } 29 | 30 | public DisruptorBindEvent(Object source) { 31 | super(source); 32 | } 33 | 34 | public DisruptorBindEvent(Object source, Object bind) { 35 | super(source); 36 | this.bind = bind; 37 | } 38 | 39 | public Object getBind() { 40 | return bind; 41 | } 42 | 43 | public void bind(Object bind) { 44 | this.bind = bind; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/DisruptorApplicationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event; 17 | 18 | import org.springframework.context.ApplicationEvent; 19 | 20 | @SuppressWarnings("serial") 21 | public class DisruptorApplicationEvent extends ApplicationEvent { 22 | 23 | /** 24 | * 当前事件绑定的数据对象 25 | */ 26 | protected Object bind; 27 | 28 | public DisruptorApplicationEvent(Object source, Object bind) { 29 | super(source); 30 | this.bind = bind; 31 | } 32 | 33 | public DisruptorApplicationEvent(Object source) { 34 | super(source); 35 | } 36 | 37 | public Object getBind() { 38 | return bind; 39 | } 40 | 41 | public void bind(Object bind) { 42 | this.bind = bind; 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/AbstractEnabledEventHandler.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 7 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChain; 8 | 9 | public abstract class AbstractEnabledEventHandler extends AbstractNameableEventHandler { 10 | 11 | protected final Logger LOG = LoggerFactory.getLogger(AbstractEnabledEventHandler.class); 12 | protected boolean enabled = true; 13 | 14 | protected abstract void doHandlerInternal(T event, HandlerChain handlerChain) throws Exception; 15 | 16 | @Override 17 | public void doHandler(T event, HandlerChain handlerChain) throws Exception { 18 | 19 | if (!isEnabled(event)) { 20 | LOG.debug("Handler '{}' is not enabled for the current event. Proceeding without invoking this handler.", 21 | getName()); 22 | // Proceed without invoking this handler... 23 | handlerChain.doHandler(event); 24 | } else { 25 | LOG.trace("Handler '{}' enabled. Executing now.", getName()); 26 | doHandlerInternal(event, handlerChain); 27 | } 28 | 29 | } 30 | 31 | protected boolean isEnabled(T event) throws Exception { 32 | return isEnabled(); 33 | } 34 | 35 | public boolean isEnabled() { 36 | return enabled; 37 | } 38 | 39 | public void setEnabled(boolean enabled) { 40 | this.enabled = enabled; 41 | } 42 | 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # disruptor-spring-boot-starter 2 | 3 | Spring Boot Starter For Disruptor 4 | 5 | ### 基于 Disruptor 的 Spring Boot Starter 实现, 异步事件推送、处理封装 6 | 7 | - 1、事件推送 8 | 9 | a、配置简单,少量配置即可实现异步事件推送 10 | 11 | - 2、事件处理 12 | 13 | a、配置简单,少量配置即可实现异步事件处理 14 | 15 | b、组件实现了基于责任链的事件处理实现;可实现对具备不同 事件规则 ruleExpression 的事件对象进行专责处理;就如 Filter,该组件实现的Handler采用了同样的原理; 16 | 17 | 18 | - /Event-DC-Output/TagA-Output/** = inDbPostHandler 该配置表示;Event = Event-DC-Output , Tags = TagA-Output , Keys = 任何类型 的事件对象交由 inDbPostHandler 来处理 19 | - /Event-DC-Output/TagB-Output/** = smsPostHandler 该配置表示;Event = Event-DC-Output , Tags = TagB-Output , Keys = 任何类型 的事件对象交由 smsPostHandler 来处理 20 | 21 | 通过这种责任链的机制,很好的实现了事件的分类异步处理;比如消息队列的消费端需要快速的消费各类消息,且每种处理实现都不相同;这时候就需要用到事件对象的分类异步处理。 22 | 23 | ### Maven 24 | 25 | ``` xml 26 | 27 | com.github.hiwepy> 28 | disruptor-spring-boot-starter 29 | ${project.version} 30 | 31 | ``` 32 | 33 | ### Sample 34 | 35 | [https://github.com/vindell/spring-boot-starter-samples/tree/master/spring-boot-sample-disruptor](https://github.com/vindell/spring-boot-starter-samples/tree/master/spring-boot-sample-disruptor "spring-boot-sample-disruptor") 36 | 37 | 38 | ## Jeebiz 技术社区 39 | 40 | Jeebiz 技术社区 **微信公共号**、**小程序**,欢迎关注反馈意见和一起交流,关注公众号回复「Jeebiz」拉你入群。 41 | 42 | |公共号|小程序| 43 | |---|---| 44 | | ![](https://raw.githubusercontent.com/hiwepy/static/main/images/qrcode_for_gh_1d965ea2dfd1_344.jpg)| ![](https://raw.githubusercontent.com/hiwepy/static/main/images/gh_09d7d00da63e_344.jpg)| 45 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/chain/HandlerChainManager.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler.chain; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 7 | import com.lmax.disruptor.spring.boot.event.handler.DisruptorHandler; 8 | import com.lmax.disruptor.spring.boot.event.handler.NamedHandlerList; 9 | 10 | /** 11 | * HandlerChain管理器,负责创建和维护HandlerChain 12 | */ 13 | public interface HandlerChainManager { 14 | 15 | /* 16 | * 获取所有HandlerChain 17 | * @return 18 | */ 19 | Map> getHandlers(); 20 | 21 | /* 22 | * 根据指定的chainName获取Handler列表 23 | */ 24 | NamedHandlerList getChain(String chainName); 25 | 26 | /* 27 | * 是否有HandlerChain 28 | */ 29 | boolean hasChains(); 30 | 31 | /* 32 | * 获取HandlerChain名称列表 33 | */ 34 | Set getChainNames(); 35 | 36 | /* 37 | *

生成代理HandlerChain,先执行chainName指定的filerChian,最后执行servlet容器的original

38 | */ 39 | HandlerChain proxy(HandlerChain original, String chainName); 40 | 41 | /* 42 | * 43 | *

增加handler到handler列表中

44 | */ 45 | void addHandler(String name, DisruptorHandler handler); 46 | 47 | /* 48 | *

创建HandlerChain

49 | */ 50 | void createChain(String chainName, String chainDefinition); 51 | 52 | /* 53 | *

追加handler到指定的HandlerChian中

54 | */ 55 | void addToChain(String chainName, String handlerName); 56 | 57 | } -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/translator/DisruptorEventOneArgTranslator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.event.translator; 17 | 18 | import com.lmax.disruptor.EventTranslatorOneArg; 19 | import com.lmax.disruptor.spring.boot.event.DisruptorBindEvent; 20 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 21 | import com.lmax.disruptor.spring.boot.util.StringUtils; 22 | 23 | public class DisruptorEventOneArgTranslator implements EventTranslatorOneArg { 24 | 25 | @Override 26 | public void translateTo(DisruptorEvent event, long sequence, DisruptorEvent bind) { 27 | event.setSource(bind.getSource()); 28 | event.setEvent(bind.getEvent()); 29 | event.setTag(bind.getTag()); 30 | event.setKey(StringUtils.hasText(bind.getKey()) ? bind.getKey() : String.valueOf(sequence)); 31 | if(event instanceof DisruptorBindEvent){ 32 | DisruptorBindEvent bindEvent = (DisruptorBindEvent)event; 33 | bindEvent.bind(bind); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/context/DisruptorApplicationContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.context; 17 | 18 | 19 | import org.springframework.beans.BeansException; 20 | import org.springframework.context.ApplicationContext; 21 | import org.springframework.context.ApplicationContextAware; 22 | 23 | import com.lmax.disruptor.spring.boot.context.event.DisruptorEventPublisher; 24 | import com.lmax.disruptor.spring.boot.event.DisruptorApplicationEvent; 25 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 26 | 27 | public class DisruptorApplicationContext implements ApplicationContextAware, DisruptorEventPublisher { 28 | 29 | protected ApplicationContext applicationContext; 30 | 31 | @Override 32 | public void publishEvent(DisruptorEvent event) { 33 | applicationContext.publishEvent(new DisruptorApplicationEvent(event)); 34 | } 35 | 36 | @Override 37 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 38 | this.applicationContext = applicationContext; 39 | } 40 | 41 | public ApplicationContext getApplicationContext() { 42 | return applicationContext; 43 | } 44 | 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/chain/ProxiedHandlerChain.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler.chain; 2 | 3 | import java.util.List; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 9 | import com.lmax.disruptor.spring.boot.event.handler.DisruptorHandler; 10 | 11 | public class ProxiedHandlerChain implements HandlerChain { 12 | 13 | private static final Logger LOG = LoggerFactory.getLogger(ProxiedHandlerChain.class); 14 | 15 | private ProxiedHandlerChain originalChain; 16 | private List> handlers; 17 | private int currentPosition = 0; 18 | 19 | public ProxiedHandlerChain() { 20 | this.currentPosition = -1; 21 | } 22 | 23 | public ProxiedHandlerChain(ProxiedHandlerChain orig, List> handlers) { 24 | if (orig == null) { 25 | throw new NullPointerException("original HandlerChain cannot be null."); 26 | } 27 | this.originalChain = orig; 28 | this.handlers = handlers; 29 | this.currentPosition = 0; 30 | } 31 | 32 | @Override 33 | public void doHandler(DisruptorEvent event) throws Exception { 34 | if (this.handlers == null || this.handlers.size() == this.currentPosition) { 35 | if (LOG.isTraceEnabled()) { 36 | LOG.trace("Invoking original filter chain."); 37 | } 38 | if(this.originalChain != null) { 39 | this.originalChain.doHandler(event); 40 | } 41 | } else { 42 | if (LOG.isTraceEnabled()) { 43 | LOG.trace("Invoking wrapped filter at index [" + this.currentPosition + "]"); 44 | } 45 | this.handlers.get(this.currentPosition++).doHandler(event, this); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/DisruptorTemplate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot; 17 | 18 | import org.springframework.beans.factory.annotation.Autowired; 19 | 20 | import com.lmax.disruptor.EventTranslatorOneArg; 21 | import com.lmax.disruptor.dsl.Disruptor; 22 | import com.lmax.disruptor.spring.boot.event.DisruptorBindEvent; 23 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 24 | 25 | public class DisruptorTemplate { 26 | 27 | @Autowired 28 | protected Disruptor disruptor; 29 | @Autowired 30 | protected EventTranslatorOneArg oneArgEventTranslator; 31 | 32 | public void publishEvent(DisruptorBindEvent event) { 33 | disruptor.publishEvent(oneArgEventTranslator, event); 34 | } 35 | 36 | public void publishEvent(String event, String tag, Object body) { 37 | DisruptorBindEvent bindEvent = new DisruptorBindEvent(); 38 | bindEvent.setEvent(event); 39 | bindEvent.setTag(tag); 40 | bindEvent.setBody(body); 41 | disruptor.publishEvent(oneArgEventTranslator, bindEvent); 42 | } 43 | 44 | public void publishEvent(String event, String tag, String key, Object body) { 45 | DisruptorBindEvent bindEvent = new DisruptorBindEvent(); 46 | bindEvent.setEvent(event); 47 | bindEvent.setTag(tag); 48 | bindEvent.setKey(key); 49 | bindEvent.setBody(body); 50 | disruptor.publishEvent(oneArgEventTranslator, bindEvent); 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/DisruptorEvent.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event; 2 | 3 | import java.util.EventObject; 4 | 5 | /** 6 | * 事件(Event) 就是通过 Disruptor 进行交换的数据类型。 7 | */ 8 | @SuppressWarnings("serial") 9 | public abstract class DisruptorEvent extends EventObject { 10 | 11 | /** System time when the event happened */ 12 | private final long timestamp; 13 | /** Event Name */ 14 | private String event; 15 | /** Event Tag */ 16 | private String tag; 17 | /** Event Keys */ 18 | private String key; 19 | /** Event body */ 20 | private Object body; 21 | 22 | /** 23 | * Create a new ConsumeEvent. 24 | * @param source the object on which the event initially occurred (never {@code null}) 25 | */ 26 | public DisruptorEvent(Object source) { 27 | super(source); 28 | this.timestamp = System.currentTimeMillis(); 29 | } 30 | 31 | /** 32 | * Return the system time in milliseconds when the event happened. 33 | * @return system time in milliseconds 34 | */ 35 | public final long getTimestamp() { 36 | return this.timestamp; 37 | } 38 | 39 | public String getRouteExpression() { 40 | return new StringBuilder("/").append(getEvent()).append("/").append(getTag()).append("/") 41 | .append(getKey()).toString(); 42 | 43 | } 44 | 45 | public void setSource(Object source){ 46 | this.source = source; 47 | } 48 | 49 | public String getEvent() { 50 | return event; 51 | } 52 | 53 | public void setEvent(String event) { 54 | this.event = event; 55 | } 56 | 57 | public String getTag() { 58 | return tag; 59 | } 60 | 61 | public void setTag(String tag) { 62 | this.tag = tag; 63 | } 64 | 65 | public String getKey() { 66 | return key; 67 | } 68 | 69 | public void setKey(String key) { 70 | this.key = key; 71 | } 72 | 73 | public Object getBody() { 74 | return body; 75 | } 76 | 77 | public void setBody(Object body) { 78 | this.body = body; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return new StringBuilder("DisruptorEvent [event :").append(getEvent()).append(",tag :").append(getTag()).append(", key :") 84 | .append(getKey()).append("]").toString(); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/DisruptorProperties.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | 8 | import com.lmax.disruptor.spring.boot.config.EventHandlerDefinition; 9 | 10 | @ConfigurationProperties(DisruptorProperties.PREFIX) 11 | public class DisruptorProperties { 12 | 13 | public static final String PREFIX = "spring.disruptor"; 14 | 15 | /** Enable Disruptor. */ 16 | private boolean enabled = false; 17 | /** 是否自动创建RingBuffer对象 */ 18 | private boolean ringBuffer = false; 19 | /** RingBuffer缓冲区大小, 默认 1024 */ 20 | private int ringBufferSize = 1024; 21 | /** 消息消费线程池大小, 默认 4 */ 22 | private int ringThreadNumbers = 4; 23 | /** 是否对生产者,如果是则通过 RingBuffer.createMultiProducer创建一个多生产者的RingBuffer,否则通过RingBuffer.createSingleProducer创建一个单生产者的RingBuffer */ 24 | private boolean multiProducer = false; 25 | /** 消息出来责任链 */ 26 | private List handlerDefinitions = new ArrayList(); 27 | 28 | public boolean isEnabled() { 29 | return enabled; 30 | } 31 | 32 | public void setEnabled(boolean enabled) { 33 | this.enabled = enabled; 34 | } 35 | 36 | public boolean isRingBuffer() { 37 | return ringBuffer; 38 | } 39 | 40 | public void setRingBuffer(boolean ringBuffer) { 41 | this.ringBuffer = ringBuffer; 42 | } 43 | 44 | public boolean isMultiProducer() { 45 | return multiProducer; 46 | } 47 | 48 | public void setMultiProducer(boolean multiProducer) { 49 | this.multiProducer = multiProducer; 50 | } 51 | 52 | public int getRingBufferSize() { 53 | return ringBufferSize; 54 | } 55 | 56 | public void setRingBufferSize(int ringBufferSize) { 57 | this.ringBufferSize = ringBufferSize; 58 | } 59 | 60 | public int getRingThreadNumbers() { 61 | return ringThreadNumbers; 62 | } 63 | 64 | public void setRingThreadNumbers(int ringThreadNumbers) { 65 | this.ringThreadNumbers = ringThreadNumbers; 66 | } 67 | 68 | public List getHandlerDefinitions() { 69 | return handlerDefinitions; 70 | } 71 | 72 | public void setHandlerDefinitions(List handlerDefinitions) { 73 | this.handlerDefinitions = handlerDefinitions; 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/AbstractRouteableEventHandler.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler; 2 | 3 | import java.io.IOException; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 9 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChain; 10 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChainResolver; 11 | import com.lmax.disruptor.spring.boot.exception.EventHandleException; 12 | 13 | public class AbstractRouteableEventHandler extends AbstractEnabledEventHandler { 14 | 15 | private static final Logger LOG = LoggerFactory.getLogger(AbstractRouteableEventHandler.class); 16 | 17 | /** 18 | * 用来判定使用那个HandlerChian 19 | */ 20 | protected HandlerChainResolver handlerChainResolver; 21 | 22 | public AbstractRouteableEventHandler() { 23 | super(); 24 | } 25 | 26 | public AbstractRouteableEventHandler(HandlerChainResolver handlerChainResolver) { 27 | super(); 28 | this.handlerChainResolver = handlerChainResolver; 29 | } 30 | 31 | @Override 32 | protected void doHandlerInternal(T event, HandlerChain handlerChain) throws Exception { 33 | Throwable t = null; 34 | try { 35 | this.executeChain(event, handlerChain); 36 | } catch (Throwable throwable) { 37 | t = throwable; 38 | } 39 | if (t != null) { 40 | if (t instanceof IOException) { 41 | throw (IOException) t; 42 | } 43 | String msg = "Handlered event failed."; 44 | throw new EventHandleException(msg, t); 45 | } 46 | } 47 | 48 | protected HandlerChain getExecutionChain(T event, HandlerChain origChain) { 49 | HandlerChain chain = origChain; 50 | 51 | HandlerChainResolver resolver = getHandlerChainResolver(); 52 | if (resolver == null) { 53 | LOG.debug("No HandlerChainResolver configured. Returning original HandlerChain."); 54 | return origChain; 55 | } 56 | 57 | HandlerChain resolved = resolver.getChain(event, origChain); 58 | if (resolved != null) { 59 | LOG.trace("Resolved a configured HandlerChain for the current event."); 60 | chain = resolved; 61 | } else { 62 | LOG.trace("No HandlerChain configured for the current event. Using the default."); 63 | } 64 | 65 | return chain; 66 | } 67 | 68 | protected void executeChain(T event, HandlerChain origChain) throws Exception { 69 | HandlerChain chain = getExecutionChain(event, origChain); 70 | chain.doHandler(event); 71 | } 72 | 73 | public HandlerChainResolver getHandlerChainResolver() { 74 | return handlerChainResolver; 75 | } 76 | 77 | public void setHandlerChainResolver(HandlerChainResolver handlerChainResolver) { 78 | this.handlerChainResolver = handlerChainResolver; 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/AbstractAdviceEventHandler.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 7 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChain; 8 | 9 | public class AbstractAdviceEventHandler extends AbstractEnabledEventHandler { 10 | 11 | protected final Logger LOG = LoggerFactory.getLogger(AbstractAdviceEventHandler.class); 12 | 13 | protected boolean preHandle(T event) throws Exception { 14 | return true; 15 | } 16 | 17 | protected void postHandle(T event) throws Exception { 18 | } 19 | 20 | public void afterCompletion(T event, Exception exception) throws Exception { 21 | } 22 | 23 | protected void executeChain(T event, HandlerChain chain) throws Exception { 24 | chain.doHandler(event); 25 | } 26 | 27 | @Override 28 | public void doHandlerInternal(T event, HandlerChain handlerChain) throws Exception { 29 | 30 | if (!isEnabled(event)) { 31 | LOG.debug("Handler '{}' is not enabled for the current event. Proceeding without invoking this handler.", getName()); 32 | // Proceed without invoking this handler... 33 | handlerChain.doHandler(event); 34 | } else { 35 | 36 | LOG.trace("Handler '{}' enabled. Executing now.", getName()); 37 | 38 | Exception exception = null; 39 | 40 | try { 41 | 42 | boolean continueChain = preHandle(event); 43 | if (LOG.isTraceEnabled()) { 44 | LOG.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]"); 45 | } 46 | if (continueChain) { 47 | executeChain(event, handlerChain); 48 | } 49 | postHandle(event); 50 | if (LOG.isTraceEnabled()) { 51 | LOG.trace("Successfully invoked postHandle method"); 52 | } 53 | 54 | } catch (Exception e) { 55 | exception = e; 56 | } finally { 57 | cleanup(event, exception); 58 | } 59 | } 60 | 61 | } 62 | 63 | protected void cleanup(T event, Exception existing) throws Exception { 64 | Exception exception = existing; 65 | try { 66 | afterCompletion(event, exception); 67 | if (LOG.isTraceEnabled()) { 68 | LOG.trace("Successfully invoked afterCompletion method."); 69 | } 70 | } catch (Exception e) { 71 | if (exception == null) { 72 | exception = e; 73 | } else { 74 | LOG.debug("afterCompletion implementation threw an exception. This will be ignored to " 75 | + "allow the original source exception to be propagated.", e); 76 | } 77 | } 78 | } 79 | 80 | protected boolean isEnabled(T event) 81 | throws Exception { 82 | return isEnabled(); 83 | } 84 | 85 | public boolean isEnabled() { 86 | return enabled; 87 | } 88 | 89 | public void setEnabled(boolean enabled) { 90 | this.enabled = enabled; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/chain/def/PathMatchingHandlerChainResolver.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler.chain.def; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.util.AntPathMatcher; 6 | import org.springframework.util.PathMatcher; 7 | 8 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 9 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChain; 10 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChainManager; 11 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChainResolver; 12 | 13 | public class PathMatchingHandlerChainResolver implements HandlerChainResolver { 14 | 15 | private static final Logger log = LoggerFactory.getLogger(PathMatchingHandlerChainResolver.class); 16 | /** 17 | * handlerChain管理器 18 | */ 19 | private HandlerChainManager handlerChainManager; 20 | 21 | /** 22 | * 路径匹配器 23 | */ 24 | private PathMatcher pathMatcher; 25 | 26 | public PathMatchingHandlerChainResolver() { 27 | this.pathMatcher = new AntPathMatcher(); 28 | this.handlerChainManager = new DefaultHandlerChainManager(); 29 | } 30 | 31 | public HandlerChainManager getHandlerChainManager() { 32 | return handlerChainManager; 33 | } 34 | 35 | public void setHandlerChainManager(HandlerChainManager handlerChainManager) { 36 | this.handlerChainManager = handlerChainManager; 37 | } 38 | 39 | public PathMatcher getPathMatcher() { 40 | return pathMatcher; 41 | } 42 | 43 | public void setPathMatcher(PathMatcher pathMatcher) { 44 | this.pathMatcher = pathMatcher; 45 | } 46 | 47 | 48 | public HandlerChain getChain(DisruptorEvent event, HandlerChain originalChain) { 49 | HandlerChainManager handlerChainManager = getHandlerChainManager(); 50 | if (!handlerChainManager.hasChains()) { 51 | return null; 52 | } 53 | String eventURI = getPathWithinEvent(event); 54 | for (String pathPattern : handlerChainManager.getChainNames()) { 55 | if (pathMatches(pathPattern, eventURI)) { 56 | if (log.isTraceEnabled()) { 57 | log.trace("Matched path pattern [" + pathPattern + "] for eventURI [" + eventURI + "]. " + 58 | "Utilizing corresponding handler chain..."); 59 | } 60 | return handlerChainManager.proxy(originalChain, pathPattern); 61 | } 62 | } 63 | return null; 64 | } 65 | 66 | protected boolean pathMatches(String pattern, String path) { 67 | PathMatcher pathMatcher = getPathMatcher(); 68 | return pathMatcher.match(pattern, path); 69 | } 70 | 71 | protected String getPathWithinEvent(DisruptorEvent event) { 72 | return event.getRouteExpression(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/context/event/EventPublicationInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.context.event; 17 | 18 | 19 | import java.lang.reflect.Constructor; 20 | 21 | import org.aopalliance.intercept.MethodInterceptor; 22 | import org.aopalliance.intercept.MethodInvocation; 23 | import org.springframework.beans.factory.InitializingBean; 24 | 25 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 26 | 27 | public class EventPublicationInterceptor 28 | implements MethodInterceptor, DisruptorEventPublisherAware, InitializingBean { 29 | 30 | private Constructor applicationEventClassConstructor; 31 | 32 | private DisruptorEventPublisher applicationEventPublisher; 33 | 34 | 35 | /** 36 | * Set the application event class to publish. 37 | *

The event class must have a constructor with a single 38 | * {@code Object} argument for the event source. The interceptor 39 | * will pass in the invoked object. 40 | * @param applicationEventClass the application event class 41 | * @throws IllegalArgumentException if the supplied {@code Class} is 42 | * {@code null} or if it is not an {@code ApplicationEvent} subclass or 43 | * if it does not expose a constructor that takes a single {@code Object} argument 44 | */ 45 | public void setApplicationEventClass(Class applicationEventClass) { 46 | if (DisruptorEvent.class == applicationEventClass || !DisruptorEvent.class.isAssignableFrom(applicationEventClass)) { 47 | throw new IllegalArgumentException("applicationEventClass needs to extend DisruptorEvent"); 48 | } 49 | try { 50 | this.applicationEventClassConstructor = applicationEventClass.getConstructor( new Class[] {Object.class} ); 51 | } 52 | catch (NoSuchMethodException ex) { 53 | throw new IllegalArgumentException("applicationEventClass [" + 54 | applicationEventClass.getName() + "] does not have the required Object constructor: " + ex); 55 | } 56 | } 57 | 58 | @Override 59 | public void setDisruptorEventPublisher(DisruptorEventPublisher applicationEventPublisher) { 60 | this.applicationEventPublisher = applicationEventPublisher; 61 | } 62 | 63 | @Override 64 | public void afterPropertiesSet() throws Exception { 65 | if (this.applicationEventClassConstructor == null) { 66 | throw new IllegalArgumentException("applicationEventClass is required"); 67 | } 68 | } 69 | 70 | @Override 71 | public Object invoke(MethodInvocation invocation) throws Throwable { 72 | Object retVal = invocation.proceed(); 73 | DisruptorEvent event = (DisruptorEvent) this.applicationEventClassConstructor.newInstance(new Object[] {invocation.getThis()}); 74 | this.applicationEventPublisher.publishEvent(event); 75 | return retVal; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/AbstractPathMatchEventHandler.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.util.AntPathMatcher; 9 | import org.springframework.util.PathMatcher; 10 | 11 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 12 | 13 | public abstract class AbstractPathMatchEventHandler extends AbstractAdviceEventHandler implements PathProcessor { 14 | 15 | private static final Logger LOG = LoggerFactory.getLogger(AbstractPathMatchEventHandler.class); 16 | 17 | protected PathMatcher pathMatcher = new AntPathMatcher(); 18 | 19 | // 需要过滤的路径 20 | protected List appliedPaths = new ArrayList(); 21 | 22 | @Override 23 | public DisruptorHandler processPath(String path) { 24 | this.appliedPaths.add(path); 25 | return this; 26 | } 27 | 28 | protected String getPathWithinEvent(T event) { 29 | return event.getRouteExpression(); 30 | } 31 | 32 | protected boolean pathsMatch(String path, T event) { 33 | String eventExp = getPathWithinEvent(event); 34 | LOG.trace("Attempting to match pattern '{}' with current Event Expression '{}'...", path, eventExp); 35 | return pathsMatch(path, eventExp); 36 | } 37 | 38 | protected boolean pathsMatch(String pattern, String path) { 39 | return pathMatcher.match(pattern, path); 40 | } 41 | 42 | 43 | protected boolean preHandle(T event) throws Exception { 44 | 45 | if (this.appliedPaths == null || this.appliedPaths.isEmpty()) { 46 | if (LOG.isTraceEnabled()) { 47 | LOG.trace("appliedPaths property is null or empty. This Handler will passthrough immediately."); 48 | } 49 | return true; 50 | } 51 | 52 | for (String path : this.appliedPaths) { 53 | // If the path does match, then pass on to the subclass 54 | // implementation for specific checks 55 | // (first match 'wins'): 56 | if (pathsMatch(path, event)) { 57 | LOG.trace("Current Event Expression matches pattern '{}'. Determining handler chain execution...", path); 58 | return isHandlerChainContinued(event, path); 59 | } 60 | } 61 | 62 | // no path matched, allow the request to go through: 63 | return true; 64 | } 65 | 66 | private boolean isHandlerChainContinued(T event, String path) throws Exception { 67 | 68 | if (isEnabled(event, path)) { // isEnabled check 69 | 70 | if (LOG.isTraceEnabled()) { 71 | LOG.trace("Handler '{}' is enabled for the current event under path '{}'. " + "Delegating to subclass implementation for 'onPreHandle' check.", new Object[] { getName(), path }); 72 | } 73 | // The handler is enabled for this specific request, so delegate to 74 | // subclass implementations 75 | // so they can decide if the request should continue through the 76 | // chain or not: 77 | return onPreHandle(event); 78 | } 79 | 80 | if (LOG.isTraceEnabled()) { 81 | LOG.trace("Handler '{}' is disabled for the current event under path '{}'. " + "The next element in the HandlerChain will be called immediately.", new Object[] { getName(), path }); 82 | } 83 | // This handler is disabled for this specific request, 84 | // return 'true' immediately to indicate that the handler will not 85 | // process the request 86 | // and let the request/response to continue through the handler chain: 87 | return true; 88 | } 89 | 90 | protected boolean onPreHandle(T event) throws Exception { 91 | return true; 92 | } 93 | 94 | protected boolean isEnabled(T event, String path) throws Exception { 95 | return isEnabled(event); 96 | } 97 | 98 | public PathMatcher getPathMatcher() { 99 | return pathMatcher; 100 | } 101 | 102 | public void setPathMatcher(PathMatcher pathMatcher) { 103 | this.pathMatcher = pathMatcher; 104 | } 105 | 106 | public List getAppliedPaths() { 107 | return appliedPaths; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/context/DisruptorEventAwareProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, hiwepy (https://github.com/hiwepy). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.lmax.disruptor.spring.boot.context; 17 | 18 | import java.security.AccessControlContext; 19 | import java.security.AccessController; 20 | import java.security.PrivilegedAction; 21 | 22 | import org.springframework.beans.BeansException; 23 | import org.springframework.beans.factory.Aware; 24 | import org.springframework.beans.factory.InitializingBean; 25 | import org.springframework.beans.factory.config.BeanPostProcessor; 26 | import org.springframework.beans.factory.config.ConfigurableBeanFactory; 27 | import org.springframework.beans.factory.support.SecurityContextProvider; 28 | import org.springframework.context.ApplicationContext; 29 | import org.springframework.context.ApplicationContextAware; 30 | 31 | import com.lmax.disruptor.spring.boot.context.event.DisruptorEventPublisherAware; 32 | 33 | public class DisruptorEventAwareProcessor implements ApplicationContextAware ,BeanPostProcessor, InitializingBean { 34 | 35 | private DisruptorApplicationContext disruptorContext; 36 | private ApplicationContext applicationContext; 37 | 38 | /** Security context used when running with a SecurityManager */ 39 | private SecurityContextProvider securityContextProvider; 40 | 41 | /** 42 | * Set the security context provider for this bean factory. If a security manager 43 | * is set, interaction with the user code will be executed using the privileged 44 | * of the provided security context. 45 | * @param securityProvider {@link SecurityContextProvider} instance 46 | */ 47 | public void setSecurityContextProvider(SecurityContextProvider securityProvider) { 48 | this.securityContextProvider = securityProvider; 49 | } 50 | 51 | /** 52 | * Delegate the creation of the access control context to the 53 | * {@link #setSecurityContextProvider SecurityContextProvider}. 54 | * @return {@link AccessControlContext} instance 55 | */ 56 | public AccessControlContext getAccessControlContext() { 57 | if(this.securityContextProvider != null){ 58 | return this.securityContextProvider.getAccessControlContext(); 59 | } 60 | 61 | if(this.disruptorContext.getApplicationContext().getAutowireCapableBeanFactory() instanceof ConfigurableBeanFactory){ 62 | ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) this.disruptorContext.getApplicationContext().getAutowireCapableBeanFactory() ; 63 | return beanFactory.getAccessControlContext(); 64 | } 65 | 66 | return AccessController.getContext(); 67 | } 68 | 69 | /** 70 | * Create a new ApplicationContextAwareProcessor for the given context. 71 | */ 72 | public DisruptorEventAwareProcessor() { 73 | } 74 | 75 | @Override 76 | public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException { 77 | AccessControlContext acc = null; 78 | if (System.getSecurityManager() != null && (bean instanceof DisruptorEventPublisherAware )) { 79 | acc = getAccessControlContext(); 80 | } 81 | if (acc != null) { 82 | AccessController.doPrivileged(new PrivilegedAction() { 83 | @Override 84 | public Object run() { 85 | invokeAwareInterfaces(bean); 86 | return null; 87 | } 88 | }, acc); 89 | } 90 | else { 91 | invokeAwareInterfaces(bean); 92 | } 93 | 94 | return bean; 95 | } 96 | 97 | protected void invokeAwareInterfaces(Object bean) { 98 | if (bean instanceof Aware) { 99 | //扩展 DisruptorEventPublisherAware 100 | if (bean instanceof DisruptorEventPublisherAware) { 101 | DisruptorEventPublisherAware awareBean = (DisruptorEventPublisherAware) bean; 102 | awareBean.setDisruptorEventPublisher( this.disruptorContext ); 103 | } 104 | } 105 | } 106 | 107 | @Override 108 | public Object postProcessAfterInitialization(Object bean, String beanName) { 109 | return bean; 110 | } 111 | 112 | @Override 113 | public void afterPropertiesSet() throws Exception { 114 | disruptorContext = new DisruptorApplicationContext(); 115 | disruptorContext.setApplicationContext(applicationContext); 116 | } 117 | 118 | @Override 119 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 120 | this.applicationContext = applicationContext; 121 | } 122 | 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/chain/def/DefaultNamedHandlerList.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler.chain.def; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | import java.util.ListIterator; 8 | 9 | import org.apache.commons.lang3.StringUtils; 10 | 11 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 12 | import com.lmax.disruptor.spring.boot.event.handler.DisruptorHandler; 13 | import com.lmax.disruptor.spring.boot.event.handler.NamedHandlerList; 14 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChain; 15 | import com.lmax.disruptor.spring.boot.event.handler.chain.ProxiedHandlerChain; 16 | 17 | public class DefaultNamedHandlerList implements NamedHandlerList { 18 | 19 | private String name; 20 | 21 | private List> backingList; 22 | 23 | public DefaultNamedHandlerList(String name) { 24 | this(name, new ArrayList>()); 25 | } 26 | 27 | public DefaultNamedHandlerList(String name, List> backingList) { 28 | if (backingList == null) { 29 | throw new NullPointerException("backingList constructor argument cannot be null."); 30 | } 31 | this.backingList = backingList; 32 | setName(name); 33 | } 34 | 35 | public void setName(String name) { 36 | if (StringUtils.isBlank(name)) { 37 | throw new IllegalArgumentException("Cannot specify a null or empty name."); 38 | } 39 | this.name = name; 40 | } 41 | 42 | @Override 43 | public String getName() { 44 | return this.name; 45 | } 46 | 47 | @Override 48 | public HandlerChain proxy(HandlerChain handlerChain) { 49 | return new ProxiedHandlerChain((ProxiedHandlerChain) handlerChain, this); 50 | } 51 | 52 | @Override 53 | public int size() { 54 | return this.backingList.size(); 55 | } 56 | 57 | @Override 58 | public boolean isEmpty() { 59 | return this.backingList.isEmpty(); 60 | } 61 | 62 | @Override 63 | public boolean contains(Object o) { 64 | return this.backingList.contains(o); 65 | } 66 | 67 | @Override 68 | public Iterator> iterator() { 69 | return this.backingList.iterator(); 70 | } 71 | 72 | @Override 73 | public Object[] toArray() { 74 | return this.backingList.toArray(); 75 | } 76 | 77 | @Override 78 | public T[] toArray(T[] a) { 79 | return this.backingList.toArray(a); 80 | } 81 | 82 | @Override 83 | public boolean add(DisruptorHandler e) { 84 | return this.backingList.add(e); 85 | } 86 | 87 | @Override 88 | public boolean remove(Object o) { 89 | return this.backingList.remove(o); 90 | } 91 | 92 | @Override 93 | public boolean containsAll(Collection c) { 94 | return this.backingList.containsAll(c); 95 | } 96 | 97 | @Override 98 | public boolean addAll(Collection> c) { 99 | return this.backingList.addAll(c); 100 | } 101 | 102 | @Override 103 | public boolean addAll(int index, Collection> c) { 104 | return this.backingList.addAll(index, c); 105 | } 106 | 107 | @Override 108 | public boolean removeAll(Collection c) { 109 | return this.backingList.removeAll(c); 110 | } 111 | 112 | @Override 113 | public boolean retainAll(Collection c) { 114 | return this.backingList.retainAll(c); 115 | } 116 | 117 | @Override 118 | public void clear() { 119 | this.backingList.clear(); 120 | } 121 | 122 | @Override 123 | public DisruptorHandler get(int index) { 124 | return this.backingList.get(index); 125 | } 126 | 127 | @Override 128 | public DisruptorHandler set(int index, DisruptorHandler element) { 129 | return this.backingList.set(index, element); 130 | } 131 | 132 | @Override 133 | public void add(int index, DisruptorHandler element) { 134 | this.backingList.add(index, element); 135 | } 136 | 137 | @Override 138 | public DisruptorHandler remove(int index) { 139 | return this.backingList.remove(index); 140 | } 141 | 142 | @Override 143 | public int indexOf(Object o) { 144 | return this.backingList.indexOf(o); 145 | } 146 | 147 | @Override 148 | public int lastIndexOf(Object o) { 149 | return this.backingList.lastIndexOf(o); 150 | } 151 | 152 | @Override 153 | public ListIterator> listIterator() { 154 | return this.backingList.listIterator(); 155 | } 156 | 157 | @Override 158 | public ListIterator> listIterator(int index) { 159 | return this.backingList.listIterator(index); 160 | } 161 | 162 | @Override 163 | public List> subList(int fromIndex, int toIndex) { 164 | return this.backingList.subList(fromIndex, toIndex); 165 | } 166 | 167 | } 168 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/RingBufferAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | 6 | import org.springframework.beans.BeansException; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 11 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 12 | import org.springframework.context.ApplicationContext; 13 | import org.springframework.context.ApplicationContextAware; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Configuration; 16 | 17 | import com.lmax.disruptor.BatchEventProcessor; 18 | import com.lmax.disruptor.EventFactory; 19 | import com.lmax.disruptor.RingBuffer; 20 | import com.lmax.disruptor.SequenceBarrier; 21 | import com.lmax.disruptor.WaitStrategy; 22 | import com.lmax.disruptor.dsl.Disruptor; 23 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 24 | import com.lmax.disruptor.spring.boot.event.factory.DisruptorBindEventFactory; 25 | import com.lmax.disruptor.spring.boot.event.handler.DisruptorEventDispatcher; 26 | import com.lmax.disruptor.spring.boot.util.WaitStrategys; 27 | 28 | @Configuration 29 | @ConditionalOnClass({ Disruptor.class }) 30 | @ConditionalOnProperty(prefix = DisruptorProperties.PREFIX, value = "enabled", havingValue = "true") 31 | @EnableConfigurationProperties({ DisruptorProperties.class }) 32 | public class RingBufferAutoConfiguration implements ApplicationContextAware { 33 | 34 | private ApplicationContext applicationContext; 35 | 36 | /** 37 | * 决定一个消费者将如何等待生产者将Event置入Disruptor的策略。用来权衡当生产者无法将新的事件放进RingBuffer时的处理策略。 38 | * (例如:当生产者太快,消费者太慢,会导致生成者获取不到新的事件槽来插入新事件,则会根据该策略进行处理,默认会堵塞) 39 | * @return {@link WaitStrategy} instance 40 | */ 41 | @Bean 42 | @ConditionalOnMissingBean 43 | public WaitStrategy waitStrategy() { 44 | return WaitStrategys.YIELDING_WAIT; 45 | } 46 | 47 | @Bean 48 | @ConditionalOnMissingBean 49 | public EventFactory eventFactory() { 50 | return new DisruptorBindEventFactory(); 51 | } 52 | 53 | /** 54 | * 55 | *

56 | * 创建RingBuffer 57 | *

58 | *

59 | * 1 eventFactory 为 60 | *

61 | * 2 ringBufferSize为RingBuffer缓冲区大小,最好是2的指数倍 62 | *

63 | * 64 | * @param properties :配置参数 65 | * @param waitStrategy : 一种策略,用来均衡数据生产者和消费者之间的处理效率,默认提供了3个实现类 66 | * @param eventFactory : 工厂类对象,用于创建一个个的LongEvent, LongEvent是实际的消费数据,初始化启动Disruptor的时候,Disruptor会调用该工厂方法创建一个个的消费数据实例存放到RingBuffer缓冲区里面去,创建的对象个数为ringBufferSize指定的 67 | * @param preEventHandler :事件前置处理器 68 | * @param postEventHandler : 事件后置处理器 69 | * @return {@link RingBuffer} instance 70 | */ 71 | @Bean 72 | @ConditionalOnClass({ RingBuffer.class }) 73 | @ConditionalOnProperty(prefix = DisruptorProperties.PREFIX, value = "ring-buffer", havingValue = "true") 74 | public RingBuffer ringBuffer(DisruptorProperties properties, WaitStrategy waitStrategy, 75 | EventFactory eventFactory, 76 | @Autowired(required = false) DisruptorEventDispatcher preEventHandler, 77 | @Autowired(required = false) DisruptorEventDispatcher postEventHandler) { 78 | // http://blog.csdn.net/a314368439/article/details/72642653?utm_source=itdadao&utm_medium=referral 79 | // 创建线程池 80 | ExecutorService executor = Executors.newFixedThreadPool(properties.getRingThreadNumbers()); 81 | /* 82 | * 第一个参数叫EventFactory,从名字上理解就是“事件工厂”,其实它的职责就是产生数据填充RingBuffer的区块。 83 | * 第二个参数是RingBuffer的大小,它必须是2的指数倍 目的是为了将求模运算转为&运算提高效率 84 | * 第三个参数是RingBuffer的生产都在没有可用区块的时候(可能是消费者(或者说是事件处理器) 太慢了)的等待策略 85 | */ 86 | RingBuffer ringBuffer = null; 87 | if (properties.isMultiProducer()) { 88 | // RingBuffer.createMultiProducer创建一个多生产者的RingBuffer 89 | ringBuffer = RingBuffer.createMultiProducer(eventFactory, properties.getRingBufferSize(), waitStrategy); 90 | } else { 91 | // RingBuffer.createSingleProducer创建一个单生产者的RingBuffer 92 | ringBuffer = RingBuffer.createSingleProducer(eventFactory, properties.getRingBufferSize(), waitStrategy); 93 | } 94 | 95 | // 单个处理器 96 | if (null != preEventHandler) { 97 | // 创建SequenceBarrier 98 | SequenceBarrier sequenceBarrier = ringBuffer.newBarrier(); 99 | // 创建消息处理器 100 | BatchEventProcessor transProcessor = new BatchEventProcessor(ringBuffer, 101 | sequenceBarrier, preEventHandler); 102 | // 这一部的目的是让RingBuffer根据消费者的状态 如果只有一个消费者的情况可以省略 103 | ringBuffer.addGatingSequences(transProcessor.getSequence()); 104 | // 把消息处理器提交到线程池 105 | executor.submit(transProcessor); 106 | } 107 | 108 | return ringBuffer; 109 | } 110 | 111 | @Override 112 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 113 | this.applicationContext = applicationContext; 114 | } 115 | 116 | public ApplicationContext getApplicationContext() { 117 | return applicationContext; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/event/handler/chain/def/DefaultHandlerChainManager.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.event.handler.chain.def; 2 | 3 | import java.util.Collections; 4 | import java.util.LinkedHashMap; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.util.CollectionUtils; 11 | 12 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 13 | import com.lmax.disruptor.spring.boot.event.handler.DisruptorHandler; 14 | import com.lmax.disruptor.spring.boot.event.handler.Nameable; 15 | import com.lmax.disruptor.spring.boot.event.handler.NamedHandlerList; 16 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChain; 17 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChainManager; 18 | import com.lmax.disruptor.spring.boot.util.StringUtils; 19 | 20 | public class DefaultHandlerChainManager implements HandlerChainManager { 21 | 22 | private static transient final Logger log = LoggerFactory.getLogger(DefaultHandlerChainManager.class); 23 | 24 | private Map> handlers; 25 | 26 | private Map> handlerChains; 27 | 28 | private final static String DEFAULT_CHAIN_DEFINATION_DELIMITER_CHAR = ","; 29 | 30 | public DefaultHandlerChainManager() { 31 | this.handlers = new LinkedHashMap>(); 32 | this.handlerChains = new LinkedHashMap>(); 33 | } 34 | 35 | public Map> getHandlers() { 36 | return handlers; 37 | } 38 | 39 | public void setHandlers(Map> handlers) { 40 | this.handlers = handlers; 41 | } 42 | 43 | public Map> getHandlerChains() { 44 | return handlerChains; 45 | } 46 | 47 | public void setHandlerChains(Map> handlerChains) { 48 | this.handlerChains = handlerChains; 49 | } 50 | 51 | public DisruptorHandler getHandler(String name) { 52 | return this.handlers.get(name); 53 | } 54 | 55 | public void addHandler(String name, DisruptorHandler handler) { 56 | addHandler(name, handler, true); 57 | } 58 | 59 | protected void addHandler(String name, DisruptorHandler handler, boolean overwrite) { 60 | DisruptorHandler existing = getHandler(name); 61 | if (existing == null || overwrite) { 62 | if (handler instanceof Nameable) { 63 | ((Nameable) handler).setName(name); 64 | } 65 | this.handlers.put(name, handler); 66 | } 67 | } 68 | 69 | public void createChain(String chainName, String chainDefinition) { 70 | if (StringUtils.isBlank(chainName)) { 71 | throw new NullPointerException("chainName cannot be null or empty."); 72 | } 73 | if (StringUtils.isBlank(chainDefinition)) { 74 | throw new NullPointerException("chainDefinition cannot be null or empty."); 75 | } 76 | if (log.isDebugEnabled()) { 77 | log.debug("Creating chain [" + chainName + "] from String definition [" + chainDefinition + "]"); 78 | } 79 | String[] handlerTokens = splitChainDefinition(chainDefinition); 80 | for (String token : handlerTokens) { 81 | addToChain(chainName, token); 82 | } 83 | } 84 | 85 | /** 86 | * Splits the comma-delimited handler chain definition line into individual handler definition tokens. 87 | * @param chainDefinition chain definition line 88 | * @return array of chain definition 89 | */ 90 | protected String[] splitChainDefinition(String chainDefinition) { 91 | String trimToNull = StringUtils.trimToNull(chainDefinition); 92 | if(trimToNull == null){ 93 | return null; 94 | } 95 | String[] split = StringUtils.splits(trimToNull, DEFAULT_CHAIN_DEFINATION_DELIMITER_CHAR); 96 | for (int i = 0; i < split.length; i++) { 97 | split[i] = StringUtils.trimToNull(split[i]); 98 | } 99 | return split; 100 | } 101 | 102 | public static void main(String[] args) { 103 | 104 | } 105 | 106 | public void addToChain(String chainName, String handlerName) { 107 | if (StringUtils.isBlank(chainName)) { 108 | throw new IllegalArgumentException("chainName cannot be null or empty."); 109 | } 110 | DisruptorHandler handler = getHandler(handlerName); 111 | if (handler == null) { 112 | throw new IllegalArgumentException("There is no handler with name '" + handlerName + 113 | "' to apply to chain [" + chainName + "] in the pool of available Handlers. Ensure a " + 114 | "handler with that name/path has first been registered with the addHandler method(s)."); 115 | } 116 | NamedHandlerList chain = ensureChain(chainName); 117 | chain.add(handler); 118 | } 119 | 120 | protected NamedHandlerList ensureChain(String chainName) { 121 | NamedHandlerList chain = getChain(chainName); 122 | if (chain == null) { 123 | chain = new DefaultNamedHandlerList(chainName); 124 | this.handlerChains.put(chainName, chain); 125 | } 126 | return chain; 127 | } 128 | 129 | public NamedHandlerList getChain(String chainName) { 130 | return this.handlerChains.get(chainName); 131 | } 132 | 133 | public boolean hasChains() { 134 | return !CollectionUtils.isEmpty(this.handlerChains); 135 | } 136 | 137 | @SuppressWarnings("unchecked") 138 | public Set getChainNames() { 139 | return this.handlerChains != null ? this.handlerChains.keySet() : Collections.EMPTY_SET; 140 | } 141 | 142 | @Override 143 | public HandlerChain proxy(HandlerChain original, String chainName) { 144 | NamedHandlerList configured = getChain(chainName); 145 | if (configured == null) { 146 | String msg = "There is no configured chain under the name/key [" + chainName + "]."; 147 | throw new IllegalArgumentException(msg); 148 | } 149 | return configured.proxy(original); 150 | } 151 | 152 | 153 | 154 | 155 | } 156 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/DisruptorAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.HashMap; 6 | import java.util.Iterator; 7 | import java.util.LinkedHashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.Map.Entry; 11 | import java.util.concurrent.ThreadFactory; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.beans.BeansException; 16 | import org.springframework.beans.factory.annotation.Qualifier; 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 18 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 20 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 21 | import org.springframework.context.ApplicationContext; 22 | import org.springframework.context.ApplicationContextAware; 23 | import org.springframework.context.ApplicationListener; 24 | import org.springframework.context.annotation.Bean; 25 | import org.springframework.context.annotation.Configuration; 26 | import org.springframework.core.OrderComparator; 27 | import org.springframework.util.CollectionUtils; 28 | import org.springframework.util.ObjectUtils; 29 | 30 | import com.lmax.disruptor.EventFactory; 31 | import com.lmax.disruptor.EventTranslatorOneArg; 32 | import com.lmax.disruptor.EventTranslatorThreeArg; 33 | import com.lmax.disruptor.EventTranslatorTwoArg; 34 | import com.lmax.disruptor.WaitStrategy; 35 | import com.lmax.disruptor.dsl.Disruptor; 36 | import com.lmax.disruptor.dsl.EventHandlerGroup; 37 | import com.lmax.disruptor.dsl.ProducerType; 38 | import com.lmax.disruptor.spring.boot.annotation.EventRule; 39 | import com.lmax.disruptor.spring.boot.config.EventHandlerDefinition; 40 | import com.lmax.disruptor.spring.boot.config.Ini; 41 | import com.lmax.disruptor.spring.boot.context.DisruptorEventAwareProcessor; 42 | import com.lmax.disruptor.spring.boot.event.DisruptorApplicationEvent; 43 | import com.lmax.disruptor.spring.boot.event.DisruptorEvent; 44 | import com.lmax.disruptor.spring.boot.event.factory.DisruptorBindEventFactory; 45 | import com.lmax.disruptor.spring.boot.event.factory.DisruptorEventThreadFactory; 46 | import com.lmax.disruptor.spring.boot.event.handler.DisruptorEventDispatcher; 47 | import com.lmax.disruptor.spring.boot.event.handler.DisruptorHandler; 48 | import com.lmax.disruptor.spring.boot.event.handler.Nameable; 49 | import com.lmax.disruptor.spring.boot.event.handler.chain.HandlerChainManager; 50 | import com.lmax.disruptor.spring.boot.event.handler.chain.def.DefaultHandlerChainManager; 51 | import com.lmax.disruptor.spring.boot.event.handler.chain.def.PathMatchingHandlerChainResolver; 52 | import com.lmax.disruptor.spring.boot.event.translator.DisruptorEventOneArgTranslator; 53 | import com.lmax.disruptor.spring.boot.event.translator.DisruptorEventThreeArgTranslator; 54 | import com.lmax.disruptor.spring.boot.event.translator.DisruptorEventTwoArgTranslator; 55 | import com.lmax.disruptor.spring.boot.hooks.DisruptorShutdownHook; 56 | import com.lmax.disruptor.spring.boot.util.StringUtils; 57 | import com.lmax.disruptor.spring.boot.util.WaitStrategys; 58 | 59 | @Configuration 60 | @ConditionalOnClass({ Disruptor.class }) 61 | @ConditionalOnProperty(prefix = DisruptorProperties.PREFIX, value = "enabled", havingValue = "true") 62 | @EnableConfigurationProperties({ DisruptorProperties.class }) 63 | @SuppressWarnings({ "unchecked", "rawtypes" }) 64 | public class DisruptorAutoConfiguration implements ApplicationContextAware { 65 | 66 | private static final Logger LOG = LoggerFactory.getLogger(DisruptorAutoConfiguration.class); 67 | private ApplicationContext applicationContext; 68 | /** 69 | * 处理器链定义 70 | */ 71 | private Map handlerChainDefinitionMap = new HashMap(); 72 | 73 | /** 74 | * 决定一个消费者将如何等待生产者将Event置入Disruptor的策略。用来权衡当生产者无法将新的事件放进RingBuffer时的处理策略。 75 | * (例如:当生产者太快,消费者太慢,会导致生成者获取不到新的事件槽来插入新事件,则会根据该策略进行处理,默认会堵塞) 76 | * @return {@link WaitStrategy} instance 77 | */ 78 | @Bean 79 | @ConditionalOnMissingBean 80 | public WaitStrategy waitStrategy() { 81 | return WaitStrategys.YIELDING_WAIT; 82 | } 83 | 84 | @Bean 85 | @ConditionalOnMissingBean 86 | public ThreadFactory threadFactory() { 87 | return new DisruptorEventThreadFactory(); 88 | } 89 | 90 | @Bean 91 | @ConditionalOnMissingBean 92 | public EventFactory eventFactory() { 93 | return new DisruptorBindEventFactory(); 94 | } 95 | 96 | /* 97 | * Handler实现集合 98 | */ 99 | @Bean("disruptorHandlers") 100 | public Map> disruptorHandlers() { 101 | 102 | Map> disruptorPreHandlers = new LinkedHashMap>(); 103 | 104 | Map beansOfType = getApplicationContext().getBeansOfType(DisruptorHandler.class); 105 | if (!ObjectUtils.isEmpty(beansOfType)) { 106 | Iterator> ite = beansOfType.entrySet().iterator(); 107 | while (ite.hasNext()) { 108 | Entry entry = ite.next(); 109 | if (entry.getValue() instanceof DisruptorEventDispatcher) { 110 | // 跳过入口实现类 111 | continue; 112 | } 113 | 114 | EventRule annotationType = getApplicationContext().findAnnotationOnBean(entry.getKey(), EventRule.class); 115 | if(annotationType == null) { 116 | // 注解为空,则打印错误信息 117 | LOG.error("Not Found AnnotationType {0} on Bean {1} Whith Name {2}", EventRule.class, entry.getValue().getClass(), entry.getKey()); 118 | } else { 119 | handlerChainDefinitionMap.put(annotationType.value(), entry.getKey()); 120 | } 121 | 122 | disruptorPreHandlers.put(entry.getKey(), entry.getValue()); 123 | } 124 | } 125 | // BeanFactoryUtils.beansOfTypeIncludingAncestors(getApplicationContext(), 126 | // EventHandler.class); 127 | 128 | return disruptorPreHandlers; 129 | } 130 | 131 | /* 132 | * 处理器链集合 133 | */ 134 | @Bean("disruptorEventHandlers") 135 | public List disruptorEventHandlers(DisruptorProperties properties, 136 | @Qualifier("disruptorHandlers") Map> eventHandlers) { 137 | // 获取定义 拦截链规则 138 | List handlerDefinitions = properties.getHandlerDefinitions(); 139 | // 拦截器集合 140 | List disruptorEventHandlers = new ArrayList(); 141 | // 未定义,则使用默认规则 142 | if (CollectionUtils.isEmpty(handlerDefinitions)) { 143 | 144 | EventHandlerDefinition definition = new EventHandlerDefinition(); 145 | 146 | definition.setOrder(0); 147 | definition.setDefinitionMap(handlerChainDefinitionMap); 148 | 149 | // 构造DisruptorEventHandler 150 | disruptorEventHandlers.add(this.createDisruptorEventHandler(definition, eventHandlers)); 151 | 152 | } else { 153 | // 迭代拦截器规则 154 | for (EventHandlerDefinition handlerDefinition : handlerDefinitions) { 155 | 156 | // 构造DisruptorEventHandler 157 | disruptorEventHandlers.add(this.createDisruptorEventHandler(handlerDefinition, eventHandlers)); 158 | 159 | } 160 | } 161 | // 进行排序 162 | Collections.sort(disruptorEventHandlers, new OrderComparator()); 163 | 164 | return disruptorEventHandlers; 165 | } 166 | 167 | /* 168 | * 构造DisruptorEventHandler 169 | */ 170 | protected DisruptorEventDispatcher createDisruptorEventHandler(EventHandlerDefinition handlerDefinition, 171 | Map> eventHandlers) { 172 | 173 | if (StringUtils.isNotEmpty(handlerDefinition.getDefinitions())) { 174 | handlerChainDefinitionMap.putAll(this.parseHandlerChainDefinitions(handlerDefinition.getDefinitions())); 175 | } else if (!CollectionUtils.isEmpty(handlerDefinition.getDefinitionMap())) { 176 | handlerChainDefinitionMap.putAll(handlerDefinition.getDefinitionMap()); 177 | } 178 | 179 | HandlerChainManager manager = createHandlerChainManager(eventHandlers, handlerChainDefinitionMap); 180 | PathMatchingHandlerChainResolver chainResolver = new PathMatchingHandlerChainResolver(); 181 | chainResolver.setHandlerChainManager(manager); 182 | return new DisruptorEventDispatcher(chainResolver, handlerDefinition.getOrder()); 183 | } 184 | 185 | protected Map parseHandlerChainDefinitions(String definitions) { 186 | Ini ini = new Ini(); 187 | ini.load(definitions); 188 | Ini.Section section = ini.getSection("urls"); 189 | if (CollectionUtils.isEmpty(section)) { 190 | section = ini.getSection(Ini.DEFAULT_SECTION_NAME); 191 | } 192 | return section; 193 | } 194 | 195 | protected HandlerChainManager createHandlerChainManager( 196 | Map> eventHandlers, 197 | Map handlerChainDefinitionMap) { 198 | 199 | HandlerChainManager manager = new DefaultHandlerChainManager(); 200 | if (!CollectionUtils.isEmpty(eventHandlers)) { 201 | for (Map.Entry> entry : eventHandlers.entrySet()) { 202 | String name = entry.getKey(); 203 | DisruptorHandler handler = entry.getValue(); 204 | if (handler instanceof Nameable) { 205 | ((Nameable) handler).setName(name); 206 | } 207 | manager.addHandler(name, handler); 208 | } 209 | } 210 | 211 | if (!CollectionUtils.isEmpty(handlerChainDefinitionMap)) { 212 | for (Map.Entry entry : handlerChainDefinitionMap.entrySet()) { 213 | // ant匹配规则 214 | String rule = entry.getKey(); 215 | String chainDefinition = entry.getValue(); 216 | manager.createChain(rule, chainDefinition); 217 | } 218 | } 219 | 220 | return manager; 221 | } 222 | 223 | /** 224 | * 225 | *

226 | * 创建Disruptor 227 | *

228 | *

229 | * 1 eventFactory 为 230 | *

231 | * 2 ringBufferSize为RingBuffer缓冲区大小,最好是2的指数倍 232 | *

233 | * 234 | * @param properties : 配置参数 235 | * @param waitStrategy : 一种策略,用来均衡数据生产者和消费者之间的处理效率,默认提供了3个实现类 236 | * @param threadFactory : 线程工厂 237 | * @param eventFactory : 工厂类对象,用于创建一个个的LongEvent, LongEvent是实际的消费数据,初始化启动Disruptor的时候,Disruptor会调用该工厂方法创建一个个的消费数据实例存放到RingBuffer缓冲区里面去,创建的对象个数为ringBufferSize指定的 238 | * @param disruptorEventHandlers : 事件分发器 239 | * @return {@link Disruptor} instance 240 | */ 241 | @Bean 242 | @ConditionalOnClass({ Disruptor.class }) 243 | @ConditionalOnProperty(prefix = DisruptorProperties.PREFIX, value = "enabled", havingValue = "true") 244 | public Disruptor disruptor( 245 | DisruptorProperties properties, 246 | WaitStrategy waitStrategy, 247 | ThreadFactory threadFactory, 248 | EventFactory eventFactory, 249 | @Qualifier("disruptorEventHandlers") 250 | List disruptorEventHandlers) { 251 | 252 | // http://blog.csdn.net/a314368439/article/details/72642653?utm_source=itdadao&utm_medium=referral 253 | 254 | Disruptor disruptor = null; 255 | if (properties.isMultiProducer()) { 256 | disruptor = new Disruptor(eventFactory, properties.getRingBufferSize(), threadFactory, 257 | ProducerType.MULTI, waitStrategy); 258 | } else { 259 | disruptor = new Disruptor(eventFactory, properties.getRingBufferSize(), threadFactory, 260 | ProducerType.SINGLE, waitStrategy); 261 | } 262 | 263 | if (!ObjectUtils.isEmpty(disruptorEventHandlers)) { 264 | 265 | // 进行排序 266 | Collections.sort(disruptorEventHandlers, new OrderComparator()); 267 | 268 | // 使用disruptor创建消费者组 269 | EventHandlerGroup handlerGroup = null; 270 | for (int i = 0; i < disruptorEventHandlers.size(); i++) { 271 | // 连接消费事件方法,其中EventHandler的是为消费者消费消息的实现类 272 | DisruptorEventDispatcher eventHandler = disruptorEventHandlers.get(i); 273 | if(i < 1) { 274 | handlerGroup = disruptor.handleEventsWith(eventHandler); 275 | } else { 276 | // 完成前置事件处理之后执行后置事件处理 277 | handlerGroup.then(eventHandler); 278 | } 279 | } 280 | } 281 | 282 | // 启动 283 | disruptor.start(); 284 | 285 | /** 286 | * 应用退出时,要调用shutdown来清理资源,关闭网络连接,从MetaQ服务器上注销自己 287 | * 注意:我们建议应用在JBOSS、Tomcat等容器的退出钩子里调用shutdown方法 288 | */ 289 | Runtime.getRuntime().addShutdownHook(new DisruptorShutdownHook(disruptor)); 290 | 291 | return disruptor; 292 | 293 | } 294 | 295 | @Bean 296 | @ConditionalOnMissingBean 297 | public EventTranslatorOneArg oneArgEventTranslator() { 298 | return new DisruptorEventOneArgTranslator(); 299 | } 300 | 301 | @Bean 302 | @ConditionalOnMissingBean 303 | public EventTranslatorTwoArg twoArgEventTranslator() { 304 | return new DisruptorEventTwoArgTranslator(); 305 | } 306 | 307 | @Bean 308 | @ConditionalOnMissingBean 309 | public EventTranslatorThreeArg threeArgEventTranslator() { 310 | return new DisruptorEventThreeArgTranslator(); 311 | } 312 | 313 | @Bean 314 | public DisruptorTemplate disruptorTemplate() { 315 | return new DisruptorTemplate(); 316 | } 317 | 318 | @Bean 319 | public ApplicationListener disruptorEventListener(Disruptor disruptor, 320 | EventTranslatorOneArg oneArgEventTranslator) { 321 | return new ApplicationListener() { 322 | 323 | @Override 324 | public void onApplicationEvent(DisruptorApplicationEvent appEvent) { 325 | DisruptorEvent event = (DisruptorEvent) appEvent.getSource(); 326 | disruptor.publishEvent(oneArgEventTranslator, event); 327 | } 328 | 329 | }; 330 | } 331 | 332 | @Bean 333 | public DisruptorEventAwareProcessor disruptorEventAwareProcessor() { 334 | return new DisruptorEventAwareProcessor(); 335 | } 336 | 337 | @Override 338 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 339 | this.applicationContext = applicationContext; 340 | } 341 | 342 | public ApplicationContext getApplicationContext() { 343 | return applicationContext; 344 | } 345 | 346 | } 347 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.6.0 10 | 11 | 12 | 13 | com.github.hiwepy 14 | disruptor-spring-boot-starter 15 | Spring Boot Starter For Disruptor 16 | 2.0.1-SNAPSHOT 17 | ${project.groupId}:${project.artifactId} 18 | https://github.com/hiwepy/${project.artifactId} 19 | jar 20 | 21 | 22 | 23 | The Apache Software License, Version 2.0 24 | http://www.apache.org/licenses/LICENSE-2.0.txt 25 | 26 | 27 | 28 | 29 | scm:git:https:github.com/hiwepy/${project.artifactId}.git 30 | scm:git:https:github.com/hiwepy/${project.artifactId}.git 31 | https:github.com/hiwepy/${project.artifactId} 32 | ${project.artifactId} 33 | 34 | 35 | 36 | 37 | wandl 38 | hnxyhcwdl1003@163.com 39 | 40 | developer 41 | 42 | +8 43 | 44 | 45 | 46 | 47 | 48 | ossrh 49 | Maven Snapshots Repository 50 | https://oss.sonatype.org/content/repositories/snapshots 51 | 52 | 53 | ossrh 54 | Maven Central Staging Repository 55 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-compiler-plugin 66 | ${maven-compiler-plugin.version} 67 | 68 | ${java.version} 69 | ${java.version} 70 | ${project.build.sourceEncoding} 71 | 512M 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-enforcer-plugin 78 | ${maven-enforcer-plugin.version} 79 | 80 | 81 | default-cli 82 | 83 | enforce 84 | 85 | validate 86 | 87 | 88 | 89 | 90 | 91 | 92 | [${maven.version}.0,) 93 | 94 | 95 | 96 | 97 | 98 | [${java.version}.0,) 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | org.apache.maven.plugins 108 | maven-gpg-plugin 109 | ${maven-gpg-plugin.version} 110 | 111 | 112 | sign-artifacts 113 | verify 114 | 115 | sign 116 | 117 | 118 | 119 | 120 | 121 | 122 | org.apache.maven.plugins 123 | maven-resources-plugin 124 | ${maven-resources-plugin.version} 125 | 126 | ${project.build.sourceEncoding} 127 | 128 | 129 | 130 | 131 | org.apache.maven.plugins 132 | maven-release-plugin 133 | ${maven-release-plugin.version} 134 | 135 | v@{project.version} 136 | true 137 | false 138 | release 139 | deploy 140 | 141 | 142 | 143 | 144 | org.apache.maven.plugins 145 | maven-source-plugin 146 | ${maven-source-plugin.version} 147 | 148 | true 149 | 150 | 151 | 152 | attach-sources 153 | 154 | jar-no-fork 155 | 156 | 157 | 158 | 159 | 160 | 161 | org.apache.maven.plugins 162 | maven-surefire-plugin 163 | ${maven-surefire-plugin.version} 164 | 165 | 166 | true 167 | true 168 | 171 | once 172 | -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m -Dfile.encoding=UTF-8 173 | 174 | ${basedir}/target/test-classes 175 | 176 | 177 | **/*Test.java 178 | 179 | 180 | **/TestBean.java 181 | 182 | 183 | 184 | 185 | 186 | org.apache.maven.plugins 187 | maven-jar-plugin 188 | ${maven-jar-plugin.version} 189 | 190 | true 191 | 192 | 193 | true 194 | true 195 | 196 | 197 | 198 | 199 | 200 | 201 | org.apache.maven.plugins 202 | maven-javadoc-plugin 203 | ${maven-javadoc-plugin.version} 204 | 205 | ${project.build.sourceEncoding} 206 | ${project.build.sourceEncoding} 207 | ${project.build.sourceEncoding} 208 | 209 | 210 | 211 | attach-javadocs 212 | package 213 | 214 | jar 215 | 216 | 217 | 218 | 219 | 220 | org.sonatype.plugins 221 | nexus-staging-maven-plugin 222 | ${maven-nexus-staging-plugin.version} 223 | true 224 | 225 | ossrh 226 | https://oss.sonatype.org/ 227 | true 228 | 229 | 60 230 | 20 231 | true 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | org.apache.maven.plugins 240 | maven-enforcer-plugin 241 | 242 | 243 | 244 | org.apache.maven.plugins 245 | maven-compiler-plugin 246 | 247 | 248 | 249 | org.apache.maven.plugins 250 | maven-resources-plugin 251 | 252 | 253 | 254 | org.apache.maven.plugins 255 | maven-surefire-plugin 256 | 257 | 258 | 259 | org.apache.maven.plugins 260 | maven-jar-plugin 261 | 262 | 263 | 264 | org.apache.maven.plugins 265 | maven-source-plugin 266 | 267 | 268 | 269 | org.apache.maven.plugins 270 | maven-install-plugin 271 | 272 | 273 | 274 | org.apache.maven.plugins 275 | maven-deploy-plugin 276 | 277 | 278 | 279 | 280 | 281 | 282 | disable-javadoc-doclint 283 | 284 | [1.8,) 285 | 286 | 287 | -Xdoclint:none 288 | 289 | 290 | 291 | release 292 | 293 | 294 | 295 | 296 | org.apache.maven.plugins 297 | maven-enforcer-plugin 298 | 299 | 300 | 301 | org.apache.maven.plugins 302 | maven-compiler-plugin 303 | 304 | 305 | 306 | org.apache.maven.plugins 307 | maven-resources-plugin 308 | 309 | 310 | 311 | org.apache.maven.plugins 312 | maven-surefire-plugin 313 | 314 | 315 | 316 | org.apache.maven.plugins 317 | maven-jar-plugin 318 | 319 | 320 | 321 | org.apache.maven.plugins 322 | maven-source-plugin 323 | 324 | 325 | 326 | org.apache.maven.plugins 327 | maven-javadoc-plugin 328 | 329 | 330 | 331 | org.apache.maven.plugins 332 | maven-install-plugin 333 | 334 | 335 | 336 | org.apache.maven.plugins 337 | maven-gpg-plugin 338 | 339 | 340 | 341 | org.apache.maven.plugins 342 | maven-deploy-plugin 343 | 344 | 345 | 346 | org.apache.maven.plugins 347 | maven-release-plugin 348 | 349 | 350 | org.sonatype.plugins 351 | nexus-staging-maven-plugin 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | UTF-8 360 | UTF-8 361 | 4.1 362 | 3.4.3 363 | 3.0 364 | 1.6 365 | 3.1.1 366 | 2.5.3 367 | 3.1.0 368 | 2.22.1 369 | 1.6.8 370 | 371 | 372 | 373 | 374 | 375 | 376 | javax.servlet 377 | javax.servlet-api 378 | true 379 | 380 | 381 | 382 | org.slf4j 383 | slf4j-api 384 | 385 | 386 | 387 | org.slf4j 388 | slf4j-simple 389 | test 390 | 391 | 392 | 393 | 394 | org.springframework.boot 395 | spring-boot-starter 396 | 397 | 398 | org.springframework.boot 399 | spring-boot-starter-logging 400 | 401 | 402 | 403 | 404 | 405 | org.springframework.boot 406 | spring-boot-starter-log4j2 407 | 408 | 409 | 410 | org.springframework.boot 411 | spring-boot-starter-test 412 | test 413 | 414 | 415 | 416 | org.springframework.boot 417 | spring-boot-configuration-processor 418 | true 419 | 420 | 421 | 422 | org.springframework.boot 423 | spring-boot-autoconfigure 424 | 425 | 426 | 427 | 428 | com.lmax 429 | disruptor 430 | ${disruptor.version} 431 | 432 | 433 | 434 | org.apache.commons 435 | commons-lang3 436 | 437 | 438 | 439 | org.apache.commons 440 | commons-collections4 441 | ${commons-collections4.version} 442 | 443 | 444 | 445 | 446 | 447 | -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/config/Ini.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.config; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.io.Reader; 7 | import java.io.UnsupportedEncodingException; 8 | import java.util.Collection; 9 | import java.util.Collections; 10 | import java.util.LinkedHashMap; 11 | import java.util.Map; 12 | import java.util.Scanner; 13 | import java.util.Set; 14 | 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import com.lmax.disruptor.spring.boot.exception.EventHandleException; 19 | import com.lmax.disruptor.spring.boot.util.StringUtils; 20 | 21 | public class Ini implements Map { 22 | 23 | private static transient final Logger log = LoggerFactory.getLogger(Ini.class); 24 | 25 | public static final String DEFAULT_SECTION_NAME = ""; //empty string means the first unnamed section 26 | public static final String DEFAULT_CHARSET_NAME = "UTF-8"; 27 | 28 | public static final String COMMENT_POUND = "#"; 29 | public static final String COMMENT_SEMICOLON = ";"; 30 | public static final String SECTION_PREFIX = "["; 31 | public static final String SECTION_SUFFIX = "]"; 32 | 33 | protected static final char ESCAPE_TOKEN = '\\'; 34 | 35 | private final Map sections; 36 | 37 | /** 38 | * Creates a new empty {@code Ini} instance. 39 | */ 40 | public Ini() { 41 | this.sections = new LinkedHashMap(); 42 | } 43 | 44 | /** 45 | * Creates a new {@code Ini} instance with the specified defaults. 46 | * 47 | * @param defaults the default sections and/or key-value pairs to copy into the new instance. 48 | */ 49 | public Ini(Ini defaults) { 50 | this(); 51 | if (defaults == null) { 52 | throw new NullPointerException("Defaults cannot be null."); 53 | } 54 | for (Section section : defaults.getSections()) { 55 | Section copy = new Section(section); 56 | this.sections.put(section.getName(), copy); 57 | } 58 | } 59 | 60 | /** 61 | * Returns {@code true} if no sections have been configured, or if there are sections, but the sections themselves 62 | * are all empty, {@code false} otherwise. 63 | * 64 | * @return {@code true} if no sections have been configured, or if there are sections, but the sections themselves 65 | * are all empty, {@code false} otherwise. 66 | */ 67 | public boolean isEmpty() { 68 | Collection
sections = this.sections.values(); 69 | if (!sections.isEmpty()) { 70 | for (Section section : sections) { 71 | if (!section.isEmpty()) { 72 | return false; 73 | } 74 | } 75 | } 76 | return true; 77 | } 78 | 79 | /** 80 | * Returns the names of all sections managed by this {@code Ini} instance or an empty collection if there are 81 | * no sections. 82 | * 83 | * @return the names of all sections managed by this {@code Ini} instance or an empty collection if there are 84 | * no sections. 85 | */ 86 | public Set getSectionNames() { 87 | return Collections.unmodifiableSet(sections.keySet()); 88 | } 89 | 90 | /** 91 | * Returns the sections managed by this {@code Ini} instance or an empty collection if there are 92 | * no sections. 93 | * 94 | * @return the sections managed by this {@code Ini} instance or an empty collection if there are 95 | * no sections. 96 | */ 97 | public Collection
getSections() { 98 | return Collections.unmodifiableCollection(sections.values()); 99 | } 100 | 101 | /** 102 | * Returns the {@link Section} with the given name or {@code null} if no section with that name exists. 103 | * 104 | * @param sectionName the name of the section to retrieve. 105 | * @return the {@link Section} with the given name or {@code null} if no section with that name exists. 106 | */ 107 | public Section getSection(String sectionName) { 108 | String name = cleanName(sectionName); 109 | return sections.get(name); 110 | } 111 | 112 | /** 113 | * Ensures a section with the specified name exists, adding a new one if it does not yet exist. 114 | * 115 | * @param sectionName the name of the section to ensure existence 116 | * @return the section created if it did not yet exist, or the existing Section that already existed. 117 | */ 118 | public Section addSection(String sectionName) { 119 | String name = cleanName(sectionName); 120 | Section section = getSection(name); 121 | if (section == null) { 122 | section = new Section(name); 123 | this.sections.put(name, section); 124 | } 125 | return section; 126 | } 127 | 128 | /** 129 | * Removes the section with the specified name and returns it, or {@code null} if the section did not exist. 130 | * 131 | * @param sectionName the name of the section to remove. 132 | * @return the section with the specified name or {@code null} if the section did not exist. 133 | */ 134 | public Section removeSection(String sectionName) { 135 | String name = cleanName(sectionName); 136 | return this.sections.remove(name); 137 | } 138 | 139 | private static String cleanName(String sectionName) { 140 | String name = StringUtils.trimToNull(sectionName); 141 | if (name == null) { 142 | log.trace("Specified name was null or empty. Defaulting to the default section (name = \"\")"); 143 | name = DEFAULT_SECTION_NAME; 144 | } 145 | return name; 146 | } 147 | 148 | /** 149 | * Sets a name/value pair for the section with the given {@code sectionName}. If the section does not yet exist, 150 | * it will be created. If the {@code sectionName} is null or empty, the name/value pair will be placed in the 151 | * default (unnamed, empty string) section. 152 | * 153 | * @param sectionName the name of the section to add the name/value pair 154 | * @param propertyName the name of the property to add 155 | * @param propertyValue the property value 156 | */ 157 | public void setSectionProperty(String sectionName, String propertyName, String propertyValue) { 158 | String name = cleanName(sectionName); 159 | Section section = getSection(name); 160 | if (section == null) { 161 | section = addSection(name); 162 | } 163 | section.put(propertyName, propertyValue); 164 | } 165 | 166 | /** 167 | * Returns the value of the specified section property, or {@code null} if the section or property do not exist. 168 | * 169 | * @param sectionName the name of the section to retrieve to acquire the property value 170 | * @param propertyName the name of the section property for which to return the value 171 | * @return the value of the specified section property, or {@code null} if the section or property do not exist. 172 | */ 173 | public String getSectionProperty(String sectionName, String propertyName) { 174 | Section section = getSection(sectionName); 175 | return section != null ? section.get(propertyName) : null; 176 | } 177 | 178 | /** 179 | * Returns the value of the specified section property, or the {@code defaultValue} if the section or 180 | * property do not exist. 181 | * 182 | * @param sectionName the name of the section to add the name/value pair 183 | * @param propertyName the name of the property to add 184 | * @param defaultValue the default value to return if the section or property do not exist. 185 | * @return the value of the specified section property, or the {@code defaultValue} if the section or 186 | * property do not exist. 187 | */ 188 | public String getSectionProperty(String sectionName, String propertyName, String defaultValue) { 189 | String value = getSectionProperty(sectionName, propertyName); 190 | return value != null ? value : defaultValue; 191 | } 192 | 193 | 194 | 195 | /** 196 | * Loads the specified raw INI-formatted text into this instance. 197 | * 198 | * @param iniConfig the raw INI-formatted text to load into this instance. 199 | * @throws EventHandleException if the text cannot be loaded 200 | */ 201 | public void load(String iniConfig) throws EventHandleException { 202 | load(new Scanner(iniConfig)); 203 | } 204 | 205 | /** 206 | * Loads the INI-formatted text backed by the given InputStream into this instance. This implementation will 207 | * close the input stream after it has finished loading. It is expected that the stream's contents are 208 | * UTF-8 encoded. 209 | * 210 | * @param is the {@code InputStream} from which to read the INI-formatted text 211 | * @throws IOException if unable 212 | */ 213 | public void load(InputStream is) throws IOException { 214 | if (is == null) { 215 | throw new NullPointerException("InputStream argument cannot be null."); 216 | } 217 | InputStreamReader isr; 218 | try { 219 | isr = new InputStreamReader(is, DEFAULT_CHARSET_NAME); 220 | } catch (UnsupportedEncodingException e) { 221 | throw new EventHandleException(e); 222 | } 223 | load(isr); 224 | } 225 | 226 | /** 227 | * Loads the INI-formatted text backed by the given Reader into this instance. This implementation will close the 228 | * reader after it has finished loading. 229 | * 230 | * @param reader the {@code Reader} from which to read the INI-formatted text 231 | */ 232 | public void load(Reader reader) { 233 | Scanner scanner = new Scanner(reader); 234 | try { 235 | load(scanner); 236 | } finally { 237 | try { 238 | scanner.close(); 239 | } catch (Exception e) { 240 | log.debug("Unable to cleanly close the InputStream scanner. Non-critical - ignoring.", e); 241 | } 242 | } 243 | } 244 | 245 | private void addSection(String name, StringBuilder content) { 246 | if (content.length() > 0) { 247 | String contentString = content.toString(); 248 | String cleaned = StringUtils.trimToNull(contentString); 249 | if (cleaned != null) { 250 | Section section = new Section(name, contentString); 251 | if (!section.isEmpty()) { 252 | sections.put(name, section); 253 | } 254 | } 255 | } 256 | } 257 | 258 | /** 259 | * Loads the INI-formatted text backed by the given Scanner. This implementation will close the 260 | * scanner after it has finished loading. 261 | * 262 | * @param scanner the {@code Scanner} from which to read the INI-formatted text 263 | */ 264 | public void load(Scanner scanner) { 265 | 266 | String sectionName = DEFAULT_SECTION_NAME; 267 | StringBuilder sectionContent = new StringBuilder(); 268 | 269 | while (scanner.hasNextLine()) { 270 | 271 | String rawLine = scanner.nextLine(); 272 | String line = StringUtils.trimToNull(rawLine); 273 | 274 | if (line == null || line.startsWith(COMMENT_POUND) || line.startsWith(COMMENT_SEMICOLON)) { 275 | //skip empty lines and comments: 276 | continue; 277 | } 278 | 279 | String newSectionName = getSectionName(line); 280 | if (newSectionName != null) { 281 | //found a new section - convert the currently buffered one into a Section object 282 | addSection(sectionName, sectionContent); 283 | 284 | //reset the buffer for the new section: 285 | sectionContent = new StringBuilder(); 286 | 287 | sectionName = newSectionName; 288 | 289 | if (log.isDebugEnabled()) { 290 | log.debug("Parsing " + SECTION_PREFIX + sectionName + SECTION_SUFFIX); 291 | } 292 | } else { 293 | //normal line - add it to the existing content buffer: 294 | sectionContent.append(rawLine).append("\n"); 295 | } 296 | } 297 | 298 | //finish any remaining buffered content: 299 | addSection(sectionName, sectionContent); 300 | } 301 | 302 | protected static boolean isSectionHeader(String line) { 303 | String s = StringUtils.trimToNull(line); 304 | return s != null && s.startsWith(SECTION_PREFIX) && s.endsWith(SECTION_SUFFIX); 305 | } 306 | 307 | protected static String getSectionName(String line) { 308 | String s = StringUtils.trimToNull(line); 309 | if (isSectionHeader(s)) { 310 | return cleanName(s.substring(1, s.length() - 1)); 311 | } 312 | return null; 313 | } 314 | 315 | public boolean equals(Object obj) { 316 | if (obj instanceof Ini) { 317 | Ini ini = (Ini) obj; 318 | return this.sections.equals(ini.sections); 319 | } 320 | return false; 321 | } 322 | 323 | @Override 324 | public int hashCode() { 325 | return this.sections.hashCode(); 326 | } 327 | 328 | public String toString() { 329 | if (this.sections == null || this.sections.isEmpty()) { 330 | return ""; 331 | } else { 332 | StringBuilder sb = new StringBuilder("sections="); 333 | int i = 0; 334 | for (Ini.Section section : this.sections.values()) { 335 | if (i > 0) { 336 | sb.append(","); 337 | } 338 | sb.append(section.toString()); 339 | i++; 340 | } 341 | return sb.toString(); 342 | } 343 | } 344 | 345 | public int size() { 346 | return this.sections.size(); 347 | } 348 | 349 | public boolean containsKey(Object key) { 350 | return this.sections.containsKey(key); 351 | } 352 | 353 | public boolean containsValue(Object value) { 354 | return this.sections.containsValue(value); 355 | } 356 | 357 | public Section get(Object key) { 358 | return this.sections.get(key); 359 | } 360 | 361 | public Section put(String key, Section value) { 362 | return this.sections.put(key, value); 363 | } 364 | 365 | public Section remove(Object key) { 366 | return this.sections.remove(key); 367 | } 368 | 369 | public void putAll(Map m) { 370 | this.sections.putAll(m); 371 | } 372 | 373 | public void clear() { 374 | this.sections.clear(); 375 | } 376 | 377 | public Set keySet() { 378 | return Collections.unmodifiableSet(this.sections.keySet()); 379 | } 380 | 381 | public Collection
values() { 382 | return Collections.unmodifiableCollection(this.sections.values()); 383 | } 384 | 385 | public Set> entrySet() { 386 | return Collections.unmodifiableSet(this.sections.entrySet()); 387 | } 388 | 389 | /** 390 | * An {@code Ini.Section} is String-key-to-String-value Map, identifiable by a 391 | * {@link #getName() name} unique within an {@link Ini} instance. 392 | */ 393 | public static class Section implements Map { 394 | private final String name; 395 | private final Map props; 396 | 397 | private Section(String name) { 398 | if (name == null) { 399 | throw new NullPointerException("name"); 400 | } 401 | this.name = name; 402 | this.props = new LinkedHashMap(); 403 | } 404 | 405 | private Section(String name, String sectionContent) { 406 | if (name == null) { 407 | throw new NullPointerException("name"); 408 | } 409 | this.name = name; 410 | Map props; 411 | if (StringUtils.isNotBlank(sectionContent) ) { 412 | props = toMapProps(sectionContent); 413 | } else { 414 | props = new LinkedHashMap(); 415 | } 416 | if ( props != null ) { 417 | this.props = props; 418 | } else { 419 | this.props = new LinkedHashMap(); 420 | } 421 | } 422 | 423 | private Section(Section defaults) { 424 | this(defaults.getName()); 425 | putAll(defaults.props); 426 | } 427 | 428 | //Protected to access in a test case - NOT considered part of Shiro's public API 429 | 430 | protected static boolean isContinued(String line) { 431 | if (StringUtils.isBlank(line)) { 432 | return false; 433 | } 434 | int length = line.length(); 435 | //find the number of backslashes at the end of the line. If an even number, the 436 | //backslashes are considered escaped. If an odd number, the line is considered continued on the next line 437 | int backslashCount = 0; 438 | for (int i = length - 1; i > 0; i--) { 439 | if (line.charAt(i) == ESCAPE_TOKEN) { 440 | backslashCount++; 441 | } else { 442 | break; 443 | } 444 | } 445 | return backslashCount % 2 != 0; 446 | } 447 | 448 | private static boolean isKeyValueSeparatorChar(char c) { 449 | return Character.isWhitespace(c) || c == ':' || c == '='; 450 | } 451 | 452 | private static boolean isCharEscaped(CharSequence s, int index) { 453 | return index > 0 && s.charAt(index - 1) == ESCAPE_TOKEN; 454 | } 455 | 456 | //Protected to access in a test case - NOT considered part of Shiro's public API 457 | protected static String[] splitKeyValue(String keyValueLine) { 458 | String line = StringUtils.trimToNull(keyValueLine); 459 | if (line == null) { 460 | return null; 461 | } 462 | StringBuilder keyBuffer = new StringBuilder(); 463 | StringBuilder valueBuffer = new StringBuilder(); 464 | 465 | boolean buildingKey = true; //we'll build the value next: 466 | 467 | for (int i = 0; i < line.length(); i++) { 468 | char c = line.charAt(i); 469 | 470 | if (buildingKey) { 471 | if (isKeyValueSeparatorChar(c) && !isCharEscaped(line, i)) { 472 | buildingKey = false;//now start building the value 473 | } else { 474 | keyBuffer.append(c); 475 | } 476 | } else { 477 | if (valueBuffer.length() == 0 && isKeyValueSeparatorChar(c) && !isCharEscaped(line, i)) { 478 | //swallow the separator chars before we start building the value 479 | } else { 480 | valueBuffer.append(c); 481 | } 482 | } 483 | } 484 | 485 | String key = StringUtils.trimToNull(keyBuffer.toString()); 486 | String value = StringUtils.trimToNull(valueBuffer.toString()); 487 | 488 | if (key == null || value == null) { 489 | String msg = "Line argument must contain a key and a value. Only one string token was found."; 490 | throw new IllegalArgumentException(msg); 491 | } 492 | 493 | log.trace("Discovered key/value pair: {}={}", key, value); 494 | 495 | return new String[]{key, value}; 496 | } 497 | 498 | @SuppressWarnings("resource") 499 | private static Map toMapProps(String content) { 500 | Map props = new LinkedHashMap(); 501 | String line; 502 | StringBuilder lineBuffer = new StringBuilder(); 503 | Scanner scanner = new Scanner(content); 504 | while (scanner.hasNextLine()) { 505 | line = StringUtils.trimToNull(scanner.nextLine()); 506 | if (isContinued(line)) { 507 | //strip off the last continuation backslash: 508 | line = line.substring(0, line.length() - 1); 509 | lineBuffer.append(line); 510 | continue; 511 | } else { 512 | lineBuffer.append(line); 513 | } 514 | line = lineBuffer.toString(); 515 | lineBuffer = new StringBuilder(); 516 | String[] kvPair = splitKeyValue(line); 517 | props.put(kvPair[0], kvPair[1]); 518 | } 519 | 520 | return props; 521 | } 522 | 523 | public String getName() { 524 | return this.name; 525 | } 526 | 527 | public void clear() { 528 | this.props.clear(); 529 | } 530 | 531 | public boolean containsKey(Object key) { 532 | return this.props.containsKey(key); 533 | } 534 | 535 | public boolean containsValue(Object value) { 536 | return this.props.containsValue(value); 537 | } 538 | 539 | public Set> entrySet() { 540 | return this.props.entrySet(); 541 | } 542 | 543 | public String get(Object key) { 544 | return this.props.get(key); 545 | } 546 | 547 | public boolean isEmpty() { 548 | return this.props.isEmpty(); 549 | } 550 | 551 | public Set keySet() { 552 | return this.props.keySet(); 553 | } 554 | 555 | public String put(String key, String value) { 556 | return this.props.put(key, value); 557 | } 558 | 559 | public void putAll(Map m) { 560 | this.props.putAll(m); 561 | } 562 | 563 | public String remove(Object key) { 564 | return this.props.remove(key); 565 | } 566 | 567 | public int size() { 568 | return this.props.size(); 569 | } 570 | 571 | public Collection values() { 572 | return this.props.values(); 573 | } 574 | 575 | public String toString() { 576 | String name = getName(); 577 | if (DEFAULT_SECTION_NAME.equals(name)) { 578 | return ""; 579 | } 580 | return name; 581 | } 582 | 583 | @Override 584 | public boolean equals(Object obj) { 585 | if (obj instanceof Section) { 586 | Section other = (Section) obj; 587 | return getName().equals(other.getName()) && this.props.equals(other.props); 588 | } 589 | return false; 590 | } 591 | 592 | @Override 593 | public int hashCode() { 594 | return this.name.hashCode() * 31 + this.props.hashCode(); 595 | } 596 | } 597 | 598 | } -------------------------------------------------------------------------------- /src/main/java/com/lmax/disruptor/spring/boot/util/StringUtils.java: -------------------------------------------------------------------------------- 1 | package com.lmax.disruptor.spring.boot.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Collection; 6 | import java.util.Collections; 7 | import java.util.Enumeration; 8 | import java.util.HashMap; 9 | import java.util.Iterator; 10 | import java.util.LinkedList; 11 | import java.util.List; 12 | import java.util.Locale; 13 | import java.util.Map; 14 | import java.util.Properties; 15 | import java.util.Random; 16 | import java.util.Set; 17 | import java.util.StringTokenizer; 18 | import java.util.TimeZone; 19 | import java.util.TreeSet; 20 | 21 | import org.springframework.util.CollectionUtils; 22 | import org.springframework.util.ObjectUtils; 23 | 24 | public abstract class StringUtils extends org.apache.commons.lang3.StringUtils { 25 | 26 | private static final String FOLDER_SEPARATOR = "/"; 27 | 28 | private static final String WINDOWS_FOLDER_SEPARATOR = "\\"; 29 | 30 | private static final String TOP_PATH = ".."; 31 | 32 | private static final String CURRENT_PATH = "."; 33 | 34 | private static final char EXTENSION_SEPARATOR = '.'; 35 | 36 | /* 37 | * Any number of these characters are considered delimiters between 38 | * multiple context config paths in a single String value. 39 | */ 40 | public static String CONFIG_LOCATION_DELIMITERS = ",; \t\n"; 41 | 42 | private static final int[] allChineseScope = { 1601, 1637, 1833, 2078, 43 | 2274, 2302, 2433, 2594, 2787, 3106, 3212, 3472, 3635, 3722, 3730, 44 | 3858, 4027, 4086, 4390, 4558, 4684, 4925, 5249, 5600, 45 | Integer.MAX_VALUE }; 46 | public static final char unknowChar = '*'; 47 | private static final char[] allEnglishLetter = { 'A', 'B', 'C', 'D', 'E', 48 | 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 49 | 'T', 'W', 'X', 'Y', 'Z', unknowChar }; 50 | 51 | /* 52 | * 字串是否为空 53 | * 54 | * @param str 55 | * @return 56 | */ 57 | public static boolean isEmpty(String str) { 58 | if (str == null) { 59 | return true; 60 | } else if (str.length() == 0) { 61 | return true; 62 | } else if ("NULL".equals(str.toUpperCase())) { 63 | return true; 64 | } 65 | return false; 66 | } 67 | 68 | /* 69 | * 该方法不能被删除;可能引起调用者代码报错 70 | */ 71 | public static boolean isNotEmpty(String str) { 72 | return !isEmpty(str); 73 | } 74 | 75 | 76 | /* 77 | * 判断 Null 或 空字符串 78 | * 79 | * @param str 80 | * @return 81 | */ 82 | public static boolean isNull(String str) { 83 | 84 | return str == null || str.trim().length() == 0; 85 | } 86 | 87 | //--------------------------------------------------------------------- 88 | // General convenience methods for working with Strings 89 | //--------------------------------------------------------------------- 90 | 91 | /* 92 | * Check whether the given String is empty. 93 | *

This method accepts any Object as an argument, comparing it to 94 | * {@code null} and the empty String. As a consequence, this method 95 | * will never return {@code true} for a non-null non-String object. 96 | *

The Object signature is useful for general attribute handling code 97 | * that commonly deals with Strings but generally has to iterate over 98 | * Objects since attributes may e.g. be primitive value objects as well. 99 | * @param str the candidate String 100 | * @since 3.2.1 101 | */ 102 | public static boolean isEmpty(Object str) { 103 | return (str == null || "".equals(str)); 104 | } 105 | 106 | /* 107 | * Check that the given CharSequence is neither {@code null} nor of length 0. 108 | * Note: Will return {@code true} for a CharSequence that purely consists of whitespace. 109 | *

 110 | 	 * StringUtils.hasLength(null) = false
 111 | 	 * StringUtils.hasLength("") = false
 112 | 	 * StringUtils.hasLength(" ") = true
 113 | 	 * StringUtils.hasLength("Hello") = true
 114 | 	 * 
115 | * @param str the CharSequence to check (may be {@code null}) 116 | * @return {@code true} if the CharSequence is not null and has length 117 | * @see #hasText(String) 118 | */ 119 | public static boolean hasLength(CharSequence str) { 120 | return (str != null && str.length() > 0); 121 | } 122 | 123 | /* 124 | * Check that the given String is neither {@code null} nor of length 0. 125 | * Note: Will return {@code true} for a String that purely consists of whitespace. 126 | * @param str the String to check (may be {@code null}) 127 | * @return {@code true} if the String is not null and has length 128 | * @see #hasLength(CharSequence) 129 | */ 130 | public static boolean hasLength(String str) { 131 | return hasLength((CharSequence) str); 132 | } 133 | 134 | /* 135 | * Check whether the given CharSequence has actual text. 136 | * More specifically, returns {@code true} if the string not {@code null}, 137 | * its length is greater than 0, and it contains at least one non-whitespace character. 138 | *

 139 | 	 * StringUtils.hasText(null) = false
 140 | 	 * StringUtils.hasText("") = false
 141 | 	 * StringUtils.hasText(" ") = false
 142 | 	 * StringUtils.hasText("12345") = true
 143 | 	 * StringUtils.hasText(" 12345 ") = true
 144 | 	 * 
145 | * @param str the CharSequence to check (may be {@code null}) 146 | * @return {@code true} if the CharSequence is not {@code null}, 147 | * its length is greater than 0, and it does not contain whitespace only 148 | * @see Character#isWhitespace 149 | */ 150 | public static boolean hasText(CharSequence str) { 151 | if (!hasLength(str)) { 152 | return false; 153 | } 154 | int strLen = str.length(); 155 | for (int i = 0; i < strLen; i++) { 156 | if (!Character.isWhitespace(str.charAt(i))) { 157 | return true; 158 | } 159 | } 160 | return false; 161 | } 162 | 163 | /* 164 | * Check whether the given String has actual text. 165 | * More specifically, returns {@code true} if the string not {@code null}, 166 | * its length is greater than 0, and it contains at least one non-whitespace character. 167 | * @param str the String to check (may be {@code null}) 168 | * @return {@code true} if the String is not {@code null}, its length is 169 | * greater than 0, and it does not contain whitespace only 170 | * @see #hasText(CharSequence) 171 | */ 172 | public static boolean hasText(String str) { 173 | return hasText((CharSequence) str); 174 | } 175 | 176 | /* 177 | * Check whether the given CharSequence contains any whitespace characters. 178 | * @param str the CharSequence to check (may be {@code null}) 179 | * @return {@code true} if the CharSequence is not empty and 180 | * contains at least 1 whitespace character 181 | * @see Character#isWhitespace 182 | */ 183 | public static boolean containsWhitespace(CharSequence str) { 184 | if (!hasLength(str)) { 185 | return false; 186 | } 187 | int strLen = str.length(); 188 | for (int i = 0; i < strLen; i++) { 189 | if (Character.isWhitespace(str.charAt(i))) { 190 | return true; 191 | } 192 | } 193 | return false; 194 | } 195 | 196 | /* 197 | * Check whether the given String contains any whitespace characters. 198 | * @param str the String to check (may be {@code null}) 199 | * @return {@code true} if the String is not empty and 200 | * contains at least 1 whitespace character 201 | * @see #containsWhitespace(CharSequence) 202 | */ 203 | public static boolean containsWhitespace(String str) { 204 | return containsWhitespace((CharSequence) str); 205 | } 206 | 207 | /* 208 | * Trim leading and trailing whitespace from the given String. 209 | * @param str the String to check 210 | * @return the trimmed String 211 | * @see java.lang.Character#isWhitespace 212 | */ 213 | public static String trimWhitespace(String str) { 214 | if (!hasLength(str)) { 215 | return str; 216 | } 217 | StringBuilder sb = new StringBuilder(str); 218 | while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) { 219 | sb.deleteCharAt(0); 220 | } 221 | while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) { 222 | sb.deleteCharAt(sb.length() - 1); 223 | } 224 | return sb.toString(); 225 | } 226 | 227 | /* 228 | * Trim all whitespace from the given String: 229 | * leading, trailing, and in between characters. 230 | * @param str the String to check 231 | * @return the trimmed String 232 | * @see java.lang.Character#isWhitespace 233 | */ 234 | public static String trimAllWhitespace(String str) { 235 | if (!hasLength(str)) { 236 | return str; 237 | } 238 | int len = str.length(); 239 | StringBuilder sb = new StringBuilder(str.length()); 240 | for (int i = 0; i < len; i++) { 241 | char c = str.charAt(i); 242 | if (!Character.isWhitespace(c)) { 243 | sb.append(c); 244 | } 245 | } 246 | return sb.toString(); 247 | } 248 | 249 | /* 250 | * Trim leading whitespace from the given String. 251 | * @param str the String to check 252 | * @return the trimmed String 253 | * @see java.lang.Character#isWhitespace 254 | */ 255 | public static String trimLeadingWhitespace(String str) { 256 | if (!hasLength(str)) { 257 | return str; 258 | } 259 | StringBuilder sb = new StringBuilder(str); 260 | while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) { 261 | sb.deleteCharAt(0); 262 | } 263 | return sb.toString(); 264 | } 265 | 266 | /* 267 | * Trim trailing whitespace from the given String. 268 | * @param str the String to check 269 | * @return the trimmed String 270 | * @see java.lang.Character#isWhitespace 271 | */ 272 | public static String trimTrailingWhitespace(String str) { 273 | if (!hasLength(str)) { 274 | return str; 275 | } 276 | StringBuilder sb = new StringBuilder(str); 277 | while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) { 278 | sb.deleteCharAt(sb.length() - 1); 279 | } 280 | return sb.toString(); 281 | } 282 | 283 | /* 284 | * Trim all occurrences of the supplied leading character from the given String. 285 | * @param str the String to check 286 | * @param leadingCharacter the leading character to be trimmed 287 | * @return the trimmed String 288 | */ 289 | public static String trimLeadingCharacter(String str, char leadingCharacter) { 290 | if (!hasLength(str)) { 291 | return str; 292 | } 293 | StringBuilder sb = new StringBuilder(str); 294 | while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) { 295 | sb.deleteCharAt(0); 296 | } 297 | return sb.toString(); 298 | } 299 | 300 | /* 301 | * Trim all occurrences of the supplied trailing character from the given String. 302 | * @param str the String to check 303 | * @param trailingCharacter the trailing character to be trimmed 304 | * @return the trimmed String 305 | */ 306 | public static String trimTrailingCharacter(String str, char trailingCharacter) { 307 | if (!hasLength(str)) { 308 | return str; 309 | } 310 | StringBuilder sb = new StringBuilder(str); 311 | while (sb.length() > 0 && sb.charAt(sb.length() - 1) == trailingCharacter) { 312 | sb.deleteCharAt(sb.length() - 1); 313 | } 314 | return sb.toString(); 315 | } 316 | 317 | 318 | /* 319 | * Test if the given String starts with the specified prefix, 320 | * ignoring upper/lower case. 321 | * @param str the String to check 322 | * @param prefix the prefix to look for 323 | * @see java.lang.String#startsWith 324 | */ 325 | public static boolean startsWithIgnoreCase(String str, String prefix) { 326 | if (str == null || prefix == null) { 327 | return false; 328 | } 329 | if (str.startsWith(prefix)) { 330 | return true; 331 | } 332 | if (str.length() < prefix.length()) { 333 | return false; 334 | } 335 | String lcStr = str.substring(0, prefix.length()).toLowerCase(); 336 | String lcPrefix = prefix.toLowerCase(); 337 | return lcStr.equals(lcPrefix); 338 | } 339 | 340 | /* 341 | * Test if the given String ends with the specified suffix, 342 | * ignoring upper/lower case. 343 | * @param str the String to check 344 | * @param suffix the suffix to look for 345 | * @see java.lang.String#endsWith 346 | */ 347 | public static boolean endsWithIgnoreCase(String str, String suffix) { 348 | if (str == null || suffix == null) { 349 | return false; 350 | } 351 | if (str.endsWith(suffix)) { 352 | return true; 353 | } 354 | if (str.length() < suffix.length()) { 355 | return false; 356 | } 357 | 358 | String lcStr = str.substring(str.length() - suffix.length()).toLowerCase(); 359 | String lcSuffix = suffix.toLowerCase(); 360 | return lcStr.equals(lcSuffix); 361 | } 362 | 363 | /* 364 | * Test whether the given string matches the given substring 365 | * at the given index. 366 | * @param str the original string (or StringBuilder) 367 | * @param index the index in the original string to start matching against 368 | * @param substring the substring to match at the given index 369 | */ 370 | public static boolean substringMatch(CharSequence str, int index, CharSequence substring) { 371 | for (int j = 0; j < substring.length(); j++) { 372 | int i = index + j; 373 | if (i >= str.length() || str.charAt(i) != substring.charAt(j)) { 374 | return false; 375 | } 376 | } 377 | return true; 378 | } 379 | 380 | /* 381 | * Count the occurrences of the substring in string s. 382 | * @param str string to search in. Return 0 if this is null. 383 | * @param sub string to search for. Return 0 if this is null. 384 | */ 385 | public static int countOccurrencesOf(String str, String sub) { 386 | if (str == null || sub == null || str.length() == 0 || sub.length() == 0) { 387 | return 0; 388 | } 389 | int count = 0; 390 | int pos = 0; 391 | int idx; 392 | while ((idx = str.indexOf(sub, pos)) != -1) { 393 | ++count; 394 | pos = idx + sub.length(); 395 | } 396 | return count; 397 | } 398 | 399 | /* 400 | * Replace all occurrences of a substring within a string with 401 | * another string. 402 | * @param inString String to examine 403 | * @param oldPattern String to replace 404 | * @param newPattern String to insert 405 | * @return a String with the replacements 406 | */ 407 | public static String replace(String inString, String oldPattern, String newPattern) { 408 | if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) { 409 | return inString; 410 | } 411 | StringBuilder sb = new StringBuilder(); 412 | int pos = 0; // our position in the old string 413 | int index = inString.indexOf(oldPattern); 414 | // the index of an occurrence we've found, or -1 415 | int patLen = oldPattern.length(); 416 | while (index >= 0) { 417 | sb.append(inString.substring(pos, index)); 418 | sb.append(newPattern); 419 | pos = index + patLen; 420 | index = inString.indexOf(oldPattern, pos); 421 | } 422 | sb.append(inString.substring(pos)); 423 | // remember to append any characters to the right of a match 424 | return sb.toString(); 425 | } 426 | 427 | /* 428 | * Delete all occurrences of the given substring. 429 | * @param inString the original String 430 | * @param pattern the pattern to delete all occurrences of 431 | * @return the resulting String 432 | */ 433 | public static String delete(String inString, String pattern) { 434 | return replace(inString, pattern, ""); 435 | } 436 | 437 | /* 438 | * Delete any character in a given String. 439 | * @param inString the original String 440 | * @param charsToDelete a set of characters to delete. 441 | * E.g. "az\n" will delete 'a's, 'z's and new lines. 442 | * @return the resulting String 443 | */ 444 | public static String deleteAny(String inString, String charsToDelete) { 445 | if (!hasLength(inString) || !hasLength(charsToDelete)) { 446 | return inString; 447 | } 448 | StringBuilder sb = new StringBuilder(); 449 | for (int i = 0; i < inString.length(); i++) { 450 | char c = inString.charAt(i); 451 | if (charsToDelete.indexOf(c) == -1) { 452 | sb.append(c); 453 | } 454 | } 455 | return sb.toString(); 456 | } 457 | 458 | 459 | //--------------------------------------------------------------------- 460 | // Convenience methods for working with formatted Strings 461 | //--------------------------------------------------------------------- 462 | 463 | 464 | /* 465 | * Unqualify a string qualified by a '.' dot character. For example, 466 | * "this.name.is.qualified", returns "qualified". 467 | * @param qualifiedName the qualified name 468 | */ 469 | public static String unqualify(String qualifiedName) { 470 | return unqualify(qualifiedName, '.'); 471 | } 472 | 473 | /* 474 | * Unqualify a string qualified by a separator character. For example, 475 | * "this:name:is:qualified" returns "qualified" if using a ':' separator. 476 | * @param qualifiedName the qualified name 477 | * @param separator the separator 478 | */ 479 | public static String unqualify(String qualifiedName, char separator) { 480 | return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1); 481 | } 482 | 483 | /* 484 | * Capitalize a {@code String}, changing the first letter to 485 | * upper case as per {@link Character#toUpperCase(char)}. 486 | * No other letters are changed. 487 | * @param str the String to capitalize, may be {@code null} 488 | * @return the capitalized String, {@code null} if null 489 | */ 490 | public static String capitalize(String str) { 491 | return changeFirstCharacterCase(str, true); 492 | } 493 | 494 | /* 495 | * Uncapitalize a {@code String}, changing the first letter to 496 | * lower case as per {@link Character#toLowerCase(char)}. 497 | * No other letters are changed. 498 | * @param str the String to uncapitalize, may be {@code null} 499 | * @return the uncapitalized String, {@code null} if null 500 | */ 501 | public static String uncapitalize(String str) { 502 | return changeFirstCharacterCase(str, false); 503 | } 504 | 505 | private static String changeFirstCharacterCase(String str, boolean capitalize) { 506 | if (str == null || str.length() == 0) { 507 | return str; 508 | } 509 | StringBuilder sb = new StringBuilder(str.length()); 510 | if (capitalize) { 511 | sb.append(Character.toUpperCase(str.charAt(0))); 512 | } 513 | else { 514 | sb.append(Character.toLowerCase(str.charAt(0))); 515 | } 516 | sb.append(str.substring(1)); 517 | return sb.toString(); 518 | } 519 | 520 | /* 521 | * Extract the filename from the given path, 522 | * e.g. "mypath/myfile.txt" -> "myfile.txt". 523 | * @param path the file path (may be {@code null}) 524 | * @return the extracted filename, or {@code null} if none 525 | */ 526 | public static String getFilename(String path) { 527 | if (path == null) { 528 | return null; 529 | } 530 | int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR); 531 | return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path); 532 | } 533 | 534 | /* 535 | * Extract the filename extension from the given path, 536 | * e.g. "mypath/myfile.txt" -> "txt". 537 | * @param path the file path (may be {@code null}) 538 | * @return the extracted filename extension, or {@code null} if none 539 | */ 540 | public static String getFilenameExtension(String path) { 541 | if (path == null) { 542 | return null; 543 | } 544 | int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR); 545 | if (extIndex == -1) { 546 | return null; 547 | } 548 | int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR); 549 | if (folderIndex > extIndex) { 550 | return null; 551 | } 552 | return path.substring(extIndex + 1); 553 | } 554 | 555 | /* 556 | * Strip the filename extension from the given path, 557 | * e.g. "mypath/myfile.txt" -> "mypath/myfile". 558 | * @param path the file path (may be {@code null}) 559 | * @return the path with stripped filename extension, 560 | * or {@code null} if none 561 | */ 562 | public static String stripFilenameExtension(String path) { 563 | if (path == null) { 564 | return null; 565 | } 566 | int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR); 567 | if (extIndex == -1) { 568 | return path; 569 | } 570 | int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR); 571 | if (folderIndex > extIndex) { 572 | return path; 573 | } 574 | return path.substring(0, extIndex); 575 | } 576 | 577 | /* 578 | * Apply the given relative path to the given path, 579 | * assuming standard Java folder separation (i.e. "/" separators). 580 | * @param path the path to start from (usually a full file path) 581 | * @param relativePath the relative path to apply 582 | * (relative to the full file path above) 583 | * @return the full file path that results from applying the relative path 584 | */ 585 | public static String applyRelativePath(String path, String relativePath) { 586 | int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR); 587 | if (separatorIndex != -1) { 588 | String newPath = path.substring(0, separatorIndex); 589 | if (!relativePath.startsWith(FOLDER_SEPARATOR)) { 590 | newPath += FOLDER_SEPARATOR; 591 | } 592 | return newPath + relativePath; 593 | } 594 | else { 595 | return relativePath; 596 | } 597 | } 598 | 599 | /* 600 | * Normalize the path by suppressing sequences like "path/.." and 601 | * inner simple dots. 602 | *

The result is convenient for path comparison. For other uses, 603 | * notice that Windows separators ("\") are replaced by simple slashes. 604 | * @param path the original path 605 | * @return the normalized path 606 | */ 607 | public static String cleanPath(String path) { 608 | if (path == null) { 609 | return null; 610 | } 611 | String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR); 612 | 613 | // Strip prefix from path to analyze, to not treat it as part of the 614 | // first path element. This is necessary to correctly parse paths like 615 | // "file:core/../core/io/Resource.class", where the ".." should just 616 | // strip the first "core" directory while keeping the "file:" prefix. 617 | int prefixIndex = pathToUse.indexOf(":"); 618 | String prefix = ""; 619 | if (prefixIndex != -1) { 620 | prefix = pathToUse.substring(0, prefixIndex + 1); 621 | if (prefix.contains("/")) { 622 | prefix = ""; 623 | } 624 | else { 625 | pathToUse = pathToUse.substring(prefixIndex + 1); 626 | } 627 | } 628 | if (pathToUse.startsWith(FOLDER_SEPARATOR)) { 629 | prefix = prefix + FOLDER_SEPARATOR; 630 | pathToUse = pathToUse.substring(1); 631 | } 632 | 633 | String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR); 634 | List pathElements = new LinkedList(); 635 | int tops = 0; 636 | 637 | for (int i = pathArray.length - 1; i >= 0; i--) { 638 | String element = pathArray[i]; 639 | if (CURRENT_PATH.equals(element)) { 640 | // Points to current directory - drop it. 641 | } 642 | else if (TOP_PATH.equals(element)) { 643 | // Registering top path found. 644 | tops++; 645 | } 646 | else { 647 | if (tops > 0) { 648 | // Merging path element with element corresponding to top path. 649 | tops--; 650 | } 651 | else { 652 | // Normal path element found. 653 | pathElements.add(0, element); 654 | } 655 | } 656 | } 657 | 658 | // Remaining top paths need to be retained. 659 | for (int i = 0; i < tops; i++) { 660 | pathElements.add(0, TOP_PATH); 661 | } 662 | 663 | return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR); 664 | } 665 | 666 | /* 667 | * Compare two paths after normalization of them. 668 | * @param path1 first path for comparison 669 | * @param path2 second path for comparison 670 | * @return whether the two paths are equivalent after normalization 671 | */ 672 | public static boolean pathEquals(String path1, String path2) { 673 | return cleanPath(path1).equals(cleanPath(path2)); 674 | } 675 | 676 | /* 677 | * Parse the given {@code localeString} value into a {@link Locale}. 678 | *

This is the inverse operation of {@link Locale#toString Locale's toString}. 679 | * @param localeString the locale String, following {@code Locale's} 680 | * {@code toString()} format ("en", "en_UK", etc); 681 | * also accepts spaces as separators, as an alternative to underscores 682 | * @return a corresponding {@code Locale} instance 683 | * @throws IllegalArgumentException in case of an invalid locale specification 684 | */ 685 | public static Locale parseLocaleString(String localeString) { 686 | String[] parts = tokenizeToStringArray(localeString, "_ ", false, false); 687 | String language = (parts.length > 0 ? parts[0] : ""); 688 | String country = (parts.length > 1 ? parts[1] : ""); 689 | validateLocalePart(language); 690 | validateLocalePart(country); 691 | String variant = ""; 692 | if (parts.length > 2) { 693 | // There is definitely a variant, and it is everything after the country 694 | // code sans the separator between the country code and the variant. 695 | int endIndexOfCountryCode = localeString.indexOf(country, language.length()) + country.length(); 696 | // Strip off any leading '_' and whitespace, what's left is the variant. 697 | variant = trimLeadingWhitespace(localeString.substring(endIndexOfCountryCode)); 698 | if (variant.startsWith("_")) { 699 | variant = trimLeadingCharacter(variant, '_'); 700 | } 701 | } 702 | return (language.length() > 0 ? new Locale(language, country, variant) : null); 703 | } 704 | 705 | private static void validateLocalePart(String localePart) { 706 | for (int i = 0; i < localePart.length(); i++) { 707 | char ch = localePart.charAt(i); 708 | if (ch != '_' && ch != ' ' && !Character.isLetterOrDigit(ch)) { 709 | throw new IllegalArgumentException( 710 | "Locale part \"" + localePart + "\" contains invalid characters"); 711 | } 712 | } 713 | } 714 | 715 | /* 716 | * Determine the RFC 3066 compliant language tag, 717 | * as used for the HTTP "Accept-Language" header. 718 | * @param locale the Locale to transform to a language tag 719 | * @return the RFC 3066 compliant language tag as String 720 | */ 721 | public static String toLanguageTag(Locale locale) { 722 | return locale.getLanguage() + (hasText(locale.getCountry()) ? "-" + locale.getCountry() : ""); 723 | } 724 | 725 | /* 726 | * Parse the given {@code timeZoneString} value into a {@link TimeZone}. 727 | * @param timeZoneString the time zone String, following {@link TimeZone#getTimeZone(String)} 728 | * but throwing {@link IllegalArgumentException} in case of an invalid time zone specification 729 | * @return a corresponding {@link TimeZone} instance 730 | * @throws IllegalArgumentException in case of an invalid time zone specification 731 | */ 732 | public static TimeZone parseTimeZoneString(String timeZoneString) { 733 | TimeZone timeZone = TimeZone.getTimeZone(timeZoneString); 734 | if ("GMT".equals(timeZone.getID()) && !timeZoneString.startsWith("GMT")) { 735 | // We don't want that GMT fallback... 736 | throw new IllegalArgumentException("Invalid time zone specification '" + timeZoneString + "'"); 737 | } 738 | return timeZone; 739 | } 740 | 741 | 742 | //--------------------------------------------------------------------- 743 | // Convenience methods for working with String arrays 744 | //--------------------------------------------------------------------- 745 | 746 | /* 747 | * Append the given String to the given String array, returning a new array 748 | * consisting of the input array contents plus the given String. 749 | * @param array the array to append to (can be {@code null}) 750 | * @param str the String to append 751 | * @return the new array (never {@code null}) 752 | */ 753 | public static String[] addStringToArray(String[] array, String str) { 754 | if (ObjectUtils.isEmpty(array)) { 755 | return new String[] {str}; 756 | } 757 | String[] newArr = new String[array.length + 1]; 758 | System.arraycopy(array, 0, newArr, 0, array.length); 759 | newArr[array.length] = str; 760 | return newArr; 761 | } 762 | 763 | /* 764 | * Concatenate the given String arrays into one, 765 | * with overlapping array elements included twice. 766 | *

The order of elements in the original arrays is preserved. 767 | * @param array1 the first array (can be {@code null}) 768 | * @param array2 the second array (can be {@code null}) 769 | * @return the new array ({@code null} if both given arrays were {@code null}) 770 | */ 771 | public static String[] concatenateStringArrays(String[] array1, String[] array2) { 772 | if (ObjectUtils.isEmpty(array1)) { 773 | return array2; 774 | } 775 | if (ObjectUtils.isEmpty(array2)) { 776 | return array1; 777 | } 778 | String[] newArr = new String[array1.length + array2.length]; 779 | System.arraycopy(array1, 0, newArr, 0, array1.length); 780 | System.arraycopy(array2, 0, newArr, array1.length, array2.length); 781 | return newArr; 782 | } 783 | 784 | /* 785 | * Merge the given String arrays into one, with overlapping 786 | * array elements only included once. 787 | *

The order of elements in the original arrays is preserved 788 | * (with the exception of overlapping elements, which are only 789 | * included on their first occurrence). 790 | * @param array1 the first array (can be {@code null}) 791 | * @param array2 the second array (can be {@code null}) 792 | * @return the new array ({@code null} if both given arrays were {@code null}) 793 | */ 794 | public static String[] mergeStringArrays(String[] array1, String[] array2) { 795 | if (ObjectUtils.isEmpty(array1)) { 796 | return array2; 797 | } 798 | if (ObjectUtils.isEmpty(array2)) { 799 | return array1; 800 | } 801 | List result = new ArrayList(); 802 | result.addAll(Arrays.asList(array1)); 803 | for (String str : array2) { 804 | if (!result.contains(str)) { 805 | result.add(str); 806 | } 807 | } 808 | return toStringArray(result); 809 | } 810 | 811 | /* 812 | * Turn given source String array into sorted array. 813 | * @param array the source array 814 | * @return the sorted array (never {@code null}) 815 | */ 816 | public static String[] sortStringArray(String[] array) { 817 | if (ObjectUtils.isEmpty(array)) { 818 | return new String[0]; 819 | } 820 | Arrays.sort(array); 821 | return array; 822 | } 823 | 824 | /* 825 | * Copy the given Collection into a String array. 826 | * The Collection must contain String elements only. 827 | * @param collection the Collection to copy 828 | * @return the String array ({@code null} if the passed-in 829 | * Collection was {@code null}) 830 | */ 831 | public static String[] toStringArray(Collection collection) { 832 | if (collection == null) { 833 | return null; 834 | } 835 | return collection.toArray(new String[collection.size()]); 836 | } 837 | 838 | /* 839 | * Copy the given Enumeration into a String array. 840 | * The Enumeration must contain String elements only. 841 | * @param enumeration the Enumeration to copy 842 | * @return the String array ({@code null} if the passed-in 843 | * Enumeration was {@code null}) 844 | */ 845 | public static String[] toStringArray(Enumeration enumeration) { 846 | if (enumeration == null) { 847 | return null; 848 | } 849 | List list = Collections.list(enumeration); 850 | return list.toArray(new String[list.size()]); 851 | } 852 | 853 | /* 854 | * Trim the elements of the given String array, 855 | * calling {@code String.trim()} on each of them. 856 | * @param array the original String array 857 | * @return the resulting array (of the same size) with trimmed elements 858 | */ 859 | public static String[] trimArrayElements(String[] array) { 860 | if (ObjectUtils.isEmpty(array)) { 861 | return new String[0]; 862 | } 863 | String[] result = new String[array.length]; 864 | for (int i = 0; i < array.length; i++) { 865 | String element = array[i]; 866 | result[i] = (element != null ? element.trim() : null); 867 | } 868 | return result; 869 | } 870 | 871 | /* 872 | * Remove duplicate Strings from the given array. 873 | * Also sorts the array, as it uses a TreeSet. 874 | * @param array the String array 875 | * @return an array without duplicates, in natural sort order 876 | */ 877 | public static String[] removeDuplicateStrings(String[] array) { 878 | if (ObjectUtils.isEmpty(array)) { 879 | return array; 880 | } 881 | Set set = new TreeSet(); 882 | for (String element : array) { 883 | set.add(element); 884 | } 885 | return toStringArray(set); 886 | } 887 | 888 | /* 889 | * Take an array Strings and split each element based on the given delimiter. 890 | * A {@code Properties} instance is then generated, with the left of the 891 | * delimiter providing the key, and the right of the delimiter providing the value. 892 | *

Will trim both the key and value before adding them to the 893 | * {@code Properties} instance. 894 | * @param array the array to process 895 | * @param delimiter to split each element using (typically the equals symbol) 896 | * @return a {@code Properties} instance representing the array contents, 897 | * or {@code null} if the array to process was null or empty 898 | */ 899 | public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) { 900 | return splitArrayElementsIntoProperties(array, delimiter, null); 901 | } 902 | 903 | /* 904 | * Take an array Strings and split each element based on the given delimiter. 905 | * A {@code Properties} instance is then generated, with the left of the 906 | * delimiter providing the key, and the right of the delimiter providing the value. 907 | *

Will trim both the key and value before adding them to the 908 | * {@code Properties} instance. 909 | * @param array the array to process 910 | * @param delimiter to split each element using (typically the equals symbol) 911 | * @param charsToDelete one or more characters to remove from each element 912 | * prior to attempting the split operation (typically the quotation mark 913 | * symbol), or {@code null} if no removal should occur 914 | * @return a {@code Properties} instance representing the array contents, 915 | * or {@code null} if the array to process was {@code null} or empty 916 | */ 917 | public static Properties splitArrayElementsIntoProperties( 918 | String[] array, String delimiter, String charsToDelete) { 919 | 920 | if (ObjectUtils.isEmpty(array)) { 921 | return null; 922 | } 923 | Properties result = new Properties(); 924 | for (String element : array) { 925 | if (charsToDelete != null) { 926 | element = deleteAny(element, charsToDelete); 927 | } 928 | String[] splittedElement = split(element, delimiter); 929 | if (splittedElement == null) { 930 | continue; 931 | } 932 | result.setProperty(splittedElement[0].trim(), splittedElement[1].trim()); 933 | } 934 | return result; 935 | } 936 | 937 | /* 938 | * 939 | * @description :获得以 ",; \t\n"分割的字符数组 940 | * @author : hiwepy 941 | * @date :Dec 17, 2015 9:07:47 PM 942 | * @param str 943 | * @return 944 | */ 945 | public static String[] tokenizeToStringArray(String str) { 946 | return tokenizeToStringArray(str, CONFIG_LOCATION_DELIMITERS, true, true); 947 | } 948 | 949 | /* 950 | * Tokenize the given String into a String array via a StringTokenizer. 951 | * Trims tokens and omits empty tokens. 952 | *

The given delimiters string is supposed to consist of any number of 953 | * delimiter characters. Each of those characters can be used to separate 954 | * tokens. A delimiter is always a single character; for multi-character 955 | * delimiters, consider using {@code delimitedListToStringArray} 956 | * @param str the String to tokenize 957 | * @param delimiters the delimiter characters, assembled as String 958 | * (each of those characters is individually considered as delimiter). 959 | * @return an array of the tokens 960 | * @see java.util.StringTokenizer 961 | * @see String#trim() 962 | * @see #delimitedListToStringArray 963 | */ 964 | public static String[] tokenizeToStringArray(String str, String delimiters) { 965 | return tokenizeToStringArray(str, delimiters, true, true); 966 | } 967 | 968 | /* 969 | * Tokenize the given String into a String array via a StringTokenizer. 970 | *

The given delimiters string is supposed to consist of any number of 971 | * delimiter characters. Each of those characters can be used to separate 972 | * tokens. A delimiter is always a single character; for multi-character 973 | * delimiters, consider using {@code delimitedListToStringArray} 974 | * @param str the String to tokenize 975 | * @param delimiters the delimiter characters, assembled as String 976 | * (each of those characters is individually considered as delimiter) 977 | * @param trimTokens trim the tokens via String's {@code trim} 978 | * @param ignoreEmptyTokens omit empty tokens from the result array 979 | * (only applies to tokens that are empty after trimming; StringTokenizer 980 | * will not consider subsequent delimiters as token in the first place). 981 | * @return an array of the tokens ({@code null} if the input String 982 | * was {@code null}) 983 | * @see java.util.StringTokenizer 984 | * @see String#trim() 985 | * @see #delimitedListToStringArray 986 | */ 987 | public static String[] tokenizeToStringArray( 988 | String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { 989 | 990 | if (str == null) { 991 | return null; 992 | } 993 | StringTokenizer st = new StringTokenizer(str, delimiters); 994 | List tokens = new ArrayList(); 995 | while (st.hasMoreTokens()) { 996 | String token = st.nextToken(); 997 | if (trimTokens) { 998 | token = token.trim(); 999 | } 1000 | if (!ignoreEmptyTokens || token.length() > 0) { 1001 | tokens.add(token); 1002 | } 1003 | } 1004 | return toStringArray(tokens); 1005 | } 1006 | 1007 | /* 1008 | * Take a String which is a delimited list and convert it to a String array. 1009 | *

A single delimiter can consists of more than one character: It will still 1010 | * be considered as single delimiter string, rather than as bunch of potential 1011 | * delimiter characters - in contrast to {@code tokenizeToStringArray}. 1012 | * @param str the input String 1013 | * @param delimiter the delimiter between elements (this is a single delimiter, 1014 | * rather than a bunch individual delimiter characters) 1015 | * @return an array of the tokens in the list 1016 | * @see #tokenizeToStringArray 1017 | */ 1018 | public static String[] delimitedListToStringArray(String str, String delimiter) { 1019 | return delimitedListToStringArray(str, delimiter, null); 1020 | } 1021 | 1022 | /* 1023 | * Take a String which is a delimited list and convert it to a String array. 1024 | *

A single delimiter can consists of more than one character: It will still 1025 | * be considered as single delimiter string, rather than as bunch of potential 1026 | * delimiter characters - in contrast to {@code tokenizeToStringArray}. 1027 | * @param str the input String 1028 | * @param delimiter the delimiter between elements (this is a single delimiter, 1029 | * rather than a bunch individual delimiter characters) 1030 | * @param charsToDelete a set of characters to delete. Useful for deleting unwanted 1031 | * line breaks: e.g. "\r\n\f" will delete all new lines and line feeds in a String. 1032 | * @return an array of the tokens in the list 1033 | * @see #tokenizeToStringArray 1034 | */ 1035 | public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) { 1036 | if (str == null) { 1037 | return new String[0]; 1038 | } 1039 | if (delimiter == null) { 1040 | return new String[] {str}; 1041 | } 1042 | List result = new ArrayList(); 1043 | if ("".equals(delimiter)) { 1044 | for (int i = 0; i < str.length(); i++) { 1045 | result.add(deleteAny(str.substring(i, i + 1), charsToDelete)); 1046 | } 1047 | } 1048 | else { 1049 | int pos = 0; 1050 | int delPos; 1051 | while ((delPos = str.indexOf(delimiter, pos)) != -1) { 1052 | result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); 1053 | pos = delPos + delimiter.length(); 1054 | } 1055 | if (str.length() > 0 && pos <= str.length()) { 1056 | // Add rest of String, but not in case of empty input. 1057 | result.add(deleteAny(str.substring(pos), charsToDelete)); 1058 | } 1059 | } 1060 | return toStringArray(result); 1061 | } 1062 | 1063 | /* 1064 | * Convert a CSV list into an array of Strings. 1065 | * @param str the input String 1066 | * @return an array of Strings, or the empty array in case of empty input 1067 | */ 1068 | public static String[] commaDelimitedListToStringArray(String str) { 1069 | return delimitedListToStringArray(str, ","); 1070 | } 1071 | 1072 | /* 1073 | * Convenience method to convert a CSV string list to a set. 1074 | * Note that this will suppress duplicates. 1075 | * @param str the input String 1076 | * @return a Set of String entries in the list 1077 | */ 1078 | public static Set commaDelimitedListToSet(String str) { 1079 | Set set = new TreeSet(); 1080 | String[] tokens = commaDelimitedListToStringArray(str); 1081 | for (String token : tokens) { 1082 | set.add(token); 1083 | } 1084 | return set; 1085 | } 1086 | 1087 | /* 1088 | * Convenience method to return a Collection as a delimited (e.g. CSV) 1089 | * String. E.g. useful for {@code toString()} implementations. 1090 | * @param coll the Collection to display 1091 | * @param delim the delimiter to use (probably a ",") 1092 | * @param prefix the String to start each element with 1093 | * @param suffix the String to end each element with 1094 | * @return the delimited String 1095 | */ 1096 | public static String collectionToDelimitedString(Collection coll, String delim, String prefix, String suffix) { 1097 | if (CollectionUtils.isEmpty(coll)) { 1098 | return ""; 1099 | } 1100 | StringBuilder sb = new StringBuilder(); 1101 | Iterator it = coll.iterator(); 1102 | while (it.hasNext()) { 1103 | sb.append(prefix).append(it.next()).append(suffix); 1104 | if (it.hasNext()) { 1105 | sb.append(delim); 1106 | } 1107 | } 1108 | return sb.toString(); 1109 | } 1110 | 1111 | /* 1112 | * Convenience method to return a Collection as a delimited (e.g. CSV) 1113 | * String. E.g. useful for {@code toString()} implementations. 1114 | * @param coll the Collection to display 1115 | * @param delim the delimiter to use (probably a ",") 1116 | * @return the delimited String 1117 | */ 1118 | public static String collectionToDelimitedString(Collection coll, String delim) { 1119 | return collectionToDelimitedString(coll, delim, "", ""); 1120 | } 1121 | 1122 | /* 1123 | * Convenience method to return a Collection as a CSV String. 1124 | * E.g. useful for {@code toString()} implementations. 1125 | * @param coll the Collection to display 1126 | * @return the delimited String 1127 | */ 1128 | public static String collectionToCommaDelimitedString(Collection coll) { 1129 | return collectionToDelimitedString(coll, ","); 1130 | } 1131 | 1132 | /* 1133 | * Convenience method to return a String array as a delimited (e.g. CSV) 1134 | * String. E.g. useful for {@code toString()} implementations. 1135 | * @param arr the array to display 1136 | * @param delim the delimiter to use (probably a ",") 1137 | * @return the delimited String 1138 | */ 1139 | public static String arrayToDelimitedString(Object[] arr, String delim) { 1140 | if (ObjectUtils.isEmpty(arr)) { 1141 | return ""; 1142 | } 1143 | if (arr.length == 1) { 1144 | return ObjectUtils.nullSafeToString(arr[0]); 1145 | } 1146 | StringBuilder sb = new StringBuilder(); 1147 | for (int i = 0; i < arr.length; i++) { 1148 | if (i > 0) { 1149 | sb.append(delim); 1150 | } 1151 | sb.append(arr[i]); 1152 | } 1153 | return sb.toString(); 1154 | } 1155 | 1156 | /* 1157 | * Convenience method to return a String array as a CSV String. 1158 | * E.g. useful for {@code toString()} implementations. 1159 | * @param arr the array to display 1160 | * @return the delimited String 1161 | */ 1162 | public static String arrayToCommaDelimitedString(Object[] arr) { 1163 | return arrayToDelimitedString(arr, ","); 1164 | } 1165 | 1166 | /* 1167 | * 生成查询字串Map 1168 | * 1169 | * @param str 1170 | * @return 1171 | */ 1172 | public static Map getMapFromQueryParamString(String str) { 1173 | Map param = new HashMap(); 1174 | String keyValues[] = str.split("`"); 1175 | for (int i = 0; i < keyValues.length; i++) { 1176 | 1177 | } 1178 | return param; 1179 | } 1180 | 1181 | /* 1182 | * 全替换 1183 | * 1184 | * @param src 1185 | * 替换字串 1186 | * @param tar 1187 | * 替换目标 1188 | * @param str 1189 | * 主字串 1190 | * @return 1191 | */ 1192 | public static String replaceAll(String src, String tar, String str) { 1193 | StringBuilder sb = new StringBuilder(); 1194 | byte bytesSrc[] = src.getBytes(); 1195 | 1196 | byte bytes[] = str.getBytes(); 1197 | int point = 0; 1198 | for (int i = 0; i < bytes.length; i++) { 1199 | 1200 | if (isStartWith(bytes, i, bytesSrc, 0)) { 1201 | 1202 | sb.append(new String(bytes, point, i)); 1203 | sb.append(tar); 1204 | i += bytesSrc.length; 1205 | point = i; 1206 | } 1207 | 1208 | } 1209 | sb.append(new String(bytes, point, bytes.length)); 1210 | return sb.toString(); 1211 | } 1212 | 1213 | /* 1214 | * 1215 | * @param bytesSrc 1216 | * @param bytesTar 1217 | * @return 1218 | */ 1219 | private static boolean isStartWith(byte bytesSrc[], int startSrc, 1220 | byte bytesTar[], int startTar) { 1221 | for (int j = startTar; j < bytesTar.length; j++) { 1222 | if (bytesSrc[startSrc + j] != bytesTar[j]) { 1223 | return false; 1224 | } 1225 | } 1226 | return true; 1227 | } 1228 | 1229 | /* 1230 | * 取中文拼音首字符 1231 | * 1232 | * @param str 1233 | * @return 1234 | */ 1235 | public static char getFirstLetterFromChinessWord(String str) { 1236 | char result = '*'; 1237 | String temp = str.toUpperCase(); 1238 | try { 1239 | byte[] bytes = temp.getBytes("gbk"); 1240 | if (bytes[0] < 128 && bytes[0] > 0) { 1241 | return (char) bytes[0]; 1242 | } 1243 | 1244 | int gbkIndex = 0; 1245 | 1246 | for (int i = 0; i < bytes.length; i++) { 1247 | bytes[i] -= 160; 1248 | } 1249 | gbkIndex = bytes[0] * 100 + bytes[1]; 1250 | for (int i = 0; i < allEnglishLetter.length; i++) { 1251 | if (i == 22) { 1252 | // System.out.println(allEnglishLetter.length 1253 | // +" "+allChineseScope.length); 1254 | } 1255 | if (gbkIndex >= allChineseScope[i] 1256 | && gbkIndex < allChineseScope[i + 1]) { 1257 | result = allEnglishLetter[i]; 1258 | break; 1259 | } 1260 | } 1261 | 1262 | } catch (Exception e) { 1263 | 1264 | } 1265 | return result; 1266 | } 1267 | 1268 | /* 1269 | * 字串分割 1270 | * 1271 | * @param src 1272 | * @param letter 1273 | * @return 1274 | */ 1275 | public static String[] split(String src, char letter) { 1276 | if (src == null) { 1277 | return new String[0]; 1278 | } 1279 | List ret = new ArrayList(); 1280 | byte bytes[] = src.getBytes(); 1281 | int curPoint = 0; 1282 | for (int i = 0; i < bytes.length; i++) { 1283 | if (bytes[i] == letter) { 1284 | String s = new String(bytes, curPoint, i - curPoint); 1285 | ret.add(s); 1286 | curPoint = i + 1; 1287 | } 1288 | } 1289 | if (ret.size() == 0) { 1290 | return new String[] { src }; 1291 | } 1292 | // ret.add(new String(bytes, curPoint, src.length() - curPoint)); 1293 | String[] retStr = new String[ret.size()]; 1294 | for (int i = 0; i < ret.size(); i++) { 1295 | retStr[i] = ret.get(i); 1296 | } 1297 | return retStr; 1298 | } 1299 | 1300 | /* 1301 | * Split a String at the first occurrence of the delimiter. 1302 | * Does not include the delimiter in the result. 1303 | * @param toSplit the string to split 1304 | * @param delimiter to split the string up with 1305 | * @return a two element array with index 0 being before the delimiter, and 1306 | * index 1 being after the delimiter (neither element includes the delimiter); 1307 | * or {@code null} if the delimiter wasn't found in the given input String 1308 | */ 1309 | public static String[] split(String toSplit, String delimiter) { 1310 | if (!hasLength(toSplit) || !hasLength(delimiter)) { 1311 | return null; 1312 | } 1313 | int offset = toSplit.indexOf(delimiter); 1314 | if (offset < 0) { 1315 | return null; 1316 | } 1317 | String beforeDelimiter = toSplit.substring(0, offset); 1318 | String afterDelimiter = toSplit.substring(offset + delimiter.length()); 1319 | return new String[] {beforeDelimiter, afterDelimiter}; 1320 | } 1321 | 1322 | public static String[] splits(String toSplit, String regex) { 1323 | if (!hasLength(toSplit) || !hasLength(regex)) { 1324 | return new String[] {}; 1325 | } 1326 | return toSplit.split(regex); 1327 | } 1328 | 1329 | /* 1330 | * 去除最后一个字符 1331 | * 1332 | * @param str 1333 | * @return 1334 | */ 1335 | public static String removeLast(String str) { 1336 | if (isNull(str)) { 1337 | return str; 1338 | } 1339 | return str.substring(0, str.length() - 1); 1340 | 1341 | } 1342 | 1343 | /* 1344 | * 为字符串的每个元素增加单引号,供sql语句调用 如字符串"123,567"变成"'123','567'" 1345 | * 1346 | * @param str 1347 | * @return 1348 | */ 1349 | public static String addQuotation(String str) { 1350 | if (str == null) { 1351 | return null; 1352 | } 1353 | String newStr = ""; 1354 | String[] strs = split(str, ","); 1355 | for (int i = 0; i < strs.length; i++) { 1356 | if (i > 0) { 1357 | newStr += ","; 1358 | } 1359 | newStr += "'" + strs[i] + "'"; 1360 | } 1361 | return newStr; 1362 | 1363 | } 1364 | 1365 | /* 1366 | * list转string数组 1367 | * 1368 | * @param list 1369 | * @return String[] 1370 | */ 1371 | public static String[] listToArray(List list) { 1372 | String[] strs = new String[list.size()]; 1373 | return list.toArray(strs); 1374 | } 1375 | 1376 | /* 1377 | * list转string字符串,以符号分隔 1378 | * 1379 | * @param list 1380 | * @param separator 1381 | * @return String 1382 | */ 1383 | public static String listToString(List list, String separator) { 1384 | return StringUtils.join(listToArray(list), separator); 1385 | } 1386 | 1387 | /* 1388 | * 生成随即密码 1389 | * 1390 | * @author 来自网上 1391 | * @param pwd_len 1392 | * 生成的密码的总长度 1393 | * @return 密码的字符串 1394 | */ 1395 | public static String genRandomNum(int pwd_len) { 1396 | // 36是因为数组是从0开始的,26个字母+10个数字 + 下划线 1397 | final int maxNum = 37; 1398 | int i; // 生成的随机数 1399 | int count = 0; // 生成的密码的长度 1400 | char[] str = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 1401 | 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 1402 | 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', 1403 | '9', '_' }; 1404 | StringBuilder pwd = new StringBuilder(""); 1405 | Random r = new Random(); 1406 | while (count < pwd_len) { 1407 | // 生成随机数,取绝对值,防止生成负数, 1408 | i = Math.abs(r.nextInt(maxNum)); // 生成的数最大为36-1 1409 | if (i >= 0 && i < str.length) { 1410 | pwd.append(str[i]); 1411 | count++; 1412 | } 1413 | } 1414 | return pwd.toString(); 1415 | } 1416 | 1417 | /* 1418 | * 将传入字符串改为非NULL值. 1419 | * 1420 | * @param str 1421 | * @return 传入NULL,返回空"". 1422 | */ 1423 | public static String killNull(String str) { 1424 | if (str == null) { 1425 | return ""; 1426 | } 1427 | return str; 1428 | } 1429 | 1430 | /* 1431 | * 1432 | * @description: 圆括号()包裹 1433 | * @author : hiwepy 1434 | * @date : 2014-4-29 1435 | * @time : 下午03:11:57 1436 | * @param source 1437 | * @return 1438 | */ 1439 | public static String parentheses(String source) { 1440 | return (source != null ? "(" + source + ")" : null); 1441 | } 1442 | 1443 | /* 1444 | * 1445 | * @description: 方括号[]包裹 1446 | * @author : hiwepy 1447 | * @date : 2014-4-29 1448 | * @time : 下午03:11:57 1449 | * @param source 1450 | * @return 1451 | */ 1452 | public static String brackets(String source) { 1453 | return (source != null ? "[" + source + "]" : null); 1454 | } 1455 | 1456 | public static String ditto(String source) { 1457 | return (source != null ? "\"" + source + "\"" : null); 1458 | } 1459 | 1460 | /* 1461 | * Quote the given String with single quotes. 1462 | * 1463 | * @param str 1464 | * the input String (e.g. "myString") 1465 | * @return the quoted String (e.g. "'myString'"), or 1466 | * null if the input was null 1467 | */ 1468 | public static String quote(String str) { 1469 | return (str != null ? "'" + str + "'" : null); 1470 | } 1471 | 1472 | /* 1473 | * 1474 | * @description: 将String集合元素用'包围,并拼接 1475 | * @author : hiwepy 1476 | * @date : 2014-4-29 1477 | * @time : 下午02:13:09 1478 | * @param list 1479 | * @param separator 1480 | * @return 1481 | */ 1482 | public static String quote(String[] array, String separator) { 1483 | if (null != array && array.length != 0) { 1484 | String[] last = new String[array.length]; 1485 | for (int i = 0; i < array.length; i++) { 1486 | last[i] = StringUtils.quote(array[i]); 1487 | } 1488 | return StringUtils.join(last, separator); 1489 | } else { 1490 | return ""; 1491 | } 1492 | 1493 | } 1494 | 1495 | /* 1496 | * Turn the given Object into a String with single quotes if it is a String; 1497 | * keeping the Object as-is else. 1498 | * 1499 | * @param obj 1500 | * the input Object (e.g. "myString") 1501 | * @return the quoted String (e.g. "'myString'"), or the input object as-is 1502 | * if not a String 1503 | */ 1504 | public static Object quoteIfString(Object obj) { 1505 | return (obj instanceof String ? quote((String) obj) : obj); 1506 | } 1507 | 1508 | /* 1509 | * 1510 | * @param string 1511 | * @return 1512 | * @description: 把一个字符的非Alpha字符都去掉,String string = "1\r\n1\r\n";-->结果:"11"; 1513 | * @return: String 1514 | * @method: trimToAlphaString 1515 | * @author: hiwepy 1516 | * @version: 2010-12-15 下午09:06:02 1517 | */ 1518 | public static String trimToAlphaString(String string) { 1519 | if (string == null || string.length() == 0) { 1520 | return ""; 1521 | } 1522 | return string.replaceAll("[^\\w]", ""); 1523 | } 1524 | 1525 | /* 1526 | * 1527 | * @param string 1528 | * @return 1529 | * @description: 把一个字符的非Alpha字符都去掉,并返回每个字符的数组,String string = 1530 | * "1\r\n1\r\n";-->结果:new String[]{"1","1"}; 1531 | * 1532 | * @return: String[] 1533 | * @method: trimToAlphaStrings 1534 | * @author: hiwepy 1535 | * @version: 2010-12-15 下午09:06:31 1536 | */ 1537 | public static String[] trimToAlphaStrings(String string) { 1538 | if (string == null || string.length() == 0) { 1539 | return new String[0]; 1540 | } 1541 | char[] chars = string.replaceAll("[^\\w]", "").toCharArray(); 1542 | String[] strs = new String[chars.length]; 1543 | for (int i = 0; i < chars.length; i++) { 1544 | strs[i] = String.valueOf(chars[i]); 1545 | } 1546 | return strs; 1547 | } 1548 | 1549 | public static String trimToString(String str) { 1550 | if (str == null || str.trim().length() == 0) { 1551 | return null; 1552 | } else { 1553 | return str.trim(); 1554 | } 1555 | } 1556 | 1557 | } --------------------------------------------------------------------------------