├── .gitignore ├── README.md ├── call.png ├── louie-account ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── louie │ │ └── account │ │ └── service │ │ └── UserService.java │ ├── resources │ ├── META-INF │ │ └── dubbo │ │ │ └── com.alibaba.dubbo.rpc.Filter │ ├── application-dubbo.xml │ ├── application-mvc.xml │ ├── applicationContext.xml │ ├── brave.properties │ └── log4j.properties │ └── webapp │ └── WEB-INF │ └── web.xml ├── louie-common ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── louie │ ├── common │ ├── config │ │ └── ZipkinConfig.java │ ├── constant │ │ └── ZipkinConstants.java │ └── dubbo │ │ ├── DrpcClientInterceptor.java │ │ ├── DrpcServerInterceptor.java │ │ ├── RpcManager.java │ │ └── RpcManagerImpl.java │ ├── core │ ├── CallParams.java │ ├── CallResult.java │ ├── CommonAdapter.java │ ├── Service.java │ ├── ServiceCall.java │ ├── ServiceCallAdapter.java │ ├── ServiceDispacher.java │ ├── ServiceDispacherImpl.java │ └── SpringApplicationContext.java │ ├── exception │ └── LouieException.java │ └── utils │ ├── JsonUtils.java │ └── StringUtils.java ├── louie-order ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── louie │ │ └── order │ │ ├── dto │ │ └── OrderDto.java │ │ └── service │ │ └── OrderService.java │ ├── resources │ ├── META-INF │ │ └── dubbo │ │ │ └── com.alibaba.dubbo.rpc.Filter │ ├── application-dubbo.xml │ ├── application-mvc.xml │ ├── applicationContext.xml │ ├── brave.properties │ └── log4j.properties │ └── webapp │ └── WEB-INF │ └── web.xml ├── louie-webapi ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── louie │ │ │ ├── HttpTraceBeans.java │ │ │ ├── controller │ │ │ ├── ApiController.java │ │ │ └── ServletAdapter.java │ │ │ └── trace │ │ │ ├── CusHttpServerRequestAdapter.java │ │ │ ├── CusHttpServletHandlerInterceptor.java │ │ │ └── WebTracingConfiguration.java │ ├── resources │ │ ├── META-INF │ │ │ └── dubbo │ │ │ │ └── com.alibaba.dubbo.rpc.Filter │ │ ├── application-dubbo.xml │ │ ├── application-mvc.xml │ │ ├── applicationContext.xml │ │ ├── brave.properties │ │ └── log4j.properties │ └── webapp │ │ └── WEB-INF │ │ └── web.xml │ └── test │ └── resources │ └── log4j.properties ├── pom.xml ├── request-params.png ├── service.png └── trace-info.png /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | dependency-reduced-pom.xml 8 | buildNumber.properties 9 | .mvn/timing.properties 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 通过模拟订单详情的查询,演示系统的调用链跟踪,跟踪信息包括调用服务及请求参数。 2 | 涉及的各工程作用: 3 | 4 | louie-webapi:向外部提供http调用,返回json; 5 | 6 | louie-order:模拟订单系统,查询订单详情; 7 | 8 | louie-account:模拟账户系统,查询账户信息,调用链如图 9 | ![调用流程](https://github.com/blacklau/http-dubbo-zipkin/blob/master/call.png) 10 | 11 | 使用: 12 | 13 | 1、下载zipkin并运行 14 | wget -O zipkin.jar 'https://search.maven.org/remote_content?g=io.zipkin.java&a=zipkin-server&v=LATEST&c=exec' 15 | java -jar zipkin.jar 16 | 17 | 2、下载本项目并安装 18 | mvn install 19 | 20 | 3、将生成的三个war包部署到Tomcat 21 | 22 | 4、http调用 23 | http://localhost:8080/louie-webapi/service.do?service=order.customer.orderInfo&data={"token":"jkfldjsliewklkklls","id":89} 24 | 25 |   service参数为Service注解值加上方法名,为对应提供的rpc服务,data参数为rpc请求参数 26 |   ![service](https://github.com/blacklau/http-dubbo-zipkin/blob/master/service.png) 27 | 28 |   打开zipkin ui,http://localhost:9411/,查看调用链信息, 29 |   ![跟踪信息](https://github.com/blacklau/http-dubbo-zipkin/blob/master/trace-info.png) 30 | 31 |    span信息,带请求参数 32 |    ![span信息,带请求参数](https://github.com/blacklau/http-dubbo-zipkin/blob/master/request-params.png) 33 | 34 | -------------------------------------------------------------------------------- /call.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacklau/http-dubbo-zipkin/7c38dbe53e95afbb38612ec5c8e39c848769f00a/call.png -------------------------------------------------------------------------------- /louie-account/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | louie 5 | louie-account 6 | war 7 | 0.0.1-SNAPSHOT 8 | louie-account 9 | 10 | 11 | 12 | http-dubbo-zipkin 13 | louie 14 | 0.0.1-SNAPSHOT 15 | 16 | 17 | 18 | UTF-8 19 | 4.3.3.RELEASE 20 | 8.1.20.v20160902 21 | 4.0.6 22 | 0.6.12 23 | 2.8.41 24 | 25 | 26 | 27 | 28 | louie 29 | louie-common 30 | 0.0.1-SNAPSHOT 31 | 32 | 33 | io.zipkin.brave 34 | brave-core-spring 35 | ${brave.version} 36 | 37 | 38 | 39 | 40 | 41 | account 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-eclipse-plugin 46 | 2.9 47 | 48 | true 49 | false 50 | 2.0 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | 2.3.2 57 | 58 | 1.7 59 | 1.7 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /louie-account/src/main/java/com/louie/account/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.louie.account.service; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.springframework.context.annotation.Lazy; 7 | import org.springframework.stereotype.Service; 8 | 9 | import com.louie.core.CallParams; 10 | import com.louie.core.CallResult; 11 | 12 | /** 13 | * 14 | * @author 15 | * 16 | */ 17 | @Lazy 18 | @Service("account.user") 19 | public class UserService{ 20 | 21 | /** 22 | * 用户信息 23 | * @param params 24 | * @return 25 | */ 26 | public CallResult info(CallParams params){ 27 | 28 | CallResult cr = new CallResult(); 29 | Map data = new HashMap(); 30 | data.put("userId", "ujklskkkkliiekkikhhjkj"); 31 | cr.setCode(0); 32 | cr.setData(data); 33 | 34 | return cr; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /louie-account/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter: -------------------------------------------------------------------------------- 1 | traceFilters=com.louie.common.dubbo.DrpcServerInterceptor 2 | traceFilterc=com.louie.common.dubbo.DrpcClientInterceptor -------------------------------------------------------------------------------- /louie-account/src/main/resources/application-dubbo.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /louie-account/src/main/resources/application-mvc.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /louie-account/src/main/resources/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 13 | 14 | 15 | 16 | classpath*:brave.properties 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /louie-account/src/main/resources/brave.properties: -------------------------------------------------------------------------------- 1 | brave.name=account 2 | http.sender.address=http://127.0.0.1:9411/api/v1/spans -------------------------------------------------------------------------------- /louie-account/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.appender.Stdout=org.apache.log4j.ConsoleAppender 2 | log4j.appender.Stdout.layout=org.apache.log4j.PatternLayout 3 | log4j.appender.Stdout.layout.conversionPattern=%-5p - %-26.26c{1} - %m\n 4 | 5 | log4j.rootLogger=INFO,Stdout 6 | 7 | log4j.logger.org.apache.wicket=INFO 8 | log4j.logger.org.apache.wicket.protocol.http.HttpSessionStore=INFO 9 | log4j.logger.org.apache.wicket.version=INFO 10 | log4j.logger.org.apache.wicket.RequestCycle=INFO -------------------------------------------------------------------------------- /louie-account/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | Spring Web MVC Application 7 | 8 | 9 | mvc-dispatcher 10 | org.springframework.web.servlet.DispatcherServlet 11 | 12 | 13 | contextConfigLocation 14 | classpath:applicationContext.xml 15 | 16 | 17 | 1 18 | 19 | 20 | 21 | mvc-dispatcher 22 | /rest/* 23 | 24 | 25 | 26 | contextConfigLocation 27 | classpath:applicationContext.xml 28 | 29 | 30 | 31 | org.springframework.web.context.ContextLoaderListener 32 | 33 | 34 | -------------------------------------------------------------------------------- /louie-common/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | louie 5 | louie-common 6 | jar 7 | 0.0.1-SNAPSHOT 8 | louie-common 9 | http://maven.apache.org 10 | 11 | 12 | http-dubbo-zipkin 13 | louie 14 | 0.0.1-SNAPSHOT 15 | 16 | 17 | 18 | UTF-8 19 | 4.3.3.RELEASE 20 | 8.1.20.v20160902 21 | 4.0.6 22 | 0.6.12 23 | 2.4.1 24 | 2.5.3 25 | 26 | 27 | 28 | 29 | io.zipkin.brave 30 | brave-core-spring 31 | ${brave.version} 32 | 33 | 34 | io.zipkin.reporter 35 | zipkin-sender-okhttp3 36 | ${zipkin-reporter.version} 37 | 38 | 39 | io.zipkin.reporter 40 | zipkin-sender-libthrift 41 | ${zipkin-reporter.version} 42 | 43 | 44 | io.zipkin.reporter 45 | zipkin-sender-kafka08 46 | ${zipkin-reporter.version} 47 | 48 | 49 | io.zipkin.brave 50 | brave-spring-web-servlet-interceptor 51 | ${brave.version} 52 | 53 | 54 | io.zipkin.brave 55 | brave-spring-resttemplate-interceptors 56 | ${brave.version} 57 | 58 | 59 | org.springframework 60 | spring-core 61 | ${spring.version} 62 | 63 | 64 | org.springframework 65 | spring-context 66 | ${spring.version} 67 | 68 | 69 | org.springframework 70 | spring-beans 71 | ${spring.version} 72 | 73 | 74 | org.springframework 75 | spring-web 76 | ${spring.version} 77 | 78 | 79 | org.springframework 80 | spring-webmvc 81 | ${spring.version} 82 | 83 | 84 | org.eclipse.jetty 85 | jetty-server 86 | ${jetty.version} 87 | test 88 | 89 | 90 | org.eclipse.jetty 91 | jetty-webapp 92 | ${jetty.version} 93 | test 94 | 95 | 96 | log4j 97 | log4j 98 | 1.2.17 99 | 100 | 101 | org.slf4j 102 | slf4j-log4j12 103 | 1.7.21 104 | 105 | 106 | org.slf4j 107 | jcl-over-slf4j 108 | 1.7.21 109 | 110 | 111 | 112 | com.fasterxml.jackson.core 113 | jackson-annotations 114 | ${fasterxml-jackson-version} 115 | 116 | 117 | 118 | com.fasterxml.jackson.core 119 | jackson-core 120 | ${fasterxml-jackson-version} 121 | 122 | 123 | 124 | com.fasterxml.jackson.core 125 | jackson-databind 126 | ${fasterxml-jackson-version} 127 | 128 | 129 | 130 | com.alibaba 131 | dubbo 132 | ${dubbo-version} 133 | 134 | 135 | aopalliance 136 | aopalliance 137 | 138 | 139 | commons-pool 140 | commons-pool 141 | 142 | 143 | org.apache.curator 144 | curator-client 145 | 146 | 147 | org.apache.curator 148 | curator-framework 149 | 150 | 151 | com.google.guava 152 | guava 153 | 154 | 155 | 156 | javax.servlet 157 | javax.servlet-api 158 | 159 | 160 | io.netty 161 | netty 162 | 163 | 164 | 165 | spring 166 | org.springframework 167 | 168 | 169 | 170 | 171 | javassist 172 | javassist 173 | 3.6.0.GA 174 | 175 | 176 | org.jboss.netty 177 | netty 178 | 3.2.5.Final 179 | 180 | 181 | com.101tec 182 | zkclient 183 | 0.3 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/common/config/ZipkinConfig.java: -------------------------------------------------------------------------------- 1 | package com.louie.common.config; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Properties; 6 | 7 | import org.springframework.beans.BeansException; 8 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 9 | import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; 10 | 11 | /** 12 | * 读取系统配置文件中值 (*.properties) 13 | * 14 | * @author 15 | * 16 | */ 17 | public class ZipkinConfig extends PropertyPlaceholderConfigurer { 18 | 19 | private static Map ctxPropertiesMap; 20 | 21 | @Override 22 | protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) 23 | throws BeansException { 24 | super.processProperties(beanFactoryToProcess, props); 25 | ctxPropertiesMap = new HashMap(); 26 | for (Object key : props.keySet()) { 27 | String keyStr = key.toString(); 28 | String value = props.getProperty(keyStr); 29 | ctxPropertiesMap.put(keyStr, value); 30 | } 31 | } 32 | 33 | /** 获取配置值 */ 34 | private static String getValue(String name) { 35 | String value = ctxPropertiesMap.get(name); 36 | if (value == null) 37 | return null; 38 | return value; 39 | } 40 | 41 | /** 42 | * 取出String类型的Property
43 | * 44 | * 如果没配置,默认返回空字符串 45 | */ 46 | public static String getProperty(String key) { 47 | return getProperty(key, ""); 48 | } 49 | 50 | /** 51 | * 取出String类型的Property,如果都為Null則返回Default值. 52 | */ 53 | public static String getProperty(String key, String defaultValue) { 54 | String value = getValue(key); 55 | return value != null ? value : defaultValue; 56 | } 57 | 58 | /** 59 | * 取出Integer类型的Property
60 | * 61 | * 如果没配置,默认返回0 62 | */ 63 | public static Integer getInteger(String key) { 64 | return getInteger(key, 0); 65 | } 66 | 67 | /** 68 | * 取出Integer类型的Property.如果都為Null則返回Default值,如果内容错误则抛出异常 69 | */ 70 | public static Integer getInteger(String key, Integer defaultValue) { 71 | String value = getValue(key); 72 | return value != null ? Integer.valueOf(value) : defaultValue; 73 | } 74 | 75 | /** 76 | *
77 | * 78 | * 如果没配置,默认返回0L 79 | * 80 | * @param key 81 | * @return 82 | */ 83 | public static final Long getLong(String key) { 84 | return getLong(key, 0L); 85 | } 86 | 87 | public static final Long getLong(String key, Long defaultValue) { 88 | String value = getValue(key); 89 | return value != null ? Long.valueOf(value) : defaultValue; 90 | } 91 | 92 | /** 93 | * 取出Double类型的Property.如果内容错误则抛出异常.
94 | * 95 | * 如果没配置,默认返回0.0D 96 | */ 97 | public static Double getDouble(String key) { 98 | return getDouble(key, 0.0D); 99 | } 100 | 101 | /** 102 | * 取出Double类型的Property.如果都為Null則返回Default值,如果内容错误则抛出异常 103 | */ 104 | public static Double getDouble(String key, Double defaultValue) { 105 | String value = getValue(key); 106 | return value != null ? Double.valueOf(value) : defaultValue; 107 | } 108 | 109 | /** 110 | * 取出Boolean类型的Property.如果内容不是true/false则返回false.
111 | * 112 | * 如果没配置,默认返回false 113 | */ 114 | public static Boolean getBoolean(String key) { 115 | return getBoolean(key, false); 116 | } 117 | 118 | /** 119 | * 取出Boolean类型的Property.如果都為Null則返回Default值,如果内容不为true/false则返回false. 120 | */ 121 | public static Boolean getBoolean(String key, boolean defaultValue) { 122 | String value = getValue(key); 123 | return value != null ? Boolean.valueOf(value) : defaultValue; 124 | } 125 | 126 | public static Map getProperties() { 127 | return ctxPropertiesMap; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/common/constant/ZipkinConstants.java: -------------------------------------------------------------------------------- 1 | package com.louie.common.constant; 2 | 3 | /** 4 | * zipkin配置常量 5 | * @author 6 | */ 7 | public class ZipkinConstants{ 8 | 9 | public static final String BRAVE_NAME="brave.name"; 10 | 11 | public static final String SEND_ADDRESS="http.sender.address"; 12 | } 13 | -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/common/dubbo/DrpcClientInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.louie.common.dubbo; 2 | 3 | import java.util.Collection; 4 | import java.util.Collections; 5 | import java.util.Map; 6 | 7 | import com.alibaba.dubbo.common.Constants; 8 | import com.alibaba.dubbo.common.extension.Activate; 9 | import com.alibaba.dubbo.rpc.Filter; 10 | import com.alibaba.dubbo.rpc.Invocation; 11 | import com.alibaba.dubbo.rpc.Invoker; 12 | import com.alibaba.dubbo.rpc.Result; 13 | import com.alibaba.dubbo.rpc.RpcException; 14 | import com.github.kristofa.brave.Brave; 15 | import com.github.kristofa.brave.ClientRequestAdapter; 16 | import com.github.kristofa.brave.ClientRequestInterceptor; 17 | import com.github.kristofa.brave.ClientResponseAdapter; 18 | import com.github.kristofa.brave.ClientResponseInterceptor; 19 | import com.github.kristofa.brave.ClientSpanThreadBinder; 20 | import com.github.kristofa.brave.IdConversion; 21 | import com.github.kristofa.brave.KeyValueAnnotation; 22 | import com.github.kristofa.brave.SpanId; 23 | import com.github.kristofa.brave.internal.Nullable; 24 | import com.github.kristofa.brave.internal.Util; 25 | import com.louie.common.config.ZipkinConfig; 26 | import com.louie.common.constant.ZipkinConstants; 27 | import com.louie.core.Service; 28 | import com.louie.utils.JsonUtils; 29 | import com.twitter.zipkin.gen.Span; 30 | 31 | import zipkin.reporter.AsyncReporter; 32 | import zipkin.reporter.Reporter; 33 | import zipkin.reporter.Sender; 34 | import zipkin.reporter.okhttp3.OkHttpSender; 35 | 36 | @Activate(group = Constants.CONSUMER) 37 | public class DrpcClientInterceptor implements Filter{ 38 | 39 | private final ClientRequestInterceptor clientRequestInterceptor; 40 | private final ClientResponseInterceptor clientResponseInterceptor; 41 | private final ClientSpanThreadBinder clientSpanThreadBinder; 42 | 43 | public DrpcClientInterceptor() { 44 | String sendUrl = ZipkinConfig.getProperty(ZipkinConstants.SEND_ADDRESS); 45 | Sender sender = OkHttpSender.create(sendUrl); 46 | Reporter reporter = AsyncReporter.builder(sender).build(); 47 | String application = ZipkinConfig.getProperty(ZipkinConstants.BRAVE_NAME); 48 | Brave brave = new Brave.Builder(application).reporter(reporter).build(); 49 | this.clientRequestInterceptor = Util.checkNotNull(brave.clientRequestInterceptor(),null); 50 | this.clientResponseInterceptor = Util.checkNotNull(brave.clientResponseInterceptor(),null); 51 | this.clientSpanThreadBinder = Util.checkNotNull(brave.clientSpanThreadBinder(),null); 52 | } 53 | 54 | 55 | public Result invoke(Invoker arg0, Invocation arg1) throws RpcException { 56 | clientRequestInterceptor.handle(new GrpcClientRequestAdapter(arg1)); 57 | Map att = arg1.getAttachments(); 58 | final Span currentClientSpan = clientSpanThreadBinder.getCurrentClientSpan(); 59 | Result result ; 60 | try { 61 | result = arg0.invoke(arg1); 62 | clientSpanThreadBinder.setCurrentSpan(currentClientSpan); 63 | clientResponseInterceptor.handle(new GrpcClientResponseAdapter(result)); 64 | } finally { 65 | clientSpanThreadBinder.setCurrentSpan(null); 66 | } 67 | return result; 68 | } 69 | 70 | static final class GrpcClientRequestAdapter implements ClientRequestAdapter { 71 | private Invocation invocation; 72 | public GrpcClientRequestAdapter(Invocation invocation) { 73 | this.invocation = invocation; 74 | } 75 | 76 | 77 | public String getSpanName() { 78 | Service ls = (Service) invocation.getArguments()[0]; 79 | String serviceName = ls == null || ls.getName() == null?"unkown":ls.getName(); 80 | return serviceName; 81 | } 82 | 83 | 84 | public void addSpanIdToRequest(@Nullable SpanId spanId) { 85 | Map at = this.invocation.getAttachments(); 86 | if (spanId == null) { 87 | at.put("Sampled", "0"); 88 | } else { 89 | 90 | at.put("Sampled", "1"); 91 | at.put("TraceId", spanId.traceIdString()); 92 | at.put("SpanId", IdConversion.convertToString(spanId.spanId)); 93 | 94 | if (spanId.nullableParentId() != null) { 95 | at.put("ParentSpanId", IdConversion.convertToString(spanId.parentId)); 96 | } 97 | } 98 | } 99 | 100 | 101 | public Collection requestAnnotations() { 102 | Service ls = (Service) invocation.getArguments()[0]; 103 | Map data = ls.getData(); 104 | KeyValueAnnotation an = KeyValueAnnotation.create("params", JsonUtils.map2Json(data)); 105 | return Collections.singletonList(an); 106 | } 107 | 108 | public com.twitter.zipkin.gen.Endpoint serverAddress() { 109 | return null; 110 | } 111 | } 112 | 113 | static final class GrpcClientResponseAdapter implements ClientResponseAdapter { 114 | 115 | private final Result result; 116 | 117 | public GrpcClientResponseAdapter(Result result) { 118 | this.result = result; 119 | } 120 | 121 | 122 | public Collection responseAnnotations() { 123 | return Collections.emptyList(); 124 | /* 125 | return !result.hasException() 126 | ? Collections.emptyList() 127 | : Collections.singletonList(KeyValueAnnotation.create("error", result.getException().getMessage())); 128 | */ 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/common/dubbo/DrpcServerInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.louie.common.dubbo; 2 | 3 | import static com.github.kristofa.brave.IdConversion.convertToLong; 4 | 5 | import java.net.SocketAddress; 6 | import java.util.Collection; 7 | import java.util.Collections; 8 | import java.util.Map; 9 | 10 | import com.alibaba.dubbo.common.Constants; 11 | import com.alibaba.dubbo.common.extension.Activate; 12 | import com.alibaba.dubbo.rpc.Filter; 13 | import com.alibaba.dubbo.rpc.Invocation; 14 | import com.alibaba.dubbo.rpc.Invoker; 15 | import com.alibaba.dubbo.rpc.Result; 16 | import com.alibaba.dubbo.rpc.RpcException; 17 | import com.github.kristofa.brave.Brave; 18 | import com.github.kristofa.brave.KeyValueAnnotation; 19 | import com.github.kristofa.brave.ServerRequestAdapter; 20 | import com.github.kristofa.brave.ServerRequestInterceptor; 21 | import com.github.kristofa.brave.ServerResponseAdapter; 22 | import com.github.kristofa.brave.ServerResponseInterceptor; 23 | import com.github.kristofa.brave.SpanId; 24 | import com.github.kristofa.brave.TraceData; 25 | import com.louie.common.config.ZipkinConfig; 26 | import com.louie.common.constant.ZipkinConstants; 27 | import com.louie.core.Service; 28 | 29 | import zipkin.reporter.AsyncReporter; 30 | import zipkin.reporter.Reporter; 31 | import zipkin.reporter.Sender; 32 | import zipkin.reporter.okhttp3.OkHttpSender; 33 | 34 | @Activate(group = Constants.PROVIDER) 35 | public class DrpcServerInterceptor implements Filter{ 36 | 37 | private final ServerRequestInterceptor serverRequestInterceptor; 38 | private final ServerResponseInterceptor serverResponseInterceptor; 39 | 40 | public DrpcServerInterceptor() { 41 | String sendUrl = ZipkinConfig.getProperty(ZipkinConstants.SEND_ADDRESS); 42 | Sender sender = OkHttpSender.create(sendUrl); 43 | Reporter reporter = AsyncReporter.builder(sender).build(); 44 | String application = ZipkinConfig.getProperty(ZipkinConstants.BRAVE_NAME);//RpcContext.getContext().getUrl().getParameter("application"); 45 | Brave brave = new Brave.Builder(application).reporter(reporter).build(); 46 | this.serverRequestInterceptor = brave.serverRequestInterceptor(); 47 | this.serverResponseInterceptor = brave.serverResponseInterceptor(); 48 | } 49 | 50 | public Result invoke(Invoker arg0, Invocation arg1) throws RpcException { 51 | serverRequestInterceptor.handle(new DrpcServerRequestAdapter(arg1)); 52 | Result result ; 53 | try { 54 | result = arg0.invoke(arg1); 55 | serverResponseInterceptor.handle(new GrpcServerResponseAdapter(result)); 56 | } finally { 57 | 58 | } 59 | return result; 60 | } 61 | 62 | static final class DrpcServerRequestAdapter implements ServerRequestAdapter { 63 | private Invocation invocation; 64 | DrpcServerRequestAdapter(Invocation invocation) { 65 | this.invocation = invocation; 66 | } 67 | 68 | 69 | public TraceData getTraceData() { 70 | //Random randoml = new Random(); 71 | Map at = this.invocation.getAttachments(); 72 | String sampled = at.get("Sampled"); 73 | String parentSpanId = at.get("ParentSpanId"); 74 | String traceId = at.get("TraceId"); 75 | String spanId = at.get("SpanId"); 76 | 77 | // Official sampled value is 1, though some old instrumentation send true 78 | Boolean parsedSampled = sampled != null 79 | ? sampled.equals("1") || sampled.equalsIgnoreCase("true") 80 | : null; 81 | 82 | if (traceId != null && spanId != null) { 83 | return TraceData.create(getSpanId(traceId, spanId, parentSpanId, parsedSampled)); 84 | } else if (parsedSampled == null) { 85 | return TraceData.EMPTY; 86 | } else if (parsedSampled.booleanValue()) { 87 | // Invalid: The caller requests the trace to be sampled, but didn't pass IDs 88 | return TraceData.EMPTY; 89 | } else { 90 | return TraceData.NOT_SAMPLED; 91 | } 92 | } 93 | 94 | 95 | public String getSpanName() { 96 | Service ls = (Service) invocation.getArguments()[0]; 97 | String serviceName = ls == null || ls.getName() == null?"unkown":ls.getName(); 98 | return serviceName; 99 | } 100 | 101 | 102 | public Collection requestAnnotations() { 103 | SocketAddress socketAddress = null; 104 | if (socketAddress != null) { 105 | KeyValueAnnotation remoteAddrAnnotation = KeyValueAnnotation.create( 106 | "DRPC_REMOTE_ADDR", socketAddress.toString()); 107 | return Collections.singleton(remoteAddrAnnotation); 108 | } else { 109 | return Collections.emptyList(); 110 | } 111 | } 112 | } 113 | 114 | static final class GrpcServerResponseAdapter implements ServerResponseAdapter { 115 | 116 | final Result result; 117 | 118 | public GrpcServerResponseAdapter(Result result) { 119 | this.result = result; 120 | } 121 | 122 | @SuppressWarnings("unchecked") 123 | public Collection responseAnnotations() { 124 | return !result.hasException() 125 | ? Collections.emptyList() 126 | : Collections.singletonList(KeyValueAnnotation.create("error", result.getException().getMessage())); 127 | } 128 | 129 | } 130 | 131 | static SpanId getSpanId(String traceId, String spanId, String parentSpanId, Boolean sampled) { 132 | return SpanId.builder() 133 | .traceIdHigh(traceId.length() == 32 ? convertToLong(traceId, 0) : 0) 134 | .traceId(convertToLong(traceId)) 135 | .spanId(convertToLong(spanId)) 136 | .sampled(sampled) 137 | .parentId(parentSpanId == null ? null : convertToLong(parentSpanId)).build(); 138 | } 139 | } -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/common/dubbo/RpcManager.java: -------------------------------------------------------------------------------- 1 | package com.louie.common.dubbo; 2 | 3 | import com.louie.core.CallResult; 4 | import com.louie.core.Service; 5 | 6 | public interface RpcManager { 7 | CallResult invoke(Service service); 8 | } 9 | -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/common/dubbo/RpcManagerImpl.java: -------------------------------------------------------------------------------- 1 | package com.louie.common.dubbo; 2 | 3 | import com.louie.core.CallResult; 4 | import com.louie.core.Service; 5 | import com.louie.core.ServiceDispacher; 6 | import com.louie.core.ServiceDispacherImpl; 7 | 8 | public class RpcManagerImpl implements RpcManager{ 9 | 10 | 11 | public CallResult invoke(Service service) { 12 | // TODO Auto-generated method stub 13 | //System.out.println("client r:"+obj.toString()); 14 | //return obj.toString(); 15 | /* 16 | ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "applicationConsumer.xml" }); 17 | context.start(); 18 | RpcManager demoServer = (RpcManager) context.getBean("demoService"); 19 | Map params = new HashMap(); 20 | params.put("name", "account.user.login"); 21 | ServiceCall shop = new ServiceCall(); 22 | shop.setName("account.user.info"); 23 | String result = demoServer.sayHello(shop); 24 | System.out.println("eshop:"+result); 25 | */ 26 | ServiceDispacher serviceDispacher = new ServiceDispacherImpl(); 27 | return serviceDispacher.invoke(service); 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/core/CallParams.java: -------------------------------------------------------------------------------- 1 | package com.louie.core; 2 | 3 | import java.io.Serializable; 4 | import java.util.Map; 5 | /** 6 | * 服务调用参数定义 7 | * @author liuyong 8 | * 9 | */ 10 | public class CallParams implements Serializable{ 11 | 12 | private static final long serialVersionUID = 273828239238L; 13 | 14 | /** 15 | * 调用参数 16 | */ 17 | Map data; 18 | 19 | public Map getData() { 20 | return data; 21 | } 22 | 23 | public void setData(Map data) { 24 | this.data = data; 25 | } 26 | 27 | public Object get(String key){ 28 | Object value = data != null?data.get(key):null; 29 | return value; 30 | } 31 | } -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/core/CallResult.java: -------------------------------------------------------------------------------- 1 | package com.louie.core; 2 | 3 | import java.io.Serializable; 4 | import java.util.Map; 5 | /** 6 | * 服务调用结果定义 7 | * @author liuyong 8 | * 9 | */ 10 | public class CallResult implements Serializable{ 11 | 12 | private static final long serialVersionUID = 28738282394238L; 13 | /** 14 | * 结果编码 , 0:成功,其它:失败 15 | */ 16 | Integer code = 0; 17 | 18 | String message = ""; 19 | /** 20 | * 结果 21 | */ 22 | Map data; 23 | 24 | public Integer getCode() { 25 | return code; 26 | } 27 | public void setCode(Integer code) { 28 | this.code = code; 29 | } 30 | public Map getData() { 31 | return data; 32 | } 33 | public void setData(Map data) { 34 | this.data = data; 35 | } 36 | public String getMessage() { 37 | return message; 38 | } 39 | public void setMessage(String message) { 40 | this.message = message; 41 | } 42 | public Object get(String key){ 43 | Object value = data != null?data.get(key):null; 44 | return value; 45 | } 46 | } -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/core/CommonAdapter.java: -------------------------------------------------------------------------------- 1 | package com.louie.core; 2 | 3 | import java.util.Map; 4 | /** 5 | * 服务调用参数定义 6 | * @author liuyong 7 | * 8 | */ 9 | public class CommonAdapter implements ServiceCallAdapter{ 10 | 11 | private String service; 12 | private Map data; 13 | 14 | public CommonAdapter(String service,Map data) { 15 | super(); 16 | this.service = service; 17 | this.data = data; 18 | } 19 | public String getName(){ 20 | return service; 21 | } 22 | public Map getData(){ 23 | return data; 24 | } 25 | } -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/core/Service.java: -------------------------------------------------------------------------------- 1 | package com.louie.core; 2 | 3 | import java.io.Serializable; 4 | import java.util.Map; 5 | 6 | import com.louie.common.dubbo.RpcManager; 7 | import com.louie.exception.LouieException; 8 | /** 9 | * 服务定义 10 | * @author liuyong 11 | * 12 | */ 13 | public class Service implements Serializable{ 14 | private static final long serialVersionUID = 287382239238L; 15 | /** 16 | * 服务名,规范: 系统名+业务名+方法名,用"."隔开,如 "account.user.info" 17 | */ 18 | private String name; 19 | /** 20 | * 调用参数 21 | */ 22 | private Map data; 23 | 24 | public Service() { 25 | } 26 | 27 | public Service(String name, Map data) { 28 | super(); 29 | this.name = name; 30 | this.data = data; 31 | } 32 | 33 | public String getName() { 34 | return name; 35 | } 36 | 37 | public void setName(String name) { 38 | this.name = name; 39 | } 40 | 41 | public Map getData() { 42 | return data; 43 | } 44 | 45 | public void setData(Map data) { 46 | this.data = data; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/core/ServiceCall.java: -------------------------------------------------------------------------------- 1 | package com.louie.core; 2 | 3 | import java.io.Serializable; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import com.louie.common.dubbo.RpcManager; 8 | import com.louie.exception.LouieException; 9 | /** 10 | * 服务调用定义 11 | * @author liuyong 12 | * 13 | */ 14 | public class ServiceCall implements Serializable{ 15 | private static final long serialVersionUID = 2873828239238L; 16 | 17 | private ServiceCallAdapter adapter; 18 | 19 | public ServiceCall(ServiceCallAdapter adapter) { 20 | this.adapter = adapter; 21 | } 22 | 23 | public CallResult invoke(){ 24 | String serviceName = adapter.getName(); 25 | if(serviceName == null || serviceName == ""){ 26 | throw new LouieException("服务未定义"); 27 | } 28 | String[] names = serviceName.split("\\."); 29 | if(names.length < 3){ 30 | throw new LouieException("服务定义错误"); 31 | } 32 | String sys = names[0]; 33 | RpcManager rpc = (RpcManager) SpringApplicationContext.getBean(sys + "Call"); 34 | if(rpc == null){ 35 | throw new LouieException("未定义远程系统"); 36 | } 37 | Map data = adapter.getData(); 38 | data = data == null ? new HashMap():data; 39 | Service service = new Service(serviceName,data); 40 | return rpc.invoke(service); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/core/ServiceCallAdapter.java: -------------------------------------------------------------------------------- 1 | package com.louie.core; 2 | 3 | import java.util.Map; 4 | 5 | public interface ServiceCallAdapter { 6 | public String getName(); 7 | public Map getData(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/core/ServiceDispacher.java: -------------------------------------------------------------------------------- 1 | package com.louie.core; 2 | 3 | /** 4 | * 服务调用分发 5 | * @author 6 | * 7 | */ 8 | public interface ServiceDispacher { 9 | CallResult invoke(Service service); 10 | } 11 | -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/core/ServiceDispacherImpl.java: -------------------------------------------------------------------------------- 1 | package com.louie.core; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | import org.springframework.stereotype.Component; 6 | 7 | import com.louie.exception.LouieException; 8 | 9 | @Component 10 | public class ServiceDispacherImpl implements ServiceDispacher{ 11 | /** 12 | * 服务调用 13 | */ 14 | public CallResult invoke(Service service){ 15 | String serviceName = service.getName(); 16 | if(serviceName == null || serviceName == ""){ 17 | throw new LouieException("服务未定义"); 18 | } 19 | String[] names = serviceName.split("\\."); 20 | if(names.length < 3){ 21 | throw new LouieException("服务定义错误"); 22 | } 23 | StringBuffer sb = new StringBuffer(); 24 | for(int i = 0;i < names.length - 2;i++){ 25 | sb.append(names[i]); 26 | sb.append("."); 27 | } 28 | sb.append(names[names.length - 2]); 29 | String serviceN = sb.toString(); 30 | String methodstring = names[names.length -1]; 31 | Object object = null; 32 | try{ 33 | object = SpringApplicationContext.getBean(serviceN); 34 | }catch(Exception ex){ 35 | 36 | } 37 | if(object == null){ 38 | throw new LouieException("找不到该服务:"+serviceN); 39 | } 40 | Method[] methods = object.getClass().getMethods(); 41 | Method method = null; 42 | for(Method met:methods){ 43 | if(met.getName().equals(methodstring)){ 44 | method = met; 45 | } 46 | } 47 | if(method == null){ 48 | throw new LouieException(serviceName+"方法未定义"); 49 | } 50 | 51 | Class[] clses = method.getParameterTypes(); 52 | Object result = null; 53 | try{ 54 | CallParams params = new CallParams(); 55 | if(clses.length == 0){ 56 | result = method.invoke(object, params); 57 | }else if(clses.length >= 1){ 58 | Class cls = clses[0]; 59 | if(cls.getName().equals(CallParams.class.getName())){ 60 | params.setData(service.getData()); 61 | result = method.invoke(object, params); 62 | }else{ 63 | result = method.invoke(object, params); 64 | } 65 | } 66 | }catch(Exception ex){ 67 | ex.printStackTrace(); 68 | throw new LouieException("调用异常"); 69 | } 70 | 71 | CallResult sr = (CallResult) result; 72 | 73 | return sr; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/core/SpringApplicationContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Rainbow and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software;Designed and Developed mainly by many Chinese 6 | * opensource volunteers. you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License version 2 only, as published by the 8 | * Free Software Foundation. 9 | * 10 | * This code is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 | * version 2 for more details (a copy is included in the LICENSE file that 14 | * accompanied this code). 15 | * 16 | * You should have received a copy of the GNU General Public License version 17 | * 2 along with this work; if not, write to the Free Software Foundation, 18 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 | * 20 | * 21 | */ 22 | package com.louie.core; 23 | 24 | import org.springframework.beans.BeansException; 25 | import org.springframework.beans.factory.support.DefaultListableBeanFactory; 26 | import org.springframework.context.ApplicationContext; 27 | import org.springframework.context.ApplicationContextAware; 28 | import org.springframework.context.ConfigurableApplicationContext; 29 | import org.springframework.stereotype.Component; 30 | 31 | @Component 32 | public class SpringApplicationContext implements ApplicationContextAware { 33 | 34 | private static ApplicationContext context; 35 | 36 | public void setApplicationContext(ApplicationContext applicationContext) 37 | throws BeansException { 38 | SpringApplicationContext.context = applicationContext; 39 | } 40 | 41 | public static ApplicationContext getApplicationContext() 42 | throws BeansException { 43 | return context; 44 | } 45 | 46 | public static Object getBean(String beanId) { 47 | if (beanId == null || beanId.length() == 0) { 48 | return null; 49 | } 50 | Object object = null; 51 | object = context.getBean(beanId); 52 | return object; 53 | } 54 | 55 | /* 56 | public static Object getController(String controllerName) { 57 | Map beansWithAnnotation = context.getBeansWithAnnotation(ApiController.class); 58 | for(String s : beansWithAnnotation.keySet()) { 59 | System.out.println(s); 60 | } 61 | return null; 62 | } 63 | */ 64 | 65 | public static T getBean(Class clazz) { 66 | if (clazz == null ) { 67 | return null; 68 | } 69 | return context.getBean(clazz); 70 | } 71 | 72 | /** 73 | * 从spring容器中获取指定的bean 74 | * 75 | * @param name 76 | * @param requiredType 77 | * @return 78 | * @throws BeansException 79 | */ 80 | public static T getBean(String name, Class requiredType) throws BeansException { 81 | return context.getBean(name, requiredType); 82 | } 83 | 84 | public static void removeBean(String beanId){ 85 | if (beanId == null || beanId.isEmpty()) { 86 | return ; 87 | } 88 | ConfigurableApplicationContext applicationContexts = (ConfigurableApplicationContext)context; 89 | DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContexts.getBeanFactory(); 90 | beanFactory.removeBeanDefinition(beanId); 91 | } 92 | 93 | public static void removeBeans(String... beanIds){ 94 | if(beanIds == null || beanIds.length == 0){ 95 | return; 96 | } 97 | ConfigurableApplicationContext applicationContexts = (ConfigurableApplicationContext)context; 98 | DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContexts.getBeanFactory(); 99 | for(String beanId : beanIds){ 100 | if(beanId != null && !beanId.isEmpty()){ 101 | if(beanFactory.isBeanNameInUse(beanId)){ 102 | beanFactory.removeBeanDefinition(beanId); 103 | } 104 | } 105 | } 106 | } 107 | 108 | 109 | } -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/exception/LouieException.java: -------------------------------------------------------------------------------- 1 | package com.louie.exception; 2 | 3 | public class LouieException extends RuntimeException{ 4 | public LouieException(String message){ 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/utils/JsonUtils.java: -------------------------------------------------------------------------------- 1 | package com.louie.utils; 2 | 3 | import java.util.Map; 4 | import java.util.SortedMap; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import com.fasterxml.jackson.core.JsonProcessingException; 10 | import com.fasterxml.jackson.databind.DeserializationFeature; 11 | import com.fasterxml.jackson.databind.ObjectMapper; 12 | 13 | /** 14 | * JSON工具类 15 | * @author 丁威 2015年9月25日 下午5:56:25 16 | * @version 1.0 17 | */ 18 | @SuppressWarnings("rawtypes") 19 | public abstract class JsonUtils { 20 | private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class); 21 | private static final ObjectMapper objectMapper = new ObjectMapper(); 22 | 23 | static { 24 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 25 | } 26 | 27 | @SuppressWarnings("unchecked") 28 | public static final Map jsonToMap(String jsonStr) { 29 | return (Map)json2Map(jsonStr); 30 | } 31 | 32 | public static String mapToJson(Map map) { 33 | return object2JSON(map); 34 | } 35 | 36 | public static final Map json2Map(String jsonStr) { 37 | if(StringUtils.isBlank(jsonStr)) return null; 38 | 39 | try { 40 | return objectMapper.readValue(jsonStr, Map.class); 41 | } catch (Exception e) { 42 | logger.error("Json转换异常", e); 43 | return null; 44 | } 45 | } 46 | 47 | 48 | public static final SortedMap json2SortedMap(String jsonStr) { 49 | if(StringUtils.isBlank(jsonStr)) return null; 50 | 51 | try { 52 | return objectMapper.readValue(jsonStr, SortedMap.class); 53 | } catch (Exception e) { 54 | logger.error("Json转换异常", e); 55 | return null; 56 | } 57 | } 58 | 59 | public static String object2JSON(Object obj) { 60 | if(obj == null){ 61 | return "{}"; 62 | } 63 | 64 | try { 65 | return objectMapper.writeValueAsString(obj); 66 | } catch (JsonProcessingException e) { 67 | // TODO Auto-generated catch block 68 | //e.printStackTrace(); 69 | logger.error("转换异常", e); 70 | return ""; 71 | } 72 | //return JSON.toJSONString(obj,SerializerFeature.WriteDateUseDateFormat); 73 | } 74 | 75 | public static String map2Json(Map map) { 76 | return object2JSON(map); 77 | } 78 | 79 | public static final T json2Bean(String content, Class valueType) { 80 | if (StringUtils.isBlank(content)) 81 | return null; 82 | 83 | try { 84 | return objectMapper.readValue(content, valueType); 85 | } catch (Exception e) { 86 | logger.error("Json转换异常", e); 87 | return null; 88 | } 89 | } 90 | 91 | public static final Map beanToMap(Object obj) { 92 | try { 93 | Map map = objectMapper.convertValue(obj, Map.class); 94 | return map; 95 | } catch (Exception e) { 96 | logger.error("Json转换异常", e); 97 | return null; 98 | } 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /louie-common/src/main/java/com/louie/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | package com.louie.utils; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.security.MessageDigest; 5 | import java.security.NoSuchAlgorithmException; 6 | import java.text.BreakIterator; 7 | import java.text.ParseException; 8 | import java.util.ArrayList; 9 | import java.util.Collection; 10 | import java.util.Collections; 11 | import java.util.Date; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Random; 16 | import java.util.StringTokenizer; 17 | import java.util.concurrent.ConcurrentHashMap; 18 | import java.util.regex.Matcher; 19 | import java.util.regex.Pattern; 20 | 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | public class StringUtils { 25 | 26 | //字符串转map 如String str="a=b,c=d"; 27 | public static Map str2map(String string){ 28 | String[] strs=string.split(","); 29 | Map str2map=new HashMap(); 30 | for (String str : strs) { 31 | String[] strs2=str.split("="); 32 | String value=strs2.length>1?strs2[1].trim():null; 33 | str2map.put(strs2[0].trim(), value); 34 | } 35 | return str2map; 36 | } 37 | 38 | private static final Logger Log = LoggerFactory.getLogger(StringUtils.class); 39 | //TODO - complete JavaDoc 40 | /** 41 | * Constant representing the empty string, equal to "" 42 | */ 43 | public static final String EMPTY_STRING = ""; 44 | 45 | /** 46 | * Constant representing the default delimiter character (comma), equal to 47 | * ',' 48 | */ 49 | public static final char DEFAULT_DELIMITER_CHAR = ','; 50 | 51 | /** 52 | * Constant representing the default quote character (double quote), equal 53 | * to '"' 54 | */ 55 | public static final char DEFAULT_QUOTE_CHAR = '"'; 56 | 57 | /** 58 | * Check whether the given String has actual text. More specifically, 59 | * returns true if the string not null, its length 60 | * is greater than 0, and it contains at least one non-whitespace character. 61 | *

62 | * StringUtils.hasText(null) == false
63 | * StringUtils.hasText("") == false
64 | * StringUtils.hasText(" ") == false
65 | * StringUtils.hasText("12345") == true
66 | * StringUtils.hasText(" 12345 ") == true
67 | *

68 | *

69 | * Copied from the Spring Framework while retaining all license, copyright 70 | * and author information. 71 | * 72 | * @param str the String to check (may be null) 73 | * @return true if the String is not null, its 74 | * length is greater than 0, and it does not contain whitespace only 75 | * @see Character#isWhitespace 76 | */ 77 | public static boolean hasText(String str) { 78 | if (!hasLength(str)) { 79 | return false; 80 | } 81 | int strLen = str.length(); 82 | for (int i = 0; i < strLen; i++) { 83 | if (!Character.isWhitespace(str.charAt(i))) { 84 | return true; 85 | } 86 | } 87 | return false; 88 | } 89 | 90 | /** 91 | * Check that the given String is neither null nor of length 0. 92 | * Note: Will return true for a String that purely consists of 93 | * whitespace. 94 | *

95 | * StringUtils.hasLength(null) == false
96 | * StringUtils.hasLength("") == false
97 | * StringUtils.hasLength(" ") == true
98 | * StringUtils.hasLength("Hello") == true
99 | *

100 | * Copied from the Spring Framework while retaining all license, copyright 101 | * and author information. 102 | * 103 | * @param str the String to check (may be null) 104 | * @return true if the String is not null and has length 105 | * @see #hasText(String) 106 | */ 107 | public static boolean hasLength(String str) { 108 | return (str != null && str.length() > 0); 109 | } 110 | 111 | /** 112 | * Test if the given String starts with the specified prefix, ignoring 113 | * upper/lower case. 114 | *

115 | *

116 | * Copied from the Spring Framework while retaining all license, copyright 117 | * and author information. 118 | * 119 | * @param str the String to check 120 | * @param prefix the prefix to look for 121 | * @return true starts with the specified prefix (ignoring 122 | * case), false if it does not. 123 | * @see String#startsWith 124 | */ 125 | public static boolean startsWithIgnoreCase(String str, String prefix) { 126 | if (str == null || prefix == null) { 127 | return false; 128 | } 129 | if (str.startsWith(prefix)) { 130 | return true; 131 | } 132 | if (str.length() < prefix.length()) { 133 | return false; 134 | } 135 | String lcStr = str.substring(0, prefix.length()).toLowerCase(); 136 | String lcPrefix = prefix.toLowerCase(); 137 | return lcStr.equals(lcPrefix); 138 | } 139 | 140 | /** 141 | * Returns a 'cleaned' representation of the specified argument. 'Cleaned' 142 | * is defined as the following: 143 | *

144 | *

    145 | *
  1. If the specified String is null, return 146 | * null
  2. 147 | *
  3. If not null, {@link String#trim() trim()} it.
  4. 148 | *
  5. If the trimmed string is equal to the empty String (i.e. 149 | * ""), return null
  6. 150 | *
  7. If the trimmed string is not the empty string, return the trimmed 151 | * version
  8. . 152 | *
153 | *

154 | * Therefore this method always ensures that any given string has trimmed 155 | * text, and if it doesn't, null is returned. 156 | * 157 | * @param in the input String to clean. 158 | * @return a populated-but-trimmed String or null otherwise 159 | */ 160 | public static String clean(String in) { 161 | String out = in; 162 | 163 | if (in != null) { 164 | out = in.trim(); 165 | if (out.equals(EMPTY_STRING)) { 166 | out = null; 167 | } 168 | } 169 | 170 | return out; 171 | } 172 | 173 | /** 174 | * Returns the specified array as a comma-delimited (',') string. 175 | * 176 | * @param array the array whose contents will be converted to a string. 177 | * @return the array's contents as a comma-delimited (',') string. 178 | * @since 1.0 179 | */ 180 | public static String toString(Object[] array) { 181 | return toDelimitedString(array, ","); 182 | } 183 | 184 | /** 185 | * Returns the array's contents as a string, with each element delimited by 186 | * the specified {@code delimiter} argument. Useful for {@code toString()} 187 | * implementations and log messages. 188 | * 189 | * @param array the array whose contents will be converted to a string 190 | * @param delimiter the delimiter to use between each element 191 | * @return a single string, delimited by the specified {@code delimiter}. 192 | * @since 1.0 193 | */ 194 | public static String toDelimitedString(Object[] array, String delimiter) { 195 | if (array == null || array.length == 0) { 196 | return EMPTY_STRING; 197 | } 198 | StringBuilder sb = new StringBuilder(); 199 | for (int i = 0; i < array.length; i++) { 200 | if (i > 0) { 201 | sb.append(delimiter); 202 | } 203 | sb.append(array[i]); 204 | } 205 | return sb.toString(); 206 | } 207 | 208 | /** 209 | * Tokenize the given String into a String array via a StringTokenizer. 210 | * Trims tokens and omits empty tokens. 211 | *

212 | * The given delimiters string is supposed to consist of any number of 213 | * delimiter characters. Each of those characters can be used to separate 214 | * tokens. A delimiter is always a single character; for multi-character 215 | * delimiters, consider using delimitedListToStringArray 216 | *

217 | *

218 | * Copied from the Spring Framework while retaining all license, copyright 219 | * and author information. 220 | * 221 | * @param str the String to tokenize 222 | * @param delimiters the delimiter characters, assembled as String (each of 223 | * those characters is individually considered as delimiter). 224 | * @return an array of the tokens 225 | * @see StringTokenizer 226 | * @see String#trim() 227 | */ 228 | public static String[] tokenizeToStringArray(String str, String delimiters) { 229 | return tokenizeToStringArray(str, delimiters, true, true); 230 | } 231 | 232 | /** 233 | * Tokenize the given String into a String array via a StringTokenizer. 234 | *

235 | * The given delimiters string is supposed to consist of any number of 236 | * delimiter characters. Each of those characters can be used to separate 237 | * tokens. A delimiter is always a single character; for multi-character 238 | * delimiters, consider using delimitedListToStringArray 239 | *

240 | *

241 | * Copied from the Spring Framework while retaining all license, copyright 242 | * and author information. 243 | * 244 | * @param str the String to tokenize 245 | * @param delimiters the delimiter characters, assembled as String (each of 246 | * those characters is individually considered as delimiter) 247 | * @param trimTokens trim the tokens via String's trim 248 | * @param ignoreEmptyTokens omit empty tokens from the result array (only 249 | * applies to tokens that are empty after trimming; StringTokenizer will not 250 | * consider subsequent delimiters as token in the first place). 251 | * @return an array of the tokens (null if the input String was 252 | * null) 253 | * @see StringTokenizer 254 | * @see String#trim() 255 | */ 256 | @SuppressWarnings({"unchecked"}) 257 | public static String[] tokenizeToStringArray( 258 | String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { 259 | 260 | if (str == null) { 261 | return null; 262 | } 263 | StringTokenizer st = new StringTokenizer(str, delimiters); 264 | List tokens = new ArrayList(); 265 | while (st.hasMoreTokens()) { 266 | String token = st.nextToken(); 267 | if (trimTokens) { 268 | token = token.trim(); 269 | } 270 | if (!ignoreEmptyTokens || token.length() > 0) { 271 | tokens.add(token); 272 | } 273 | } 274 | return toStringArray(tokens); 275 | } 276 | 277 | /** 278 | * Copy the given Collection into a String array. The Collection must 279 | * contain String elements only. 280 | *

281 | *

282 | * Copied from the Spring Framework while retaining all license, copyright 283 | * and author information. 284 | * 285 | * @param collection the Collection to copy 286 | * @return the String array (null if the passed-in Collection 287 | * was null) 288 | */ 289 | @SuppressWarnings({"unchecked"}) 290 | public static String[] toStringArray(Collection collection) { 291 | if (collection == null) { 292 | return null; 293 | } 294 | return (String[]) collection.toArray(new String[collection.size()]); 295 | } 296 | 297 | public static String[] splitKeyValue(String aLine) throws ParseException { 298 | String line = clean(aLine); 299 | if (line == null) { 300 | return null; 301 | } 302 | String[] split = line.split(" ", 2); 303 | if (split.length != 2) { 304 | //fallback to checking for an equals sign 305 | split = line.split("=", 2); 306 | if (split.length != 2) { 307 | String msg = "Unable to determine Key/Value pair from line [" + line + "]. There is no space from " 308 | + "which the split location could be determined."; 309 | throw new ParseException(msg, 0); 310 | } 311 | 312 | } 313 | 314 | split[0] = clean(split[0]); 315 | split[1] = clean(split[1]); 316 | if (split[1].startsWith("=")) { 317 | //they used spaces followed by an equals followed by zero or more spaces to split the key/value pair, so 318 | //remove the equals sign to result in only the key and values in the 319 | split[1] = clean(split[1].substring(1)); 320 | } 321 | 322 | if (split[0] == null) { 323 | String msg = "No valid key could be found in line [" + line + "] to form a key/value pair."; 324 | throw new ParseException(msg, 0); 325 | } 326 | if (split[1] == null) { 327 | String msg = "No corresponding value could be found in line [" + line + "] for key [" + split[0] + "]"; 328 | throw new ParseException(msg, 0); 329 | } 330 | 331 | return split; 332 | } 333 | 334 | public static String[] split(String line) { 335 | return split(line, DEFAULT_DELIMITER_CHAR); 336 | } 337 | 338 | public static String[] split(String line, char delimiter) { 339 | return split(line, delimiter, DEFAULT_QUOTE_CHAR); 340 | } 341 | 342 | public static String[] split(String line, char delimiter, char quoteChar) { 343 | return split(line, delimiter, quoteChar, quoteChar); 344 | } 345 | 346 | public static String[] split(String line, char delimiter, char beginQuoteChar, char endQuoteChar) { 347 | return split(line, delimiter, beginQuoteChar, endQuoteChar, false, true); 348 | } 349 | 350 | /** 351 | * Splits the specified delimited String into tokens, supporting quoted 352 | * tokens so that quoted strings themselves won't be tokenized. 353 | *

354 | * This method's implementation is very loosely based (with significant 355 | * modifications) on 356 | * Glen Smith's open-source 357 | * CSVReader.java 359 | * file. 360 | *

361 | * That file is Apache 2.0 licensed as well, making Glen's code a great 362 | * starting point for us to modify to our needs. 363 | * 364 | * @param aLine the String to parse 365 | * @param delimiter the delimiter by which the line argument is to 366 | * be split 367 | * @param beginQuoteChar the character signifying the start of quoted text 368 | * (so the quoted text will not be split) 369 | * @param endQuoteChar the character signifying the end of quoted text 370 | * @param retainQuotes if the quotes themselves should be retained when 371 | * constructing the corresponding token 372 | * @param trimTokens if leading and trailing whitespace should be trimmed 373 | * from discovered tokens. 374 | * @return the tokens discovered from parsing the given delimited 375 | * line. 376 | */ 377 | public static String[] split(String aLine, char delimiter, char beginQuoteChar, char endQuoteChar, 378 | boolean retainQuotes, boolean trimTokens) { 379 | String line = clean(aLine); 380 | if (line == null) { 381 | return null; 382 | } 383 | 384 | List tokens = new ArrayList(); 385 | StringBuilder sb = new StringBuilder(); 386 | boolean inQuotes = false; 387 | 388 | for (int i = 0; i < line.length(); i++) { 389 | 390 | char c = line.charAt(i); 391 | if (c == beginQuoteChar) { 392 | // this gets complex... the quote may end a quoted block, or escape another quote. 393 | // do a 1-char lookahead: 394 | if (inQuotes // we are in quotes, therefore there can be escaped quotes in here. 395 | && line.length() > (i + 1) // there is indeed another character to check. 396 | && line.charAt(i + 1) == beginQuoteChar) { // ..and that char. is a quote also. 397 | // we have two quote chars in a row == one quote char, so consume them both and 398 | // put one on the token. we do *not* exit the quoted text. 399 | sb.append(line.charAt(i + 1)); 400 | i++; 401 | } else { 402 | inQuotes = !inQuotes; 403 | if (retainQuotes) { 404 | sb.append(c); 405 | } 406 | } 407 | } else if (c == endQuoteChar) { 408 | inQuotes = !inQuotes; 409 | if (retainQuotes) { 410 | sb.append(c); 411 | } 412 | } else if (c == delimiter && !inQuotes) { 413 | String s = sb.toString(); 414 | if (trimTokens) { 415 | s = s.trim(); 416 | } 417 | tokens.add(s); 418 | sb = new StringBuilder(); // start work on next token 419 | } else { 420 | sb.append(c); 421 | } 422 | } 423 | String s = sb.toString(); 424 | if (trimTokens) { 425 | s = s.trim(); 426 | } 427 | tokens.add(s); 428 | return tokens.toArray(new String[tokens.size()]); 429 | } 430 | 431 | /** 432 | * Returns the input argument, but ensures the first character is 433 | * capitalized (if possible). 434 | * 435 | * @param in the string to uppercase the first character. 436 | * @return the input argument, but with the first character capitalized (if 437 | * possible). 438 | * @since 1.2 439 | */ 440 | public static String uppercaseFirstChar(String in) { 441 | if (in == null || in.length() == 0) { 442 | return in; 443 | } 444 | int length = in.length(); 445 | StringBuilder sb = new StringBuilder(length); 446 | 447 | sb.append(Character.toUpperCase(in.charAt(0))); 448 | if (length > 1) { 449 | String remaining = in.substring(1); 450 | sb.append(remaining); 451 | } 452 | return sb.toString(); 453 | } 454 | private static final char[] QUOTE_ENCODE = """.toCharArray(); 455 | private static final char[] AMP_ENCODE = "&".toCharArray(); 456 | private static final char[] LT_ENCODE = "<".toCharArray(); 457 | private static final char[] GT_ENCODE = ">".toCharArray(); 458 | 459 | private static String[] chars = new String[]{"a", "b", "c", "d", "e", "f", "g", "h", 460 | "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", 461 | "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", 462 | "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", 463 | "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", 464 | "U", "V", "W", "X", "Y", "Z" 465 | }; 466 | 467 | private StringUtils() { 468 | // Not instantiable. 469 | } 470 | 471 | public static String shortUrl(String key) { 472 | 473 | // 要使用生成 URL 的字符 474 | // 对传入网址进行 MD5 加密 475 | String sMD5EncryptResult = StringUtils.hash(key, "MD5"); 476 | String hex = sMD5EncryptResult; 477 | StringBuilder sb = new StringBuilder(); 478 | for (int i = 0; i < 4; i++) { 479 | // 把加密字符按照 8 位一组 16 进制与 0x3FFFFFFF 进行位与运算 480 | String sTempSubString = hex.substring(i * 8, i * 8 + 8); 481 | // 这里需要使用 long 型来转换,因为 Inteper .parseInt() 只能处理 31 位 , 首位为符号位 , 如果不用 long ,则会越界 482 | long lHexLong = 0x3FFFFFFF & Long.parseLong(sTempSubString, 16); 483 | for (int j = 0; j < 6; j++) { 484 | // 把得到的值与 0x0000003D 进行位与运算,取得字符数组 chars 索引 485 | long index = 0x0000003D & lHexLong; 486 | // 把取得的字符相加 487 | sb.append(chars[(int) index]); 488 | // 每次循环按位右移 5 位 489 | lHexLong = lHexLong >> 5; 490 | } 491 | } 492 | return sb.toString(); 493 | } 494 | 495 | /** 496 | * Replaces all instances of oldString with newString in string. 497 | * 498 | * @param string the String to search to perform replacements on. 499 | * @param oldString the String that should be replaced by newString. 500 | * @param newString the String that will replace all instances of oldString. 501 | * @return a String will all instances of oldString replaced by newString. 502 | */ 503 | public static String replace(String string, String oldString, String newString) { 504 | if (string == null) { 505 | return null; 506 | } 507 | int i = 0; 508 | // Make sure that oldString appears at least once before doing any processing. 509 | if ((i = string.indexOf(oldString, i)) >= 0) { 510 | // Use char []'s, as they are more efficient to deal with. 511 | char[] string2 = string.toCharArray(); 512 | char[] newString2 = newString.toCharArray(); 513 | int oLength = oldString.length(); 514 | StringBuilder buf = new StringBuilder(string2.length); 515 | buf.append(string2, 0, i).append(newString2); 516 | i += oLength; 517 | int j = i; 518 | // Replace all remaining instances of oldString with newString. 519 | while ((i = string.indexOf(oldString, i)) > 0) { 520 | buf.append(string2, j, i - j).append(newString2); 521 | i += oLength; 522 | j = i; 523 | } 524 | buf.append(string2, j, string2.length - j); 525 | return buf.toString(); 526 | } 527 | return string; 528 | } 529 | 530 | /** 531 | * Replaces all instances of oldString with newString in line with the added 532 | * feature that matches of newString in oldString ignore case. 533 | * 534 | * @param line the String to search to perform replacements on 535 | * @param oldString the String that should be replaced by newString 536 | * @param newString the String that will replace all instances of oldString 537 | * @return a String will all instances of oldString replaced by newString 538 | */ 539 | public static String replaceIgnoreCase(String line, String oldString, 540 | String newString) { 541 | if (line == null) { 542 | return null; 543 | } 544 | String lcLine = line.toLowerCase(); 545 | String lcOldString = oldString.toLowerCase(); 546 | int i = 0; 547 | if ((i = lcLine.indexOf(lcOldString, i)) >= 0) { 548 | char[] line2 = line.toCharArray(); 549 | char[] newString2 = newString.toCharArray(); 550 | int oLength = oldString.length(); 551 | StringBuilder buf = new StringBuilder(line2.length); 552 | buf.append(line2, 0, i).append(newString2); 553 | i += oLength; 554 | int j = i; 555 | while ((i = lcLine.indexOf(lcOldString, i)) > 0) { 556 | buf.append(line2, j, i - j).append(newString2); 557 | i += oLength; 558 | j = i; 559 | } 560 | buf.append(line2, j, line2.length - j); 561 | return buf.toString(); 562 | } 563 | return line; 564 | } 565 | 566 | /** 567 | * Replaces all instances of oldString with newString in line with the added 568 | * feature that matches of newString in oldString ignore case. The count 569 | * paramater is set to the number of replaces performed. 570 | * 571 | * @param line the String to search to perform replacements on 572 | * @param oldString the String that should be replaced by newString 573 | * @param newString the String that will replace all instances of oldString 574 | * @param count a value that will be updated with the number of replaces 575 | * performed. 576 | * @return a String will all instances of oldString replaced by newString 577 | */ 578 | public static String replaceIgnoreCase(String line, String oldString, 579 | String newString, int[] count) { 580 | if (line == null) { 581 | return null; 582 | } 583 | String lcLine = line.toLowerCase(); 584 | String lcOldString = oldString.toLowerCase(); 585 | int i = 0; 586 | if ((i = lcLine.indexOf(lcOldString, i)) >= 0) { 587 | int counter = 1; 588 | char[] line2 = line.toCharArray(); 589 | char[] newString2 = newString.toCharArray(); 590 | int oLength = oldString.length(); 591 | StringBuilder buf = new StringBuilder(line2.length); 592 | buf.append(line2, 0, i).append(newString2); 593 | i += oLength; 594 | int j = i; 595 | while ((i = lcLine.indexOf(lcOldString, i)) > 0) { 596 | counter++; 597 | buf.append(line2, j, i - j).append(newString2); 598 | i += oLength; 599 | j = i; 600 | } 601 | buf.append(line2, j, line2.length - j); 602 | count[0] = counter; 603 | return buf.toString(); 604 | } 605 | return line; 606 | } 607 | 608 | /** 609 | * Replaces all instances of oldString with newString in line. The count 610 | * Integer is updated with number of replaces. 611 | * 612 | * @param line the String to search to perform replacements on. 613 | * @param oldString the String that should be replaced by newString. 614 | * @param newString the String that will replace all instances of oldString. 615 | * @return a String will all instances of oldString replaced by newString. 616 | */ 617 | public static String replace(String line, String oldString, 618 | String newString, int[] count) { 619 | if (line == null) { 620 | return null; 621 | } 622 | int i = 0; 623 | if ((i = line.indexOf(oldString, i)) >= 0) { 624 | int counter = 1; 625 | char[] line2 = line.toCharArray(); 626 | char[] newString2 = newString.toCharArray(); 627 | int oLength = oldString.length(); 628 | StringBuilder buf = new StringBuilder(line2.length); 629 | buf.append(line2, 0, i).append(newString2); 630 | i += oLength; 631 | int j = i; 632 | while ((i = line.indexOf(oldString, i)) > 0) { 633 | counter++; 634 | buf.append(line2, j, i - j).append(newString2); 635 | i += oLength; 636 | j = i; 637 | } 638 | buf.append(line2, j, line2.length - j); 639 | count[0] = counter; 640 | return buf.toString(); 641 | } 642 | return line; 643 | } 644 | 645 | /** 646 | * This method takes a string and strips out all tags except
tags while 647 | * still leaving the tag body intact. 648 | * 649 | * @param in the text to be converted. 650 | * @return the input string with all tags removed. 651 | */ 652 | public static String stripTags(String in) { 653 | if (in == null) { 654 | return null; 655 | } 656 | char ch; 657 | int i = 0; 658 | int last = 0; 659 | char[] input = in.toCharArray(); 660 | int len = input.length; 661 | StringBuilder out = new StringBuilder((int) (len * 1.3)); 662 | for (; i < len; i++) { 663 | ch = input[i]; 664 | if (ch > '>') { 665 | } else if (ch == '<') { 666 | if (i + 3 < len && input[i + 1] == 'b' && input[i + 2] == 'r' && input[i + 3] == '>') { 667 | i += 3; 668 | continue; 669 | } 670 | if (i > last) { 671 | out.append(input, last, i - last); 672 | } 673 | last = i + 1; 674 | } else if (ch == '>') { 675 | last = i + 1; 676 | } 677 | } 678 | if (last == 0) { 679 | return in; 680 | } 681 | if (i > last) { 682 | out.append(input, last, i - last); 683 | } 684 | return out.toString(); 685 | } 686 | 687 | /** 688 | * This method takes a string which may contain HTML tags (ie, <b>, 689 | * <table>, etc) and converts the '<'' and '>' characters to 690 | * their HTML escape sequences. It will also replace LF with <br>. 691 | * 692 | * @param in the text to be converted. 693 | * @return the input string with the characters '<' and '>' replaced 694 | * with their HTML escape sequences. 695 | */ 696 | public static String escapeHTMLTags(String in) { 697 | if (in == null) { 698 | return null; 699 | } 700 | char ch; 701 | int i = 0; 702 | int last = 0; 703 | char[] input = in.toCharArray(); 704 | int len = input.length; 705 | StringBuilder out = new StringBuilder((int) (len * 1.3)); 706 | for (; i < len; i++) { 707 | ch = input[i]; 708 | if (ch > '>') { 709 | } else if (ch == '<') { 710 | if (i > last) { 711 | out.append(input, last, i - last); 712 | } 713 | last = i + 1; 714 | out.append(LT_ENCODE); 715 | } else if (ch == '>') { 716 | if (i > last) { 717 | out.append(input, last, i - last); 718 | } 719 | last = i + 1; 720 | out.append(GT_ENCODE); 721 | } else if (ch == '\n') { 722 | if (i > last) { 723 | out.append(input, last, i - last); 724 | } 725 | last = i + 1; 726 | out.append("
"); 727 | } 728 | } 729 | if (last == 0) { 730 | return in; 731 | } 732 | if (i > last) { 733 | out.append(input, last, i - last); 734 | } 735 | return out.toString(); 736 | } 737 | /** 738 | * Used by the hash method. 739 | */ 740 | private static Map digests 741 | = new ConcurrentHashMap(); 742 | 743 | /** 744 | * Hashes a String using the Md5 algorithm and returns the result as a 745 | * String of hexadecimal numbers. This method is synchronized to avoid 746 | * excessive MessageDigest object creation. If calling this method becomes a 747 | * bottleneck in your code, you may wish to maintain a pool of MessageDigest 748 | * objects instead of using this method. 749 | *

750 | * A hash is a one-way function -- that is, given an input, an output is 751 | * easily computed. However, given the output, the input is almost 752 | * impossible to compute. This is useful for passwords since we can store 753 | * the hash and a hacker will then have a very hard time determining the 754 | * original password. 755 | *

756 | * In Jive, every time a user logs in, we simply take their plain text 757 | * password, compute the hash, and compare the generated hash to the stored 758 | * hash. Since it is almost impossible that two passwords will generate the 759 | * same hash, we know if the user gave us the correct password or not. The 760 | * only negative to this system is that password recovery is basically 761 | * impossible. Therefore, a reset password method is used instead. 762 | * 763 | * @param data the String to compute the hash of. 764 | * @return a hashed version of the passed-in String 765 | */ 766 | public static String hash(String data) { 767 | return hash(data, "MD5"); 768 | } 769 | 770 | /** 771 | * Hashes a String using the specified algorithm and returns the result as a 772 | * String of hexadecimal numbers. This method is synchronized to avoid 773 | * excessive MessageDigest object creation. If calling this method becomes a 774 | * bottleneck in your code, you may wish to maintain a pool of MessageDigest 775 | * objects instead of using this method. 776 | *

777 | * A hash is a one-way function -- that is, given an input, an output is 778 | * easily computed. However, given the output, the input is almost 779 | * impossible to compute. This is useful for passwords since we can store 780 | * the hash and a hacker will then have a very hard time determining the 781 | * original password. 782 | *

783 | * In Jive, every time a user logs in, we simply take their plain text 784 | * password, compute the hash, and compare the generated hash to the stored 785 | * hash. Since it is almost impossible that two passwords will generate the 786 | * same hash, we know if the user gave us the correct password or not. The 787 | * only negative to this system is that password recovery is basically 788 | * impossible. Therefore, a reset password method is used instead. 789 | * 790 | * @param data the String to compute the hash of. 791 | * @param algorithm the name of the algorithm requested. 792 | * @return a hashed version of the passed-in String 793 | */ 794 | public static String hash(String data, String algorithm) { 795 | try { 796 | return hash(data.getBytes("UTF-8"), algorithm); 797 | } catch (UnsupportedEncodingException e) { 798 | Log.error(e.getMessage(), e); 799 | } 800 | return data; 801 | } 802 | 803 | /** 804 | * Hashes a byte array using the specified algorithm and returns the result 805 | * as a String of hexadecimal numbers. This method is synchronized to avoid 806 | * excessive MessageDigest object creation. If calling this method becomes a 807 | * bottleneck in your code, you may wish to maintain a pool of MessageDigest 808 | * objects instead of using this method. 809 | *

810 | * A hash is a one-way function -- that is, given an input, an output is 811 | * easily computed. However, given the output, the input is almost 812 | * impossible to compute. This is useful for passwords since we can store 813 | * the hash and a hacker will then have a very hard time determining the 814 | * original password. 815 | *

816 | * In Jive, every time a user logs in, we simply take their plain text 817 | * password, compute the hash, and compare the generated hash to the stored 818 | * hash. Since it is almost impossible that two passwords will generate the 819 | * same hash, we know if the user gave us the correct password or not. The 820 | * only negative to this system is that password recovery is basically 821 | * impossible. Therefore, a reset password method is used instead. 822 | * 823 | * @param bytes the byte array to compute the hash of. 824 | * @param algorithm the name of the algorithm requested. 825 | * @return a hashed version of the passed-in String 826 | */ 827 | public static String hash(byte[] bytes, String algorithm) { 828 | synchronized (algorithm.intern()) { 829 | MessageDigest digest = digests.get(algorithm); 830 | if (digest == null) { 831 | try { 832 | digest = MessageDigest.getInstance(algorithm); 833 | digests.put(algorithm, digest); 834 | } catch (NoSuchAlgorithmException nsae) { 835 | Log.error("Failed to load the " + algorithm + " MessageDigest. " 836 | + "Jive will be unable to function normally.", nsae); 837 | return null; 838 | } 839 | } 840 | // Now, compute hash. 841 | digest.update(bytes); 842 | return encodeHex(digest.digest()); 843 | } 844 | } 845 | 846 | /** 847 | * Turns an array of bytes into a String representing each byte as an 848 | * unsigned hex number. 849 | *

850 | * Method by Santeri Paavolainen, Helsinki Finland 1996
(c) Santeri 851 | * Paavolainen, Helsinki Finland 1996
Distributed under LGPL. 852 | * 853 | * @param bytes an array of bytes to convert to a hex-string 854 | * @return generated hex string 855 | */ 856 | public static String encodeHex(byte[] bytes) { 857 | StringBuilder buf = new StringBuilder(bytes.length * 2); 858 | int i; 859 | 860 | for (i = 0; i < bytes.length; i++) { 861 | if (((int) bytes[i] & 0xff) < 0x10) { 862 | buf.append("0"); 863 | } 864 | buf.append(Long.toString((int) bytes[i] & 0xff, 16)); 865 | } 866 | return buf.toString(); 867 | } 868 | 869 | /** 870 | * Turns a hex encoded string into a byte array. It is specifically meant to 871 | * "reverse" the toHex(byte[]) method. 872 | * 873 | * @param hex a hex encoded String to transform into a byte array. 874 | * @return a byte array representing the hex String[ 875 | */ 876 | public static byte[] decodeHex(String hex) { 877 | char[] chars = hex.toCharArray(); 878 | byte[] bytes = new byte[chars.length / 2]; 879 | int byteCount = 0; 880 | for (int i = 0; i < chars.length; i += 2) { 881 | int newByte = 0x00; 882 | newByte |= hexCharToByte(chars[i]); 883 | newByte <<= 4; 884 | newByte |= hexCharToByte(chars[i + 1]); 885 | bytes[byteCount] = (byte) newByte; 886 | byteCount++; 887 | } 888 | return bytes; 889 | } 890 | 891 | /** 892 | * Returns the the byte value of a hexadecmical char (0-f). It's assumed 893 | * that the hexidecimal chars are lower case as appropriate. 894 | * 895 | * @param ch a hexedicmal character (0-f) 896 | * @return the byte value of the character (0x00-0x0F) 897 | */ 898 | private static byte hexCharToByte(char ch) { 899 | switch (ch) { 900 | case '0': 901 | return 0x00; 902 | case '1': 903 | return 0x01; 904 | case '2': 905 | return 0x02; 906 | case '3': 907 | return 0x03; 908 | case '4': 909 | return 0x04; 910 | case '5': 911 | return 0x05; 912 | case '6': 913 | return 0x06; 914 | case '7': 915 | return 0x07; 916 | case '8': 917 | return 0x08; 918 | case '9': 919 | return 0x09; 920 | case 'a': 921 | return 0x0A; 922 | case 'b': 923 | return 0x0B; 924 | case 'c': 925 | return 0x0C; 926 | case 'd': 927 | return 0x0D; 928 | case 'e': 929 | return 0x0E; 930 | case 'f': 931 | return 0x0F; 932 | } 933 | return 0x00; 934 | } 935 | 936 | 937 | /** 938 | * Converts a line of text into an array of lower case words using a 939 | * BreakIterator.wordInstance() 940 | * .

941 | * 942 | * This method is under the Jive Open Source Software License and was 943 | * written by Mark Imbriaco. 944 | * 945 | * @param text a String of text to convert into an array of words 946 | * @return text broken up into an array of words. 947 | */ 948 | public static String[] toLowerCaseWordArray(String text) { 949 | if (text == null || text.length() == 0) { 950 | return new String[0]; 951 | } 952 | 953 | List wordList = new ArrayList(); 954 | BreakIterator boundary = BreakIterator.getWordInstance(); 955 | boundary.setText(text); 956 | int start = 0; 957 | 958 | for (int end = boundary.next(); end != BreakIterator.DONE; 959 | start = end, end = boundary.next()) { 960 | String tmp = text.substring(start, end).trim(); 961 | // Remove characters that are not needed. 962 | tmp = replace(tmp, "+", ""); 963 | tmp = replace(tmp, "/", ""); 964 | tmp = replace(tmp, "\\", ""); 965 | tmp = replace(tmp, "#", ""); 966 | tmp = replace(tmp, "*", ""); 967 | tmp = replace(tmp, ")", ""); 968 | tmp = replace(tmp, "(", ""); 969 | tmp = replace(tmp, "&", ""); 970 | if (tmp.length() > 0) { 971 | wordList.add(tmp); 972 | } 973 | } 974 | return wordList.toArray(new String[wordList.size()]); 975 | } 976 | /** 977 | * Pseudo-random number generator object for use with randomString(). The 978 | * Random class is not considered to be cryptographically secure, so only 979 | * use these random Strings for low to medium security applications. 980 | */ 981 | private static Random randGen = new Random(); 982 | /** 983 | * Array of numbers and letters of mixed case. Numbers appear in the list 984 | * twice so that there is a more equal chance that a number will be picked. 985 | * We can use the array to get a random number or letter by picking a random 986 | * array index. 987 | */ 988 | private static char[] numbersAndLetters = ("0123456789abcdefghijklmnopqrstuvwxyz" 989 | + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray(); 990 | 991 | /** 992 | * Returns a random String of numbers and letters (lower and upper case) of 993 | * the specified length. The method uses the Random class that is built-in 994 | * to Java which is suitable for low to medium grade security uses. This 995 | * means that the output is only pseudo random, i.e., each number is 996 | * mathematically generated so is not truly random.

997 | *

998 | * The specified length must be at least one. If not, the method will return 999 | * null. 1000 | * 1001 | * @param length the desired length of the random String to return. 1002 | * @return a random String of numbers and letters of the specified length. 1003 | */ 1004 | public static String randomString(int length) { 1005 | if (length < 1) { 1006 | return null; 1007 | } 1008 | // Create a char buffer to put random letters and numbers in. 1009 | char[] randBuffer = new char[length]; 1010 | for (int i = 0; i < randBuffer.length; i++) { 1011 | randBuffer[i] = numbersAndLetters[randGen.nextInt(71)]; 1012 | } 1013 | return new String(randBuffer); 1014 | } 1015 | 1016 | /** 1017 | * Intelligently chops a String at a word boundary (whitespace) that occurs 1018 | * at the specified index in the argument or before. However, if there is a 1019 | * newline character before length, the String will be chopped 1020 | * there. If no newline or whitespace is found in string up to 1021 | * the index length, the String will chopped at 1022 | * length. 1023 | *

1024 | * For example, chopAtWord("This is a nice String", 10) will return "This is 1025 | * a" which is the first word boundary less than or equal to 10 characters 1026 | * into the original String. 1027 | * 1028 | * @param string the String to chop. 1029 | * @param length the index in string to start looking for a 1030 | * whitespace boundary at. 1031 | * @return a substring of string whose length is less than or 1032 | * equal to length, and that is chopped at whitespace. 1033 | */ 1034 | public static String chopAtWord(String string, int length) { 1035 | if (string == null || string.length() == 0) { 1036 | return string; 1037 | } 1038 | 1039 | char[] charArray = string.toCharArray(); 1040 | int sLength = string.length(); 1041 | if (length < sLength) { 1042 | sLength = length; 1043 | } 1044 | 1045 | // First check if there is a newline character before length; if so, 1046 | // chop word there. 1047 | for (int i = 0; i < sLength - 1; i++) { 1048 | // Windows 1049 | if (charArray[i] == '\r' && charArray[i + 1] == '\n') { 1050 | return string.substring(0, i + 1); 1051 | } // Unix 1052 | else if (charArray[i] == '\n') { 1053 | return string.substring(0, i); 1054 | } 1055 | } 1056 | // Also check boundary case of Unix newline 1057 | if (charArray[sLength - 1] == '\n') { 1058 | return string.substring(0, sLength - 1); 1059 | } 1060 | 1061 | // Done checking for newline, now see if the total string is less than 1062 | // the specified chop point. 1063 | if (string.length() < length) { 1064 | return string; 1065 | } 1066 | 1067 | // No newline, so chop at the first whitespace. 1068 | for (int i = length - 1; i > 0; i--) { 1069 | if (charArray[i] == ' ') { 1070 | return string.substring(0, i).trim(); 1071 | } 1072 | } 1073 | 1074 | // Did not find word boundary so return original String chopped at 1075 | // specified length. 1076 | return string.substring(0, length); 1077 | } 1078 | 1079 | /** 1080 | * Escapes all necessary characters in the String so that it can be used in 1081 | * SQL 1082 | * 1083 | * @param string the string to escape. 1084 | * @return the string with appropriate characters escaped. 1085 | */ 1086 | public static String escapeForSQL(String string) { 1087 | if (string == null) { 1088 | return null; 1089 | } else if (string.length() == 0) { 1090 | return string; 1091 | } 1092 | 1093 | char ch; 1094 | char[] input = string.toCharArray(); 1095 | int i = 0; 1096 | int last = 0; 1097 | int len = input.length; 1098 | StringBuilder out = null; 1099 | for (; i < len; i++) { 1100 | ch = input[i]; 1101 | 1102 | if (ch == '\'') { 1103 | if (out == null) { 1104 | out = new StringBuilder(len + 2); 1105 | } 1106 | if (i > last) { 1107 | out.append(input, last, i - last); 1108 | } 1109 | last = i + 1; 1110 | out.append('\'').append('\''); 1111 | } 1112 | } 1113 | 1114 | if (out == null) { 1115 | return string; 1116 | } else if (i > last) { 1117 | out.append(input, last, i - last); 1118 | } 1119 | 1120 | return out.toString(); 1121 | } 1122 | 1123 | /** 1124 | * Escapes all necessary characters in the String so that it can be used in 1125 | * an XML doc. 1126 | * 1127 | * @param string the string to escape. 1128 | * @return the string with appropriate characters escaped. 1129 | */ 1130 | public static String escapeForXML(String string) { 1131 | if (string == null) { 1132 | return null; 1133 | } 1134 | char ch; 1135 | int i = 0; 1136 | int last = 0; 1137 | char[] input = string.toCharArray(); 1138 | int len = input.length; 1139 | StringBuilder out = new StringBuilder((int) (len * 1.3)); 1140 | for (; i < len; i++) { 1141 | ch = input[i]; 1142 | if (ch > '>') { 1143 | } else if (ch == '<') { 1144 | if (i > last) { 1145 | out.append(input, last, i - last); 1146 | } 1147 | last = i + 1; 1148 | out.append(LT_ENCODE); 1149 | } else if (ch == '&') { 1150 | if (i > last) { 1151 | out.append(input, last, i - last); 1152 | } 1153 | last = i + 1; 1154 | out.append(AMP_ENCODE); 1155 | } else if (ch == '"') { 1156 | if (i > last) { 1157 | out.append(input, last, i - last); 1158 | } 1159 | last = i + 1; 1160 | out.append(QUOTE_ENCODE); 1161 | } 1162 | } 1163 | if (last == 0) { 1164 | return string; 1165 | } 1166 | if (i > last) { 1167 | out.append(input, last, i - last); 1168 | } 1169 | return out.toString(); 1170 | } 1171 | 1172 | /** 1173 | * Unescapes the String by converting XML escape sequences back into normal 1174 | * characters. 1175 | * 1176 | * @param string the string to unescape. 1177 | * @return the string with appropriate characters unescaped. 1178 | */ 1179 | public static String unescapeFromXML(String string) { 1180 | string = replace(string, "<", "<"); 1181 | string = replace(string, ">", ">"); 1182 | string = replace(string, """, "\""); 1183 | return replace(string, "&", "&"); 1184 | } 1185 | private static final char[] zeroArray 1186 | = "0000000000000000000000000000000000000000000000000000000000000000".toCharArray(); 1187 | 1188 | /** 1189 | * Pads the supplied String with 0's to the specified length and returns the 1190 | * result as a new String. For example, if the initial String is "9999" and 1191 | * the desired length is 8, the result would be "00009999". This type of 1192 | * padding is useful for creating numerical values that need to be stored 1193 | * and sorted as character data. Note: the current implementation of this 1194 | * method allows for a maximum length of 64. 1195 | * 1196 | * @param string the original String to pad. 1197 | * @param length the desired length of the new padded String. 1198 | * @return a new String padded with the required number of 0's. 1199 | */ 1200 | public static String zeroPadString(String string, int length) { 1201 | if (string == null || string.length() > length) { 1202 | return string; 1203 | } 1204 | StringBuilder buf = new StringBuilder(length); 1205 | buf.append(zeroArray, 0, length - string.length()).append(string); 1206 | return buf.toString(); 1207 | } 1208 | 1209 | /** 1210 | * Formats a Date as a fifteen character long String made up of the Date's 1211 | * padded millisecond value. 1212 | * 1213 | * @param date 1214 | * @return a Date encoded as a String. 1215 | */ 1216 | public static String dateToMillis(Date date) { 1217 | return zeroPadString(Long.toString(date.getTime()), 15); 1218 | } 1219 | 1220 | /** 1221 | * Returns a formatted String from time. 1222 | * 1223 | * @param diff the amount of elapsed time. 1224 | * @return the formatte String. 1225 | */ 1226 | public static String getTimeFromLong(long diff) { 1227 | final String HOURS = "h"; 1228 | final String MINUTES = "min"; 1229 | //final String SECONDS = "sec"; 1230 | 1231 | final long MS_IN_A_DAY = 1000 * 60 * 60 * 24; 1232 | final long MS_IN_AN_HOUR = 1000 * 60 * 60; 1233 | final long MS_IN_A_MINUTE = 1000 * 60; 1234 | final long MS_IN_A_SECOND = 1000; 1235 | //Date currentTime = new Date(); 1236 | //long numDays = diff / MS_IN_A_DAY; 1237 | diff = diff % MS_IN_A_DAY; 1238 | long numHours = diff / MS_IN_AN_HOUR; 1239 | diff = diff % MS_IN_AN_HOUR; 1240 | long numMinutes = diff / MS_IN_A_MINUTE; 1241 | diff = diff % MS_IN_A_MINUTE; 1242 | //long numSeconds = diff / MS_IN_A_SECOND; 1243 | diff = diff % MS_IN_A_SECOND; 1244 | //long numMilliseconds = diff; 1245 | 1246 | StringBuffer buf = new StringBuffer(); 1247 | if (numHours > 0) { 1248 | buf.append(numHours + " " + HOURS + ", "); 1249 | } 1250 | 1251 | if (numMinutes > 0) { 1252 | buf.append(numMinutes + " " + MINUTES); 1253 | } 1254 | 1255 | //buf.append(numSeconds + " " + SECONDS); 1256 | String result = buf.toString(); 1257 | 1258 | if (numMinutes < 1) { 1259 | result = "< 1 minute"; 1260 | } 1261 | 1262 | return result; 1263 | } 1264 | 1265 | public static boolean isBlank(String s) { 1266 | if (s == null) { 1267 | return true; 1268 | } 1269 | if (s.trim().length() == 0) { 1270 | return true; 1271 | } 1272 | return false; 1273 | } 1274 | 1275 | public static boolean isEmpty(String s) { 1276 | return isBlank(s); 1277 | } 1278 | 1279 | /** 1280 | * 1281 | * @param str 1282 | * @return 1283 | * @author mclaren.pan 1284 | */ 1285 | public static boolean isNotBlank(String str) { 1286 | return !isBlank(str); 1287 | } 1288 | 1289 | /** 1290 | * Returns a collection of Strings as a comma-delimitted list of strings. 1291 | * 1292 | * @return a String representing the Collection. 1293 | */ 1294 | public static String collectionToString(Collection collection) { 1295 | if (collection == null || collection.isEmpty()) { 1296 | return ""; 1297 | } 1298 | StringBuilder buf = new StringBuilder(); 1299 | String delim = ""; 1300 | for (String element : collection) { 1301 | buf.append(delim); 1302 | buf.append(element); 1303 | delim = ","; 1304 | } 1305 | return buf.toString(); 1306 | } 1307 | 1308 | /** 1309 | * Returns a comma-delimitted list of Strings as a Collection. 1310 | * 1311 | * @return a Collection representing the String. 1312 | */ 1313 | public static Collection stringToCollection(String string) { 1314 | if (string == null || string.trim().length() == 0) { 1315 | return Collections.emptyList(); 1316 | } 1317 | Collection collection = new ArrayList(); 1318 | StringTokenizer tokens = new StringTokenizer(string, ","); 1319 | while (tokens.hasMoreTokens()) { 1320 | collection.add(tokens.nextToken().trim()); 1321 | } 1322 | return collection; 1323 | } 1324 | 1325 | /** 1326 | * Abbreviates a string to a specified length and then adds an ellipsis if 1327 | * the input is greater than the maxWidth. Example input: 1328 | *

1329 |      *      user1@jivesoftware.com/home
1330 |      * 
and a maximum length of 20 characters, the abbreviate method will 1331 | * return: 1332 | *
1333 |      *      user1@jivesoftware.c...
1334 |      * 
1335 | * 1336 | * @param str the String to abbreviate. 1337 | * @param maxWidth the maximum size of the string, minus the ellipsis. 1338 | * @return the abbreviated String, or null if the string was 1339 | * null. 1340 | */ 1341 | public static String abbreviate(String str, int maxWidth) { 1342 | if (null == str) { 1343 | return null; 1344 | } 1345 | 1346 | if (str.length() <= maxWidth) { 1347 | return str; 1348 | } 1349 | 1350 | return str.substring(0, maxWidth) + "..."; 1351 | } 1352 | 1353 | /** 1354 | * Removes characters likely to enable Cross Site Scripting attacks from the 1355 | * provided input string. The characters that are removed from the input 1356 | * string, if present, are: 1357 | * 1358 | *
1359 |      * < > " ' % ; ) ( & + -
1360 |      * 
1361 | * 1362 | * @param string input 1363 | * @return Input without certain characters; 1364 | */ 1365 | public static String removeXSSCharacters(String input) { 1366 | final String[] xss = {"<", ">", "\"", "'", "%", ";", ")", "(", "&", 1367 | "+", "-"}; 1368 | for (int i = 0; i < xss.length; i++) { 1369 | input = input.replace(xss[i], ""); 1370 | } 1371 | return input; 1372 | } 1373 | 1374 | public static String valueOf(Object object) { 1375 | 1376 | return valueOf(object, "").trim(); 1377 | } 1378 | 1379 | public static String valueOf(Object object, String defaultEmptyValue) { 1380 | 1381 | if (object == null) { 1382 | return defaultEmptyValue; 1383 | } 1384 | 1385 | return String.valueOf(object); 1386 | } 1387 | /** 1388 | * 判断对象是否为空 1389 | * @param object 1390 | * @return 1391 | */ 1392 | public static boolean isEmptyOrNull(Object object) { 1393 | 1394 | if (StringUtils.valueOf(object).equals("")) { 1395 | return true; 1396 | } 1397 | if(StringUtils.valueOf(object).equals("null")){ 1398 | return true; 1399 | } 1400 | return false; 1401 | } 1402 | /** 1403 | * 判断对象是否为,如果为空 1404 | * @param object 1405 | * @return 1406 | */ 1407 | public static boolean isNotEmptyOrNull(Object object) { 1408 | return !isEmptyOrNull(object); 1409 | } 1410 | 1411 | 1412 | /** 1413 | * 1414 | * @Title: 去除字符串中的首尾空格、回车、换行符、制表符 1415 | * @Description: 1416 | * @Version 1.0 1417 | * @param str 1418 | * @return 1419 | */ 1420 | public static String replaceBlank(String str) { 1421 | String dest = ""; 1422 | if (str!=null) { 1423 | Pattern p = Pattern.compile("\\t|\r|\n"); 1424 | Matcher m = p.matcher(str); 1425 | dest = m.replaceAll(""); 1426 | return dest.trim(); 1427 | } 1428 | return ""; 1429 | } 1430 | 1431 | /** 1432 | * 仅将形如{0}替换为参数,不支持字符转义 1433 | * 1434 | * @param text 1435 | * @param paramsArray 1436 | * @return 1437 | */ 1438 | public static String format(String text, String[] paramsArray) { 1439 | for (int i = 0; i < paramsArray.length; i++) { 1440 | String param = paramsArray[i]; 1441 | if (param != null) { 1442 | text = text.replace("{" + i + "}", param); 1443 | } 1444 | } 1445 | 1446 | return text; 1447 | } 1448 | 1449 | } 1450 | -------------------------------------------------------------------------------- /louie-order/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | louie 5 | louie-order 6 | war 7 | 0.0.1-SNAPSHOT 8 | louie-order 9 | 10 | 11 | 12 | http-dubbo-zipkin 13 | louie 14 | 0.0.1-SNAPSHOT 15 | 16 | 17 | 18 | 4.3.3.RELEASE 19 | 1.9.10 20 | 1.7 21 | 4.0.6 22 | 0.6.12 23 | 2.8.41 24 | 25 | 26 | 27 | 28 | maven2 29 | maven2 30 | http://central.maven.org/maven2/ 31 | 32 | 33 | 34 | 35 | 36 | louie 37 | louie-common 38 | 0.0.1-SNAPSHOT 39 | 40 | 41 | 42 | 43 | eshop 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-eclipse-plugin 48 | 2.9 49 | 50 | true 51 | false 52 | 2.0 53 | 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-compiler-plugin 58 | 2.3.2 59 | 60 | 1.7 61 | 1.7 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /louie-order/src/main/java/com/louie/order/dto/OrderDto.java: -------------------------------------------------------------------------------- 1 | package com.louie.order.dto; 2 | 3 | public class OrderDto { 4 | private String goodsName; 5 | private Double price; 6 | 7 | public OrderDto() { 8 | super(); 9 | // TODO Auto-generated constructor stub 10 | } 11 | 12 | public OrderDto(String goodsName, Double price) { 13 | super(); 14 | this.goodsName = goodsName; 15 | this.price = price; 16 | } 17 | 18 | public String getGoodsName() { 19 | return goodsName; 20 | } 21 | public void setGoodsName(String goodsName) { 22 | this.goodsName = goodsName; 23 | } 24 | public Double getPrice() { 25 | return price; 26 | } 27 | public void setPrice(Double price) { 28 | this.price = price; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /louie-order/src/main/java/com/louie/order/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.louie.order.service; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.springframework.context.annotation.Lazy; 7 | import org.springframework.stereotype.Service; 8 | 9 | import com.louie.core.CallParams; 10 | import com.louie.core.CallResult; 11 | import com.louie.core.CommonAdapter; 12 | import com.louie.core.ServiceCall; 13 | import com.louie.order.dto.OrderDto; 14 | import com.louie.utils.JsonUtils; 15 | 16 | /** 17 | * 18 | * @author 19 | * 20 | */ 21 | @Lazy 22 | @Service("order.customer") 23 | public class OrderService{ 24 | 25 | /** 26 | * 订单详情 27 | * @param params 28 | * @return 29 | */ 30 | public CallResult orderInfo(CallParams params){ 31 | System.out.println("request params:"+params.getData()); 32 | Map userParams = new HashMap(); 33 | userParams.put("token", params.get("token")); 34 | ServiceCall call = new ServiceCall(new CommonAdapter("account.user.info",userParams)); 35 | CallResult cr = call.invoke(); 36 | if(!cr.getCode().equals(0)) return cr; 37 | String userId = (String) cr.get("userId"); 38 | OrderDto od = getOrder(userId,(Integer)params.get("orderId")); 39 | CallResult result = new CallResult(); 40 | Map data = new HashMap(); 41 | data .putAll(JsonUtils.beanToMap(od)); 42 | result.setData(data); 43 | 44 | return result; 45 | } 46 | 47 | private OrderDto getOrder(String uid,Integer orderId){ 48 | OrderDto od = new OrderDto("computer",5200d); 49 | return od; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /louie-order/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter: -------------------------------------------------------------------------------- 1 | traceFilters=com.louie.common.dubbo.DrpcServerInterceptor 2 | traceFilterc=com.louie.common.dubbo.DrpcClientInterceptor -------------------------------------------------------------------------------- /louie-order/src/main/resources/application-dubbo.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /louie-order/src/main/resources/application-mvc.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /louie-order/src/main/resources/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 13 | 14 | 15 | 16 | classpath*:brave.properties 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /louie-order/src/main/resources/brave.properties: -------------------------------------------------------------------------------- 1 | brave.name=order 2 | http.sender.address=http://127.0.0.1:9411/api/v1/spans -------------------------------------------------------------------------------- /louie-order/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.appender.Stdout=org.apache.log4j.ConsoleAppender 2 | log4j.appender.Stdout.layout=org.apache.log4j.PatternLayout 3 | log4j.appender.Stdout.layout.conversionPattern=%-5p - %-26.26c{1} - %m\n 4 | 5 | log4j.rootLogger=INFO,Stdout 6 | 7 | log4j.logger.org.apache.wicket=INFO 8 | log4j.logger.org.apache.wicket.protocol.http.HttpSessionStore=INFO 9 | log4j.logger.org.apache.wicket.version=INFO 10 | log4j.logger.org.apache.wicket.RequestCycle=INFO -------------------------------------------------------------------------------- /louie-order/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | Spring Web MVC Application 7 | 8 | 9 | mvc-dispatcher 10 | org.springframework.web.servlet.DispatcherServlet 11 | 12 | 13 | contextConfigLocation 14 | classpath:applicationContext.xml 15 | 16 | 17 | 1 18 | 19 | 20 | 21 | mvc-dispatcher 22 | *.do 23 | 24 | 25 | 26 | contextConfigLocation 27 | classpath:applicationContext.xml 28 | 29 | 30 | 31 | org.springframework.web.context.ContextLoaderListener 32 | 33 | 34 | -------------------------------------------------------------------------------- /louie-webapi/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | louie 6 | louie-webapi 7 | 0.0.1-SNAPSHOT 8 | war 9 | 10 | louie-webapi 11 | 12 | 13 | 14 | 15 | http-dubbo-zipkin 16 | louie 17 | 0.0.1-SNAPSHOT 18 | 19 | 20 | 21 | UTF-8 22 | 4.3.3.RELEASE 23 | 8.1.20.v20160902 24 | 4.0.6 25 | 0.6.12 26 | 2.8.41 27 | 28 | 29 | 30 | 31 | javax.servlet 32 | servlet-api 33 | 3.0.1 34 | 35 | 36 | 37 | louie 38 | louie-common 39 | 0.0.1-SNAPSHOT 40 | 41 | 42 | 43 | 44 | 45 | true 46 | org.apache.maven.plugins 47 | maven-compiler-plugin 48 | 2.5.1 49 | 50 | 1.7 51 | 1.7 52 | true 53 | true 54 | 55 | 56 | 57 | org.apache.maven.plugins 58 | maven-failsafe-plugin 59 | 2.19.1 60 | 61 | 62 | integration-test 63 | 64 | integration-test 65 | 66 | 67 | 68 | verify 69 | 70 | verify 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-war-plugin 78 | 3.0.0 79 | 80 | 81 | 82 | ${basedir}/src/main/webapp 83 | true 84 | 85 | **/index.html 86 | 87 | 88 | 89 | ${basedir}/src/main/webapp/WEB-INF 90 | true 91 | WEB-INF 92 | 93 | **/web.xml 94 | 95 | 96 | 97 | WEB-INF/lib/servlet-api-*.jar 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /louie-webapi/src/main/java/com/louie/HttpTraceBeans.java: -------------------------------------------------------------------------------- 1 | package com.louie; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.core.annotation.Order; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | import com.github.kristofa.brave.Brave; 9 | import com.github.kristofa.brave.http.DefaultSpanNameProvider; 10 | import com.github.kristofa.brave.http.SpanNameProvider; 11 | 12 | import zipkin.Span; 13 | import zipkin.reporter.AsyncReporter; 14 | import zipkin.reporter.Reporter; 15 | import zipkin.reporter.Sender; 16 | import zipkin.reporter.okhttp3.OkHttpSender; 17 | 18 | @Configuration 19 | @Order(value=1) 20 | public class HttpTraceBeans { 21 | @Bean RestTemplate restTemplate(){ 22 | return new RestTemplate(); 23 | } 24 | // Configuration for how to send spans to Zipkin 25 | @Bean Sender sender() { 26 | return OkHttpSender.create("http://127.0.0.1:9411/api/v1/spans"); 27 | //return LibthriftSender.create("127.0.0.1"); 28 | // return KafkaSender.create("127.0.0.1:9092"); 29 | } 30 | // Configuration for how to buffer spans into messages for Zipkin 31 | @Bean Reporter reporter() { 32 | //return new LoggingReporter(); 33 | // uncomment to actually send to zipkin! 34 | return AsyncReporter.builder(sender()).build(); 35 | } 36 | 37 | @Bean Brave brave() { 38 | return new Brave.Builder("webapi").reporter(reporter()).build(); 39 | } 40 | 41 | // decide how to name spans. By default they are named the same as the http method. 42 | @Bean SpanNameProvider spanNameProvider() { 43 | return new DefaultSpanNameProvider(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /louie-webapi/src/main/java/com/louie/controller/ApiController.java: -------------------------------------------------------------------------------- 1 | package com.louie.controller; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 8 | 9 | import com.louie.core.CallResult; 10 | import com.louie.core.ServiceCall; 11 | import com.louie.utils.JsonUtils; 12 | 13 | @RestController 14 | @EnableWebMvc 15 | public class ApiController { 16 | 17 | @RequestMapping("/service") 18 | public String b( HttpServletRequest request) throws InterruptedException { 19 | 20 | ServiceCall call = new ServiceCall(new ServletAdapter(request)); 21 | CallResult cr = call.invoke(); 22 | 23 | return JsonUtils.object2JSON(cr); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /louie-webapi/src/main/java/com/louie/controller/ServletAdapter.java: -------------------------------------------------------------------------------- 1 | package com.louie.controller; 2 | 3 | import java.util.Map; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | 7 | import com.louie.core.ServiceCallAdapter; 8 | import com.louie.utils.JsonUtils; 9 | 10 | public class ServletAdapter implements ServiceCallAdapter { 11 | 12 | private HttpServletRequest request; 13 | 14 | public ServletAdapter(HttpServletRequest request) { 15 | this.request = request; 16 | } 17 | 18 | @Override 19 | public String getName() { 20 | return request.getParameter("service"); 21 | } 22 | 23 | @Override 24 | public Map getData() { 25 | String datastr = request.getParameter("data"); 26 | return JsonUtils.json2Map(datastr); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /louie-webapi/src/main/java/com/louie/trace/CusHttpServerRequestAdapter.java: -------------------------------------------------------------------------------- 1 | package com.louie.trace; 2 | 3 | import static com.github.kristofa.brave.IdConversion.convertToLong; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collection; 7 | import java.util.List; 8 | 9 | import com.github.kristofa.brave.KeyValueAnnotation; 10 | import com.github.kristofa.brave.ServerRequestAdapter; 11 | import com.github.kristofa.brave.SpanId; 12 | import com.github.kristofa.brave.TraceData; 13 | import com.github.kristofa.brave.http.BraveHttpHeaders; 14 | import com.github.kristofa.brave.http.HttpServerRequest; 15 | import com.github.kristofa.brave.http.SpanNameProvider; 16 | 17 | public class CusHttpServerRequestAdapter implements ServerRequestAdapter { 18 | private final HttpServerRequest request; 19 | private final SpanNameProvider spanNameProvider; 20 | private final String service; 21 | private final String data; 22 | 23 | public CusHttpServerRequestAdapter(HttpServerRequest request,String service,String data, SpanNameProvider spanNameProvider) { 24 | this.request = request; 25 | this.spanNameProvider = spanNameProvider; 26 | this.service = service; 27 | this.data = data; 28 | } 29 | 30 | public TraceData getTraceData() { 31 | String sampled = request.getHttpHeaderValue(BraveHttpHeaders.Sampled.getName()); 32 | String parentSpanId = request.getHttpHeaderValue(BraveHttpHeaders.ParentSpanId.getName()); 33 | String traceId = request.getHttpHeaderValue(BraveHttpHeaders.TraceId.getName()); 34 | String spanId = request.getHttpHeaderValue(BraveHttpHeaders.SpanId.getName()); 35 | 36 | // Official sampled value is 1, though some old instrumentation send true 37 | Boolean parsedSampled = sampled != null 38 | ? sampled.equals("1") || sampled.equalsIgnoreCase("true") 39 | : null; 40 | 41 | if (traceId != null && spanId != null) { 42 | return TraceData.create(getSpanId(traceId, spanId, parentSpanId, parsedSampled)); 43 | } else if (parsedSampled == null) { 44 | return TraceData.EMPTY; 45 | } else if (parsedSampled.booleanValue()) { 46 | // Invalid: The caller requests the trace to be sampled, but didn't pass IDs 47 | return TraceData.EMPTY; 48 | } else { 49 | return TraceData.NOT_SAMPLED; 50 | } 51 | } 52 | 53 | public String getSpanName() { 54 | return spanNameProvider.spanName(request); 55 | } 56 | 57 | public Collection requestAnnotations() { 58 | KeyValueAnnotation sera = KeyValueAnnotation.create( 59 | "service", service); 60 | KeyValueAnnotation dataa = KeyValueAnnotation.create( 61 | "data", data); 62 | List kas = new ArrayList(); 63 | kas.add(sera); 64 | kas.add(dataa); 65 | return kas; 66 | //return Collections..singleton(sera); 67 | } 68 | 69 | static SpanId getSpanId(String traceId, String spanId, String parentSpanId, Boolean sampled) { 70 | return SpanId.builder() 71 | .traceIdHigh(traceId.length() == 32 ? convertToLong(traceId, 0) : 0) 72 | .traceId(convertToLong(traceId)) 73 | .spanId(convertToLong(spanId)) 74 | .sampled(sampled) 75 | .parentId(parentSpanId == null ? null : convertToLong(parentSpanId)).build(); 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /louie-webapi/src/main/java/com/louie/trace/CusHttpServletHandlerInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.louie.trace; 2 | 3 | import java.net.URI; 4 | import java.net.URISyntaxException; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 10 | 11 | import com.github.kristofa.brave.Brave; 12 | import com.github.kristofa.brave.ServerRequestInterceptor; 13 | import com.github.kristofa.brave.ServerResponseInterceptor; 14 | import com.github.kristofa.brave.ServerSpan; 15 | import com.github.kristofa.brave.ServerSpanThreadBinder; 16 | import com.github.kristofa.brave.http.DefaultSpanNameProvider; 17 | import com.github.kristofa.brave.http.HttpResponse; 18 | import com.github.kristofa.brave.http.HttpServerRequest; 19 | import com.github.kristofa.brave.http.HttpServerResponseAdapter; 20 | import com.github.kristofa.brave.http.SpanNameProvider; 21 | 22 | import zipkin.reporter.AsyncReporter; 23 | import zipkin.reporter.Reporter; 24 | import zipkin.reporter.Sender; 25 | import zipkin.reporter.okhttp3.OkHttpSender; 26 | 27 | public class CusHttpServletHandlerInterceptor extends HandlerInterceptorAdapter { 28 | 29 | 30 | static final String HTTP_SERVER_SPAN_ATTRIBUTE = CusHttpServletHandlerInterceptor.class.getName() + ".server-span"; 31 | 32 | private final ServerRequestInterceptor requestInterceptor; 33 | private final ServerResponseInterceptor responseInterceptor; 34 | private final ServerSpanThreadBinder serverThreadBinder; 35 | private final SpanNameProvider spanNameProvider; 36 | 37 | CusHttpServletHandlerInterceptor() { 38 | Sender sender = OkHttpSender.create("http://127.0.0.1:9411/api/v1/spans"); 39 | Reporter reporter = AsyncReporter.builder(sender).build(); 40 | Brave brave = new Brave.Builder("webapi").reporter(reporter).build(); 41 | 42 | this.requestInterceptor = brave.serverRequestInterceptor(); 43 | this.responseInterceptor = brave.serverResponseInterceptor(); 44 | this.serverThreadBinder = brave.serverSpanThreadBinder(); 45 | this.spanNameProvider = new DefaultSpanNameProvider(); 46 | } 47 | 48 | 49 | /** 50 | * @deprecated please use {@link #create(Brave)} or {@link #builder(Brave)} 51 | */ 52 | @Deprecated 53 | public CusHttpServletHandlerInterceptor(ServerRequestInterceptor requestInterceptor, ServerResponseInterceptor responseInterceptor, SpanNameProvider spanNameProvider, final ServerSpanThreadBinder serverThreadBinder) { 54 | this.requestInterceptor = requestInterceptor; 55 | this.spanNameProvider = spanNameProvider; 56 | this.responseInterceptor = responseInterceptor; 57 | this.serverThreadBinder = serverThreadBinder; 58 | } 59 | 60 | @Override 61 | public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) { 62 | String service = request.getParameter("service"); 63 | service = service == null || service == ""?"unkown":service; 64 | String data = request.getParameter("data"); 65 | data = data == null || data == ""?"{}":data; 66 | 67 | requestInterceptor.handle(new CusHttpServerRequestAdapter(new HttpServerRequest() { 68 | 69 | public String getHttpHeaderValue(String headerName) { 70 | return request.getHeader(headerName); 71 | } 72 | 73 | 74 | public URI getUri() { 75 | try { 76 | return new URI(request.getRequestURI()); 77 | } catch (URISyntaxException e) { 78 | throw new RuntimeException(e); 79 | } 80 | } 81 | 82 | 83 | public String getHttpMethod() { 84 | String service = request.getParameter("service"); 85 | service = service == null || service == ""?"unkown":service; 86 | return service; 87 | } 88 | },service,data, spanNameProvider)); 89 | 90 | return true; 91 | } 92 | 93 | @Override 94 | public void afterConcurrentHandlingStarted(final HttpServletRequest request, final HttpServletResponse response, final Object handler) { 95 | request.setAttribute(HTTP_SERVER_SPAN_ATTRIBUTE, serverThreadBinder.getCurrentServerSpan()); 96 | serverThreadBinder.setCurrentSpan(null); 97 | } 98 | 99 | @Override 100 | public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) { 101 | 102 | final ServerSpan span = (ServerSpan) request.getAttribute(HTTP_SERVER_SPAN_ATTRIBUTE); 103 | 104 | if (span != null) { 105 | serverThreadBinder.setCurrentSpan(span); 106 | } 107 | 108 | responseInterceptor.handle(new HttpServerResponseAdapter(new HttpResponse() { 109 | 110 | public int getHttpStatusCode() { 111 | return response.getStatus(); 112 | } 113 | })); 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /louie-webapi/src/main/java/com/louie/trace/WebTracingConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.louie.trace; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.annotation.PostConstruct; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Import; 11 | import org.springframework.http.client.ClientHttpRequestInterceptor; 12 | import org.springframework.web.client.RestTemplate; 13 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 14 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 15 | 16 | import com.github.kristofa.brave.spring.BraveClientHttpRequestInterceptor; 17 | 18 | /** 19 | * This adds tracing configuration to any web mvc controllers or rest template clients. This should 20 | * be configured last. 21 | */ 22 | // import as the interceptors are annotation with javax.inject and not automatically wired 23 | @Configuration 24 | @Import({BraveClientHttpRequestInterceptor.class, CusHttpServletHandlerInterceptor.class}) 25 | public class WebTracingConfiguration extends WebMvcConfigurerAdapter { 26 | 27 | 28 | @Autowired 29 | //private ServletHandlerInterceptor serverInterceptor; 30 | private CusHttpServletHandlerInterceptor serverInterceptor; 31 | 32 | @Autowired 33 | private BraveClientHttpRequestInterceptor clientInterceptor; 34 | 35 | @Autowired 36 | private RestTemplate restTemplate; 37 | 38 | // adds tracing to the application-defined rest template 39 | @PostConstruct 40 | public void init() { 41 | List interceptors = 42 | new ArrayList(restTemplate.getInterceptors()); 43 | interceptors.add(clientInterceptor); 44 | restTemplate.setInterceptors(interceptors); 45 | } 46 | 47 | // adds tracing to the application-defined web controllers 48 | @Override 49 | public void addInterceptors(InterceptorRegistry registry) { 50 | registry.addInterceptor(serverInterceptor); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /louie-webapi/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter: -------------------------------------------------------------------------------- 1 | traceFilterc=com.louie.common.dubbo.DrpcClientInterceptor -------------------------------------------------------------------------------- /louie-webapi/src/main/resources/application-dubbo.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /louie-webapi/src/main/resources/application-mvc.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /louie-webapi/src/main/resources/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 14 | 15 | 16 | 17 | classpath*:brave.properties 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /louie-webapi/src/main/resources/brave.properties: -------------------------------------------------------------------------------- 1 | brave.name=webapi 2 | http.sender.address=http://127.0.0.1:9411/api/v1/spans -------------------------------------------------------------------------------- /louie-webapi/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=info, stdout 2 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 3 | log4j.appender.stdout.Target=System.out 4 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %-5p [%t] %C{2} (%F:%L) - %m%n -------------------------------------------------------------------------------- /louie-webapi/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | Spring Web MVC Application 7 | 8 | 9 | mvc-dispatcher 10 | org.springframework.web.servlet.DispatcherServlet 11 | 12 | 13 | contextConfigLocation 14 | classpath:applicationContext.xml 15 | 16 | 17 | 1 18 | 19 | 20 | 21 | mvc-dispatcher 22 | *.do 23 | 24 | 25 | 26 | contextConfigLocation 27 | classpath:applicationContext.xml 28 | 29 | 30 | 31 | org.springframework.web.context.ContextLoaderListener 32 | 33 | 34 | -------------------------------------------------------------------------------- /louie-webapi/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=info, stdout 2 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 3 | log4j.appender.stdout.Target=System.out 4 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %-5p [%t] %C{2} (%F:%L) - %m%n -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | louie 5 | http-dubbo-zipkin 6 | 0.0.1-SNAPSHOT 7 | pom 8 | 9 | louie-common 10 | louie-webapi 11 | louie-order 12 | louie-account 13 | 14 | -------------------------------------------------------------------------------- /request-params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacklau/http-dubbo-zipkin/7c38dbe53e95afbb38612ec5c8e39c848769f00a/request-params.png -------------------------------------------------------------------------------- /service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacklau/http-dubbo-zipkin/7c38dbe53e95afbb38612ec5c8e39c848769f00a/service.png -------------------------------------------------------------------------------- /trace-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blacklau/http-dubbo-zipkin/7c38dbe53e95afbb38612ec5c8e39c848769f00a/trace-info.png --------------------------------------------------------------------------------