Browse Source

first commit

YinBin 1 year ago
commit
a8d08d2b76
100 changed files with 26748 additions and 0 deletions
  1. BIN
      .DS_Store
  2. 32 0
      .classpath
  3. 1 0
      .gitignore
  4. 145 0
      .idea/artifacts/yyb_war_exploded.xml
  5. 42 0
      .project
  6. 12 0
      .settings/org.eclipse.jdt.core.prefs
  7. 801 0
      pom.xml
  8. BIN
      src/.DS_Store
  9. BIN
      src/main/.DS_Store
  10. 20 0
      src/main/java/com/jeeplus/common/annotation/FieldName.java
  11. 16 0
      src/main/java/com/jeeplus/common/annotation/IgnoreAuth.java
  12. 13 0
      src/main/java/com/jeeplus/common/beanvalidator/AddGroup.java
  13. 116 0
      src/main/java/com/jeeplus/common/beanvalidator/BeanValidators.java
  14. 12 0
      src/main/java/com/jeeplus/common/beanvalidator/DefaultGroup.java
  15. 12 0
      src/main/java/com/jeeplus/common/beanvalidator/EditGroup.java
  16. 317 0
      src/main/java/com/jeeplus/common/config/Global.java
  17. 73 0
      src/main/java/com/jeeplus/common/json/AjaxJson.java
  18. 36 0
      src/main/java/com/jeeplus/common/json/DataJson.java
  19. 103 0
      src/main/java/com/jeeplus/common/json/JSTreeJson.java
  20. 28 0
      src/main/java/com/jeeplus/common/json/PrintJSON.java
  21. 20 0
      src/main/java/com/jeeplus/common/mail/MailAuthenticator.java
  22. 96 0
      src/main/java/com/jeeplus/common/mail/MailBody.java
  23. 161 0
      src/main/java/com/jeeplus/common/mail/MailSendUtils.java
  24. 75 0
      src/main/java/com/jeeplus/common/sms/SMSUtils.java
  25. 32 0
      src/main/java/com/jeeplus/common/swagger/SwaggerConfig.java
  26. 127 0
      src/main/java/com/jeeplus/common/tag/AniMenuTag.java
  27. 221 0
      src/main/java/com/jeeplus/common/tag/JPMenuTag.java
  28. 82 0
      src/main/java/com/jeeplus/common/utils/CacheUtils.java
  29. 176 0
      src/main/java/com/jeeplus/common/utils/Collections3.java
  30. 115 0
      src/main/java/com/jeeplus/common/utils/CookieUtils.java
  31. 223 0
      src/main/java/com/jeeplus/common/utils/DateUtils.java
  32. 151 0
      src/main/java/com/jeeplus/common/utils/Encodes.java
  33. 72 0
      src/main/java/com/jeeplus/common/utils/Exceptions.java
  34. 699 0
      src/main/java/com/jeeplus/common/utils/FileUtils.java
  35. 67 0
      src/main/java/com/jeeplus/common/utils/FreeMarkers.java
  36. 131 0
      src/main/java/com/jeeplus/common/utils/HanyuPinyinHelper.java
  37. 67 0
      src/main/java/com/jeeplus/common/utils/IdGen.java
  38. 52 0
      src/main/java/com/jeeplus/common/utils/JWT.java
  39. 42 0
      src/main/java/com/jeeplus/common/utils/MD5Util.java
  40. 31 0
      src/main/java/com/jeeplus/common/utils/MobileUtils.java
  41. 311 0
      src/main/java/com/jeeplus/common/utils/MyBeanUtils.java
  42. 88 0
      src/main/java/com/jeeplus/common/utils/ObjectUtils.java
  43. 154 0
      src/main/java/com/jeeplus/common/utils/PropertiesLoader.java
  44. 304 0
      src/main/java/com/jeeplus/common/utils/Reflections.java
  45. 81 0
      src/main/java/com/jeeplus/common/utils/SmsUtils.java
  46. 95 0
      src/main/java/com/jeeplus/common/utils/SpringContextHolder.java
  47. 411 0
      src/main/java/com/jeeplus/common/utils/StringUtils.java
  48. 324 0
      src/main/java/com/jeeplus/common/utils/TimeUtils.java
  49. 151 0
      src/main/java/com/jeeplus/common/utils/ValidateCode.java
  50. 268 0
      src/main/java/com/jeeplus/common/utils/ValidationCode.java
  51. 70 0
      src/main/java/com/jeeplus/common/utils/base/BooleanUtil.java
  52. 36 0
      src/main/java/com/jeeplus/common/utils/base/EnumUtil.java
  53. 320 0
      src/main/java/com/jeeplus/common/utils/base/ExceptionUtil.java
  54. 117 0
      src/main/java/com/jeeplus/common/utils/base/MoreValidate.java
  55. 84 0
      src/main/java/com/jeeplus/common/utils/base/ObjectUtil.java
  56. 76 0
      src/main/java/com/jeeplus/common/utils/base/Platforms.java
  57. 86 0
      src/main/java/com/jeeplus/common/utils/base/PropertiesUtil.java
  58. 241 0
      src/main/java/com/jeeplus/common/utils/base/SystemPropertiesUtil.java
  59. 5 0
      src/main/java/com/jeeplus/common/utils/base/annotation/NotNull.java
  60. 5 0
      src/main/java/com/jeeplus/common/utils/base/annotation/Nullable.java
  61. 136 0
      src/main/java/com/jeeplus/common/utils/collection/ArrayUtil.java
  62. 181 0
      src/main/java/com/jeeplus/common/utils/collection/CollectionUtil.java
  63. 372 0
      src/main/java/com/jeeplus/common/utils/collection/ListUtil.java
  64. 421 0
      src/main/java/com/jeeplus/common/utils/collection/MapUtil.java
  65. 128 0
      src/main/java/com/jeeplus/common/utils/collection/QueueUtil.java
  66. 178 0
      src/main/java/com/jeeplus/common/utils/collection/SetUtil.java
  67. 90 0
      src/main/java/com/jeeplus/common/utils/collection/type/ConcurrentHashSet.java
  68. 87 0
      src/main/java/com/jeeplus/common/utils/collection/type/Pair.java
  69. 185 0
      src/main/java/com/jeeplus/common/utils/collection/type/SortedArrayList.java
  70. 725 0
      src/main/java/com/jeeplus/common/utils/collection/type/primitive/IntObjectHashMap.java
  71. 84 0
      src/main/java/com/jeeplus/common/utils/collection/type/primitive/IntObjectMap.java
  72. 728 0
      src/main/java/com/jeeplus/common/utils/collection/type/primitive/LongObjectHashMap.java
  73. 84 0
      src/main/java/com/jeeplus/common/utils/collection/type/primitive/LongObjectMap.java
  74. 139 0
      src/main/java/com/jeeplus/common/utils/concurrent/BasicFuture.java
  75. 54 0
      src/main/java/com/jeeplus/common/utils/concurrent/ConcurrentTools.java
  76. 121 0
      src/main/java/com/jeeplus/common/utils/concurrent/ThreadDumpper.java
  77. 55 0
      src/main/java/com/jeeplus/common/utils/concurrent/ThreadLocalContext.java
  78. 100 0
      src/main/java/com/jeeplus/common/utils/concurrent/ThreadUtil.java
  79. 6310 0
      src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/ConcurrentHashMapV8.java
  80. 753 0
      src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/CountedCompleter.java
  81. 3349 0
      src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/ForkJoinPool.java
  82. 1557 0
      src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/ForkJoinTask.java
  83. 125 0
      src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/ForkJoinWorkerThread.java
  84. 204 0
      src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/LongAdder.java
  85. 166 0
      src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/RecursiveAction.java
  86. 71 0
      src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/RecursiveTask.java
  87. 336 0
      src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/Striped64.java
  88. 135 0
      src/main/java/com/jeeplus/common/utils/concurrent/threadpool/QueuableCachedThreadPool.java
  89. 390 0
      src/main/java/com/jeeplus/common/utils/concurrent/threadpool/ThreadPoolBuilder.java
  90. 114 0
      src/main/java/com/jeeplus/common/utils/concurrent/threadpool/ThreadPoolUtil.java
  91. 72 0
      src/main/java/com/jeeplus/common/utils/concurrent/throttle/Sampler.java
  92. 477 0
      src/main/java/com/jeeplus/common/utils/excel/ExportExcel.java
  93. 397 0
      src/main/java/com/jeeplus/common/utils/excel/ImportExcel.java
  94. 59 0
      src/main/java/com/jeeplus/common/utils/excel/annotation/ExcelField.java
  95. 38 0
      src/main/java/com/jeeplus/common/utils/excel/fieldtype/AreaType.java
  96. 38 0
      src/main/java/com/jeeplus/common/utils/excel/fieldtype/OfficeType.java
  97. 52 0
      src/main/java/com/jeeplus/common/utils/excel/fieldtype/RoleListType.java
  98. 88 0
      src/main/java/com/jeeplus/common/utils/io/FilePathUtil.java
  99. 146 0
      src/main/java/com/jeeplus/common/utils/io/FileTreeWalker.java
  100. 357 0
      src/main/java/com/jeeplus/common/utils/io/FileUtil.java

BIN
.DS_Store


+ 32 - 0
.classpath

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry including="**/*.java" kind="src" output="target/classes" path="src/main/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+			<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+/target

+ 145 - 0
.idea/artifacts/yyb_war_exploded.xml

@@ -0,0 +1,145 @@
+<component name="ArtifactManager">
+  <artifact type="exploded-war" name="yyb:war exploded">
+    <output-path>$PROJECT_DIR$/target/yyb</output-path>
+    <root id="root">
+      <element id="directory" name="WEB-INF">
+        <element id="directory" name="classes">
+          <element id="module-output" name="yyb" />
+        </element>
+        <element id="directory" name="lib">
+          <element id="library" level="project" name="Maven: org.springframework:spring-core:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-beans:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-context:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-expression:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-context-support:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-aop:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-tx:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-orm:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-jdbc:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-websocket:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-messaging:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.hibernate:hibernate-validator:5.4.0.Final" />
+          <element id="library" level="project" name="Maven: javax.validation:validation-api:1.1.0.Final" />
+          <element id="library" level="project" name="Maven: org.jboss.logging:jboss-logging:3.3.0.Final" />
+          <element id="library" level="project" name="Maven: com.fasterxml:classmate:1.3.1" />
+          <element id="library" level="project" name="Maven: org.aspectj:aspectjrt:1.7.4" />
+          <element id="library" level="project" name="Maven: org.aspectj:aspectjweaver:1.7.4" />
+          <element id="library" level="project" name="Maven: cglib:cglib:3.1" />
+          <element id="library" level="project" name="Maven: org.ow2.asm:asm:4.2" />
+          <element id="library" level="project" name="Maven: org.mybatis:mybatis:3.2.8" />
+          <element id="library" level="project" name="Maven: org.mybatis:mybatis-spring:1.2.2" />
+          <element id="library" level="project" name="Maven: com.alibaba:druid:1.0.11" />
+          <element id="library" level="project" name="Maven: mysql:mysql-connector-java:5.1.30" />
+          <element id="library" level="project" name="Maven: net.sourceforge.jtds:jtds:1.3.1" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-web:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-webmvc:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-oxm:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: opensymphony:sitemesh:2.4.2" />
+          <element id="library" level="project" name="Maven: taglibs:standard:1.1.2" />
+          <element id="library" level="project" name="Maven: javax.servlet:jstl:1.2" />
+          <element id="library" level="project" name="Maven: com.github.ben-manes.caffeine:caffeine:2.6.1" />
+          <element id="library" level="project" name="Maven: net.sf.ehcache:ehcache:2.10.4" />
+          <element id="library" level="project" name="Maven: org.ehcache:ehcache:3.4.0" />
+          <element id="library" level="project" name="Maven: redis.clients:jedis:2.9.0" />
+          <element id="library" level="project" name="Maven: org.apache.commons:commons-pool2:2.4.2" />
+          <element id="library" level="project" name="Maven: org.jgroups:jgroups:3.6.13.Final" />
+          <element id="library" level="project" name="Maven: de.ruedigermoeller:fst:2.57" />
+          <element id="library" level="project" name="Maven: org.javassist:javassist:3.21.0-GA" />
+          <element id="library" level="project" name="Maven: org.objenesis:objenesis:2.5.1" />
+          <element id="library" level="project" name="Maven: com.esotericsoftware:kryo-shaded:3.0.0" />
+          <element id="library" level="project" name="Maven: com.esotericsoftware:minlog:1.3.0" />
+          <element id="library" level="project" name="Maven: org.xerial.snappy:snappy-java:1.1.7.1" />
+          <element id="library" level="project" name="Maven: jline:jline:2.14.2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-core:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-lang:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-cache:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-crypto-hash:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-crypto-core:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-crypto-cipher:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-config-core:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-config-ogdl:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-event:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-spring:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-cas:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: org.jasig.cas.client:cas-client-core:3.2.2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-web:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: org.apache.shiro:shiro-ehcache:1.4.0-RC2" />
+          <element id="library" level="project" name="Maven: net.sf.ehcache:ehcache-core:2.6.11" />
+          <element id="library" level="project" name="Maven: org.slf4j:slf4j-api:1.7.25" />
+          <element id="library" level="project" name="Maven: org.slf4j:slf4j-log4j12:1.7.25" />
+          <element id="library" level="project" name="Maven: log4j:log4j:1.2.17" />
+          <element id="library" level="project" name="Maven: org.slf4j:slf4j-simple:1.7.25" />
+          <element id="library" level="project" name="Maven: org.slf4j:jcl-over-slf4j:1.7.25" />
+          <element id="library" level="project" name="Maven: org.slf4j:jul-to-slf4j:1.7.25" />
+          <element id="library" level="project" name="Maven: commons-io:commons-io:2.4" />
+          <element id="library" level="project" name="Maven: commons-codec:commons-codec:1.9" />
+          <element id="library" level="project" name="Maven: commons-fileupload:commons-fileupload:1.3.1" />
+          <element id="library" level="project" name="Maven: commons-beanutils:commons-beanutils:1.9.1" />
+          <element id="library" level="project" name="Maven: commons-collections:commons-collections:3.2.1" />
+          <element id="library" level="project" name="Maven: commons-dbcp:commons-dbcp:1.4" />
+          <element id="library" level="project" name="Maven: commons-pool:commons-pool:1.5.4" />
+          <element id="library" level="project" name="Maven: com.fasterxml.jackson.core:jackson-core:2.8.6" />
+          <element id="library" level="project" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.8.6" />
+          <element id="library" level="project" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.8.6" />
+          <element id="library" level="project" name="Maven: com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.8.6" />
+          <element id="library" level="project" name="Maven: com.alibaba:fastjson:1.2.45" />
+          <element id="library" level="project" name="Maven: com.thoughtworks.xstream:xstream:1.4.9" />
+          <element id="library" level="project" name="Maven: xmlpull:xmlpull:1.1.3.1" />
+          <element id="library" level="project" name="Maven: xpp3:xpp3_min:1.1.4c" />
+          <element id="library" level="project" name="Maven: net.sf.dozer:dozer:5.5.1" />
+          <element id="library" level="project" name="Maven: org.freemarker:freemarker:2.3.25-incubating" />
+          <element id="library" level="project" name="Maven: javax.mail:mail:1.4.7" />
+          <element id="library" level="project" name="Maven: javax.activation:activation:1.1.1" />
+          <element id="library" level="project" name="Maven: org.apache.poi:poi:3.9" />
+          <element id="library" level="project" name="Maven: org.apache.poi:poi-ooxml:3.9" />
+          <element id="library" level="project" name="Maven: dom4j:dom4j:1.6.1" />
+          <element id="library" level="project" name="Maven: xml-apis:xml-apis:1.0.b2" />
+          <element id="library" level="project" name="Maven: org.apache.poi:poi-ooxml-schemas:3.9" />
+          <element id="library" level="project" name="Maven: org.apache.xmlbeans:xmlbeans:2.3.0" />
+          <element id="library" level="project" name="Maven: stax:stax-api:1.0.1" />
+          <element id="library" level="project" name="Maven: com.drewnoakes:metadata-extractor:2.6.2" />
+          <element id="library" level="project" name="Maven: com.adobe.xmp:xmpcore:5.1.2" />
+          <element id="library" level="project" name="Maven: xerces:xercesImpl:2.8.1" />
+          <element id="library" level="project" name="Maven: com.google.zxing:core:2.2" />
+          <element id="library" level="project" name="Maven: com.google.zxing:javase:2.2" />
+          <element id="library" level="project" name="Maven: org.quartz-scheduler:quartz:2.2.3" />
+          <element id="library" level="project" name="Maven: c3p0:c3p0:0.9.1.1" />
+          <element id="library" level="project" name="Maven: org.quartz-scheduler:quartz-jobs:2.2.3" />
+          <element id="library" level="project" name="Maven: com.github.abel533:ECharts:3.0.0.2" />
+          <element id="library" level="project" name="Maven: com.google.code.gson:gson:2.6.2" />
+          <element id="library" level="project" name="Maven: junit:junit:4.11" />
+          <element id="library" level="project" name="Maven: org.hamcrest:hamcrest-core:1.3" />
+          <element id="library" level="project" name="Maven: org.springframework:spring-test:4.3.5.RELEASE" />
+          <element id="library" level="project" name="Maven: eu.bitwalker:UserAgentUtils:1.20" />
+          <element id="library" level="project" name="Maven: org.apache.httpcomponents:httpcore:4.4.4" />
+          <element id="library" level="project" name="Maven: io.springfox:springfox-swagger2:2.5.0" />
+          <element id="library" level="project" name="Maven: io.swagger:swagger-annotations:1.5.9" />
+          <element id="library" level="project" name="Maven: io.swagger:swagger-models:1.5.9" />
+          <element id="library" level="project" name="Maven: io.springfox:springfox-spi:2.5.0" />
+          <element id="library" level="project" name="Maven: io.springfox:springfox-core:2.5.0" />
+          <element id="library" level="project" name="Maven: io.springfox:springfox-schema:2.5.0" />
+          <element id="library" level="project" name="Maven: io.springfox:springfox-swagger-common:2.5.0" />
+          <element id="library" level="project" name="Maven: io.springfox:springfox-spring-web:2.5.0" />
+          <element id="library" level="project" name="Maven: org.springframework.plugin:spring-plugin-core:1.2.0.RELEASE" />
+          <element id="library" level="project" name="Maven: org.springframework.plugin:spring-plugin-metadata:1.2.0.RELEASE" />
+          <element id="library" level="project" name="Maven: org.mapstruct:mapstruct:1.0.0.Final" />
+          <element id="library" level="project" name="Maven: io.springfox:springfox-swagger-ui:2.5.0" />
+          <element id="library" level="project" name="Maven: org.apache.httpcomponents:httpclient:4.5.2" />
+          <element id="library" level="project" name="Maven: commons-logging:commons-logging:1.2" />
+          <element id="library" level="project" name="Maven: com.google.guava:guava:20.0" />
+          <element id="library" level="project" name="Maven: org.apache.commons:commons-lang3:3.5" />
+          <element id="library" level="project" name="Maven: org.postgresql:postgresql:42.1.1" />
+          <element id="library" level="project" name="Maven: com.alipay.sdk:alipay-sdk-java:3.4.49.ALL" />
+          <element id="library" level="project" name="Maven: com.belerweb:pinyin4j:2.5.1" />
+          <element id="library" level="project" name="Maven: com.github.pagehelper:pagehelper:5.1.2" />
+          <element id="library" level="project" name="Maven: com.github.jsqlparser:jsqlparser:1.0" />
+          <element id="library" level="project" name="Maven: com.auth0:java-jwt:2.2.0" />
+        </element>
+      </element>
+      <element id="directory" name="META-INF">
+        <element id="file-copy" path="$PROJECT_DIR$/target/yyb/META-INF/MANIFEST.MF" />
+      </element>
+      <element id="javaee-facet-resources" facet="yyb/web/Web" />
+    </root>
+  </artifact>
+</component>

+ 42 - 0
.project

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>jeeplus</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
+			<triggers>full,incremental,</triggers>
+			<arguments>
+				<dictionary>
+					<key>LaunchConfigHandle</key>
+					<value>&lt;project&gt;/.externalToolBuilders/org.eclipse.wst.validation.validationbuilder.launch</value>
+				</dictionary>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+		<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
+	</natures>
+</projectDescription>

+ 12 - 0
.settings/org.eclipse.jdt.core.prefs

@@ -0,0 +1,12 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8

+ 801 - 0
pom.xml

@@ -0,0 +1,801 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>jeeplus</groupId>
+    <artifactId>jeeplus</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>war</packaging>
+    <name>yyb</name>
+
+    <!-- 项目属性 -->
+    <properties>
+        <lib.path>${basedir}/src/main/webapp/WEB-INF/lib</lib.path>
+        <!-- main version setting -->
+        <spring.version>4.3.5.RELEASE</spring.version>
+        <validator.version>5.4.0.Final</validator.version>
+        <mybatis.version>3.2.8</mybatis.version>
+        <mybatis-spring.version>1.2.2</mybatis-spring.version>
+        <druid.version>1.0.11</druid.version>
+        <ehcache.version>2.6.11</ehcache.version>
+        <ehcache-web.version>2.0.4</ehcache-web.version>
+        <shiro.version>1.4.0-RC2</shiro.version>
+        <sitemesh.version>2.4.2</sitemesh.version>
+
+
+        <!-- tools version setting -->
+        <slf4j.version>1.7.25</slf4j.version>
+        <commons-io.version>2.4</commons-io.version>
+        <commons-codec.version>1.9</commons-codec.version>
+        <commons-fileupload.version>1.3.1</commons-fileupload.version>
+        <commons-beanutils.version>1.9.1</commons-beanutils.version>
+        <jackson.version>2.8.6</jackson.version>
+        <fastjson.version>1.2.45</fastjson.version>
+        <xstream.version>1.4.9</xstream.version>
+        <dozer.version>5.5.1</dozer.version>
+        <poi.version>3.9</poi.version>
+        <freemarker.version>2.3.25-incubating</freemarker.version>
+        <quartz.version>2.2.3</quartz.version>
+
+        <guava.version>20.0</guava.version>
+        <commons-lang3.version>3.5</commons-lang3.version>
+
+        <!-- jdbc driver setting -->
+        <mysql.driver.version>5.1.30</mysql.driver.version>
+        <oracle.driver.version>10.2.0.4.0</oracle.driver.version>
+        <mssql.driver.version>1.3.1</mssql.driver.version>
+
+        <!-- environment setting -->
+        <jdk.version>1.8</jdk.version>
+        <tomcat.version>2.2</tomcat.version>
+        <jetty.version>7.6.14.v20131031</jetty.version>
+        <webserver.port>8080</webserver.port>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <downloadSources>true</downloadSources>
+
+    </properties>
+	
+	<!-- 设定仓库 -->
+	<repositories>
+		<repository>
+			<id>central-repos</id>
+			<name>Central Repository</name>
+			<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
+			<releases>
+				<enabled>true</enabled>
+			</releases>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</repository>
+
+		<repository>
+			<id>central-repos2</id>
+			<name>Central Repository 2</name>
+			<url>http://mvnrepo.code.taobao.org/nexus/content/repositories/snapshots/</url>
+			<releases>
+				<enabled>true</enabled>
+			</releases>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</repository>
+
+		<repository>
+			<id>springsource-repos</id>
+			<name>SpringSource Repository</name>
+			<url>http://mvnrepo.code.taobao.org/nexus/content/repositories/releases/</url>
+			<releases>
+				<enabled>true</enabled>
+			</releases>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</repository>
+	
+		<repository>
+			<id>bitwalker.user-agent-utils.mvn.repo</id>
+			<url>https://nexus.jackpinetech.com/nexus/content/groups/public</url>
+			<!-- use snapshot version -->
+			<snapshots>
+				<updatePolicy>always</updatePolicy>
+			</snapshots>
+		</repository>
+	</repositories>
+
+
+    <build>
+        <finalName>yyb</finalName>
+        <resources>
+			<resource>
+				<directory>src/main/java</directory>
+				<includes>
+					<include>**/*.xml</include>
+				</includes>
+			</resource>
+			<resource>
+				<directory>src/main/resources</directory>
+			</resource>
+		</resources>
+        <plugins>
+           
+           <!-- tomcat6插件 -->
+			<plugin>
+				<groupId>org.apache.tomcat.maven</groupId>
+				<artifactId>tomcat6-maven-plugin</artifactId>
+				<version>${tomcat.version}</version> 
+				<configuration>
+					<port>${webserver.port}</port>
+					<path>/${project.artifactId}</path>
+					<uriEncoding>${project.build.sourceEncoding}</uriEncoding>
+				</configuration>
+			</plugin>
+			
+			<!-- tomcat7插件 -->
+			<plugin>
+				<groupId>org.apache.tomcat.maven</groupId>
+				<artifactId>tomcat7-maven-plugin</artifactId>
+				<version>${tomcat.version}</version> 
+				<configuration>
+					<port>${webserver.port}</port>
+					<path>/${project.artifactId}</path>
+					<uriEncoding>${project.build.sourceEncoding}</uriEncoding>
+				</configuration>
+			</plugin>
+           
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.3</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+                <version>${jetty.version}</version>
+                <configuration>
+                    <connectors>
+                        <connector
+                                implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
+                            <port>${webserver.port}</port>
+                        </connector>
+                    </connectors>
+                    <webAppConfig>
+                        <contextPath>/${project.artifactId}</contextPath>
+                    </webAppConfig>
+                    <systemProperties>
+                        <systemProperty>
+                            <name>org.mortbay.util.URI.charset</name>
+                            <value>${project.build.sourceEncoding}</value>
+                        </systemProperty>
+                    </systemProperties>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <!-- 依赖项定义 -->
+    <dependencies>
+        <!-- SPRING begin -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-core</artifactId>
+            <version>${spring.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context-support</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-aop</artifactId>
+            <version>${spring.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-tx</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+
+        <!-- spring orm -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-orm</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-jdbc</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        
+        <!-- spring websocket--> 
+		<dependency>  
+           <groupId>org.springframework</groupId>  
+           <artifactId>spring-websocket</artifactId>  
+           <version>${spring.version}</version>  
+        </dependency>  
+        <dependency>  
+           <groupId>org.springframework</groupId>  
+           <artifactId>spring-messaging</artifactId>  
+           <version>${spring.version}</version>  
+        </dependency>
+
+        <!-- bean validate -->
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-validator</artifactId>
+            <version>${validator.version}</version>
+        </dependency>
+        <!-- SPRING end -->
+
+        <!-- AOP begin -->
+        <dependency>
+            <groupId>org.aspectj</groupId>
+            <artifactId>aspectjrt</artifactId>
+            <version>1.7.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.aspectj</groupId>
+            <artifactId>aspectjweaver</artifactId>
+            <version>1.7.4</version>
+        </dependency>
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib</artifactId>
+            <version>3.1</version>
+        </dependency>
+        <!-- AOP end -->
+
+        <!-- PERSISTENCE begin -->
+
+        <!-- MyBatis -->
+        <dependency>
+            <groupId>org.mybatis</groupId>
+            <artifactId>mybatis</artifactId>
+            <version>${mybatis.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis</groupId>
+            <artifactId>mybatis-spring</artifactId>
+            <version>${mybatis-spring.version}</version>
+        </dependency>
+
+        <!-- connection pool -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid</artifactId>
+            <version>${druid.version}</version>
+        </dependency>
+
+        <!-- jdbc driver -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>${mysql.driver.version}</version>
+        </dependency>
+        <!--<dependency>-->
+            <!--<groupId>ojdbc14</groupId>-->
+            <!--<artifactId>ojdbc14</artifactId>-->
+            <!--<version>10.2.0.3.0</version>-->
+        <!--</dependency>-->
+        <dependency>
+            <groupId>ojdbc14</groupId>
+            <artifactId>ojdbc14</artifactId>
+            <version>10.2.0.3.0</version>
+            <systemPath>${lib.path}/ojdbc14-10.2.0.3.0.jar</systemPath>
+            <scope>system</scope>
+	</dependency>
+        <dependency>
+            <groupId>net.sourceforge.jtds</groupId>
+            <artifactId>jtds</artifactId>
+            <version>${mssql.driver.version}</version>
+        </dependency>
+        <!-- PERSISTENCE end -->
+
+        <!-- WEB begin -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-oxm</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>opensymphony</groupId>
+            <artifactId>sitemesh</artifactId>
+            <version>${sitemesh.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>taglibs</groupId>
+            <artifactId>standard</artifactId>
+            <version>1.1.2</version>
+            <type>jar</type>
+        </dependency>
+
+
+        <dependency>
+            <groupId>javax</groupId>
+            <artifactId>javaee-api</artifactId>
+            <version>7.0</version>
+             <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>jstl</artifactId>
+            <version>1.2</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet.jsp</groupId>
+            <artifactId>jsp-api</artifactId>
+            <version>2.1</version>
+            <scope>provided</scope>
+        </dependency>
+       <!--  <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>3.0.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet.jsp</groupId>
+            <artifactId>jsp-api</artifactId>
+            <version>2.2</version>
+            <scope>provided</scope>
+        </dependency>--> 
+
+
+        <!-- J2CACHE begin -->
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+            <version>2.6.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>net.sf.ehcache</groupId>
+            <artifactId>ehcache</artifactId>
+            <version>2.10.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.ehcache</groupId>
+            <artifactId>ehcache</artifactId>
+            <version>3.4.0</version>
+        </dependency>
+        <dependency>
+            <groupId>redis.clients</groupId>
+            <artifactId>jedis</artifactId>
+            <version>2.9.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jgroups</groupId>
+            <artifactId>jgroups</artifactId>
+            <version>3.6.13.Final</version>
+        </dependency>
+
+        <dependency>
+            <groupId>de.ruedigermoeller</groupId>
+            <artifactId>fst</artifactId>
+            <version>2.57</version>
+        </dependency>
+        <dependency>
+            <groupId>com.esotericsoftware</groupId>
+            <artifactId>kryo-shaded</artifactId>
+            <version>3.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.xerial.snappy</groupId>
+            <artifactId>snappy-java</artifactId>
+            <version>1.1.7.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>jline</groupId>
+            <artifactId>jline</artifactId>
+            <version>2.14.2</version>
+        </dependency>
+        <!-- J2CACHE end -->
+
+        <!-- SECURITY begin -->
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-core</artifactId>
+            <version>${shiro.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-spring</artifactId>
+            <version>${shiro.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-cas</artifactId>
+            <version>${shiro.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-web</artifactId>
+            <version>${shiro.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-ehcache</artifactId>
+            <version>${shiro.version}</version>
+        </dependency>
+        <!-- SECURITY end -->
+
+        <!-- LOGGING begin -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <!-- common-logging 实际调用slf4j -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <!-- java.util.logging 实际调用slf4j -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jul-to-slf4j</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <!-- LOGGING end -->
+
+        <!-- GENERAL UTILS begin -->
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>${commons-io.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>${commons-codec.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>${commons-fileupload.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+            <version>${commons-beanutils.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-dbcp</groupId>
+            <artifactId>commons-dbcp</artifactId>
+            <version>1.4</version>
+        </dependency>
+        <!-- jackson json -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.module</groupId>
+            <artifactId>jackson-module-jaxb-annotations</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
+        <!-- fastjson json-->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>${fastjson.version}</version>
+        </dependency> 
+
+        <!-- xstream xml -->
+        <dependency>
+            <groupId>com.thoughtworks.xstream</groupId>
+            <artifactId>xstream</artifactId>
+            <version>${xstream.version}</version>
+        </dependency>
+
+        <!-- pojo copy -->
+        <dependency>
+            <groupId>net.sf.dozer</groupId>
+            <artifactId>dozer</artifactId>
+            <version>${dozer.version}</version>
+        </dependency>
+
+        <!-- freemarker engine -->
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+            <version>${freemarker.version}</version>
+        </dependency>
+
+        <!-- email -->
+        <dependency>
+            <groupId>javax.mail</groupId>
+            <artifactId>mail</artifactId>
+            <version>1.4.7</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.activation</groupId>
+            <artifactId>activation</artifactId>
+            <version>1.1.1</version>
+        </dependency>
+
+        <!-- poi office -->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml-schemas</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
+
+        <!-- image util -->
+        <dependency>
+            <groupId>com.drewnoakes</groupId>
+            <artifactId>metadata-extractor</artifactId>
+            <version>2.6.2</version>
+        </dependency>
+
+        <!-- 条形码、二维码生成  -->
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>core</artifactId>
+            <version>2.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>javase</artifactId>
+            <version>2.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>batik</groupId>
+            <artifactId>batik-util</artifactId>
+            <version>1.6-1</version>
+            <systemPath>${lib.path}/batik-util-1.7.jar</systemPath>
+            <scope>system</scope>
+        </dependency>
+        
+        <!-- quartz -->
+		<dependency>
+			<groupId>org.quartz-scheduler</groupId>
+			<artifactId>quartz</artifactId>
+			<version>${quartz.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.quartz-scheduler</groupId>
+			<artifactId>quartz-jobs</artifactId>
+			<version>${quartz.version}</version>
+		</dependency>
+
+        <!-- 中文分词 -->
+        <dependency>
+            <groupId>org.wltea</groupId>
+            <artifactId>analyzer</artifactId>
+            <version>2012_u6</version>
+            <systemPath>${lib.path}/analyzer-2012_u6.jar</systemPath>
+            <scope>system</scope>
+        </dependency>
+        <!-- GENERAL UTILS end -->
+
+
+        <dependency>
+            <groupId>gencode</groupId>
+            <artifactId>gencode</artifactId>
+            <version>2.0</version>
+            <systemPath>${lib.path}/org.jeeframework.gencode-2.0-tomcat8.5.jar</systemPath>
+            <scope>system</scope>
+        </dependency>
+	 <!-- Echarts图表依赖包开始 -->
+        <!-- https://mvnrepository.com/artifact/com.github.abel533/ECharts -->
+        <dependency>
+            <groupId>com.github.abel533</groupId>
+            <artifactId>ECharts</artifactId>
+            <version>3.0.0.2</version>
+        </dependency>
+	    <dependency>
+	        <groupId>com.google.code.gson</groupId>
+	        <artifactId>gson</artifactId>
+	        <version>2.6.2</version>
+	    </dependency>
+	    <!-- Echarts图表依赖包结束 -->
+		
+        <!-- TEST begin -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.11</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <!-- TEST end -->
+        <dependency>
+            <groupId>eu.bitwalker</groupId>
+            <artifactId>UserAgentUtils</artifactId>
+            <version>1.20</version>
+        </dependency>
+
+        <!--httpcore -->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore</artifactId>
+            <version>4.4.4</version>
+        </dependency>
+
+	<!-- swagger-springmvc -->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>2.5.0</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>2.5.0</version>
+        </dependency>
+	    <!-- swagger-springmvc dependencies -->
+        <!--httpclient -->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.2</version>
+        </dependency>
+        <dependency>
+            <groupId>QRCoder</groupId>
+            <artifactId>QRCoder</artifactId>
+            <version>1.0</version>
+            <systemPath>${lib.path}/QRCode.jar</systemPath>
+            <scope>system</scope>
+        </dependency>
+       
+        <!-- UTILS begin -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>${commons-lang3.version}</version>
+        </dependency>
+
+
+        <!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+            <version>42.1.1</version>
+        </dependency>
+
+        <!-- UTILS end -->
+
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>1.1.1</version>
+            <scope>system</scope>
+            <!--本地jar的路径,相对或者绝对都可以-->
+            <systemPath>${lib.path}/CCP_REST_SMS_SDK_JAVA_v2.6.3r.jar</systemPath>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.alipay.sdk</groupId>
+            <artifactId>alipay-sdk-java</artifactId>
+            <version>3.4.49.ALL</version>
+        </dependency>
+        <dependency>
+            <groupId>com.belerweb</groupId>
+            <artifactId>pinyin4j</artifactId>
+            <version>2.5.1</version>
+        </dependency>
+
+
+        <!-- pagehelper分页插件 -->
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper</artifactId>
+            <version>5.1.2</version>
+        </dependency>
+
+
+        <!-- jwt -->
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>2.2.0</version>
+        </dependency>
+    </dependencies>
+
+</project>

BIN
src/.DS_Store


BIN
src/main/.DS_Store


+ 20 - 0
src/main/java/com/jeeplus/common/annotation/FieldName.java

@@ -0,0 +1,20 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * bean中文名注解
+ */
+@Target(ElementType.METHOD)  
+@Retention(RetentionPolicy.RUNTIME)  
+public @interface FieldName {
+
+	String value();
+	
+}

+ 16 - 0
src/main/java/com/jeeplus/common/annotation/IgnoreAuth.java

@@ -0,0 +1,16 @@
+package com.jeeplus.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 忽略Token验证
+ * @author chenshun
+ * @email sunlightcs@gmail.com
+ * @date 2017-03-23 15:44
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface IgnoreAuth {
+
+}

+ 13 - 0
src/main/java/com/jeeplus/common/beanvalidator/AddGroup.java

@@ -0,0 +1,13 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.beanvalidator;
+
+/**
+ * 添加Bean验证组
+ * @author jeeplus
+ *
+ */
+public interface AddGroup {
+
+}

+ 116 - 0
src/main/java/com/jeeplus/common/beanvalidator/BeanValidators.java

@@ -0,0 +1,116 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.beanvalidator;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validator;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+/**
+ * JSR303 Validator(Hibernate Validator)工具类.
+ * 
+ * ConstraintViolation中包含propertyPath, message 和invalidValue等信息.
+ * 提供了各种convert方法,适合不同的i18n需求:
+ * 1. List<String>, String内容为message
+ * 2. List<String>, String内容为propertyPath + separator + message
+ * 3. Map<propertyPath, message>
+ * 
+ * 详情见wiki: https://github.com/springside/springside4/wiki/HibernateValidator
+ * @author calvin
+ * @version 2013-01-15
+ */
+public class BeanValidators {
+
+	/**
+	 * 调用JSR303的validate方法, 验证失败时抛出ConstraintViolationException.
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public static void validateWithException(Validator validator, Object object, Class<?>... groups)
+			throws ConstraintViolationException {
+		Set constraintViolations = validator.validate(object, groups);
+		if (!constraintViolations.isEmpty()) {
+			throw new ConstraintViolationException(constraintViolations);
+		}
+	}
+
+	/**
+	 * 辅助方法, 转换ConstraintViolationException中的Set<ConstraintViolations>中为List<message>.
+	 */
+	public static List<String> extractMessage(ConstraintViolationException e) {
+		return extractMessage(e.getConstraintViolations());
+	}
+
+	/**
+	 * 辅助方法, 转换Set<ConstraintViolation>为List<message>
+	 */
+	@SuppressWarnings("rawtypes")
+	public static List<String> extractMessage(Set<? extends ConstraintViolation> constraintViolations) {
+		List<String> errorMessages = Lists.newArrayList();
+		for (ConstraintViolation violation : constraintViolations) {
+			errorMessages.add(violation.getMessage());
+		}
+		return errorMessages;
+	}
+
+	/**
+	 * 辅助方法, 转换ConstraintViolationException中的Set<ConstraintViolations>为Map<property, message>.
+	 */
+	public static Map<String, String> extractPropertyAndMessage(ConstraintViolationException e) {
+		return extractPropertyAndMessage(e.getConstraintViolations());
+	}
+
+	/**
+	 * 辅助方法, 转换Set<ConstraintViolation>为Map<property, message>.
+	 */
+	@SuppressWarnings("rawtypes")
+	public static Map<String, String> extractPropertyAndMessage(Set<? extends ConstraintViolation> constraintViolations) {
+		Map<String, String> errorMessages = Maps.newHashMap();
+		for (ConstraintViolation violation : constraintViolations) {
+			errorMessages.put(violation.getPropertyPath().toString(), violation.getMessage());
+		}
+		return errorMessages;
+	}
+
+	/**
+	 * 辅助方法, 转换ConstraintViolationException中的Set<ConstraintViolations>为List<propertyPath message>.
+	 */
+	public static List<String> extractPropertyAndMessageAsList(ConstraintViolationException e) {
+		return extractPropertyAndMessageAsList(e.getConstraintViolations(), " ");
+	}
+
+	/**
+	 * 辅助方法, 转换Set<ConstraintViolations>为List<propertyPath message>.
+	 */
+	@SuppressWarnings("rawtypes")
+	public static List<String> extractPropertyAndMessageAsList(Set<? extends ConstraintViolation> constraintViolations) {
+		return extractPropertyAndMessageAsList(constraintViolations, " ");
+	}
+
+	/**
+	 * 辅助方法, 转换ConstraintViolationException中的Set<ConstraintViolations>为List<propertyPath +separator+ message>.
+	 */
+	public static List<String> extractPropertyAndMessageAsList(ConstraintViolationException e, String separator) {
+		return extractPropertyAndMessageAsList(e.getConstraintViolations(), separator);
+	}
+
+	/**
+	 * 辅助方法, 转换Set<ConstraintViolation>为List<propertyPath +separator+ message>.
+	 */
+	@SuppressWarnings("rawtypes")
+	public static List<String> extractPropertyAndMessageAsList(Set<? extends ConstraintViolation> constraintViolations,
+			String separator) {
+		List<String> errorMessages = Lists.newArrayList();
+		for (ConstraintViolation violation : constraintViolations) {
+			errorMessages.add(violation.getPropertyPath() + separator + violation.getMessage());
+		}
+		return errorMessages;
+	}
+}

+ 12 - 0
src/main/java/com/jeeplus/common/beanvalidator/DefaultGroup.java

@@ -0,0 +1,12 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.beanvalidator;
+
+/**
+ * 默认Bean验证组
+ * @author jeeplus
+ */
+public interface DefaultGroup {
+
+}

+ 12 - 0
src/main/java/com/jeeplus/common/beanvalidator/EditGroup.java

@@ -0,0 +1,12 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.beanvalidator;
+
+/**
+ * 编辑Bena验证组
+ * @author jeeplus
+ */
+public interface EditGroup {
+
+}

+ 317 - 0
src/main/java/com/jeeplus/common/config/Global.java

@@ -0,0 +1,317 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.config;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Map;
+import java.util.Properties;
+
+import com.jeeplus.common.utils.FileUtils;
+import com.jeeplus.core.web.Servlets;
+import com.jeeplus.modules.sys.security.SystemAuthorizingRealm;
+import com.jeeplus.modules.sys.utils.UserUtils;
+import org.apache.ibatis.io.Resources;
+import org.springframework.core.io.DefaultResourceLoader;
+
+import com.google.common.collect.Maps;
+import com.jeeplus.common.utils.PropertiesLoader;
+import com.jeeplus.common.utils.StringUtils;
+
+/**
+ * 全局配置类
+ * @author jeeplus
+ * @version 2017-06-25
+ */
+public class Global {
+
+	/**
+	 * 当前对象实例
+	 */
+	private static Global global = new Global();
+	
+	/**
+	 * 保存全局属性值
+	 */
+	private static Map<String, String> map = Maps.newHashMap();
+	
+	/**
+	 * 属性文件加载对象
+	 */
+	private static PropertiesLoader loader = new PropertiesLoader("/properties/jeeplus.properties");
+
+	/**
+	 * 显示/隐藏
+	 */
+	public static final String SHOW = "1";
+	public static final String HIDE = "0";
+
+	/**
+	 * 是/否
+	 */
+	public static final String YES = "1";
+	public static final String NO = "0";
+	
+	/**
+	 * 对/错
+	 */
+	public static final String TRUE = "true";
+	public static final String FALSE = "false";
+	
+	/**
+	 * 上传文件基础虚拟路径
+	 */
+	public static final String USERFILES_BASE_URL = "/userfiles/";
+
+	/**
+	 * 共享文档物理存储地址
+	 * @return
+	 */
+	public static String getShareBaseDir(){
+		String dir =  Global.getUserfilesBaseDir() + Global.USERFILES_BASE_URL  + "共享文档/";
+		FileUtils.createDirectory(dir);
+		return dir;
+	}
+	/**
+	 * 共享文档网络访问地址
+	 * @return
+	 */
+	public static String getShareBaseUrl(){
+		SystemAuthorizingRealm.Principal principal = (SystemAuthorizingRealm.Principal) UserUtils.getPrincipal();
+		return  Servlets.getRequest().getContextPath() + Global.USERFILES_BASE_URL +  "/共享文档/";
+	}
+
+	/**
+	 * 我的文档物理存储地址
+	 * @return
+	 */
+	public static String getMyDocDir(){
+		SystemAuthorizingRealm.Principal principal = (SystemAuthorizingRealm.Principal) UserUtils.getPrincipal();
+		String dir = Global.getUserfilesBaseDir() + Global.USERFILES_BASE_URL + principal + "/我的文档/";
+		FileUtils.createDirectory(dir);
+		return dir;
+	}
+	/**
+	 * 我的文档网络访问地址
+	 * @return
+	 */
+	public static String getMyDocUrl(){
+		SystemAuthorizingRealm.Principal principal = (SystemAuthorizingRealm.Principal) UserUtils.getPrincipal();
+		return  Servlets.getRequest().getContextPath() + Global.USERFILES_BASE_URL + principal + "/我的文档/";
+	}
+
+	/**
+	 * 程序附件物理存储地址
+	 * @return
+	 */
+	public static String getAttachmentDir(){
+		SystemAuthorizingRealm.Principal principal = (SystemAuthorizingRealm.Principal) UserUtils.getPrincipal();
+		String dir = Global.getUserfilesBaseDir() + Global.USERFILES_BASE_URL + principal + "/程序附件/";
+		FileUtils.createDirectory(dir);
+		return dir;
+	}
+
+
+	/**
+	 * 程序附件物理存储地址
+	 * @return
+	 */
+	public static String getAttachmentDirApi(){
+		String dir = Global.getUserfilesBaseDir() + Global.USERFILES_BASE_URL  + "/程序附件/";
+		FileUtils.createDirectory(dir);
+		return dir;
+	}
+	/**
+	 * 程序附件网络访问地址
+	 * @return
+	 */
+	public static String getAttachmentUrl(){
+
+		SystemAuthorizingRealm.Principal principal = (SystemAuthorizingRealm.Principal) UserUtils.getPrincipal();
+		return  Servlets.getRequest().getContextPath() + Global.USERFILES_BASE_URL + principal + "/程序附件/";
+	}
+
+	/**
+	 * 程序附件网络访问地址
+	 * @return
+	 */
+	public static String getAttachmentUrlApi(){
+
+		return  Servlets.getRequest().getContextPath() + Global.USERFILES_BASE_URL + "/程序附件/";
+	}
+
+	/**
+	 * 绝对地址转换为网络地址
+	 * @return
+	 */
+	public static String transDirToUrl(String dir){
+		return   Servlets.getRequest().getContextPath()+"/" + dir.substring(Global.getUserfilesBaseDir().length());
+	}
+	/**
+	 * 获取当前对象实例
+	 */
+	public static Global getInstance() {
+		return global;
+	}
+	
+	/**
+	 * 获取配置
+	 */
+	public static String getConfig(String key) {
+		String value = map.get(key);
+		if (value == null){
+			value = loader.getProperty(key);
+			map.put(key, value != null ? value : StringUtils.EMPTY);
+		}
+		return value;
+	}
+	
+	/**
+	 * 获取管理端根路径
+	 */
+	public static String getAdminPath() {
+		return getConfig("adminPath");
+	}
+
+
+	/**
+	 * 获取管理端根路径
+	 */
+	public static String getDefaultTheme() {
+		return getConfig("defaultTheme");
+	}
+	
+	/**
+	 * 获取前端根路径
+	 */
+	public static String getFrontPath() {
+		return getConfig("frontPath");
+	}
+	
+	/**
+	 * 获取URL后缀
+	 */
+	public static String getUrlSuffix() {
+		return getConfig("urlSuffix");
+	}
+	
+	/**
+	 * 是否是演示模式,演示模式下不能修改用户、角色、密码、菜单、授权
+	 */
+	public static Boolean isDemoMode() {
+		String dm = getConfig("demoMode");
+		return "true".equals(dm) || "1".equals(dm);
+	}
+	
+	/**
+	 * 在修改系统用户和角色时是否同步到Activiti
+	 */
+	public static Boolean isSynActivitiIndetity() {
+		String dm = getConfig("activiti.isSynActivitiIndetity");
+		return "true".equals(dm) || "1".equals(dm);
+	}
+    
+	/**
+	 * 页面获取常量
+	 */
+	public static Object getConst(String field) {
+		try {
+			return Global.class.getField(field).get(null);
+		} catch (Exception e) {
+			// 异常代表无配置,这里什么也不做
+		}
+		return null;
+	}
+
+	/**
+	 * 获取上传文件的根目录
+	 * @return
+	 */
+	public static String getUserfilesBaseDir() {
+		String dir = getConfig("userfiles.basedir");
+		if (StringUtils.isBlank(dir)){
+			return "";
+		}
+		if(!dir.endsWith("/")) {
+			dir += "/";
+		}
+//		System.out.println("userfiles.basedir: " + dir);
+		return dir;
+	}
+	
+    /**
+     * 获取工程路径
+     * @return
+     */
+    public static String getProjectPath(){
+    	// 如果配置了工程路径,则直接返回,否则自动获取。
+		String projectPath = Global.getConfig("projectPath");
+		if (StringUtils.isNotBlank(projectPath)){
+			return projectPath;
+		}
+		try {
+			File file = new DefaultResourceLoader().getResource("").getFile();
+			if (file != null){
+				while(true){
+					File f = new File(file.getPath() + File.separator + "src" + File.separator + "main");
+					if (f == null || f.exists()){
+						break;
+					}
+					if (file.getParentFile() != null){
+						file = file.getParentFile();
+					}else{
+						break;
+					}
+				}
+				projectPath = file.toString();
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return projectPath;
+    }
+    
+    /**
+	 * 写入properties信息
+	 * 
+	 * @param key
+	 *            名称
+	 * @param value
+	 *            值
+	 */
+	public static void modifyConfig(String key, String value) {
+		try {
+			// 从输入流中读取属性列表(键和元素对)
+			Properties prop = getProperties();
+			prop.setProperty(key, value);
+			String path = Global.class.getResource("/properties/jeeplus.properties").getPath();
+			FileOutputStream outputFile = new FileOutputStream(path);
+			prop.store(outputFile, "modify");
+			outputFile.close();
+			outputFile.flush();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	/**
+	 * 返回 Properties
+	 * @param
+	 * @return
+	 */
+	public static Properties getProperties(){
+		Properties prop = new Properties();
+		try {
+			Reader reader = Resources.getResourceAsReader("/properties/jeeplus.properties");
+			prop.load(reader);
+		} catch (Exception e) {
+			return null;
+		}
+		return prop;
+	}
+
+	
+}

+ 73 - 0
src/main/java/com/jeeplus/common/json/AjaxJson.java

@@ -0,0 +1,73 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.json;
+
+import java.util.LinkedHashMap;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.jeeplus.core.mapper.JsonMapper;
+
+
+/**
+ * $.ajax后需要接受的JSON
+ * 
+ * @author
+ * 
+ */
+public class AjaxJson {
+
+	private boolean success = true;// 是否成功
+	private String errorCode = "-1";//错误代码
+	private String msg = "操作成功";// 提示信息
+	private LinkedHashMap<String, Object> body = new LinkedHashMap<String, Object>();//封装json的map
+	
+	public LinkedHashMap<String, Object> getBody() {
+		return body;
+	}
+
+	public void setBody(LinkedHashMap<String, Object> body) {
+		this.body = body;
+	}
+
+	public void put(String key, Object value){//向json中添加属性,在js中访问,请调用data.map.key
+		body.put(key, value);
+	}
+	
+	public void remove(String key){
+		body.remove(key);
+	}
+	
+	
+	public String getMsg() {
+		return msg;
+	}
+
+	public void setMsg(String msg) {//向json中添加属性,在js中访问,请调用data.msg
+		this.msg = msg;
+	}
+
+
+	public boolean isSuccess() {
+		return success;
+	}
+
+	public void setSuccess(boolean success) {
+		this.success = success;
+	}
+	
+	@JsonIgnore//返回对象时忽略此属性
+	public String getJsonStr() {//返回json字符串数组,将访问msg和key的方式统一化,都使用data.key的方式直接访问。
+
+		String json = JsonMapper.getInstance().toJson(this);
+		return json;
+	}
+
+	public void setErrorCode(String errorCode) {
+		this.errorCode = errorCode;
+	}
+
+	public String getErrorCode() {
+		return errorCode;
+	}
+}

+ 36 - 0
src/main/java/com/jeeplus/common/json/DataJson.java

@@ -0,0 +1,36 @@
+package com.jeeplus.common.json;
+
+import java.util.List;
+
+
+public class DataJson {
+	
+	private int total;
+	private List rows;
+	/**
+	 * @return the total
+	 */
+	public int getTotal() {
+		return total;
+	}
+	/**
+	 * @param total the total to set
+	 */
+	public void setTotal(int total) {
+		this.total = total;
+	}
+	/**
+	 * @return the list
+	 */
+	public List getRows() {
+		return rows;
+	}
+	/**
+	 * @param list the list to set
+	 */
+	public void setRows(List rows) {
+		this.rows = rows;
+	}
+	
+
+}

+ 103 - 0
src/main/java/com/jeeplus/common/json/JSTreeJson.java

@@ -0,0 +1,103 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.json;
+
+import java.util.List;
+
+/**
+ * 数据Entity类
+ * 
+ * @author jeeplus
+ * @version 2017-05-16
+ */
+public abstract class JSTreeJson {
+
+
+	private String id;
+	private String icon;
+	private String text;
+	private String type;
+	private List<JSTreeJson> children;
+	private State state;
+
+	public String getIcon() {
+		return icon;
+	}
+
+	public void setIcon(String icon) {
+		this.icon = icon;
+	}
+
+	public String getText() {
+		return text;
+	}
+
+	public void setText(String text) {
+		this.text = text;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	@SuppressWarnings("rawtypes")
+	public List<JSTreeJson> getChildren() {
+		return children;
+	}
+
+	public void setChildren(@SuppressWarnings("rawtypes") List<JSTreeJson> children) {
+		this.children = children;
+	}
+
+	public State getState() {
+		return state;
+	}
+
+	public void setState(State state) {
+		this.state = state;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+}
+
+class State {
+	boolean opened; // is the node open
+	boolean disabled; // is the node disabled
+	boolean selected; // is the node selected
+
+	public boolean isOpened() {
+		return opened;
+	}
+
+	public void setOpened(boolean opened) {
+		this.opened = opened;
+	}
+
+	public boolean isDisabled() {
+		return disabled;
+	}
+
+	public void setDisabled(boolean disabled) {
+		this.disabled = disabled;
+	}
+
+	public boolean isSelected() {
+		return selected;
+	}
+
+	public void setSelected(boolean selected) {
+		this.selected = selected;
+	}
+
+}

+ 28 - 0
src/main/java/com/jeeplus/common/json/PrintJSON.java

@@ -0,0 +1,28 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.json;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.http.HttpServletResponse;
+
+public class PrintJSON {
+	
+	
+	public static void write(HttpServletResponse response,String content) {
+		response.reset();
+		response.setContentType("application/json");
+		response.setHeader("Cache-Control", "no-store");
+		response.setCharacterEncoding("UTF-8");
+		try {
+			PrintWriter pw=response.getWriter();
+			pw.write(content);
+			pw.flush();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+}

+ 20 - 0
src/main/java/com/jeeplus/common/mail/MailAuthenticator.java

@@ -0,0 +1,20 @@
+package com.jeeplus.common.mail;
+/**   
+ *  
+ */ 
+import javax.mail.*;   
+
+public class MailAuthenticator extends Authenticator{   
+    String userName=null;   
+    String password=null;   
+        
+    public MailAuthenticator(){   
+    }   
+    public MailAuthenticator(String username, String password) {    
+        this.userName = username;    
+        this.password = password;    
+    }    
+    protected PasswordAuthentication getPasswordAuthentication(){   
+        return new PasswordAuthentication(userName, password);   
+    }   
+}   

+ 96 - 0
src/main/java/com/jeeplus/common/mail/MailBody.java

@@ -0,0 +1,96 @@
+package com.jeeplus.common.mail;   
+/**   
+ *发送邮件需要使用的基本信息 
+ *  
+ */    
+import java.util.Properties;    
+public class MailBody {    
+    // 发送邮件的服务器的IP和端口    
+    private String mailServerHost = "";
+    private String mailServerPort = "";
+    // 邮件发送者的地址    
+    private String fromAddress = "";
+    // 邮件接收者的地址    
+    private String toAddress = "";
+    // 登陆邮件发送服务器的用户名和密码    
+    private String userName="";
+    private String password = "";
+    // 是否需要身份验证    
+    private boolean validate = false;    
+    // 邮件主题    
+    private String subject;    
+    // 邮件的文本内容    
+    private String content;    
+    // 邮件附件的文件名    
+    private String[] attachFileNames;      
+    /**   
+      * 获得邮件会话属性   
+      */    
+    public Properties getProperties(){    
+      Properties p = new Properties();    
+      p.put("mail.smtp.host", this.mailServerHost);    
+      p.put("mail.smtp.port", this.mailServerPort);    
+      p.put("mail.smtp.auth", validate ? "true" : "false");    
+      return p;    
+    }    
+    public String getMailServerHost() {    
+      return mailServerHost;    
+    }    
+    public void setMailServerHost(String mailServerHost) {    
+      this.mailServerHost = mailServerHost;    
+    }   
+    public String getMailServerPort() {    
+      return mailServerPort;    
+    }   
+    public void setMailServerPort(String mailServerPort) {    
+      this.mailServerPort = mailServerPort;    
+    }   
+    public boolean isValidate() {    
+      return validate;    
+    }   
+    public void setValidate(boolean validate) {    
+      this.validate = validate;    
+    }   
+    public String[] getAttachFileNames() {    
+      return attachFileNames;    
+    }   
+    public void setAttachFileNames(String[] fileNames) {    
+      this.attachFileNames = fileNames;    
+    }   
+    public String getFromAddress() {    
+      return fromAddress;    
+    }    
+    public void setFromAddress(String fromAddress) {    
+      this.fromAddress = fromAddress;    
+    }   
+    public String getPassword() {    
+      return password;    
+    }   
+    public void setPassword(String password) {    
+      this.password = password;    
+    }   
+    public String getToAddress() {    
+      return toAddress;    
+    }    
+    public void setToAddress(String toAddress) {    
+      this.toAddress = toAddress;    
+    }    
+    public String getUserName() {    
+      return userName;    
+    }   
+    public void setUserName(String userName) {    
+      this.userName = userName;    
+    }   
+    public String getSubject() {    
+      return subject;    
+    }   
+    public void setSubject(String subject) {    
+      this.subject = subject;    
+    }   
+    public String getContent() {    
+      return content;    
+    }   
+    public void setContent(String textContent) {    
+      this.content = textContent;    
+    }    
+}   

+ 161 - 0
src/main/java/com/jeeplus/common/mail/MailSendUtils.java

@@ -0,0 +1,161 @@
+package com.jeeplus.common.mail;
+/**   
+ * 简单邮件(不带附件的邮件)发送器   
+ */ 
+import java.util.Date;
+import java.util.Properties;
+
+import javax.mail.Address;
+import javax.mail.BodyPart;
+import javax.mail.Message;
+import javax.mail.Multipart;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+    
+public class MailSendUtils   {    
+/**   
+  * 以文本格式发送邮件   
+  * @param mailInfo 待发送的邮件的信息   
+  */    
+    public boolean sendTextMail(MailBody mailInfo) throws Exception{    
+      // 判断是否需要身份认证    
+      MailAuthenticator authenticator = null;    
+      Properties pro = mailInfo.getProperties();   
+      if (mailInfo.isValidate()) {    
+      // 如果需要身份认证,则创建一个密码验证器    
+        authenticator = new MailAuthenticator(mailInfo.getUserName(), mailInfo.getPassword());    
+      }   
+      // 根据邮件会话属性和密码验证器构造一个发送邮件的session    
+      Session sendMailSession = Session.getDefaultInstance(pro,authenticator); 
+     // logBefore(logger, "构造一个发送邮件的session");
+      
+      // 根据session创建一个邮件消息    
+      Message mailMessage = new MimeMessage(sendMailSession);    
+      // 创建邮件发送者地址    
+      Address from = new InternetAddress(mailInfo.getFromAddress());    
+      // 设置邮件消息的发送者    
+      mailMessage.setFrom(from);    
+      // 创建邮件的接收者地址,并设置到邮件消息中    
+      Address to = new InternetAddress(mailInfo.getToAddress());    
+      mailMessage.setRecipient(Message.RecipientType.TO,to);    
+      // 设置邮件消息的主题    
+      mailMessage.setSubject(mailInfo.getSubject());    
+      // 设置邮件消息发送的时间    
+      mailMessage.setSentDate(new Date());    
+      // 设置邮件消息的主要内容    
+      String mailContent = mailInfo.getContent();    
+      mailMessage.setText(mailContent);    
+      // 发送邮件    
+      Transport.send(mailMessage); 
+      System.out.println("发送成功!");
+      return true;    
+    }    
+       
+    /**   
+      * 以HTML格式发送邮件   
+      * @param mailInfo 待发送的邮件信息   
+      */    
+    public  boolean sendHtmlMail(MailBody mailInfo) throws Exception{    
+      // 判断是否需要身份认证    
+      MailAuthenticator authenticator = null;   
+      Properties pro = mailInfo.getProperties();
+      pro.put("mail.smtp.sll.enable","true");
+      //如果需要身份认证,则创建一个密码验证器     
+      if (mailInfo.isValidate()) {    
+        authenticator = new MailAuthenticator(mailInfo.getUserName(), mailInfo.getPassword());   
+      }    
+      // 根据邮件会话属性和密码验证器构造一个发送邮件的session    
+      Session sendMailSession = Session.getDefaultInstance(pro,authenticator);    
+        
+      // 根据session创建一个邮件消息    
+      Message mailMessage = new MimeMessage(sendMailSession);    
+      // 创建邮件发送者地址    
+      Address from = new InternetAddress(mailInfo.getFromAddress());    
+      // 设置邮件消息的发送者    
+      mailMessage.setFrom(from);    
+      // 创建邮件的接收者地址,并设置到邮件消息中    
+      Address to = new InternetAddress(mailInfo.getToAddress());    
+      // Message.RecipientType.TO属性表示接收者的类型为TO    
+      mailMessage.setRecipient(Message.RecipientType.TO,to);    
+      // 设置邮件消息的主题    
+      mailMessage.setSubject(mailInfo.getSubject());    
+      // 设置邮件消息发送的时间    
+      mailMessage.setSentDate(new Date());    
+      // MiniMultipart类是一个容器类,包含MimeBodyPart类型的对象    
+      Multipart mainPart = new MimeMultipart();    
+      // 创建一个包含HTML内容的MimeBodyPart    
+      BodyPart html = new MimeBodyPart();    
+      // 设置HTML内容    
+      html.setContent(mailInfo.getContent(), "text/html; charset=utf-8");    
+      mainPart.addBodyPart(html);    
+      // 将MiniMultipart对象设置为邮件内容    
+      mailMessage.setContent(mainPart);    
+      // 发送邮件    
+      Transport.send(mailMessage);    
+      return true;    
+    }
+
+	/**
+	 * @param SMTP
+	 *            邮件服务器
+	 * @param PORT
+	 *            端口
+	 * @param EMAIL
+	 *            本邮箱账号
+	 * @param PAW
+	 *            本邮箱密码
+	 * @param toEMAIL
+	 *            对方箱账号
+	 * @param TITLE
+	 *            标题
+	 * @param CONTENT
+	 *            内容
+	 * @param TYPE
+	 *            1:文本格式;2:HTML格式
+	 */
+	public static boolean sendEmail(String SMTP, String PORT, String EMAIL,
+			String PAW, String toEMAIL, String TITLE, String CONTENT,
+			String TYPE) {
+
+		// 这个类主要是设置邮件
+		MailBody mailInfo = new MailBody();
+
+		mailInfo.setMailServerHost(SMTP);
+		mailInfo.setMailServerPort(PORT);
+		mailInfo.setValidate(true);
+		mailInfo.setUserName(EMAIL);
+		mailInfo.setPassword(PAW);
+		mailInfo.setFromAddress(EMAIL);
+		mailInfo.setToAddress(toEMAIL);
+		mailInfo.setSubject(TITLE);
+		mailInfo.setContent(CONTENT);
+		// 这个类主要来发送邮件
+
+		MailSendUtils sms = new MailSendUtils();
+		try {
+			if ("1".equals(TYPE)) {
+				return sms.sendTextMail(mailInfo);
+			} else {
+				return sms.sendHtmlMail(mailInfo);
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+			System.out.println(e.getMessage());
+			return false;
+		}
+
+	}
+    
+    
+public static void main(String[]agrs){
+	    MailSendUtils.sendEmail("jeeplus.org","25","jeeplus@jeeplus.org",
+                "fnst@1234","jeeplus@yeah.net","test","liugf","2");
+}
+
+    
+}   

+ 75 - 0
src/main/java/com/jeeplus/common/sms/SMSUtils.java

@@ -0,0 +1,75 @@
+package com.jeeplus.common.sms;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+
+import com.jeeplus.core.security.Digests;
+
+/*
+功能:		企信通PHP HTTP接口 发送短信
+修改日期:	2017-03-19
+说明:		http://api.cnsms.cn/?ac=send&uid=用户账号&pwd=MD5位32密码&mobile=号码&content=内容
+状态:
+	100 发送成功
+	101 验证失败
+	102 短信不足
+	103 操作失败
+	104 非法字符
+	105 内容过多
+	106 号码过多
+	107 频率过快
+	108 号码内容空
+	109 账号冻结
+	110 禁止频繁单条发送
+	111 系统暂定发送
+	112 号码不正确
+	120 系统升级
+*/
+public class SMSUtils {
+
+
+	//发送短信,uid,pwd,参数值请向企信通申请, tel:发送的手机号, content:发送的内容
+	public static String send(String uid, String pwd, String tel, String content) throws IOException {
+		
+		// 创建StringBuffer对象用来操作字符串
+		StringBuffer sb = new StringBuffer("http://api.cnsms.cn/?");
+
+		// 向StringBuffer追加用户名
+		sb.append("ac=send&uid="+uid);//在此申请企信通uid,并进行配置用户名
+		
+		// 向StringBuffer追加密码(密码采用MD5 32位 小写)
+		sb.append("&encode=utf8");
+
+		// 向StringBuffer追加密码(密码采用MD5 32位 小写)
+		sb.append("&pwd="+Digests.string2MD5(pwd));//在此申请企信通uid,并进行配置密码
+
+		// 向StringBuffer追加手机号码
+		sb.append("&mobile="+tel);
+		// 向StringBuffer追加消息内容转URL标准码
+		sb.append("&content="+URLEncoder.encode(content,"utf8"));
+
+		// 创建url对象
+		URL url = new URL(sb.toString());
+
+		// 打开url连接
+		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+		// 设置url请求方式 ‘get’ 或者 ‘post’
+		connection.setRequestMethod("POST");
+
+		// 发送
+		BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
+
+		// 返回发送结果
+		String inputline = in.readLine();
+		return inputline;
+
+	}
+
+
+
+}

+ 32 - 0
src/main/java/com/jeeplus/common/swagger/SwaggerConfig.java

@@ -0,0 +1,32 @@
+package com.jeeplus.common.swagger;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+    @Bean
+    public Docket api() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+                .build();
+    }
+    //api接口作者相关信息
+    private ApiInfo apiInfo() {
+        Contact contact = new Contact("刘高峰", "http://www.jeeplus.org", "jeeplus@126.com");
+        ApiInfo apiInfo = new ApiInfoBuilder().license("GPL").title("jeeplus快速开发平台").description("接口文档").contact(contact).version("3.0").build();
+        return apiInfo;
+    }
+}

+ 127 - 0
src/main/java/com/jeeplus/common/tag/AniMenuTag.java

@@ -0,0 +1,127 @@
+package com.jeeplus.common.tag;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.servlet.jsp.JspTagException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.tagext.TagSupport;
+
+import com.jeeplus.common.config.Global;
+import com.jeeplus.common.utils.SpringContextHolder;
+import com.jeeplus.modules.sys.entity.Menu;
+import com.jeeplus.modules.sys.utils.UserUtils;
+
+/**
+ * 
+ * 类描述:菜单标签
+ * 
+ * 刘高峰
+ * 
+ * @date: 日期:2015-1-23 时间:上午10:17:45
+ * 
+ * @version 1.0
+ */
+public class AniMenuTag extends TagSupport {
+	private static final long serialVersionUID = 1L;
+	protected Menu menu;// 菜单Map
+
+	public Menu getMenu() {
+		return menu;
+	}
+
+	public void setMenu(Menu menu) {
+		this.menu = menu;
+	}
+
+	public int doStartTag() throws JspTagException {
+		return EVAL_PAGE;
+	}
+
+	public int doEndTag() throws JspTagException {
+		try {
+			JspWriter out = this.pageContext.getOut();
+			String menu = (String) this.pageContext.getSession().getAttribute("menu");
+			if (menu != null) {
+				out.print(menu);
+			} else {
+				menu = end().toString();
+				out.print(menu);
+
+			}
+
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return EVAL_PAGE;
+	}
+
+	public StringBuffer end() {
+		StringBuffer sb = new StringBuffer();
+		sb.append(getChildOfTree(menu, 0, UserUtils.getMenuList()));
+
+		return sb;
+
+	}
+
+	private static String getChildOfTree(Menu menuItem, int level, List<Menu> menuList) {
+		StringBuffer menuString = new StringBuffer();
+		String href = "";
+		if (!menuItem.hasPermisson())
+			return "";
+		if (level > 0) {// level 为0是功能菜单
+
+			ServletContext context = SpringContextHolder.getBean(ServletContext.class);
+			if (menuItem.getHref() != null && menuItem.getHref().length() > 0) {// 如果是子节点
+
+				if (menuItem.getHref().startsWith("http://") || menuItem.getHref().startsWith("https://")) {// 如果是互联网资源
+					href = menuItem.getHref();
+				} else if (menuItem.getHref().endsWith(".html")) {// 如果是静态资源并且不是ckfinder.html,直接访问不加adminPath
+					href = context.getContextPath() + menuItem.getHref();
+				} else {
+					href = context.getContextPath() + Global.getAdminPath() + menuItem.getHref();
+				}
+				if(menuItem.getTarget()!=null && !menuItem.getTarget().equals("")){
+					menuString.append("<li><a class=\"J_menuItem\"  href=\"" + href +"\" target=\""+menuItem.getTarget()+"\" "+ "><i class=\"fa " + menuItem.getIcon() + "\"></i>&nbsp;&nbsp;"
+							+ menuItem.getName() + "</a></li>\n");
+				}else{
+					menuString.append("<li><a class=\"J_menuItem\" href=\"" + href + "\"><i class=\"fa " + menuItem.getIcon() + "\"></i>&nbsp;&nbsp;"
+							+ menuItem.getName() + "</a></li>\n");
+				}
+				
+			}
+		}
+
+		if ((menuItem.getHref() == null || menuItem.getHref().trim().equals("")) && menuItem.getIsShow().equals("1")) {// 如果是父节点且显示
+			if (level == 0) {// 如果是功能菜单
+				for (Menu child : menuList) {
+					if (child.getParentId().equals(menuItem.getId()) && child.getIsShow().equals("1")) {
+						menuString.append(getChildOfTree(child, level + 1, menuList));
+					}
+				}
+			}
+
+			if (level > 0) {// 不是功能菜单
+				menuString.append("<li class=\"panel\">\n");
+				menuString.append("<a  data-toggle=\"collapse\" data-parent=\"#"+menuItem.getParentId()+"\" class=\"collapsed\" href=\"#" + menuItem.getId()
+						+ "\"><i class=\"fa " + menuItem.getIcon() + "\"></i>&nbsp;&nbsp;"
+						+ menuItem.getName() + "<span class=\"pull-right fa fa-angle-toggle\"></span></a>\n");
+				menuString.append(
+						"<ul id=\"" + menuItem.getId() + "\"" + " class=\"nav collapse\">\n");
+
+				for (Menu child : menuList) {
+					if (child.getParentId().equals(menuItem.getId()) && child.getIsShow().equals("1")) {
+						menuString.append(getChildOfTree(child, level + 1, menuList));
+					}
+				}
+				menuString.append("</ul>\n");
+				menuString.append("</li>\n");
+			}
+
+		}
+
+		return menuString.toString();
+	}
+
+}

+ 221 - 0
src/main/java/com/jeeplus/common/tag/JPMenuTag.java

@@ -0,0 +1,221 @@
+package com.jeeplus.common.tag;
+
+import com.jeeplus.common.config.Global;
+import com.jeeplus.common.utils.SpringContextHolder;
+import com.jeeplus.common.utils.StringUtils;
+import com.jeeplus.modules.sys.entity.Menu;
+import com.jeeplus.modules.sys.utils.UserUtils;
+
+import javax.servlet.ServletContext;
+import javax.servlet.jsp.JspTagException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.tagext.TagSupport;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * 
+ * 类描述:菜单标签
+ * 
+ * 刘高峰
+ * 
+ * @date: 日期:2015-1-23 时间:上午10:17:45
+ * 
+ * @version 1.0
+ */
+public class JPMenuTag extends TagSupport {
+	private static final long serialVersionUID = 1L;
+	protected Menu menu;// 菜单Map
+
+	protected static final String DEFAULT_ICON= "fa-th-list";//左侧一级菜单图标为空时,默认显示的图标,不显示默认图标时使用空字符即可。
+
+	public String getPosition() {
+		return position;
+	}
+
+	public void setPosition(String position) {
+		this.position = position;
+	}
+
+	protected String position;//菜单输出的位置
+
+	public Menu getMenu() {
+		return menu;
+	}
+
+	public void setMenu(Menu menu) {
+		this.menu = menu;
+	}
+
+	public int doStartTag() throws JspTagException {
+		return EVAL_PAGE;
+	}
+
+	public int doEndTag() throws JspTagException {
+		try {
+			JspWriter out = this.pageContext.getOut();
+			String menu = (String) this.pageContext.getSession().getAttribute("menu");
+			if (menu != null) {
+				out.print(menu);
+			} else {
+				menu = end().toString();
+				out.print(menu);
+
+			}
+
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return EVAL_PAGE;
+	}
+
+	public StringBuffer end() {
+		StringBuffer sb = new StringBuffer();
+		if(position.equals("top")){
+			sb.append(getTopMenu(menu, 0, UserUtils.getMenuList(), -1));
+		}else{
+			sb.append(getLeftMenu(menu, 0, UserUtils.getMenuList(), -1));
+		}
+
+
+		return sb;
+
+	}
+
+
+	private static  String getTopMenu(Menu menuItem, int level, List<Menu> menuList, int first){
+
+
+
+		StringBuffer menuString = new StringBuffer();
+		String href = "";
+		if (!menuItem.hasPermisson())
+			return "";
+
+
+
+
+		if ((menuItem.getHref() == null || menuItem.getHref().trim().equals("")) && menuItem.getIsShow().equals("1")) {// 如果是父节点且显示
+			if (level == 0) {// 如果是功能菜单
+				int flag = 0;
+				for (Menu child : menuList) {
+					if (child.getParentId().equals(menuItem.getId()) && child.getIsShow().equals("1")) {
+						menuString.append(getTopMenu(child, level + 1, menuList, ++flag));
+					}
+				}
+			}
+
+			if (level  == 1) {// 不是功能菜单
+
+				if(first == 1){
+					menuString.append("<li role=\"presentation\" class='active'>\n");
+				}else{
+					menuString.append("<li>\n");
+				}
+				menuString.append("<a href=\"#"+menuItem.getId()+"\" role=\"tab\" data-toggle=\"tab\">");
+				menuString.append("<i class=\"fa " + menuItem.getIcon() +"\"></i> <span>"+ menuItem.getName() +"</span>\n");
+				menuString.append("</a>");
+				menuString.append("</li>");
+			}
+
+		}
+
+		return menuString.toString();
+
+	}
+
+	private static String getLeftMenu(Menu menuItem, int level, List<Menu> menuList, int first){
+
+
+		StringBuffer menuString = new StringBuffer();
+		String href = "";
+		if (!menuItem.hasPermisson())
+			return "";
+		if (level > 1) {// level 为1是top菜单
+
+			ServletContext context = SpringContextHolder.getBean(ServletContext.class);
+
+
+
+
+			if (menuItem.getHref() != null && menuItem.getHref().length() > 0) {// 如果是子节点
+
+				if (menuItem.getHref().startsWith("http://") || menuItem.getHref().startsWith("https://")) {// 如果是互联网资源
+					href = menuItem.getHref();
+				} else if (menuItem.getHref().endsWith(".html")) {// 如果是静态资源并且不是ckfinder.html,直接访问不加adminPath
+					href = context.getContextPath() + menuItem.getHref();
+				} else {
+					href = context.getContextPath() + Global.getAdminPath() + menuItem.getHref();
+				}
+
+
+				String icon = menuItem.getIcon();
+				if(level == 2){
+					if(StringUtils.isBlank(icon)){
+						icon =  DEFAULT_ICON;
+					}
+				}
+				if(menuItem.getTarget()!=null && !menuItem.getTarget().equals("")){
+					menuString.append("<li><a class=\"J_menuItem\"  href=\"" + href +"\" target=\""+menuItem.getTarget()+"\" "+ "><i class=\"fa " + icon + "\"></i><span>"+ menuItem.getName() + "</span></a></li>\n");
+				}else{
+					menuString.append("<li><a class=\"J_menuItem\" href=\"" + href + "\"><i class=\"fa " + icon + "\"></i><span>"+ menuItem.getName() + "</span></a></li>\n");
+				}
+
+			}
+		}
+
+		if ((menuItem.getHref() == null || menuItem.getHref().trim().equals("")) && menuItem.getIsShow().equals("1")) {// 如果是父节点且显示
+			if (level == 0) {// 如果是功能菜单
+				int flag = 0;
+				for (Menu child : menuList) {
+					if (child.getParentId().equals(menuItem.getId()) && child.getIsShow().equals("1")) {
+						menuString.append(getLeftMenu(child, level + 1, menuList, ++flag));
+					}
+				}
+			}
+
+			if(level == 1){
+				if(first == 1){
+					menuString.append("<div class=\"tab-pane fade active in\" id=\""+menuItem.getId()+"\">");
+				}else{
+					menuString.append("<div class=\"tab-pane fade\" id=\""+menuItem.getId()+"\">");
+				}
+
+				menuString.append("<ul class=\"nav nav-pills nav-stacked\">");
+				for (Menu child : menuList) {
+					if (child.getParentId().equals(menuItem.getId()) && child.getIsShow().equals("1")) {
+						menuString.append(getLeftMenu(child, level + 1, menuList, -1));
+					}
+				}
+				menuString.append("</ul>");
+				menuString.append("</div>");
+			}
+
+			if (level > 1) {// 不是功能菜单
+				String icon = menuItem.getIcon();
+				if(level == 2){
+					if(StringUtils.isBlank(icon)){
+						icon =  DEFAULT_ICON;
+					}
+				}
+				menuString.append("<li>\n");
+				menuString.append("<a href=\"#\" class=\"dropdown-toggle\"><i class=\"fa " + icon + "\"></i><span>"
+						+ menuItem.getName() + "</span><i class=\"fa fa-chevron-circle-right drop-icon\"></i></a>\n");
+				menuString.append(
+						"<ul class=\"submenu\" style=\"display: none;\">\n");
+
+				for (Menu child : menuList) {
+					if (child.getParentId().equals(menuItem.getId()) && child.getIsShow().equals("1")) {
+						menuString.append(getLeftMenu(child, level + 1, menuList, -1));
+					}
+				}
+				menuString.append("</ul>\n");
+				menuString.append("</li>\n");
+			}
+
+		}
+
+		return menuString.toString();
+	}
+
+}

+ 82 - 0
src/main/java/com/jeeplus/common/utils/CacheUtils.java

@@ -0,0 +1,82 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils;
+
+import net.oschina.j2cache.CacheObject;
+import net.oschina.j2cache.J2Cache;
+
+/**
+ * Cache工具类
+ * @author jeeplus
+ * @version 2017-1-19
+ */
+public class CacheUtils {
+	
+
+	private static final String SYS_REGION = "sysCache2";
+
+	
+	
+	/**
+	 * 获取SYS_CACHE缓存
+	 * @param key
+	 * @return
+	 */
+	public static Object get(String key) {
+		return get(SYS_REGION, key);
+	}
+	
+	/**
+	 * 写入SYS_CACHE缓存
+	 * @param key
+	 * @return
+	 */
+	public static void put(String key, Object value) {
+		put(SYS_REGION, key, value);
+	}
+	
+	/**
+	 * 从SYS_CACHE缓存中移除
+	 * @param key
+	 * @return
+	 */
+	public static void remove(String key) {
+		remove(SYS_REGION, key);
+	}
+	
+	/**
+	 * 获取缓存
+	 * @param key
+	 * @return
+	 */
+	public static Object get(String region, String key) {
+		CacheObject object = J2Cache.getChannel().get(region ,key);
+		return object==null?null:object.getValue();
+	}
+	
+	
+	
+
+	/**
+	 * 写入缓存
+	 * @param region
+	 * @param key
+	 * @param value
+	 */
+	public static void put(String region, String key, Object value) {
+		J2Cache.getChannel().set(region, key, value);
+
+	}
+
+	/**
+	 * 从缓存中移除
+	 * @param region
+	 * @param key
+	 */
+	public static void remove(String region, String key) {
+		J2Cache.getChannel().evict(region, key);
+	}
+
+	
+}

+ 176 - 0
src/main/java/com/jeeplus/common/utils/Collections3.java

@@ -0,0 +1,176 @@
+/**
+ * Copyright (c) 2005-2012 springside.org.cn
+ */
+package com.jeeplus.common.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Collections工具集.
+ * 在JDK的Collections和Guava的Collections2后, 命名为Collections3.
+ * @author calvin
+ * @version 2016-01-15
+ */
+@SuppressWarnings("rawtypes")
+public class Collections3 {
+
+	/**
+	 * 提取集合中的对象的两个属性(通过Getter函数), 组合成Map.
+	 * 
+	 * @param collection 来源集合.
+	 * @param keyPropertyName 要提取为Map中的Key值的属性名.
+	 * @param valuePropertyName 要提取为Map中的Value值的属性名.
+	 */
+	@SuppressWarnings("unchecked")
+	public static Map extractToMap(final Collection collection, final String keyPropertyName,
+			final String valuePropertyName) {
+		Map map = new HashMap(collection.size());
+
+		try {
+			for (Object obj : collection) {
+				map.put(PropertyUtils.getProperty(obj, keyPropertyName),
+						PropertyUtils.getProperty(obj, valuePropertyName));
+			}
+		} catch (Exception e) {
+			throw Reflections.convertReflectionExceptionToUnchecked(e);
+		}
+
+		return map;
+	}
+
+	/**
+	 * 提取集合中的对象的一个属性(通过Getter函数), 组合成List.
+	 * 
+	 * @param collection 来源集合.
+	 * @param propertyName 要提取的属性名.
+	 */
+	@SuppressWarnings("unchecked")
+	public static List extractToList(final Collection collection, final String propertyName) {
+		List list = new ArrayList(collection.size());
+
+		try {
+			for (Object obj : collection) {
+				list.add(PropertyUtils.getProperty(obj, propertyName));
+			}
+		} catch (Exception e) {
+			throw Reflections.convertReflectionExceptionToUnchecked(e);
+		}
+
+		return list;
+	}
+
+	/**
+	 * 提取集合中的对象的一个属性(通过Getter函数), 组合成由分割符分隔的字符串.
+	 * 
+	 * @param collection 来源集合.
+	 * @param propertyName 要提取的属性名.
+	 * @param separator 分隔符.
+	 */
+	public static String extractToString(final Collection collection, final String propertyName, final String separator) {
+		List list = extractToList(collection, propertyName);
+		return StringUtils.join(list, separator);
+	}
+
+	/**
+	 * 转换Collection所有元素(通过toString())为String, 中间以 separator分隔。
+	 */
+	public static String convertToString(final Collection collection, final String separator) {
+		return StringUtils.join(collection, separator);
+	}
+
+	/**
+	 * 转换Collection所有元素(通过toString())为String, 每个元素的前面加入prefix,后面加入postfix,如<div>mymessage</div>。
+	 */
+	public static String convertToString(final Collection collection, final String prefix, final String postfix) {
+		StringBuilder builder = new StringBuilder();
+		for (Object o : collection) {
+			builder.append(prefix).append(o).append(postfix);
+		}
+		return builder.toString();
+	}
+
+	/**
+	 * 判断是否为空.
+	 */
+	public static boolean isEmpty(Collection collection) {
+		return (collection == null || collection.isEmpty());
+	}
+
+	/**
+	 * 取得Collection的第一个元素,如果collection为空返回null.
+	 */
+	public static <T> T getFirst(Collection<T> collection) {
+		if (isEmpty(collection)) {
+			return null;
+		}
+
+		return collection.iterator().next();
+	}
+
+	/**
+	 * 获取Collection的最后一个元素 ,如果collection为空返回null.
+	 */
+	public static <T> T getLast(Collection<T> collection) {
+		if (isEmpty(collection)) {
+			return null;
+		}
+
+		//当类型为List时,直接取得最后一个元素 。
+		if (collection instanceof List) {
+			List<T> list = (List<T>) collection;
+			return list.get(list.size() - 1);
+		}
+
+		//其他类型通过iterator滚动到最后一个元素.
+		Iterator<T> iterator = collection.iterator();
+		while (true) {
+			T current = iterator.next();
+			if (!iterator.hasNext()) {
+				return current;
+			}
+		}
+	}
+
+	/**
+	 * 返回a+b的新List.
+	 */
+	public static <T> List<T> union(final Collection<T> a, final Collection<T> b) {
+		List<T> result = new ArrayList<T>(a);
+		result.addAll(b);
+		return result;
+	}
+
+	/**
+	 * 返回a-b的新List.
+	 */
+	public static <T> List<T> subtract(final Collection<T> a, final Collection<T> b) {
+		List<T> list = new ArrayList<T>(a);
+		for (T element : b) {
+			list.remove(element);
+		}
+
+		return list;
+	}
+
+	/**
+	 * 返回a与b的交集的新List.
+	 */
+	public static <T> List<T> intersection(Collection<T> a, Collection<T> b) {
+		List<T> list = new ArrayList<T>();
+
+		for (T element : a) {
+			if (b.contains(element)) {
+				list.add(element);
+			}
+		}
+		return list;
+	}
+}

+ 115 - 0
src/main/java/com/jeeplus/common/utils/CookieUtils.java

@@ -0,0 +1,115 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Cookie工具类
+ * @author jeeplus
+ * @version 2016-01-15
+ */
+public class CookieUtils {
+
+	/**
+	 * 设置 Cookie(生成时间为1天)
+	 * @param name 名称
+	 * @param value 值
+	 */
+	public static void setCookie(HttpServletResponse response, String name, String value) {
+		setCookie(response, name, value, 60*60*24);
+	}
+	
+	/**
+	 * 设置 Cookie
+	 * @param name 名称
+	 * @param value 值
+	 * @param maxAge 生存时间(单位秒)
+	 * @param uri 路径
+	 */
+	public static void setCookie(HttpServletResponse response, String name, String value, String path) {
+		setCookie(response, name, value, path, 60*60*24);
+	}
+	
+	/**
+	 * 设置 Cookie
+	 * @param name 名称
+	 * @param value 值
+	 * @param maxAge 生存时间(单位秒)
+	 * @param uri 路径
+	 */
+	public static void setCookie(HttpServletResponse response, String name, String value, int maxAge) {
+		setCookie(response, name, value, "/", maxAge);
+	}
+	
+	/**
+	 * 设置 Cookie
+	 * @param name 名称
+	 * @param value 值
+	 * @param maxAge 生存时间(单位秒)
+	 * @param uri 路径
+	 */
+	public static void setCookie(HttpServletResponse response, String name, String value, String path, int maxAge) {
+		Cookie cookie = new Cookie(name, null);
+		cookie.setPath(path);
+		cookie.setMaxAge(maxAge);
+		try {
+			cookie.setValue(URLEncoder.encode(value, "utf-8"));
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		}
+		response.addCookie(cookie);
+	}
+	
+	/**
+	 * 获得指定Cookie的值
+	 * @param name 名称
+	 * @return 值
+	 */
+	public static String getCookie(HttpServletRequest request, String name) {
+		return getCookie(request, null, name, false);
+	}
+	/**
+	 * 获得指定Cookie的值,并删除。
+	 * @param name 名称
+	 * @return 值
+	 */
+	public static String getCookie(HttpServletRequest request, HttpServletResponse response, String name) {
+		return getCookie(request, response, name, true);
+	}
+	/**
+	 * 获得指定Cookie的值
+	 * @param request 请求对象
+	 * @param response 响应对象
+	 * @param name 名字
+	 * @param isRemove 是否移除
+	 * @return 值
+	 */
+	public static String getCookie(HttpServletRequest request, HttpServletResponse response, String name, boolean isRemove) {
+		String value = null;
+		Cookie[] cookies = request.getCookies();
+		if (cookies != null) {
+			for (Cookie cookie : cookies) {
+				if (cookie.getName().equals(name)) {
+					try {
+						value = URLDecoder.decode(cookie.getValue(), "utf-8");
+					} catch (UnsupportedEncodingException e) {
+						e.printStackTrace();
+					}
+					if (isRemove) {
+						cookie.setMaxAge(0);
+						response.addCookie(cookie);
+					}
+				}
+			}
+		}
+		return value;
+	}
+}

+ 223 - 0
src/main/java/com/jeeplus/common/utils/DateUtils.java

@@ -0,0 +1,223 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.commons.lang3.time.DateFormatUtils;
+
+/**
+ * 日期工具类, 继承org.apache.commons.lang.time.DateUtils类
+ * @author jeeplus
+ * @version 2017-4-15
+ */
+public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
+	
+	private static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss";
+	
+	private static String[] parsePatterns = {
+		"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", 
+		"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
+		"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
+
+	/**
+	 * 得到当前日期字符串 格式(yyyy-MM-dd)
+	 */
+	public static String getDate() {
+		return getDate("yyyy-MM-dd");
+	}
+	
+	/**
+	 * 得到当前日期字符串 格式(yyyy-MM-dd) pattern可以为:"yyyy-MM-dd" "HH:mm:ss" "E"
+	 */
+	public static String getDate(String pattern) {
+		return DateFormatUtils.format(new Date(), pattern);
+	}
+	
+	/**
+	 * 得到日期字符串 默认格式(yyyy-MM-dd) pattern可以为:"yyyy-MM-dd" "HH:mm:ss" "E"
+	 */
+	public static String formatDate(Date date, Object... pattern) {
+		String formatDate = null;
+		if (pattern != null && pattern.length > 0) {
+			formatDate = DateFormatUtils.format(date, pattern[0].toString());
+		} else {
+			formatDate = DateFormatUtils.format(date, "yyyy-MM-dd");
+		}
+		return formatDate;
+	}
+	
+	/**
+	 * 得到日期时间字符串,转换格式(yyyy-MM-dd HH:mm:ss)
+	 */
+	public static String formatDateTime(Date date) {
+		return formatDate(date, "yyyy-MM-dd HH:mm:ss");
+	}
+
+	/**
+	 * 得到当前时间字符串 格式(HH:mm:ss)
+	 */
+	public static String getTime() {
+		return formatDate(new Date(), "HH:mm:ss");
+	}
+
+	/**
+	 * 得到当前日期和时间字符串 格式(yyyy-MM-dd HH:mm:ss)
+	 */
+	public static String getDateTime() {
+		return formatDate(new Date(), "yyyy-MM-dd HH:mm:ss");
+	}
+
+	/**
+	 * 得到当前年份字符串 格式(yyyy)
+	 */
+	public static String getYear() {
+		return formatDate(new Date(), "yyyy");
+	}
+
+	/**
+	 * 得到当前月份字符串 格式(MM)
+	 */
+	public static String getMonth() {
+		return formatDate(new Date(), "MM");
+	}
+
+	/**
+	 * 得到当天字符串 格式(dd)
+	 */
+	public static String getDay() {
+		return formatDate(new Date(), "dd");
+	}
+
+	/**
+	 * 得到当前星期字符串 格式(E)星期几
+	 */
+	public static String getWeek() {
+		return formatDate(new Date(), "E");
+	}
+	
+	/**
+	 * 日期型字符串转化为日期 格式
+	 * { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", 
+	 *   "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm",
+	 *   "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm" }
+	 */
+	public static Date parseDate(Object str) {
+		if (str == null){
+			return null;
+		}
+		try {
+			return parseDate(str.toString(), parsePatterns);
+		} catch (ParseException e) {
+			return null;
+		}
+	}
+
+	/**
+	 * 获取过去的天数
+	 * @param date
+	 * @return
+	 */
+	public static long pastDays(Date date) {
+		long t = new Date().getTime()-date.getTime();
+		return t/(24*60*60*1000);
+	}
+
+	/**
+	 * 获取过去的小时
+	 * @param date
+	 * @return
+	 */
+	public static long pastHour(Date date) {
+		long t = new Date().getTime()-date.getTime();
+		return t/(60*60*1000);
+	}
+	
+	/**
+	 * 获取过去的分钟
+	 * @param date
+	 * @return
+	 */
+	public static long pastMinutes(Date date) {
+		long t = new Date().getTime()-date.getTime();
+		return t/(60*1000);
+	}
+	
+	/**
+	 * 转换为时间(天,时:分:秒.毫秒)
+	 * @param timeMillis
+	 * @return
+	 */
+    public static String formatDateTime(long timeMillis){
+		long day = timeMillis/(24*60*60*1000);
+		long hour = (timeMillis/(60*60*1000)-day*24);
+		long min = ((timeMillis/(60*1000))-day*24*60-hour*60);
+		long s = (timeMillis/1000-day*24*60*60-hour*60*60-min*60);
+		long sss = (timeMillis-day*24*60*60*1000-hour*60*60*1000-min*60*1000-s*1000);
+		return (day>0?day+",":"")+hour+":"+min+":"+s+"."+sss;
+    }
+	
+	/**
+	 * 获取两个日期之间的天数
+	 * 
+	 * @param before
+	 * @param after
+	 * @return
+	 */
+	public static double getDistanceOfTwoDate(Date before, Date after) {
+		long beforeTime = before.getTime();
+		long afterTime = after.getTime();
+		return (afterTime - beforeTime) / (1000 * 60 * 60 * 24);
+	}
+	
+	/**
+	 * 字符串时间转LONG
+	 * @param sdate
+	 * @return
+	 */
+	public static long string2long(String sdate){
+		if(sdate.length() < 11){
+			sdate = sdate + " 00:00:00";
+		}
+		SimpleDateFormat sdf= new SimpleDateFormat(DEFAULT_PATTERN);
+		Date dt2 = null;
+		try {
+			dt2 = sdf.parse(sdate);
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+		//继续转换得到秒数的long型
+		long lTime = dt2.getTime() / 1000;
+		return lTime;
+	}
+	
+	/**
+	 * LONG时间转字符串
+	 * @param ldate
+	 * @return
+	 */
+	public static String long2string(long ldate){
+		SimpleDateFormat sdf= new SimpleDateFormat(DEFAULT_PATTERN);
+		//前面的ldate是秒数,先乘1000得到毫秒数,再转为java.util.Date类型
+		java.util.Date dt = new Date(ldate * 1000);  
+		String sDateTime = sdf.format(dt);  //得到精确到秒的表示
+		if(sDateTime.endsWith("00:00:00")){
+			sDateTime = sDateTime.substring(0,10);
+		}
+		return sDateTime;
+	}
+	
+	/**
+	 * @param args
+	 * @throws ParseException
+	 */
+	public static void main(String[] args) throws ParseException {
+		System.out.println(formatDate(parseDate("2010/3/6")));
+		System.out.println(getDate("yyyy年MM月dd日 E"));
+		long time = new Date().getTime()-parseDate("2012-11-19").getTime();
+		System.out.println(time/(24*60*60*1000));
+	}
+}

+ 151 - 0
src/main/java/com/jeeplus/common/utils/Encodes.java

@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2005-2012 springside.org.cn
+ */
+package com.jeeplus.common.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.lang3.StringEscapeUtils;
+
+/**
+ * 封装各种格式的编码解码工具类.
+ * 1.Commons-Codec的 hex/base64 编码
+ * 2.自制的base62 编码
+ * 3.Commons-Lang的xml/html escape
+ * 4.JDK提供的URLEncoder
+ * @author calvin
+ * @version 2016-01-15
+ */
+public class Encodes {
+
+	private static final String DEFAULT_URL_ENCODING = "UTF-8";
+	private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
+
+	/**
+	 * Hex编码.
+	 */
+	public static String encodeHex(byte[] input) {
+		return new String(Hex.encodeHex(input));
+	}
+
+	/**
+	 * Hex解码.
+	 */
+	public static byte[] decodeHex(String input) {
+		try {
+			return Hex.decodeHex(input.toCharArray());
+		} catch (DecoderException e) {
+			throw Exceptions.unchecked(e);
+		}
+	}
+
+	/**
+	 * Base64编码.
+	 */
+	public static String encodeBase64(byte[] input) {
+		return new String(Base64.encodeBase64(input));
+	}
+	
+	/**
+	 * Base64编码.
+	 */
+	public static String encodeBase64(String input) {
+		try {
+			return new String(Base64.encodeBase64(input.getBytes(DEFAULT_URL_ENCODING)));
+		} catch (UnsupportedEncodingException e) {
+			return "";
+		}
+	}
+
+//	/**
+//	 * Base64编码, URL安全(将Base64中的URL非法字符'+'和'/'转为'-'和'_', 见RFC3548).
+//	 */
+//	public static String encodeUrlSafeBase64(byte[] input) {
+//		return Base64.encodeBase64URLSafe(input);
+//	}
+
+	/**
+	 * Base64解码.
+	 */
+	public static byte[] decodeBase64(String input) {
+		return Base64.decodeBase64(input.getBytes());
+	}
+	
+	/**
+	 * Base64解码.
+	 */
+	public static String decodeBase64String(String input) {
+		try {
+			return new String(Base64.decodeBase64(input.getBytes()), DEFAULT_URL_ENCODING);
+		} catch (UnsupportedEncodingException e) {
+			return "";
+		}
+	}
+
+	/**
+	 * Base62编码。
+	 */
+	public static String encodeBase62(byte[] input) {
+		char[] chars = new char[input.length];
+		for (int i = 0; i < input.length; i++) {
+			chars[i] = BASE62[((input[i] & 0xFF) % BASE62.length)];
+		}
+		return new String(chars);
+	}
+
+	/**
+	 * Html 转码.
+	 */
+	public static String escapeHtml(String html) {
+		return StringEscapeUtils.escapeHtml4(html);
+	}
+
+	/**
+	 * Html 解码.
+	 */
+	public static String unescapeHtml(String htmlEscaped) {
+		return StringEscapeUtils.unescapeHtml4(htmlEscaped);
+	}
+
+	/**
+	 * Xml 转码.
+	 */
+	public static String escapeXml(String xml) {
+		return StringEscapeUtils.escapeXml10(xml);
+	}
+
+	/**
+	 * Xml 解码.
+	 */
+	public static String unescapeXml(String xmlEscaped) {
+		return StringEscapeUtils.unescapeXml(xmlEscaped);
+	}
+
+	/**
+	 * URL 编码, Encode默认为UTF-8. 
+	 */
+	public static String urlEncode(String part) {
+		try {
+			return URLEncoder.encode(part, DEFAULT_URL_ENCODING);
+		} catch (UnsupportedEncodingException e) {
+			throw Exceptions.unchecked(e);
+		}
+	}
+
+	/**
+	 * URL 解码, Encode默认为UTF-8. 
+	 */
+	public static String urlDecode(String part) {
+
+		try {
+			return URLDecoder.decode(part, DEFAULT_URL_ENCODING);
+		} catch (UnsupportedEncodingException e) {
+			throw Exceptions.unchecked(e);
+		}
+	}
+}

+ 72 - 0
src/main/java/com/jeeplus/common/utils/Exceptions.java

@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2005-2012 springside.org.cn
+ */
+package com.jeeplus.common.utils;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 关于异常的工具类.
+ * @author calvin
+ * @version 2016-01-15
+ */
+public class Exceptions {
+
+	/**
+	 * 将CheckedException转换为UncheckedException.
+	 */
+	public static RuntimeException unchecked(Exception e) {
+		if (e instanceof RuntimeException) {
+			return (RuntimeException) e;
+		} else {
+			return new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * 将ErrorStack转化为String.
+	 */
+	public static String getStackTraceAsString(Throwable e) {
+		if (e == null){
+			return "";
+		}
+		StringWriter stringWriter = new StringWriter();
+		e.printStackTrace(new PrintWriter(stringWriter));
+		return stringWriter.toString();
+	}
+
+	/**
+	 * 判断异常是否由某些底层的异常引起.
+	 */
+	public static boolean isCausedBy(Exception ex, Class<? extends Exception>... causeExceptionClasses) {
+		Throwable cause = ex.getCause();
+		while (cause != null) {
+			for (Class<? extends Exception> causeClass : causeExceptionClasses) {
+				if (causeClass.isInstance(cause)) {
+					return true;
+				}
+			}
+			cause = cause.getCause();
+		}
+		return false;
+	}
+
+	/**
+	 * 在request中获取异常类
+	 * @param request
+	 * @return 
+	 */
+	public static Throwable getThrowable(HttpServletRequest request){
+		Throwable ex = null;
+		if (request.getAttribute("exception") != null) {
+			ex = (Throwable) request.getAttribute("exception");
+		} else if (request.getAttribute("javax.servlet.error.exception") != null) {
+			ex = (Throwable) request.getAttribute("javax.servlet.error.exception");
+		}
+		return ex;
+	}
+	
+}

+ 699 - 0
src/main/java/com/jeeplus/common/utils/FileUtils.java

@@ -0,0 +1,699 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.*;
+
+import com.google.common.collect.Lists;
+import com.jeeplus.common.config.Global;
+import com.jeeplus.core.web.Servlets;
+import com.jeeplus.modules.sys.entity.FileData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 文件操作工具类
+ * 实现文件的创建、删除、复制、压缩、解压以及目录的创建、删除、复制、压缩解压等功能
+ * @author jeeplus
+ * @version 2016-06-21
+ */
+public class FileUtils extends org.apache.commons.io.FileUtils {
+	
+	private static Logger log = LoggerFactory.getLogger(FileUtils.class);
+
+
+
+	/**
+	 * 判断是否是文件
+	 * @param source
+	 */
+	public static boolean isFile(String source) {
+		return  new File(source).isFile();
+	}
+
+	/**
+	 * 判断是否是目录
+	 * @param source
+	 */
+	public static boolean isFolder(String source) {
+		return  new File(source).isDirectory();
+	}
+
+	/**
+	 * 复制单个文件,如果目标文件存在,则不覆盖
+	 * @param srcFileName 待复制的文件名
+	 * @param descFileName 目标文件名
+	 * @return 如果复制成功,则返回true,否则返回false
+	 */
+	public static boolean copyFile(String srcFileName, String descFileName) {
+		return FileUtils.copyFileCover(srcFileName, descFileName, false);
+	}
+
+	/**
+	 * 复制单个文件
+	 * @param srcFileName 待复制的文件名
+	 * @param descFileName 目标文件名
+	 * @param coverlay 如果目标文件已存在,是否覆盖
+	 * @return 如果复制成功,则返回true,否则返回false
+	 */
+	public static boolean copyFileCover(String srcFileName,
+			String descFileName, boolean coverlay) {
+		File srcFile = new File(srcFileName);
+		// 判断源文件是否存在
+		if (!srcFile.exists()) {
+			log.debug("复制文件失败,源文件 " + srcFileName + " 不存在!");
+			return false;
+		}
+		// 判断源文件是否是合法的文件
+		else if (!srcFile.isFile()) {
+			log.debug("复制文件失败," + srcFileName + " 不是一个文件!");
+			return false;
+		}
+		File descFile = new File(descFileName);
+		// 判断目标文件是否存在
+		if (descFile.exists()) {
+			// 如果目标文件存在,并且允许覆盖
+			if (coverlay) {
+				log.debug("目标文件已存在,准备删除!");
+				if (!FileUtils.delFile(descFileName)) {
+					log.debug("删除目标文件 " + descFileName + " 失败!");
+					return false;
+				}
+			} else {
+				log.debug("复制文件失败,目标文件 " + descFileName + " 已存在!");
+				return false;
+			}
+		} else {
+			if (!descFile.getParentFile().exists()) {
+				// 如果目标文件所在的目录不存在,则创建目录
+				log.debug("目标文件所在的目录不存在,创建目录!");
+				// 创建目标文件所在的目录
+				if (!descFile.getParentFile().mkdirs()) {
+					log.debug("创建目标文件所在的目录失败!");
+					return false;
+				}
+			}
+		}
+
+		// 准备复制文件
+		// 读取的位数
+		int readByte = 0;
+		InputStream ins = null;
+		OutputStream outs = null;
+		try {
+			// 打开源文件
+			ins = new FileInputStream(srcFile);
+			// 打开目标文件的输出流
+			outs = new FileOutputStream(descFile);
+			byte[] buf = new byte[1024];
+			// 一次读取1024个字节,当readByte为-1时表示文件已经读取完毕
+			while ((readByte = ins.read(buf)) != -1) {
+				// 将读取的字节流写入到输出流
+				outs.write(buf, 0, readByte);
+			}
+			log.debug("复制单个文件 " + srcFileName + " 到" + descFileName
+					+ "成功!");
+			return true;
+		} catch (Exception e) {
+			log.debug("复制文件失败:" + e.getMessage());
+			return false;
+		} finally {
+			// 关闭输入输出流,首先关闭输出流,然后再关闭输入流
+			if (outs != null) {
+				try {
+					outs.close();
+				} catch (IOException oute) {
+					oute.printStackTrace();
+				}
+			}
+			if (ins != null) {
+				try {
+					ins.close();
+				} catch (IOException ine) {
+					ine.printStackTrace();
+				}
+			}
+		}
+	}
+
+	/**
+	 * 复制整个目录的内容,如果目标目录存在,则不覆盖
+	 * @param srcDirName 源目录名
+	 * @param descDirName 目标目录名
+	 * @return 如果复制成功返回true,否则返回false
+	 */
+	public static boolean copyDirectory(String srcDirName, String descDirName) {
+		return FileUtils.copyDirectoryCover(srcDirName, descDirName,
+				false);
+	}
+
+	/**
+	 * 复制整个目录的内容 
+	 * @param srcDirName 源目录名
+	 * @param descDirName 目标目录名
+	 * @param coverlay 如果目标目录存在,是否覆盖
+	 * @return 如果复制成功返回true,否则返回false
+	 */
+	public static boolean copyDirectoryCover(String srcDirName,
+			String descDirName, boolean coverlay) {
+		File srcDir = new File(srcDirName);
+		// 判断源目录是否存在
+		if (!srcDir.exists()) {
+			log.debug("复制目录失败,源目录 " + srcDirName + " 不存在!");
+			return false;
+		}
+		// 判断源目录是否是目录
+		else if (!srcDir.isDirectory()) {
+			log.debug("复制目录失败," + srcDirName + " 不是一个目录!");
+			return false;
+		}
+		// 如果目标文件夹名不以文件分隔符结尾,自动添加文件分隔符
+		String descDirNames = descDirName;
+		if (!descDirNames.endsWith(File.separator)) {
+			descDirNames = descDirNames + File.separator;
+		}
+		File descDir = new File(descDirNames);
+		// 如果目标文件夹存在
+		if (descDir.exists()) {
+			if (coverlay) {
+				// 允许覆盖目标目录
+				log.debug("目标目录已存在,准备删除!");
+				if (!FileUtils.delFile(descDirNames)) {
+					log.debug("删除目录 " + descDirNames + " 失败!");
+					return false;
+				}
+			} else {
+				log.debug("目标目录复制失败,目标目录 " + descDirNames + " 已存在!");
+				return false;
+			}
+		} else {
+			// 创建目标目录
+			log.debug("目标目录不存在,准备创建!");
+			if (!descDir.mkdirs()) {
+				log.debug("创建目标目录失败!");
+				return false;
+			}
+
+		}
+
+		boolean flag = true;
+		// 列出源目录下的所有文件名和子目录名
+		File[] files = srcDir.listFiles();
+		for (int i = 0; i < files.length; i++) {
+			// 如果是一个单个文件,则直接复制
+			if (files[i].isFile()) {
+				flag = FileUtils.copyFile(files[i].getAbsolutePath(),
+						descDirName + files[i].getName());
+				// 如果拷贝文件失败,则退出循环
+				if (!flag) {
+					break;
+				}
+			}
+			// 如果是子目录,则继续复制目录
+			if (files[i].isDirectory()) {
+				flag = FileUtils.copyDirectory(files[i]
+						.getAbsolutePath(), descDirName + files[i].getName());
+				// 如果拷贝目录失败,则退出循环
+				if (!flag) {
+					break;
+				}
+			}
+		}
+
+		if (!flag) {
+			log.debug("复制目录 " + srcDirName + " 到 " + descDirName + " 失败!");
+			return false;
+		}
+		log.debug("复制目录 " + srcDirName + " 到 " + descDirName + " 成功!");
+		return true;
+
+	}
+
+	/**
+	 * 
+	 * 删除文件,可以删除单个文件或文件夹
+	 * 
+	 * @param fileName 被删除的文件名
+	 * @return 如果删除成功,则返回true,否是返回false
+	 */
+	public static boolean delFile(String fileName) {
+ 		File file = new File(fileName);
+		if (!file.exists()) {
+			log.debug(fileName + " 文件不存在!");
+			return true;
+		} else {
+			if (file.isFile()) {
+				return FileUtils.deleteFile(fileName);
+			} else {
+				return FileUtils.deleteDirectory(fileName);
+			}
+		}
+	}
+
+	/**
+	 * 
+	 * 删除单个文件
+	 * 
+	 * @param fileName 被删除的文件名
+	 * @return 如果删除成功,则返回true,否则返回false
+	 */
+	public static boolean deleteFile(String fileName) {
+		File file = new File(fileName);
+		if (file.exists() && file.isFile()) {
+			if (file.delete()) {
+				log.debug("删除文件 " + fileName + " 成功!");
+				return true;
+			} else {
+				log.debug("删除文件 " + fileName + " 失败!");
+				return false;
+			}
+		} else {
+			log.debug(fileName + " 文件不存在!");
+			return true;
+		}
+	}
+
+	/**
+	 * 
+	 * 删除目录及目录下的文件
+	 * 
+	 * @param dirName 被删除的目录所在的文件路径
+	 * @return 如果目录删除成功,则返回true,否则返回false
+	 */
+	public static boolean deleteDirectory(String dirName) {
+		String dirNames = dirName;
+		if (!dirNames.endsWith(File.separator)) {
+			dirNames = dirNames + File.separator;
+		}
+		File dirFile = new File(dirNames);
+		if (!dirFile.exists() || !dirFile.isDirectory()) {
+			log.debug(dirNames + " 目录不存在!");
+			return true;
+		}
+		boolean flag = true;
+		// 列出全部文件及子目录
+		File[] files = dirFile.listFiles();
+		for (int i = 0; i < files.length; i++) {
+			// 删除子文件
+			if (files[i].isFile()) {
+				flag = FileUtils.deleteFile(files[i].getAbsolutePath());
+				// 如果删除文件失败,则退出循环
+				if (!flag) {
+					break;
+				}
+			}
+			// 删除子目录
+			else if (files[i].isDirectory()) {
+				flag = FileUtils.deleteDirectory(files[i]
+						.getAbsolutePath());
+				// 如果删除子目录失败,则退出循环
+				if (!flag) {
+					break;
+				}
+			}
+		}
+
+		if (!flag) {
+			log.debug("删除目录失败!");
+			return false;
+		}
+		// 删除当前目录
+		if (dirFile.delete()) {
+			log.debug("删除目录 " + dirName + " 成功!");
+			return true;
+		} else {
+			log.debug("删除目录 " + dirName + " 失败!");
+			return false;
+		}
+
+	}
+
+	/**
+	 * 创建单个文件
+	 * @param descFileName 文件名,包含路径
+	 * @return 如果创建成功,则返回true,否则返回false
+	 */
+	public static boolean createFile(String descFileName) {
+		File file = new File(descFileName);
+		if (file.exists()) {
+			log.debug("文件 " + descFileName + " 已存在!");
+			return false;
+		}
+		if (descFileName.endsWith(File.separator)) {
+			log.debug(descFileName + " 为目录,不能创建目录!");
+			return false;
+		}
+		if (!file.getParentFile().exists()) {
+			// 如果文件所在的目录不存在,则创建目录
+			if (!file.getParentFile().mkdirs()) {
+				log.debug("创建文件所在的目录失败!");
+				return false;
+			}
+		}
+
+		// 创建文件
+		try {
+			if (file.createNewFile()) {
+				log.debug(descFileName + " 文件创建成功!");
+				return true;
+			} else {
+				log.debug(descFileName + " 文件创建失败!");
+				return false;
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+			log.debug(descFileName + " 文件创建失败!");
+			return false;
+		}
+
+	}
+
+	/**
+	 * 创建目录
+	 * @param descDirName 目录名,包含路径
+	 * @return 如果创建成功,则返回true,否则返回false
+	 */
+	public static boolean createDirectory(String descDirName) {
+		String descDirNames = descDirName;
+		if (!descDirNames.endsWith(File.separator)) {
+			descDirNames = descDirNames + File.separator;
+		}
+		File descDir = new File(descDirNames);
+		if (descDir.exists()) {
+			log.debug("目录 " + descDirNames + " 已存在!");
+			return false;
+		}
+		// 创建目录
+		if (descDir.mkdirs()) {
+			log.debug("目录 " + descDirNames + " 创建成功!");
+			return true;
+		} else {
+			log.debug("目录 " + descDirNames + " 创建失败!");
+			return false;
+		}
+
+	}
+
+	/**
+	 * 获取可以创建的文件名(如果有同名文件存在,参照Windows系统重命名为xxx(2).xxx)
+	 * @param name
+	 * @param index
+	 * @return
+	 */
+	public static File getAvailableFile(String name, int index){
+		File newFile = null;
+        String suffix = StringUtils.substringAfterLast(name, ".");
+        String filePath = StringUtils.substringBeforeLast(name, ".");
+		if(index == 0 ){
+			newFile = new File(filePath+"."+suffix);
+		}else{
+			newFile = new File(filePath+"("+index+")"+"."+suffix);
+		}
+		if(newFile.exists()){
+			return  getAvailableFile(name,index+1);
+		}else{
+			return  newFile;
+		}
+	};
+
+	/**
+	 * 获取可以创建的目录名(如果有同名目录存在,参照Windows系统重命名为xxx(2))
+	 * @param name
+	 * @param index
+	 * @return
+	 */
+	public static File getAvailableFolder(String name, int index){
+		File newFolder = null;
+		if(index == 0 ){
+			newFolder = new File(name);
+		}else{
+			newFolder = new File(name+"("+index+")");
+		}
+		if(newFolder.exists()){
+			return  getAvailableFolder(name,index+1);
+		}else{
+			return  newFolder;
+		}
+	};
+
+	/**
+	 * 写入文件
+	 */
+	public static void writeToFile(String fileName, String content, boolean append) {
+		try {
+			FileUtils.write(new File(fileName), content, "utf-8", append);
+			log.debug("文件 " + fileName + " 写入成功!");
+		} catch (IOException e) {
+			log.debug("文件 " + fileName + " 写入失败! " + e.getMessage());
+		}
+	}
+
+	/**
+	 * 写入文件
+	 */
+	public static void writeToFile(String fileName, String content, String encoding, boolean append) {
+		try {
+			FileUtils.write(new File(fileName), content, encoding, append);
+			log.debug("文件 " + fileName + " 写入成功!");
+		} catch (IOException e) {
+			log.debug("文件 " + fileName + " 写入失败! " + e.getMessage());
+		}
+	}
+	
+
+	/**
+	 * 获取待压缩文件在ZIP文件中entry的名字,即相对于跟目录的相对路径名
+	 * @param dirPath 目录名
+	 * @param file entry文件名
+	 * @return
+	 */
+	private static String getEntryName(String dirPath, File file) {
+		String dirPaths = dirPath;
+		if (!dirPaths.endsWith(File.separator)) {
+			dirPaths = dirPaths + File.separator;
+		}
+		String filePath = file.getAbsolutePath();
+		// 对于目录,必须在entry名字后面加上"/",表示它将以目录项存储
+		if (file.isDirectory()) {
+			filePath += "/";
+		}
+		int index = filePath.indexOf(dirPaths);
+
+		return filePath.substring(index + dirPaths.length());
+	}
+
+	private static Long getCreateTime(String fullFileName){
+		Path path= Paths.get(fullFileName);
+		BasicFileAttributeView basicview= Files.getFileAttributeView(path, BasicFileAttributeView.class, LinkOption.NOFOLLOW_LINKS );
+		BasicFileAttributes attr;
+		try {
+			attr = basicview.readAttributes();
+			Date createDate = new Date(attr.creationTime().toMillis());
+			return createDate.getTime()/1000;
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		Calendar cal = Calendar.getInstance();
+		cal.set(1970, 0, 1, 0, 0, 0);
+		return cal.getTime().getTime()/1000;
+	}
+
+	public  static String getFileType(String fileName){
+		String type = "file";
+		String suffix = StringUtils.substringAfterLast(fileName, ".");
+		switch (suffix){
+			//html
+			case "htm":
+			case "html":
+			case "css":
+			case "less":
+			case "asp":
+			case "php":
+			case "jsp":
+			case "js":
+			case "java":
+			case "class":
+			case "c":
+			case "sql":
+				type = "code";
+				break;
+			//word
+			case "doc":
+				type = "word";
+				break;
+			case "txt":
+				type = "text";
+				break;
+			case "wps":
+				type = "word";
+				break;
+			case "xls":
+			case "xlsx":
+				type = "excel";
+				break;
+			case "ppt":
+			case "pptx":
+				type = "pp";
+				break;
+			case "pdf":
+				//压缩文件
+				type = "pdf";
+				break;
+			case "rar":
+			case "zip":
+				type = "archive";
+				break;
+			//
+			case "exe":
+				type = "fa-windows";
+				break;
+			//视频
+			case "rmvb":
+			case "wmv":
+			case "asf":
+			case "avi":
+			case "3gp":
+			case "mpg":
+			case "mkv":
+			case "mp4":
+			case "dvd":
+			case "ogm":
+			case "mov":
+			case "mpeg2":
+			case "mpeg4":
+				type = "video";
+				break;
+			//音频
+			case "mp3":
+			case "ogg":
+			case "wav":
+			case "ape":
+			case "cda":
+			case "au":
+			case "midi":
+			case "mac":
+			case "aac":
+				type = "audio";
+				break;
+			//flash
+			case "flv":
+			case "swf":
+			case "m4v":
+			case "f4v":
+				type = "flash";
+				break;
+			//图片
+			case "gif":
+			case "jpeg":
+			case "bmp":
+			case "tif":
+			case "png":
+			case "jpg":
+			case "pcd":
+			case "qti":
+			case "qtf":
+			case "tiff":
+				type = "image";
+				break;
+			default:
+				type = "file";
+		}
+		return type;
+	}
+
+	public static List<FileData> getFileList(String pId,List<File> files) {
+		List fileDataList = Lists.newArrayList();
+		if (files != null) {
+			for (File file : files) {
+				FileData fileData = new FileData();
+				if (file.isDirectory()) { // 判断是文件还是文件夹
+					fileData.setId(file.getAbsolutePath());
+					fileData.setType("folder");
+					fileData.setOpen(true);
+					fileData.setpId(pId);
+					fileData.setDate(getCreateTime(file.getAbsolutePath()));
+					fileData.setValue(file.getName());
+					file.listFiles();
+					fileData.setData(getFileList(file.getName(), 	Lists.newArrayList(file.listFiles())));
+				} else  { // 判断文件
+					fileData.setId(file.getAbsolutePath());
+					fileData.setType(getFileType(file.getName()));
+					fileData.setpId(pId);
+					fileData.setSize(String.valueOf(file.length()));
+					fileData.setDate(getCreateTime(file.getAbsolutePath()));
+					fileData.setValue(file.getName());
+				}
+				fileDataList.add(fileData);
+			}
+
+		}
+		return fileDataList;
+	}
+	
+
+	public static  String getFileDir(String fileUrl){
+		return  fileUrl.replace(Servlets.getRequest().getContextPath() + Global.USERFILES_BASE_URL , Global.getUserfilesBaseDir() + Global.USERFILES_BASE_URL).replace("\\","/");
+	}
+
+	public static String getFileSize(String fileDir){
+	    File file = new File(fileDir);
+	    long size = file.length()*100;
+	    String label;
+	    if (size == 0F){
+	        label = "0";
+        }else if(size < 1024*100){
+	        label = String.valueOf(size/100)+"b";
+        }else if(size <1024*1024*100){
+	        label = String.valueOf(size/1024/100F)+"KB";
+        }else{
+	        label = String.valueOf(size/(1024*1024)/100F)+"M";
+        }
+        return label;
+    }
+
+
+	public static void downFile(String path, String name, HttpServletResponse response){
+
+		BufferedInputStream bis = null;
+		BufferedOutputStream bos = null;
+		try {
+
+			String s_utf8 = new String(name.getBytes("UTF-8"),"ISO8859-1");//真实文件名
+			response.setHeader("Content-disposition", "attachment; filename=" + s_utf8);//下载文件名
+			bis = new BufferedInputStream(new FileInputStream(path));//根据路径,用字节缓冲流读取文件,比字节流效率快
+			bos = new BufferedOutputStream(response.getOutputStream());//缓冲输出流
+			byte[] buff = new byte[2048000];
+			int bytesRead = 0;
+			while(-1 !=(bytesRead = (bis.read(buff, 0, buff.length)))){
+				bos.write(buff, 0, buff.length);
+			}
+		}catch (Exception e){
+			System.out.println(e);
+		}finally{
+			if(bis != null){
+				try {
+					bis.close();
+				}catch (Exception e){
+					e.printStackTrace();
+				}
+			}
+			if(bos != null){
+				try {
+					bos.close();
+				}catch (Exception e){
+					e.printStackTrace();
+				}
+			}
+		}
+	}
+}

+ 67 - 0
src/main/java/com/jeeplus/common/utils/FreeMarkers.java

@@ -0,0 +1,67 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.Map;
+
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
+/**
+ * FreeMarkers工具类
+ * @author jeeplus
+ * @version 2016-01-15
+ */
+public class FreeMarkers {
+
+	public static String renderString(String templateString, Map<String, ?> model) throws Exception {
+			StringWriter result = new StringWriter();
+			Template t = new Template("name", new StringReader(templateString), new Configuration());
+			t.process(model, result);
+			return result.toString();
+	}
+
+	public static String renderTemplate(Template template, Object model) {
+		try {
+			StringWriter result = new StringWriter();
+			template.process(model, result);
+			return result.toString();
+		} catch (Exception e) {
+			throw Exceptions.unchecked(e);
+		}
+	}
+
+	public static Configuration buildConfiguration(String directory) throws IOException {
+		Configuration cfg = new Configuration();
+		Resource path = new DefaultResourceLoader().getResource(directory);
+		cfg.setDirectoryForTemplateLoading(path.getFile());
+		return cfg;
+	}
+	
+	public static void main(String[] args) throws IOException {
+//		// renderString
+//		Map<String, String> model = com.google.common.collect.Maps.newHashMap();
+//		model.put("userName", "calvin");
+//		String result = FreeMarkers.renderString("hello ${userName}", model);
+//		System.out.println(result);
+//		// renderTemplate
+//		Configuration cfg = FreeMarkers.buildConfiguration("classpath:/");
+//		Template template = cfg.getTemplate("testTemplate.ftl");
+//		String result2 = FreeMarkers.renderTemplate(template, model);
+//		System.out.println(result2);
+		
+//		Map<String, String> model = com.google.common.collect.Maps.newHashMap();
+//		model.put("userName", "calvin");
+//		String result = FreeMarkers.renderString("hello ${userName} ${r'${userName}'}", model);
+//		System.out.println(result);
+	}
+	
+}

+ 131 - 0
src/main/java/com/jeeplus/common/utils/HanyuPinyinHelper.java

@@ -0,0 +1,131 @@
+package com.jeeplus.common.utils;
+
+import net.sourceforge.pinyin4j.PinyinHelper;
+import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
+import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
+import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
+import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
+import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
+
+public class HanyuPinyinHelper {
+
+    /**
+     * 将文字转为汉语拼音
+     * @param chineselanguage 要转成拼音的中文
+     */
+    public String toHanyuPinyin(String ChineseLanguage){
+        char[] cl_chars = ChineseLanguage.trim().toCharArray();
+        String hanyupinyin = "";
+        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
+        defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);// 输出拼音全部小写
+        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);// 不带声调
+        defaultFormat.setVCharType(HanyuPinyinVCharType.WITH_V) ;
+        try {
+            for (int i=0; i<cl_chars.length; i++){
+                if (String.valueOf(cl_chars[i]).matches("[\u4e00-\u9fa5]+")){// 如果字符是中文,则将中文转为汉语拼音
+                    hanyupinyin += PinyinHelper.toHanyuPinyinStringArray(cl_chars[i], defaultFormat)[0];
+                } else {// 如果字符不是中文,则不转换
+                    hanyupinyin += cl_chars[i];
+                }
+            }
+        } catch (BadHanyuPinyinOutputFormatCombination e) {
+            System.out.println("字符不能转成汉语拼音");
+        }
+        return hanyupinyin;
+    }
+
+    public static String getFirstLettersUp(String ChineseLanguage){
+        return getFirstLetters(ChineseLanguage ,HanyuPinyinCaseType.UPPERCASE);
+    }
+
+    public static String getFirstLettersLo(String ChineseLanguage){
+        return getFirstLetters(ChineseLanguage ,HanyuPinyinCaseType.LOWERCASE);
+    }
+
+    public static String getFirstLetters(String ChineseLanguage,HanyuPinyinCaseType caseType) {
+        char[] cl_chars = ChineseLanguage.trim().toCharArray();
+        String hanyupinyin = "";
+        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
+        defaultFormat.setCaseType(caseType);// 输出拼音全部大写
+        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);// 不带声调
+        try {
+            for (int i = 0; i < cl_chars.length; i++) {
+                String str = String.valueOf(cl_chars[i]);
+                if (str.matches("[\u4e00-\u9fa5]+")) {// 如果字符是中文,则将中文转为汉语拼音,并取第一个字母
+                    hanyupinyin += PinyinHelper.toHanyuPinyinStringArray(cl_chars[i], defaultFormat)[0].substring(0, 1);
+                } else if (str.matches("[0-9]+")) {// 如果字符是数字,取数字
+                    hanyupinyin += cl_chars[i];
+                } else if (str.matches("[a-zA-Z]+")) {// 如果字符是字母,取字母
+                    hanyupinyin += cl_chars[i];
+                } else {// 否则不转换
+                    hanyupinyin += cl_chars[i];//如果是标点符号的话,带着
+                }
+            }
+        } catch (BadHanyuPinyinOutputFormatCombination e) {
+            System.out.println("字符不能转成汉语拼音");
+        }
+        return hanyupinyin;
+    }
+
+    public static String getPinyinString(String ChineseLanguage){
+        char[] cl_chars = ChineseLanguage.trim().toCharArray();
+        String hanyupinyin = "";
+        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
+        defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);// 输出拼音全部大写
+        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);// 不带声调
+        try {
+            for (int i = 0; i < cl_chars.length; i++) {
+                String str = String.valueOf(cl_chars[i]);
+                if (str.matches("[\u4e00-\u9fa5]+")) {// 如果字符是中文,则将中文转为汉语拼音,并取第一个字母
+                    hanyupinyin += PinyinHelper.toHanyuPinyinStringArray(
+                            cl_chars[i], defaultFormat)[0];
+                } else if (str.matches("[0-9]+")) {// 如果字符是数字,取数字
+                    hanyupinyin += cl_chars[i];
+                } else if (str.matches("[a-zA-Z]+")) {// 如果字符是字母,取字母
+
+                    hanyupinyin += cl_chars[i];
+                } else {// 否则不转换
+                }
+            }
+        } catch (BadHanyuPinyinOutputFormatCombination e) {
+            System.out.println("字符不能转成汉语拼音");
+        }
+        return hanyupinyin;
+    }
+    /**
+     * 取第一个汉字的第一个字符
+     * @Title: getFirstLetter
+     * @Description: TODO
+     * @return String
+     * @throws
+     */
+    public static String getFirstLetter(String ChineseLanguage){
+        char[] cl_chars = ChineseLanguage.trim().toCharArray();
+        String hanyupinyin = "";
+        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
+        defaultFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);// 输出拼音全部大写
+        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);// 不带声调
+        try {
+            String str = String.valueOf(cl_chars[0]);
+            if (str.matches("[\u4e00-\u9fa5]+")) {// 如果字符是中文,则将中文转为汉语拼音,并取第一个字母
+                hanyupinyin = PinyinHelper.toHanyuPinyinStringArray(
+                        cl_chars[0], defaultFormat)[0].substring(0, 1);;
+            } else if (str.matches("[0-9]+")) {// 如果字符是数字,取数字
+                hanyupinyin += cl_chars[0];
+            } else if (str.matches("[a-zA-Z]+")) {// 如果字符是字母,取字母
+
+                hanyupinyin += cl_chars[0];
+            } else {// 否则不转换
+
+            }
+        } catch (BadHanyuPinyinOutputFormatCombination e) {
+            System.out.println("字符不能转成汉语拼音");
+        }
+        return hanyupinyin;
+    }
+
+    public static void main(String[] args) {
+        HanyuPinyinHelper hanyuPinyinHelper = new HanyuPinyinHelper() ;
+        System.out.println(hanyuPinyinHelper.toHanyuPinyin("多发的发独守空房阿道夫打发第三方"));
+    }
+}

+ 67 - 0
src/main/java/com/jeeplus/common/utils/IdGen.java

@@ -0,0 +1,67 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils;
+
+import java.io.Serializable;
+import java.security.SecureRandom;
+import java.util.UUID;
+
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.util.IdGenerator;
+
+/**
+ * 封装各种生成唯一性ID算法的工具类.
+ * @author jeeplus
+ * @version 2016-01-15
+ */
+@Service
+@Lazy(false)
+public class IdGen implements IdGenerator, SessionIdGenerator {
+
+	private static SecureRandom random = new SecureRandom();
+	
+	/**
+	 * 封装JDK自带的UUID, 通过Random数字生成, 中间无-分割.
+	 */
+	public static String uuid() {
+		return UUID.randomUUID().toString().replaceAll("-", "");
+	}
+	
+	/**
+	 * 使用SecureRandom随机生成Long. 
+	 */
+	public static long randomLong() {
+		return Math.abs(random.nextLong());
+	}
+
+	/**
+	 * 基于Base62编码的SecureRandom随机生成bytes.
+	 */
+	public static String randomBase62(int length) {
+		byte[] randomBytes = new byte[length];
+		random.nextBytes(randomBytes);
+		return Encodes.encodeBase62(randomBytes);
+	}
+
+	@Override
+	public Serializable generateId(Session session) {
+		return IdGen.uuid();
+	}
+	
+	public static void main(String[] args) {
+		for (int i=0; i<1000; i++){
+			System.out.println(IdGen.randomLong() + "  " + IdGen.randomBase62(5));
+		}
+	}
+
+	@Override
+	public UUID generateId() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+}

+ 52 - 0
src/main/java/com/jeeplus/common/utils/JWT.java

@@ -0,0 +1,52 @@
+package com.jeeplus.common.utils;
+
+import com.auth0.jwt.JWTSigner;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.internal.com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class JWT {
+    private static final String SECRET = "XX#$%()(#*!()!KL<><MQLlwbMNQNQJQK sdfkjsdrow32234545fdf>?N<:{LWPW";
+
+    private static final String EXP = "exp";
+
+    private static final String PAYLOAD = "payload";
+
+    //加密,传入一个对象和有效期
+    public static <T> String sign(T object, long maxAge) {
+        try {
+            final JWTSigner signer = new JWTSigner(SECRET);
+            final Map<String, Object> claims = new HashMap<String, Object>();
+            ObjectMapper mapper = new ObjectMapper();
+            String jsonString = mapper.writeValueAsString(object);
+            claims.put(PAYLOAD, jsonString);
+            claims.put(EXP, System.currentTimeMillis() + maxAge);
+            return signer.sign(claims);
+        } catch(Exception e) {
+            System.out.println(e.getMessage());
+            return null;
+        }
+    }
+
+    //解密,传入一个加密后的token字符串和解密后的类型
+    public static<T> T unsign(String jwt, Class<T> classT) {
+        final JWTVerifier verifier = new JWTVerifier(SECRET);
+        try {
+            final Map<String,Object> claims= verifier.verify(jwt);
+            if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
+                long exp = (Long)claims.get(EXP);
+                long currentTimeMillis = System.currentTimeMillis();
+                if (exp > currentTimeMillis) {
+                    String json = (String)claims.get(PAYLOAD);
+                    ObjectMapper objectMapper = new ObjectMapper();
+                    return objectMapper.readValue(json, classT);
+                }
+            }
+            return null;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+}

+ 42 - 0
src/main/java/com/jeeplus/common/utils/MD5Util.java

@@ -0,0 +1,42 @@
+package com.jeeplus.common.utils;
+
+import java.security.MessageDigest;
+
+public class MD5Util {
+
+    /**
+     * 对字符串md5加密
+     *
+     * @param s 传入要加密的字符串
+     * @return  MD5加密后的字符串
+     */
+
+    public static String MD5(String s) {
+        char hexDigits[]={'L','W','B','$','%','6','7','8','9','&','*','A','B','C','D','E','F','G','H','I','J','K','X','Y','Z'};
+
+        try {
+            byte[] btInput = s.getBytes();
+            // 获得MD5摘要算法的 MessageDigest 对象
+            MessageDigest mdInst = MessageDigest.getInstance("MD5");
+            // 使用指定的字节更新摘要
+            mdInst.update(btInput);
+            // 获得密文
+            byte[] md = mdInst.digest();
+            // 把密文转换成十六进制的字符串形式
+            int j = md.length;
+            char str[] = new char[j * 2];
+            int k = 0;
+            for (int i = 0; i < j; i++) {
+                byte byte0 = md[i];
+                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
+                str[k++] = hexDigits[byte0 & 0xf];
+            }
+            return new String(str);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+
+}

+ 31 - 0
src/main/java/com/jeeplus/common/utils/MobileUtils.java

@@ -0,0 +1,31 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils;
+
+/**
+ * Created by 刘高峰 on 2018/6/21.
+ */
+public class MobileUtils {
+    private final static String[] agent = { "Android", "iPhone", "iPod","iPad", "Windows Phone", "MQQBrowser" }; //定义移动端请求的所有可能类型
+    /**
+     * 判断User-Agent 是不是来自于手机
+     * @param ua
+     * @return
+     */
+    public static boolean checkAgentIsMobile(String ua) {
+        boolean flag = false;
+        if (!ua.contains("Windows NT") || (ua.contains("Windows NT") && ua.contains("compatible; MSIE 9.0;"))) {
+// 排除 苹果桌面系统
+            if (!ua.contains("Windows NT") && !ua.contains("Macintosh")) {
+                for (String item : agent) {
+                    if (ua.contains(item)) {
+                        flag = true;
+                        break;
+                    }
+                }
+            }
+        }
+        return flag;
+    }
+}

+ 311 - 0
src/main/java/com/jeeplus/common/utils/MyBeanUtils.java

@@ -0,0 +1,311 @@
+package com.jeeplus.common.utils;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.beanutils.DynaBean;
+import org.apache.commons.beanutils.DynaProperty;
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.commons.beanutils.PropertyUtilsBean;
+
+/**
+ * <p>Title: </p>
+ * <p>Description: </p>
+ * @author  刘高峰
+ * @version 2.0
+ */
+
+public class MyBeanUtils
+    extends PropertyUtilsBean {
+
+  private static void convert(Object dest, Object orig) throws
+      IllegalAccessException, InvocationTargetException {
+
+      // Validate existence of the specified beans
+      if (dest == null) {
+          throw new IllegalArgumentException
+              ("No destination bean specified");
+      }
+      if (orig == null) {
+          throw new IllegalArgumentException("No origin bean specified");
+      }
+
+      // Copy the properties, converting as necessary
+      if (orig instanceof DynaBean) {
+          DynaProperty origDescriptors[] =
+              ( (DynaBean) orig).getDynaClass().getDynaProperties();
+          for (int i = 0; i < origDescriptors.length; i++) {
+              String name = origDescriptors[i].getName();
+              if (PropertyUtils.isWriteable(dest, name)) {
+                  Object value = ( (DynaBean) orig).get(name);
+                  try {
+                	  getInstance().setSimpleProperty(dest, name, value);
+                  }
+                  catch (Exception e) {
+                      ; // Should not happen
+                  }
+
+              }
+          }
+      }
+      else if (orig instanceof Map) {
+          Iterator names = ( (Map) orig).keySet().iterator();
+          while (names.hasNext()) {
+              String name = (String) names.next();
+              if (PropertyUtils.isWriteable(dest, name)) {
+                  Object value = ( (Map) orig).get(name);
+                  try {
+                	  getInstance().setSimpleProperty(dest, name, value);
+                  }
+                  catch (Exception e) {
+                      ; // Should not happen
+                  }
+
+              }
+          }
+      }
+      else
+      /* if (orig is a standard JavaBean) */
+      {
+          PropertyDescriptor origDescriptors[] =
+              PropertyUtils.getPropertyDescriptors(orig);
+          for (int i = 0; i < origDescriptors.length; i++) {
+              String name = origDescriptors[i].getName();
+//              String type = origDescriptors[i].getPropertyType().toString();
+              if ("class".equals(name)) {
+                  continue; // No point in trying to set an object's class
+              }
+              if (PropertyUtils.isReadable(orig, name) &&
+                  PropertyUtils.isWriteable(dest, name)) {
+                  try {
+                      Object value = PropertyUtils.getSimpleProperty(orig, name);
+                      getInstance().setSimpleProperty(dest, name, value);
+                  }
+                  catch (java.lang.IllegalArgumentException ie) {
+                      ; // Should not happen
+                  }
+                  catch (Exception e) {
+                      ; // Should not happen
+                  }
+
+              }
+          }
+      }
+
+  }
+
+  
+  /**
+	 * 对象拷贝
+	 * 数据对象空值不拷贝到目标对象
+	 * 
+	 * @param dataObject
+	 * @param toObject
+	 * @throws NoSuchMethodException
+	 * copy
+	 */
+  public static void copyBeanNotNull2Bean(Object databean,Object tobean)throws Exception
+  {
+	  PropertyDescriptor origDescriptors[] = PropertyUtils.getPropertyDescriptors(databean);
+      for (int i = 0; i < origDescriptors.length; i++) {
+          String name = origDescriptors[i].getName();
+//          String type = origDescriptors[i].getPropertyType().toString();
+          if ("class".equals(name)) {
+              continue; // No point in trying to set an object's class
+          }
+          if (PropertyUtils.isReadable(databean, name) &&PropertyUtils.isWriteable(tobean, name)) {
+              try { 
+                  Object value = PropertyUtils.getSimpleProperty(databean, name);
+                  if(value!=null){
+                	  getInstance().setSimpleProperty(tobean, name, value);
+                  }
+              }
+              catch (java.lang.IllegalArgumentException ie) {
+                  ; // Should not happen
+              }
+              catch (Exception e) {
+                  ; // Should not happen
+              }
+
+          }
+      }
+  }
+  
+  
+  /**
+   * 把orig和dest相同属性的value复制到dest中
+   * @param dest
+   * @param orig
+   * @throws IllegalAccessException
+   * @throws InvocationTargetException
+   */
+  public static void copyBean2Bean(Object dest, Object orig) throws Exception {
+      convert(dest, orig);
+  }
+
+  public static void copyBean2Map(Map map, Object bean){
+	PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(bean);
+	for (int i =0;i<pds.length;i++)
+	{
+		PropertyDescriptor pd = pds[i];
+		String propname = pd.getName();
+		try {
+			Object propvalue = PropertyUtils.getSimpleProperty(bean,propname);
+			map.put(propname, propvalue);
+		} catch (IllegalAccessException e) {
+			//e.printStackTrace();
+		} catch (InvocationTargetException e) {
+			//e.printStackTrace();
+		} catch (NoSuchMethodException e) {
+			//e.printStackTrace();
+		}
+	}
+  }
+
+  /**
+   * 将Map内的key与Bean中属性相同的内容复制到BEAN中
+   * @param bean Object
+   * @param properties Map
+   * @throws IllegalAccessException
+   * @throws InvocationTargetException
+   */
+  public static void copyMap2Bean(Object bean, Map properties) throws
+      IllegalAccessException, InvocationTargetException {
+      // Do nothing unless both arguments have been specified
+      if ( (bean == null) || (properties == null)) {
+          return;
+      }
+      // Loop through the property name/value pairs to be set
+      Iterator names = properties.keySet().iterator();
+      while (names.hasNext()) {
+          String name = (String) names.next();
+          // Identify the property name and value(s) to be assigned
+          if (name == null) {
+              continue;
+          }
+          Object value = properties.get(name);
+          try {
+              Class clazz = PropertyUtils.getPropertyType(bean, name);
+              if (null == clazz) {
+                  continue;
+              }
+              String className = clazz.getName();
+              if (className.equalsIgnoreCase("java.sql.Timestamp")) {
+                  if (value == null || value.equals("")) {
+                      continue;
+                  }
+              }
+              getInstance().setSimpleProperty(bean, name, value);
+          }
+          catch (NoSuchMethodException e) {
+              continue;
+          }
+      }
+  }
+  
+
+  /**
+   * 自动转Map key值大写
+   * 将Map内的key与Bean中属性相同的内容复制到BEAN中
+   * @param bean Object
+   * @param properties Map
+   * @throws IllegalAccessException
+   * @throws InvocationTargetException
+   */
+  public static void copyMap2Bean_Nobig(Object bean, Map properties) throws
+      IllegalAccessException, InvocationTargetException {
+      // Do nothing unless both arguments have been specified
+      if ( (bean == null) || (properties == null)) {
+          return;
+      }
+      // Loop through the property name/value pairs to be set
+      Iterator names = properties.keySet().iterator();
+      while (names.hasNext()) {
+          String name = (String) names.next();
+          // Identify the property name and value(s) to be assigned
+          if (name == null) {
+              continue;
+          }
+          Object value = properties.get(name);
+          // 命名应该大小写应该敏感(否则取不到对象的属性)
+          //name = name.toLowerCase();
+          try {
+        	  if (value == null) {	// 不光Date类型,好多类型在null时会出错
+                  continue;	// 如果为null不用设 (对象如果有特殊初始值也可以保留?)
+              }
+              Class clazz = PropertyUtils.getPropertyType(bean, name);
+              if (null == clazz) {	// 在bean中这个属性不存在
+                  continue;
+              }
+              String className = clazz.getName();
+              // 临时对策(如果不处理默认的类型转换时会出错)
+              if (className.equalsIgnoreCase("java.util.Date")) {
+                  value = new java.util.Date(((java.sql.Timestamp)value).getTime());// wait to do:貌似有时区问题, 待进一步确认
+              }
+//              if (className.equalsIgnoreCase("java.sql.Timestamp")) {
+//                  if (value == null || value.equals("")) {
+//                      continue;
+//                  }
+//              }
+              getInstance().setSimpleProperty(bean, name, value);
+          }
+          catch (NoSuchMethodException e) {
+              continue;
+          }
+      }
+  }
+
+  /**
+   * Map内的key与Bean中属性相同的内容复制到BEAN中
+   * 对于存在空值的取默认值
+   * @param bean Object
+   * @param properties Map
+   * @param defaultValue String
+   * @throws IllegalAccessException
+   * @throws InvocationTargetException
+   */
+  public static void copyMap2Bean(Object bean, Map properties, String defaultValue) throws
+      IllegalAccessException, InvocationTargetException {
+      // Do nothing unless both arguments have been specified
+      if ( (bean == null) || (properties == null)) {
+          return;
+      }
+      // Loop through the property name/value pairs to be set
+      Iterator names = properties.keySet().iterator();
+      while (names.hasNext()) {
+          String name = (String) names.next();
+          // Identify the property name and value(s) to be assigned
+          if (name == null) {
+              continue;
+          }
+          Object value = properties.get(name);
+          try {
+              Class clazz = PropertyUtils.getPropertyType(bean, name);
+              if (null == clazz) {
+                  continue;
+              }
+              String className = clazz.getName();
+              if (className.equalsIgnoreCase("java.sql.Timestamp")) {
+                  if (value == null || value.equals("")) {
+                      continue;
+                  }
+              }
+              if (className.equalsIgnoreCase("java.lang.String")) {
+                  if (value == null) {
+                      value = defaultValue;
+                  }
+              }
+              getInstance().setSimpleProperty(bean, name, value);
+          }
+          catch (NoSuchMethodException e) {
+              continue;
+          }
+      }
+  }
+  
+  public MyBeanUtils() {
+    super();
+  }
+}

+ 88 - 0
src/main/java/com/jeeplus/common/utils/ObjectUtils.java

@@ -0,0 +1,88 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Method;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 对象操作工具类, 继承org.apache.commons.lang3.ObjectUtils类
+ * @author jeeplus
+ * @version 2017-6-29
+ */
+public class ObjectUtils extends org.apache.commons.lang3.ObjectUtils {
+
+	/**
+	 * 注解到对象复制,只复制能匹配上的方法。
+	 * @param annotation
+	 * @param object
+	 */
+	public static void annotationToObject(Object annotation, Object object){
+		if (annotation != null){
+			Class<?> annotationClass = annotation.getClass();
+			Class<?> objectClass = object.getClass();
+			for (Method m : objectClass.getMethods()){
+				if (StringUtils.startsWith(m.getName(), "set")){
+					try {
+						String s = StringUtils.uncapitalize(StringUtils.substring(m.getName(), 3));
+						Object obj = annotationClass.getMethod(s).invoke(annotation);
+						if (obj != null && !"".equals(obj.toString())){
+							if (object == null){
+								object = objectClass.newInstance();
+							}
+							m.invoke(object, obj);
+						}
+					} catch (Exception e) {
+						// 忽略所有设置失败方法
+					}
+				}
+			}
+		}
+	}
+	
+	/**
+	 * 序列化对象
+	 * @param object
+	 * @return
+	 */
+	public static byte[] serialize(Object object) {
+		ObjectOutputStream oos = null;
+		ByteArrayOutputStream baos = null;
+		try {
+			if (object != null){
+				baos = new ByteArrayOutputStream();
+				oos = new ObjectOutputStream(baos);
+				oos.writeObject(object);
+				return baos.toByteArray();
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	/**
+	 * 反序列化对象
+	 * @param bytes
+	 * @return
+	 */
+	public static Object unserialize(byte[] bytes) {
+		ByteArrayInputStream bais = null;
+		try {
+			if (bytes != null && bytes.length > 0){
+				bais = new ByteArrayInputStream(bytes);
+				ObjectInputStream ois = new ObjectInputStream(bais);
+				return ois.readObject();
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+}

+ 154 - 0
src/main/java/com/jeeplus/common/utils/PropertiesLoader.java

@@ -0,0 +1,154 @@
+/**
+ * Copyright (c) 2005-2011 springside.org.cn
+ * 
+ * $Id: PropertiesLoader.java 1690 2012-02-22 13:42:00Z calvinxiu $
+ */
+package com.jeeplus.common.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+
+/**
+ * Properties文件载入工具类. 可载入多个properties文件, 相同的属性在最后载入的文件中的值将会覆盖之前的值,但以System的Property优先.
+ * @author calvin
+ * @version 2016-05-15
+ */
+public class PropertiesLoader {
+
+	private static Logger logger = LoggerFactory.getLogger(PropertiesLoader.class);
+
+	private static ResourceLoader resourceLoader = new DefaultResourceLoader();
+
+	private final Properties properties;
+
+	public PropertiesLoader(String... resourcesPaths) {
+		properties = loadProperties(resourcesPaths);
+	}
+
+	public Properties getProperties() {
+		return properties;
+	}
+
+	/**
+	 * 取出Property,但以System的Property优先,取不到返回空字符串.
+	 */
+	private String getValue(String key) {
+		String systemProperty = System.getProperty(key);
+		if (systemProperty != null) {
+			return systemProperty;
+		}
+		if (properties.containsKey(key)) {
+	        return properties.getProperty(key);
+	    }
+	    return "";
+	}
+
+	/**
+	 * 取出String类型的Property,但以System的Property优先,如果都为Null则抛出异常.
+	 */
+	public String getProperty(String key) {
+		String value = getValue(key);
+		if (value == null) {
+			throw new NoSuchElementException();
+		}
+		return value;
+	}
+
+	/**
+	 * 取出String类型的Property,但以System的Property优先.如果都为Null则返回Default值.
+	 */
+	public String getProperty(String key, String defaultValue) {
+		String value = getValue(key);
+		return value != null ? value : defaultValue;
+	}
+
+	/**
+	 * 取出Integer类型的Property,但以System的Property优先.如果都为Null或内容错误则抛出异常.
+	 */
+	public Integer getInteger(String key) {
+		String value = getValue(key);
+		if (value == null) {
+			throw new NoSuchElementException();
+		}
+		return Integer.valueOf(value);
+	}
+
+	/**
+	 * 取出Integer类型的Property,但以System的Property优先.如果都为Null则返回Default值,如果内容错误则抛出异常
+	 */
+	public Integer getInteger(String key, Integer defaultValue) {
+		String value = getValue(key);
+		return value != null ? Integer.valueOf(value) : defaultValue;
+	}
+
+	/**
+	 * 取出Double类型的Property,但以System的Property优先.如果都为Null或内容错误则抛出异常.
+	 */
+	public Double getDouble(String key) {
+		String value = getValue(key);
+		if (value == null) {
+			throw new NoSuchElementException();
+		}
+		return Double.valueOf(value);
+	}
+
+	/**
+	 * 取出Double类型的Property,但以System的Property优先.如果都为Null则返回Default值,如果内容错误则抛出异常
+	 */
+	public Double getDouble(String key, Integer defaultValue) {
+		String value = getValue(key);
+		return value != null ? Double.valueOf(value) : defaultValue;
+	}
+
+	/**
+	 * 取出Boolean类型的Property,但以System的Property优先.如果都为Null抛出异常,如果内容不是true/false则返回false.
+	 */
+	public Boolean getBoolean(String key) {
+		String value = getValue(key);
+		if (value == null) {
+			throw new NoSuchElementException();
+		}
+		return Boolean.valueOf(value);
+	}
+
+	/**
+	 * 取出Boolean类型的Property,但以System的Property优先.如果都为Null则返回Default值,如果内容不为true/false则返回false.
+	 */
+	public Boolean getBoolean(String key, boolean defaultValue) {
+		String value = getValue(key);
+		return value != null ? Boolean.valueOf(value) : defaultValue;
+	}
+
+	/**
+	 * 载入多个文件, 文件路径使用Spring Resource格式.
+	 */
+	private Properties loadProperties(String... resourcesPaths) {
+		Properties props = new Properties();
+
+		for (String location : resourcesPaths) {
+
+//			logger.debug("Loading properties file from:" + location);
+
+			InputStream is = null;
+			try {
+				Resource resource = resourceLoader.getResource(location);
+				is = resource.getInputStream();
+				props.load(is);
+			} catch (IOException ex) {
+				logger.info("Could not load properties from path:" + location + ", " + ex.getMessage());
+			} finally {
+				IOUtils.closeQuietly(is);
+			}
+		}
+		return props;
+	}
+}

+ 304 - 0
src/main/java/com/jeeplus/common/utils/Reflections.java

@@ -0,0 +1,304 @@
+/**
+ * Copyright (c) 2005-2012 springside.org.cn
+ */
+package com.jeeplus.common.utils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.Assert;
+
+/**
+ * 反射工具类.
+ * 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
+ * @author calvin
+ * @version 2016-01-15
+ */
+@SuppressWarnings("rawtypes")
+public class Reflections {
+	
+	private static final String SETTER_PREFIX = "set";
+
+	private static final String GETTER_PREFIX = "get";
+
+	private static final String CGLIB_CLASS_SEPARATOR = "$$";
+	
+	private static Logger logger = LoggerFactory.getLogger(Reflections.class);
+
+	/**
+	 * 调用Getter方法.
+	 * 支持多级,如:对象名.对象名.方法
+	 */
+	public static Object invokeGetter(Object obj, String propertyName) {
+		Object object = obj;
+		for (String name : StringUtils.split(propertyName, ".")){
+			String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
+			object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
+		}
+		return object;
+	}
+
+	/**
+	 * 调用Setter方法, 仅匹配方法名。
+	 * 支持多级,如:对象名.对象名.方法
+	 */
+	public static void invokeSetter(Object obj, String propertyName, Object value) {
+		Object object = obj;
+		String[] names = StringUtils.split(propertyName, ".");
+		for (int i=0; i<names.length; i++){
+			if(i<names.length-1){
+				String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
+				object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
+			}else{
+				String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
+				invokeMethodByName(object, setterMethodName, new Object[] { value });
+			}
+		}
+	}
+
+	/**
+	 * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
+	 */
+	public static Object getFieldValue(final Object obj, final String fieldName) {
+		Field field = getAccessibleField(obj, fieldName);
+
+		if (field == null) {
+			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
+		}
+
+		Object result = null;
+		try {
+			result = field.get(obj);
+		} catch (IllegalAccessException e) {
+			logger.error("不可能抛出的异常{}", e.getMessage());
+		}
+		return result;
+	}
+
+	/**
+	 * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
+	 */
+	public static void setFieldValue(final Object obj, final String fieldName, final Object value) {
+		Field field = getAccessibleField(obj, fieldName);
+
+		if (field == null) {
+			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
+		}
+
+		try {
+			field.set(obj, value);
+		} catch (IllegalAccessException e) {
+			logger.error("不可能抛出的异常:{}", e.getMessage());
+		}
+	}
+
+	/**
+	 * 直接调用对象方法, 无视private/protected修饰符.
+	 * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
+	 * 同时匹配方法名+参数类型,
+	 */
+	public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
+			final Object[] args) {
+		Method method = getAccessibleMethod(obj, methodName, parameterTypes);
+		if (method == null) {
+			throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
+		}
+
+		try {
+			return method.invoke(obj, args);
+		} catch (Exception e) {
+			throw convertReflectionExceptionToUnchecked(e);
+		}
+	}
+
+	/**
+	 * 直接调用对象方法, 无视private/protected修饰符,
+	 * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
+	 * 只匹配函数名,如果有多个同名函数调用第一个。
+	 */
+	public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
+		Method method = getAccessibleMethodByName(obj, methodName);
+		if (method == null) {
+			throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
+		}
+
+		try {
+			return method.invoke(obj, args);
+		} catch (Exception e) {
+			throw convertReflectionExceptionToUnchecked(e);
+		}
+	}
+
+	/**
+	 * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
+	 * 
+	 * 如向上转型到Object仍无法找到, 返回null.
+	 */
+	public static Field getAccessibleField(final Object obj, final String fieldName) {
+		Validate.notNull(obj, "object can't be null");
+		Validate.notBlank(fieldName, "fieldName can't be blank");
+		for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
+			try {
+				Field field = superClass.getDeclaredField(fieldName);
+				makeAccessible(field);
+				return field;
+			} catch (NoSuchFieldException e) {//NOSONAR
+				// Field不在当前类定义,继续向上转型
+				continue;// new add
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
+	 * 如向上转型到Object仍无法找到, 返回null.
+	 * 匹配函数名+参数类型。
+	 * 
+	 * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
+	 */
+	public static Method getAccessibleMethod(final Object obj, final String methodName,
+			final Class<?>... parameterTypes) {
+		Validate.notNull(obj, "object can't be null");
+		Validate.notBlank(methodName, "methodName can't be blank");
+
+		for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
+			try {
+				Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
+				makeAccessible(method);
+				return method;
+			} catch (NoSuchMethodException e) {
+				// Method不在当前类定义,继续向上转型
+				continue;// new add
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
+	 * 如向上转型到Object仍无法找到, 返回null.
+	 * 只匹配函数名。
+	 * 
+	 * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
+	 */
+	public static Method getAccessibleMethodByName(final Object obj, final String methodName) {
+		Validate.notNull(obj, "object can't be null");
+		Validate.notBlank(methodName, "methodName can't be blank");
+
+		for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
+			Method[] methods = searchType.getDeclaredMethods();
+			for (Method method : methods) {
+				if (method.getName().equals(methodName)) {
+					makeAccessible(method);
+					return method;
+				}
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
+	 */
+	public static void makeAccessible(Method method) {
+		if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
+				&& !method.isAccessible()) {
+			method.setAccessible(true);
+		}
+	}
+
+	/**
+	 * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
+	 */
+	public static void makeAccessible(Field field) {
+		if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
+				.isFinal(field.getModifiers())) && !field.isAccessible()) {
+			field.setAccessible(true);
+		}
+	}
+
+	/**
+	 * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
+	 * 如无法找到, 返回Object.class.
+	 * eg.
+	 * public UserDao extends HibernateDao<User>
+	 *
+	 * @param clazz The class to introspect
+	 * @return the first generic declaration, or Object.class if cannot be determined
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> Class<T> getClassGenricType(final Class clazz) {
+		return getClassGenricType(clazz, 0);
+	}
+
+	/**
+	 * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
+	 * 如无法找到, 返回Object.class.
+	 * 
+	 * 如public UserDao extends HibernateDao<User,Long>
+	 *
+	 * @param clazz clazz The class to introspect
+	 * @param index the Index of the generic ddeclaration,start from 0.
+	 * @return the index generic declaration, or Object.class if cannot be determined
+	 */
+	public static Class getClassGenricType(final Class clazz, final int index) {
+
+		Type genType = clazz.getGenericSuperclass();
+
+		if (!(genType instanceof ParameterizedType)) {
+			logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
+			return Object.class;
+		}
+
+		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
+
+		if (index >= params.length || index < 0) {
+			logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+					+ params.length);
+			return Object.class;
+		}
+		if (!(params[index] instanceof Class)) {
+			logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
+			return Object.class;
+		}
+
+		return (Class) params[index];
+	}
+	
+	public static Class<?> getUserClass(Object instance) {
+		Assert.notNull(instance, "Instance must not be null");
+		Class clazz = instance.getClass();
+		if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
+			Class<?> superClass = clazz.getSuperclass();
+			if (superClass != null && !Object.class.equals(superClass)) {
+				return superClass;
+			}
+		}
+		return clazz;
+
+	}
+	
+	/**
+	 * 将反射时的checked exception转换为unchecked exception.
+	 */
+	public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) {
+		if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
+				|| e instanceof NoSuchMethodException) {
+			return new IllegalArgumentException(e);
+		} else if (e instanceof InvocationTargetException) {
+			return new RuntimeException(((InvocationTargetException) e).getTargetException());
+		} else if (e instanceof RuntimeException) {
+			return (RuntimeException) e;
+		}
+		return new RuntimeException("Unexpected Checked Exception.", e);
+	}
+}

+ 81 - 0
src/main/java/com/jeeplus/common/utils/SmsUtils.java

@@ -0,0 +1,81 @@
+package com.jeeplus.common.utils;
+
+import com.cloopen.rest.sdk.CCPRestSmsSDK;
+import com.jeeplus.common.config.Global;
+
+import java.util.HashMap;
+
+public class SmsUtils {
+
+    /**
+     * 短信发送, 七陌
+     * @param phone 手机号
+     * @param code 验证码
+     * @param time 有效时间分钟
+     * @param tempId 模版, 1登陆 2注册
+     */
+    public static Boolean sendSms(String phone, String[] param, String tempId){
+
+        //获取短信配置信息
+        String url = Global.getConfig("sms.serverIP");
+        String port = Global.getConfig("sms.serverPort");
+        String sid = Global.getConfig("sms.accountSid");
+        String token = Global.getConfig("sms.accountToken");
+        String appId = Global.getConfig("sms.appId");
+
+        HashMap<String, Object> result = null;
+
+        //初始化SDK
+        CCPRestSmsSDK restAPI = new CCPRestSmsSDK();
+
+        //******************************注释*********************************************
+        //*初始化服务器地址和端口                                                       *
+        //*沙盒环境(用于应用开发调试):restAPI.init("sandboxapp.cloopen.com", "8883");*
+        //*生产环境(用户应用上线使用):restAPI.init("app.cloopen.com", "8883");       *
+        //*******************************************************************************
+        restAPI.init(url, port);
+
+        //******************************注释*********************************************
+        //*初始化主帐号和主帐号令牌,对应官网开发者主账号下的ACCOUNT SID和AUTH TOKEN     *
+        //*ACOUNT SID和AUTH TOKEN在登陆官网后,在“应用-管理控制台”中查看开发者主账号获取*
+        //*参数顺序:第一个参数是ACOUNT SID,第二个参数是AUTH TOKEN。                   *
+        //*******************************************************************************
+        restAPI.setAccount(sid, token);
+
+
+        //******************************注释*********************************************
+        //*初始化应用ID                                                                 *
+        //*测试开发可使用“测试Demo”的APP ID,正式上线需要使用自己创建的应用的App ID     *
+        //*应用ID的获取:登陆官网,在“应用-应用列表”,点击应用名称,看应用详情获取APP ID*
+        //*******************************************************************************
+        restAPI.setAppId(appId);
+
+
+        //******************************注释****************************************************************
+        //*调用发送模板短信的接口发送短信                                                                  *
+        //*参数顺序说明:                                                                                  *
+        //*第一个参数:是要发送的手机号码,可以用逗号分隔,一次最多支持100个手机号                          *
+        //*第二个参数:是模板ID,在平台上创建的短信模板的ID值;测试的时候可以使用系统的默认模板,id为1。    *
+        //*系统默认模板的内容为“【云通讯】您使用的是云通讯短信模板,您的验证码是{1},请于{2}分钟内正确输入”*
+        //*第三个参数是要替换的内容数组。																														       *
+        //**************************************************************************************************
+
+        //**************************************举例说明***********************************************************************
+        //*假设您用测试Demo的APP ID,则需使用默认模板ID 1,发送手机号是13800000000,传入参数为6532和5,则调用方式为           *
+        //*result = restAPI.sendTemplateSMS("13800000000","1" ,new String[]{"6532","5"});																		  *
+        //*则13800000000手机号收到的短信内容是:【云通讯】您使用的是云通讯短信模板,您的验证码是6532,请于5分钟内正确输入     *
+        //*********************************************************************************************************************
+
+
+        result = restAPI.sendTemplateSMS(phone, tempId, param);
+
+        System.out.println("SDKTestGetSubAccounts result=" + result);
+        if("000000".equals(result.get("statusCode"))){
+            return true;
+        }else{
+            //异常返回输出错误码和错误信息
+            System.out.println("错误码=" + result.get("statusCode") +" 错误信息= "+result.get("statusMsg"));
+            return false;
+        }
+    }
+}

+ 95 - 0
src/main/java/com/jeeplus/common/utils/SpringContextHolder.java

@@ -0,0 +1,95 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Date;
+
+import org.apache.commons.lang3.Validate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+import com.jeeplus.common.config.Global;
+
+/**
+ * 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候取出ApplicaitonContext.
+ * 
+ * @author Zaric
+ * @date 2016-5-29 下午1:25:40
+ */
+@Service
+@Lazy(false)
+public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
+
+	private static ApplicationContext applicationContext = null;
+
+	private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
+
+	/**
+	 * 取得存储在静态变量中的ApplicationContext.
+	 */
+	public static ApplicationContext getApplicationContext() {
+		assertContextInjected();
+		return applicationContext;
+	}
+
+	/**
+	 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> T getBean(String name) {
+		assertContextInjected();
+		return (T) applicationContext.getBean(name);
+	}
+
+	/**
+	 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+	 */
+	public static <T> T getBean(Class<T> requiredType) {
+		assertContextInjected();
+		return applicationContext.getBean(requiredType);
+	}
+
+	/**
+	 * 清除SpringContextHolder中的ApplicationContext为Null.
+	 */
+	public static void clearHolder() {
+		if (logger.isDebugEnabled()){
+			logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
+		}
+		applicationContext = null;
+	}
+
+	/**
+	 * 实现ApplicationContextAware接口, 注入Context到静态变量中.
+	 */
+	@Override
+	public void setApplicationContext(ApplicationContext applicationContext) {
+		SpringContextHolder.applicationContext = applicationContext;
+	}
+
+	public static  String getStatic(){
+		return SpringContextHolder.getApplicationContext().getApplicationName()+ "/static";
+	}
+	/**
+	 * 实现DisposableBean接口, 在Context关闭时清理静态变量.
+	 */
+	@Override
+	public void destroy() throws Exception {
+		SpringContextHolder.clearHolder();
+	}
+
+	/**
+	 * 检查ApplicationContext不为空.
+	 */
+	private static void assertContextInjected() {
+		Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
+	}
+}

+ 411 - 0
src/main/java/com/jeeplus/common/utils/StringUtils.java

@@ -0,0 +1,411 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.servlet.LocaleResolver;
+
+import com.google.common.collect.Lists;
+
+/**
+ * 字符串工具类, 继承org.apache.commons.lang3.StringUtils类
+ * @author jeeplus
+ * @version 2016-05-22
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils {
+	
+    private static final char SEPARATOR = '_';
+    private static final String CHARSET_NAME = "UTF-8";
+    
+    /**
+     * 转换为字节数组
+     * @param str
+     * @return
+     */
+    public static byte[] getBytes(String str){
+    	if (str != null){
+    		try {
+				return str.getBytes(CHARSET_NAME);
+			} catch (UnsupportedEncodingException e) {
+				return null;
+			}
+    	}else{
+    		return null;
+    	}
+    }
+    
+    /**
+     * 转换为字节数组
+     * @param bytes
+     * @return
+     */
+    public static String toString(byte[] bytes){
+    	try {
+			return new String(bytes, CHARSET_NAME);
+		} catch (UnsupportedEncodingException e) {
+			return EMPTY;
+		}
+    }
+    
+    /**
+     * 是否包含字符串
+     * @param str 验证字符串
+     * @param strs 字符串组
+     * @return 包含返回true
+     */
+    public static boolean inString(String str, String... strs){
+    	if (str != null){
+        	for (String s : strs){
+        		if (str.equals(trim(s))){
+        			return true;
+        		}
+        	}
+    	}
+    	return false;
+    }
+    
+	/**
+	 * 替换掉HTML标签方法
+	 */
+	public static String replaceHtml(String html) {
+		if (isBlank(html)){
+			return "";
+		}
+		String regEx = "<.+?>";
+		Pattern p = Pattern.compile(regEx);
+		Matcher m = p.matcher(html);
+		String s = m.replaceAll("");
+		return s;
+	}
+	
+	/**
+	 * 替换为手机识别的HTML,去掉样式及属性,保留回车。
+	 * @param html
+	 * @return
+	 */
+	public static String replaceMobileHtml(String html){
+		if (html == null){
+			return "";
+		}
+		return html.replaceAll("<([a-z]+?)\\s+?.*?>", "<$1>");
+	}
+	
+	/**
+	 * 替换为手机识别的HTML,去掉样式及属性,保留回车。
+	 * @param txt
+	 * @return
+	 */
+	public static String toHtml(String txt){
+		if (txt == null){
+			return "";
+		}
+		return replace(replace(Encodes.escapeHtml(txt), "\n", "<br/>"), "\t", "&nbsp; &nbsp; ");
+	}
+
+	/**
+	 * 缩略字符串(不区分中英文字符)
+	 * @param str 目标字符串
+	 * @param length 截取长度
+	 * @return
+	 */
+	public static String abbr(String str, int length) {
+		if (str == null) {
+			return "";
+		}
+		try {
+			StringBuilder sb = new StringBuilder();
+			int currentLength = 0;
+			for (char c : replaceHtml(StringEscapeUtils.unescapeHtml4(str)).toCharArray()) {
+				currentLength += String.valueOf(c).getBytes("GBK").length;
+				if (currentLength <= length - 3) {
+					sb.append(c);
+				} else {
+					sb.append("...");
+					break;
+				}
+			}
+			return sb.toString();
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		}
+		return "";
+	}
+	
+	public static String abbr2(String param, int length) {
+		if (param == null) {
+			return "";
+		}
+		StringBuffer result = new StringBuffer();
+		int n = 0;
+		char temp;
+		boolean isCode = false; // 是不是HTML代码
+		boolean isHTML = false; // 是不是HTML特殊字符,如&nbsp;
+		for (int i = 0; i < param.length(); i++) {
+			temp = param.charAt(i);
+			if (temp == '<') {
+				isCode = true;
+			} else if (temp == '&') {
+				isHTML = true;
+			} else if (temp == '>' && isCode) {
+				n = n - 1;
+				isCode = false;
+			} else if (temp == ';' && isHTML) {
+				isHTML = false;
+			}
+			try {
+				if (!isCode && !isHTML) {
+					n += String.valueOf(temp).getBytes("GBK").length;
+				}
+			} catch (UnsupportedEncodingException e) {
+				e.printStackTrace();
+			}
+
+			if (n <= length - 3) {
+				result.append(temp);
+			} else {
+				result.append("...");
+				break;
+			}
+		}
+		// 取出截取字符串中的HTML标记
+		String temp_result = result.toString().replaceAll("(>)[^<>]*(<?)",
+				"$1$2");
+		// 去掉不需要结素标记的HTML标记
+		temp_result = temp_result
+				.replaceAll(
+						"</?(AREA|BASE|BASEFONT|BODY|BR|COL|COLGROUP|DD|DT|FRAME|HEAD|HR|HTML|IMG|INPUT|ISINDEX|LI|LINK|META|OPTION|P|PARAM|TBODY|TD|TFOOT|TH|THEAD|TR|area|base|basefont|body|br|col|colgroup|dd|dt|frame|head|hr|html|img|input|isindex|li|link|meta|option|p|param|tbody|td|tfoot|th|thead|tr)[^<>]*/?>",
+						"");
+		// 去掉成对的HTML标记
+		temp_result = temp_result.replaceAll("<([a-zA-Z]+)[^<>]*>(.*?)</\\1>",
+				"$2");
+		// 用正则表达式取出标记
+		Pattern p = Pattern.compile("<([a-zA-Z]+)[^<>]*>");
+		Matcher m = p.matcher(temp_result);
+		List<String> endHTML = Lists.newArrayList();
+		while (m.find()) {
+			endHTML.add(m.group(1));
+		}
+		// 补全不成对的HTML标记
+		for (int i = endHTML.size() - 1; i >= 0; i--) {
+			result.append("</");
+			result.append(endHTML.get(i));
+			result.append(">");
+		}
+		return result.toString();
+	}
+	
+	/**
+	 * 转换为Double类型
+	 */
+	public static Double toDouble(Object val){
+		if (val == null){
+			return 0D;
+		}
+		try {
+			return Double.valueOf(trim(val.toString()));
+		} catch (Exception e) {
+			return 0D;
+		}
+	}
+
+	/**
+	 * 转换为Float类型
+	 */
+	public static Float toFloat(Object val){
+		return toDouble(val).floatValue();
+	}
+
+	/**
+	 * 转换为Long类型
+	 */
+	public static Long toLong(Object val){
+		return toDouble(val).longValue();
+	}
+
+	/**
+	 * 转换为Integer类型
+	 */
+	public static Integer toInteger(Object val){
+		return toLong(val).intValue();
+	}
+	
+	/**
+	 * 获得i18n字符串
+	 */
+	public static String getMessage(String code, Object[] args) {
+		LocaleResolver localLocaleResolver = (LocaleResolver) SpringContextHolder.getBean(LocaleResolver.class);
+		HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();  
+		Locale localLocale = localLocaleResolver.resolveLocale(request);
+		return SpringContextHolder.getApplicationContext().getMessage(code, args, localLocale);
+	}
+	
+	/**
+	 * 获得用户远程地址
+	 */
+	public static String getRemoteAddr(HttpServletRequest request){
+		String remoteAddr = request.getHeader("X-Real-IP");
+        if (isNotBlank(remoteAddr)) {
+        	remoteAddr = request.getHeader("X-Forwarded-For");
+        }else if (isNotBlank(remoteAddr)) {
+        	remoteAddr = request.getHeader("Proxy-Client-IP");
+        }else if (isNotBlank(remoteAddr)) {
+        	remoteAddr = request.getHeader("WL-Proxy-Client-IP");
+        }
+        return remoteAddr != null ? remoteAddr : request.getRemoteAddr();
+	}
+
+	/**
+	 * 驼峰命名法工具
+	 * @return
+	 * 		toCamelCase("hello_world") == "helloWorld" 
+	 * 		toCapitalizeCamelCase("hello_world") == "HelloWorld"
+	 * 		toUnderScoreCase("helloWorld") = "hello_world"
+	 */
+    public static String toCamelCase(String s) {
+        if (s == null) {
+            return null;
+        }
+
+        s = s.toLowerCase();
+
+        StringBuilder sb = new StringBuilder(s.length());
+        boolean upperCase = false;
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+
+            if (c == SEPARATOR) {
+                upperCase = true;
+            } else if (upperCase) {
+                sb.append(Character.toUpperCase(c));
+                upperCase = false;
+            } else {
+                sb.append(c);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+	 * 驼峰命名法工具
+	 * @return
+	 * 		toCamelCase("hello_world") == "helloWorld" 
+	 * 		toCapitalizeCamelCase("hello_world") == "HelloWorld"
+	 * 		toUnderScoreCase("helloWorld") = "hello_world"
+	 */
+    public static String toCapitalizeCamelCase(String s) {
+        if (s == null) {
+            return null;
+        }
+        s = toCamelCase(s);
+        return s.substring(0, 1).toUpperCase() + s.substring(1);
+    }
+    
+    /**
+	 * 驼峰命名法工具
+	 * @return
+	 * 		toCamelCase("hello_world") == "helloWorld" 
+	 * 		toCapitalizeCamelCase("hello_world") == "HelloWorld"
+	 * 		toUnderScoreCase("helloWorld") = "hello_world"
+	 */
+    public static String toUnderScoreCase(String s) {
+        if (s == null) {
+            return null;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        boolean upperCase = false;
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+
+            boolean nextUpperCase = true;
+
+            if (i < (s.length() - 1)) {
+                nextUpperCase = Character.isUpperCase(s.charAt(i + 1));
+            }
+
+            if ((i > 0) && Character.isUpperCase(c)) {
+                if (!upperCase || !nextUpperCase) {
+                    sb.append(SEPARATOR);
+                }
+                upperCase = true;
+            } else {
+                upperCase = false;
+            }
+
+            sb.append(Character.toLowerCase(c));
+        }
+
+        return sb.toString();
+    }
+    
+    /**
+     * 如果不为空,则设置值
+     * @param target
+     * @param source
+     */
+    public static void setValueIfNotBlank(String target, String source) {
+		if (isNotBlank(source)){
+			target = source;
+		}
+	}
+ 
+    /**
+     * @param target
+     * @param str
+     */
+    public static int lastIndexOf(String target, String str) {
+    	if(isBlank(target) || isBlank(str)){
+    		return -1;
+    	}
+		return target.lastIndexOf(str);
+	}
+ 
+    /**
+     * 转换为JS获取对象值,生成三目运算返回结果
+     * @param objectString 对象串
+     *   例如:row.user.id
+     *   返回:!row?'':!row.user?'':!row.user.id?'':row.user.id
+     */
+    public static String jsGetVal(String objectString){
+    	StringBuilder result = new StringBuilder();
+    	StringBuilder val = new StringBuilder();
+    	String[] vals = split(objectString, ".");
+    	for (int i=0; i<vals.length; i++){
+    		val.append("." + vals[i]);
+    		result.append("!"+(val.substring(1))+"?'':");
+    	}
+    	result.append(val.substring(1));
+    	return result.toString();
+    }
+
+    public static String getLabels(String values){
+    	if (!values.contains("/")) {
+    		return values;
+		}
+    	String[] valueAttr = values.split("\\|");
+    	String labels = "";
+    	for(String value:valueAttr){
+    		labels += StringUtils.substringAfterLast(value, "/")+",";
+		}
+		if(StringUtils.isBlank(labels)){
+			System.out.println(labels);
+    		return labels;
+		}else{
+			System.out.println(labels.substring(0,labels.length()-1));
+    		return labels.substring(0,labels.length()-1);
+		}
+	}
+    
+}

+ 324 - 0
src/main/java/com/jeeplus/common/utils/TimeUtils.java

@@ -0,0 +1,324 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils;
+
+import java.util.Arrays;
+import java.util.Date;
+
+import org.apache.commons.lang3.time.DateFormatUtils;
+
+/**
+ * 时间计算工具类
+ * @author jeeplus
+ * @version 2016-11-03
+ */
+public class TimeUtils {
+	
+	public static String toTimeString(long time) {
+		TimeUtils t = new TimeUtils(time);
+		int day = t.get(TimeUtils.DAY);
+		int hour = t.get(TimeUtils.HOUR);
+		int minute = t.get(TimeUtils.MINUTE);
+		int second = t.get(TimeUtils.SECOND);
+		StringBuilder sb = new StringBuilder();
+		if (day > 0){
+			sb.append(day).append("天");
+		}
+		if (hour > 0){
+			sb.append(hour).append("时");
+		}
+		if (minute > 0){
+			sb.append(minute).append("分");
+		}
+		if (second > 0){
+			sb.append(second).append("秒");
+		}
+		return sb.toString();
+	}
+	
+    /**
+     * 时间字段常量,表示“秒”
+     */
+    public final static int SECOND = 0;
+
+    /**
+     * 时间字段常量,表示“分”
+     */
+    public final static int MINUTE = 1;
+
+    /**
+     * 时间字段常量,表示“时”
+     */
+    public final static int HOUR = 2;
+
+    /**
+     * 时间字段常量,表示“天”
+     */
+    public final static int DAY = 3;
+
+    /**
+     * 各常量允许的最大值
+     */
+    private final int[] maxFields = { 59, 59, 23, Integer.MAX_VALUE - 1 };
+
+    /**
+     * 各常量允许的最小值
+     */
+    private final int[] minFields = { 0, 0, 0, Integer.MIN_VALUE };
+
+    /**
+     * 默认的字符串格式时间分隔符
+     */
+    private String timeSeparator = ":";
+
+    /**
+     * 时间数据容器
+     */
+    private int[] fields = new int[4];
+
+    /**
+     * 无参构造,将各字段置为 0
+     */
+    public TimeUtils() {
+        this(0, 0, 0, 0);
+    }
+
+    /**
+     * 使用时、分构造一个时间
+     * @param hour      小时
+     * @param minute    分钟
+     */
+    public TimeUtils(int hour, int minute) {
+        this(0, hour, minute, 0);
+    }
+
+    /**
+     * 使用时、分、秒构造一个时间
+     * @param hour      小时
+     * @param minute    分钟
+     * @param second    秒
+     */
+    public TimeUtils(int hour, int minute, int second) {
+        this(0, hour, minute, second);
+    }
+
+    /**
+     * 使用一个字符串构造时间<br/>
+     * Time time = new Time("14:22:23");
+     * @param time      字符串格式的时间,默认采用“:”作为分隔符
+     */
+    public TimeUtils(String time) {
+        this(time, null);
+//    	System.out.println(time);
+    }
+
+    /**
+     * 使用时间毫秒构建时间
+     * @param time
+     */
+    public TimeUtils(long time){
+    	this(new Date(time));
+    }
+    
+    /**
+     * 使用日期对象构造时间
+     * @param date
+     */
+    public TimeUtils(Date date){
+    	this(DateFormatUtils.formatUTC(date, "HH:mm:ss"));
+    }
+
+    /**
+     * 使用天、时、分、秒构造时间,进行全字符的构造
+     * @param day       天
+     * @param hour      时
+     * @param minute    分
+     * @param second    秒
+     */
+    public TimeUtils(int day, int hour, int minute, int second) {
+        initialize(day, hour, minute, second);
+    }
+
+    /**
+     * 使用一个字符串构造时间,指定分隔符<br/>
+     * Time time = new Time("14-22-23", "-");
+     * @param time      字符串格式的时间
+     */
+    public TimeUtils(String time, String timeSeparator) {
+        if(timeSeparator != null) {
+            setTimeSeparator(timeSeparator);
+        }
+        parseTime(time);
+    }
+
+    /**
+     * 设置时间字段的值
+     * @param field     时间字段常量
+     * @param value     时间字段的值
+     */
+    public void set(int field, int value) {
+        if(value < minFields[field]) {
+            throw new IllegalArgumentException(value + ", time value must be positive.");
+        }
+        fields[field] = value % (maxFields[field] + 1);
+        // 进行进位计算
+        int carry = value / (maxFields[field] + 1);
+        if(carry > 0) {
+            int upFieldValue = get(field + 1);
+            set(field + 1, upFieldValue + carry);
+        }
+    }
+
+    /**
+     * 获得时间字段的值
+     * @param field     时间字段常量
+     * @return          该时间字段的值
+     */
+    public int get(int field) {
+        if(field < 0 || field > fields.length - 1) {
+            throw new IllegalArgumentException(field + ", field value is error.");
+        }
+        return fields[field];
+    }
+
+    /**
+     * 将时间进行“加”运算,即加上一个时间
+     * @param time      需要加的时间
+     * @return          运算后的时间
+     */
+    public TimeUtils addTime(TimeUtils time) {
+    	TimeUtils result = new TimeUtils();
+        int up = 0;     // 进位标志
+        for (int i = 0; i < fields.length; i++) {
+            int sum = fields[i] + time.fields[i] + up;
+            up = sum / (maxFields[i] + 1);
+            result.fields[i] = sum % (maxFields[i] + 1);
+        }
+        return result;
+    }
+
+    /**
+     * 将时间进行“减”运算,即减去一个时间
+     * @param time      需要减的时间
+     * @return          运算后的时间
+     */
+    public TimeUtils subtractTime(TimeUtils time) {
+    	TimeUtils result = new TimeUtils();
+        int down = 0;       // 退位标志
+        for (int i = 0, k = fields.length - 1; i < k; i++) {
+            int difference = fields[i] + down;
+            if (difference >= time.fields[i]) {
+                difference -= time.fields[i];
+                down = 0;
+            } else {
+                difference += maxFields[i] + 1 - time.fields[i];
+                down = -1;
+            }
+            result.fields[i] = difference;
+        }
+        result.fields[DAY] = fields[DAY] - time.fields[DAY] + down;
+        return result;
+    }
+
+    /**
+     * 获得时间字段的分隔符
+     * @return
+     */
+    public String getTimeSeparator() {
+        return timeSeparator;
+    }
+
+    /**
+     * 设置时间字段的分隔符(用于字符串格式的时间)
+     * @param timeSeparator     分隔符字符串
+     */
+    public void setTimeSeparator(String timeSeparator) {
+        this.timeSeparator = timeSeparator;
+    }
+
+    private void initialize(int day, int hour, int minute, int second) {
+        set(DAY, day);
+        set(HOUR, hour);
+        set(MINUTE, minute);
+        set(SECOND, second);
+    }
+
+    private void parseTime(String time) {
+        if(time == null) {
+            initialize(0, 0, 0, 0);
+            return;
+        }
+        String t = time;
+        int field = DAY;
+        set(field--, 0);
+        int p = -1;
+        while((p = t.indexOf(timeSeparator)) > -1) {
+            parseTimeField(time, t.substring(0, p), field--);
+            t = t.substring(p + timeSeparator.length());
+        }
+        parseTimeField(time, t, field--);
+    }
+
+    private void parseTimeField(String time, String t, int field) {
+        if(field < SECOND || t.length() < 1) {
+            parseTimeException(time);
+        }
+        char[] chs = t.toCharArray();
+        int n = 0;
+        for(int i = 0; i < chs.length; i++) {
+            if(chs[i] <= ' ') {
+                continue;
+            }
+            if(chs[i] >= '0' && chs[i] <= '9') {
+                n = n * 10 + chs[i] - '0';
+                continue;
+            }
+            parseTimeException(time);
+        }
+        set(field, n);
+    }
+
+    private void parseTimeException(String time) {
+        throw new IllegalArgumentException(time + ", time format error, HH"
+                + this.timeSeparator + "mm" + this.timeSeparator + "ss");
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder(16);
+        sb.append(fields[DAY]).append(',').append(' ');
+        buildString(sb, HOUR).append(timeSeparator);
+        buildString(sb, MINUTE).append(timeSeparator);
+        buildString(sb, SECOND);
+        return sb.toString();
+    }
+
+    private StringBuilder buildString(StringBuilder sb, int field) {
+        if(fields[field] < 10) {
+            sb.append('0');
+        }
+        return sb.append(fields[field]);
+    }
+
+    public int hashCode() {
+        final int PRIME = 31;
+        int result = 1;
+        result = PRIME * result + Arrays.hashCode(fields);
+        return result;
+    }
+
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        final TimeUtils other = (TimeUtils) obj;
+        if (!Arrays.equals(fields, other.fields)) {
+            return false;
+        }
+        return true;
+    }
+    
+}

+ 151 - 0
src/main/java/com/jeeplus/common/utils/ValidateCode.java

@@ -0,0 +1,151 @@
+package com.jeeplus.common.utils;
+
+import org.apache.commons.lang3.StringUtils;
+
+import javax.servlet.http.HttpSession;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by wenbin on 2017/12/13.
+ */
+public class ValidateCode implements Serializable {
+
+    private static final long serialVersionUID = 6486374913813791101L;
+
+    private final static int nextActionTimeSecond = 60;
+
+    private final static int frequentlySecond = 5;
+
+    public static final  String SESSION_KEY_FOR_CODE_SMS = "SESSION_KEY_FOR_CODE_SMS";
+    public static final  String SESSION_KEY_FOR_VALID = "SESSION_KEY_FOR_VALID";
+
+    private String code;
+
+    private LocalDateTime expireTime;
+
+    private LocalDateTime currentTime;
+
+    private String contactWay;
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public LocalDateTime getExpireTime() {
+        return expireTime;
+    }
+
+    public void setExpireTime(LocalDateTime expireTime) {
+        this.expireTime = expireTime;
+    }
+
+    public LocalDateTime getCurrentTime() {
+        return currentTime;
+    }
+
+    public void setCurrentTime(LocalDateTime currentTime) {
+        this.currentTime = currentTime;
+    }
+
+    public String getContactWay() {
+        return contactWay;
+    }
+
+    public void setContactWay(String contactWay) {
+        this.contactWay = contactWay;
+    }
+
+    public ValidateCode(String contactWay, String code, int expireIn) {
+        this.code = code;
+        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
+        this.contactWay = contactWay;
+        this.currentTime = LocalDateTime.now();
+    }
+
+    public ValidateCode(String code, LocalDateTime expireTime) {
+        this.code = code;
+        this.expireTime = expireTime;
+    }
+
+    /**
+     * 判断是否过期
+     *
+     * @return
+     */
+    public boolean isExpired() {
+        return LocalDateTime.now().isAfter(expireTime);
+    }
+
+
+    /**
+     * 判断相同联系方式否重复操作
+     *
+     * @return
+     */
+    public boolean isRepeat(String mobile) {
+        if (mobile.equals(this.getContactWay())) {
+            return LocalDateTime.now().isBefore(currentTime.plusSeconds(nextActionTimeSecond));
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 判断频繁操作
+     *
+     * @return
+     */
+    public boolean isFrequently() {
+        return LocalDateTime.now().isBefore(currentTime.plusSeconds(frequentlySecond));
+    }
+
+
+    /**
+     * 校验短信验证码公共方法
+     *
+     * @param session
+     * @param code
+     * @return
+     */
+    public static Map<String, Object> validateSmsPhoneCode(HttpSession session, String code, String phone) {
+        //定义返回map
+        Map<String, Object> resultMap = new HashMap<>();
+        //定义最后返回的msg
+        resultMap.put("pass", false);
+        //从session中取出短信验证码信息
+        ValidateCode validateCode = (ValidateCode) session.getAttribute(SESSION_KEY_FOR_CODE_SMS);
+        //进行验证码校验操作
+        if (validateCode == null) {
+            resultMap.put("msg", "请先获取手机验证码");
+            return resultMap;
+        }
+        if (validateCode.isExpired()) {
+            //session过期,直接移除
+            session.removeAttribute(SESSION_KEY_FOR_CODE_SMS);
+            resultMap.put("msg", "验证码已过期,请重新发送");
+            return resultMap;
+        }
+        if (!phone.equals(validateCode.getContactWay())) {
+            resultMap.put("msg", "手机号验证失败");
+            return resultMap;
+        }
+
+        if (!StringUtils.equals(validateCode.getCode(), code)) {
+            resultMap.put("msg", "验证不通过");
+            return resultMap;
+        } else {
+            //验证通过后,删除session保存的code,先不删除,为了以后可以在验证
+            resultMap.put("pass", true);
+            resultMap.put("msg", "验证通过");
+            return resultMap;
+        }
+    }
+
+}

+ 268 - 0
src/main/java/com/jeeplus/common/utils/ValidationCode.java

@@ -0,0 +1,268 @@
+package com.jeeplus.common.utils;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Random;
+
+import javax.imageio.ImageIO;
+
+
+public class ValidationCode {
+
+    //使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
+    public static final String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
+    private static Random random = new Random();
+
+
+    /**
+     * 使用系统默认字符源生成验证码
+     * @param verifySize    验证码长度
+     * @return
+     */
+    public static String generateVerifyCode(int verifySize){
+        return generateVerifyCode(verifySize, VERIFY_CODES);
+    }
+
+    /**
+     * 使用指定源生成验证码
+     * @param verifySize    验证码长度
+     * @param sources   验证码字符源
+     * @return
+     */
+    public static String generateVerifyCode(int verifySize, String sources){
+        if(sources == null || sources.length() == 0){
+            sources = VERIFY_CODES;
+        }
+        int codesLen = sources.length();
+        Random rand = new Random(System.currentTimeMillis());
+        StringBuilder verifyCode = new StringBuilder(verifySize);
+        for(int i = 0; i < verifySize; i++){
+            verifyCode.append(sources.charAt(rand.nextInt(codesLen-1)));
+        }
+        return verifyCode.toString();
+    }
+
+    /**
+     * 生成随机验证码文件,并返回验证码值
+     * @param w
+     * @param h
+     * @param outputFile
+     * @param verifySize
+     * @return
+     * @throws IOException
+     */
+    public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException{
+        String verifyCode = generateVerifyCode(verifySize);
+        outputImage(w, h, outputFile, verifyCode);
+        return verifyCode;
+    }
+
+    /**
+     * 输出随机验证码图片流,并返回验证码值
+     * @param w
+     * @param h
+     * @param os
+     * @param verifySize
+     * @return
+     * @throws IOException
+     */
+    public static String outputVerifyImage(int w, int h, OutputStream os, int verifySize) throws IOException{
+        String verifyCode = generateVerifyCode(verifySize);
+        outputImage(w, h, os, verifyCode);
+        return verifyCode;
+    }
+
+    /**
+     * 生成指定验证码图像文件
+     * @param w
+     * @param h
+     * @param outputFile
+     * @param code
+     * @throws IOException
+     */
+    public static void outputImage(int w, int h, File outputFile, String code) throws IOException{
+        if(outputFile == null){
+            return;
+        }
+        File dir = outputFile.getParentFile();
+        if(!dir.exists()){
+            dir.mkdirs();
+        }
+        try{
+            outputFile.createNewFile();
+            FileOutputStream fos = new FileOutputStream(outputFile);
+            outputImage(w, h, fos, code);
+            fos.close();
+        } catch(IOException e){
+            throw e;
+        }
+    }
+
+    /**
+     * 输出指定验证码图片流
+     * @param w
+     * @param h
+     * @param os
+     * @param code
+     * @throws IOException
+     */
+    public static void outputImage(int w, int h, OutputStream os, String code) throws IOException{
+        int verifySize = code.length();
+        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
+        Random rand = new Random();
+        Graphics2D g2 = image.createGraphics();
+        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
+        Color[] colors = new Color[5];
+        Color[] colorSpaces = new Color[] { Color.WHITE, Color.CYAN,
+                Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,
+                Color.PINK, Color.YELLOW };
+        float[] fractions = new float[colors.length];
+        for(int i = 0; i < colors.length; i++){
+            colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
+            fractions[i] = rand.nextFloat();
+        }
+        Arrays.sort(fractions);
+
+        g2.setColor(Color.GRAY);// 设置边框色
+        g2.fillRect(0, 0, w, h);
+
+        Color c = getRandColor(200, 250);
+        g2.setColor(c);// 设置背景色
+        g2.fillRect(0, 2, w, h-4);
+
+        //绘制干扰线
+        Random random = new Random();
+        g2.setColor(getRandColor(160, 200));// 设置线条的颜色
+        for (int i = 0; i < 20; i++) {
+            int x = random.nextInt(w - 1);
+            int y = random.nextInt(h - 1);
+            int xl = random.nextInt(6) + 1;
+            int yl = random.nextInt(12) + 1;
+            g2.drawLine(x, y, x + xl + 40, y + yl + 20);
+        }
+
+        // 添加噪点
+        float yawpRate = 0.05f;// 噪声率
+        int area = (int) (yawpRate * w * h);
+        for (int i = 0; i < area; i++) {
+            int x = random.nextInt(w);
+            int y = random.nextInt(h);
+            int rgb = getRandomIntColor();
+            image.setRGB(x, y, rgb);
+        }
+
+        shear(g2, w, h, c);// 使图片扭曲
+
+        g2.setColor(getRandColor(100, 160));
+        int fontSize = h-4;
+        Font font = new Font("Algerian", Font.ITALIC, fontSize);
+        g2.setFont(font);
+        char[] chars = code.toCharArray();
+        for(int i = 0; i < verifySize; i++){
+            AffineTransform affine = new AffineTransform();
+            affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), (w / verifySize) * i + fontSize/2, h/2);
+            g2.setTransform(affine);
+            g2.drawChars(chars, i, 1, ((w-10) / verifySize) * i + 5, h/2 + fontSize/2 - 10);
+        }
+
+        g2.dispose();
+        ImageIO.write(image, "png", os);
+    }
+
+    private static Color getRandColor(int fc, int bc) {
+        if (fc > 255)
+            fc = 255;
+        if (bc > 255)
+            bc = 255;
+        int r = fc + random.nextInt(bc - fc);
+        int g = fc + random.nextInt(bc - fc);
+        int b = fc + random.nextInt(bc - fc);
+        return new Color(r, g, b);
+    }
+
+    private static int getRandomIntColor() {
+        int[] rgb = getRandomRgb();
+        int color = 0;
+        for (int c : rgb) {
+            color = color << 8;
+            color = color | c;
+        }
+        return color;
+    }
+
+    private static int[] getRandomRgb() {
+        int[] rgb = new int[3];
+        for (int i = 0; i < 3; i++) {
+            rgb[i] = random.nextInt(255);
+        }
+        return rgb;
+    }
+
+    private static void shear(Graphics g, int w1, int h1, Color color) {
+        shearX(g, w1, h1, color);
+        shearY(g, w1, h1, color);
+    }
+
+    private static void shearX(Graphics g, int w1, int h1, Color color) {
+
+        int period = random.nextInt(2);
+
+        boolean borderGap = true;
+        int frames = 1;
+        int phase = random.nextInt(2);
+
+        for (int i = 0; i < h1; i++) {
+            double d = (double) (period >> 1)
+                    * Math.sin((double) i / (double) period
+                    + (6.2831853071795862D * (double) phase)
+                    / (double) frames);
+            g.copyArea(0, i, w1, 1, (int) d, 0);
+            if (borderGap) {
+                g.setColor(color);
+                g.drawLine((int) d, i, 0, i);
+                g.drawLine((int) d + w1, i, w1, i);
+            }
+        }
+
+    }
+
+    private static void shearY(Graphics g, int w1, int h1, Color color) {
+
+        int period = random.nextInt(40) + 10; // 50;
+
+        boolean borderGap = true;
+        int frames = 20;
+        int phase = 7;
+        for (int i = 0; i < w1; i++) {
+            double d = (double) (period >> 1)
+                    * Math.sin((double) i / (double) period
+                    + (6.2831853071795862D * (double) phase)
+                    / (double) frames);
+            g.copyArea(i, 0, 1, h1, 0, (int) d);
+            if (borderGap) {
+                g.setColor(color);
+                g.drawLine(i, (int) d, i, 0);
+                g.drawLine(i, (int) d + h1, i, h1);
+            }
+
+        }
+    }
+    public static void main(String[] args) throws IOException{
+        File dir = new File("C:/Users/H__D/Desktop/");
+        int w = 200, h = 80;
+        String verifyCode = generateVerifyCode(4);
+        File file = new File(dir, verifyCode + ".jpg");
+        outputImage(w, h, file, verifyCode);
+    }
+}
+

+ 70 - 0
src/main/java/com/jeeplus/common/utils/base/BooleanUtil.java

@@ -0,0 +1,70 @@
+package com.jeeplus.common.utils.base;
+
+import org.apache.commons.lang3.BooleanUtils;
+
+public class BooleanUtil {
+
+	/**
+	 * 使用标准JDK,只分析是否忽略大小写的"true", 为空时返回false
+	 */
+	public static boolean toBoolean(String str) {
+		return Boolean.parseBoolean(str);
+	}
+
+	/**
+	 * 使用标准JDK,只分析是否忽略大小写的"true", 为空时返回null
+	 */
+	public static Boolean toBooleanObject(String str) {
+		return str != null ? Boolean.valueOf(str) : null;
+	}
+
+	/**
+	 * 使用标准JDK,只分析是否忽略大小写的"true", 为空时返回defaultValue
+	 */
+	public static Boolean toBooleanObject(String str, Boolean defaultValue) {
+		return str != null ? Boolean.valueOf(str) : defaultValue;
+	}
+
+	/**
+	 * 支持true/false,on/off, y/n, yes/no的转换, str为空或无法分析时返回null
+	 */
+	public static Boolean parseGeneralString(String str) {
+		return BooleanUtils.toBooleanObject(str);
+	}
+
+	/**
+	 * 支持true/false,on/off, y/n, yes/no的转换, str为空或无法分析时返回defaultValue
+	 */
+	public static Boolean parseGeneralString(String str, Boolean defaultValue) {
+		return BooleanUtils.toBooleanDefaultIfNull(BooleanUtils.toBooleanObject(str), defaultValue);
+	}
+
+	/**
+	 * 取反
+	 */
+	public static boolean negate(final boolean bool) {
+		return !bool;
+	}
+
+	/**
+	 * 取反
+	 */
+	public static Boolean negate(final Boolean bool) {
+		return BooleanUtils.negate(bool);
+	}
+
+	/**
+	 * 多个值的and
+	 */
+	public static boolean and(final boolean... array) {
+		return BooleanUtils.and(array);
+	}
+
+	/**
+	 * 多个值的or
+	 */
+	public static boolean or(final boolean... array) {
+		return BooleanUtils.or(array);
+	}
+
+}

+ 36 - 0
src/main/java/com/jeeplus/common/utils/base/EnumUtil.java

@@ -0,0 +1,36 @@
+package com.jeeplus.common.utils.base;
+
+import java.util.EnumSet;
+
+import org.apache.commons.lang3.EnumUtils;
+
+/**
+ * 枚举工具集
+ * 
+ * @author calvin
+ *
+ */
+public class EnumUtil {
+
+	/**
+	 * 将若干个枚举值转换为long,用于使用long保存多个选项的情况.
+	 */
+	public static <E extends Enum<E>> long generateBits(final Class<E> enumClass, final Iterable<? extends E> values) {
+		return EnumUtils.generateBitVector(enumClass, values);
+	}
+
+	/**
+	 * 将若干个枚举值转换为long,用于使用long保存多个选项的情况.
+	 */
+	public static <E extends Enum<E>> long generateBits(final Class<E> enumClass, final E... values) {
+		return EnumUtils.generateBitVector(enumClass, values);
+	}
+
+	/**
+	 * long重新解析为若干个枚举值,用于使用long保存多个选项的情况.
+	 */
+	public static <E extends Enum<E>> EnumSet<E> processBits(final Class<E> enumClass, final long value) {
+		return EnumUtils.processBitVector(enumClass, value);
+	}
+
+}

+ 320 - 0
src/main/java/com/jeeplus/common/utils/base/ExceptionUtil.java

@@ -0,0 +1,320 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2017 springside.github.io
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *******************************************************************************/
+package com.jeeplus.common.utils.base;
+
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import com.jeeplus.common.utils.base.annotation.Nullable;
+
+import com.google.common.base.Throwables;
+
+/**
+ * 关于异常的工具类.
+ * 
+ * 1. 若干常用函数.
+ * 
+ * 2. StackTrace性能优化相关,尽量使用静态异常避免异常生成时获取StackTrace,及打印StackTrace的消耗
+ * 
+ * @author calvin
+ */
+public class ExceptionUtil {
+
+	private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
+
+	/**
+	 * 将CheckedException转换为RuntimeException重新抛出, 可以减少函数签名中的CheckExcetpion定义.
+	 * 
+	 * CheckedException会用UndeclaredThrowableException包裹,RunTimeException和Error则不会被转变.
+	 * 
+	 * from Commons Lange 3.5 ExceptionUtils.
+	 * 
+	 * 虽然unchecked()里已直接抛出异常,但仍然定义返回值,方便欺骗Sonar。因此本函数也改变了一下返回值
+	 * 
+	 * 示例代码:
+	 * 
+	 * <pre>
+	 * try{ ... }catch(Exception e){ throw unchecked(t); }
+	 * 
+	 * @see ExceptionUtils#wrapAndThrow(Throwable)
+	 */
+	public static RuntimeException unchecked(Throwable t) {
+
+		if (t instanceof RuntimeException) {
+			throw (RuntimeException) t;
+		}
+		if (t instanceof Error) {
+			throw (Error) t;
+		}
+		throw new UncheckedException(t);
+	}
+
+	/**
+	 * 如果是著名的包裹类,从cause中获得真正异常. 其他异常则不变.
+	 * 
+	 * Future中使用的ExecutionException 与 反射时定义的InvocationTargetException, 真正的异常都封装在Cause中
+	 * 
+	 * 前面 unchecked() 使用的UncheckedException同理.
+	 * 
+	 * from Quasar and Tomcat's ExceptionUtils
+	 */
+	public static Throwable unwrap(Throwable t) {
+		if (t instanceof java.util.concurrent.ExecutionException
+				|| t instanceof java.lang.reflect.InvocationTargetException || t instanceof UncheckedException) {
+			return t.getCause();
+		}
+
+		return t;
+	}
+
+	/**
+	 * 组合unchecked与unwrap的效果
+	 */
+	public static RuntimeException uncheckedAndWrap(Throwable t) {
+
+		Throwable unwrapped = unwrap(t);
+		if (unwrapped instanceof RuntimeException) {
+			throw (RuntimeException) unwrapped;
+		}
+		if (unwrapped instanceof Error) {
+			throw (Error) unwrapped;
+		}
+		throw new UncheckedException(unwrapped);
+	}
+
+	/**
+	 * 将StackTrace[]转换为String, 供Logger或e.printStackTrace()外的其他地方使用.
+	 * 
+	 * @see Throwables#getStackTraceAsString(Throwable)
+	 */
+	public static String stackTraceText(Throwable t) {
+		return Throwables.getStackTraceAsString(t);
+	}
+
+	/**
+	 * 获取异常的Root Cause.
+	 * 
+	 * 如无底层Cause, 则返回自身
+	 * 
+	 * @see Throwables#getRootCause(Throwable)
+	 */
+	public static Throwable getRootCause(Throwable t) {
+		return Throwables.getRootCause(t);
+	}
+
+	/**
+	 * 判断异常是否由某些底层的异常引起.
+	 */
+	@SuppressWarnings("unchecked")
+	public static boolean isCausedBy(Throwable t, Class<? extends Exception>... causeExceptionClasses) {
+		Throwable cause = t;
+
+		while (cause != null) {
+			for (Class<? extends Exception> causeClass : causeExceptionClasses) {
+				if (causeClass.isInstance(cause)) {
+					return true;
+				}
+			}
+			cause = cause.getCause();
+		}
+		return false;
+	}
+
+	/**
+	 * 拼装 短异常类名: 异常信息.
+	 * 
+	 * 与Throwable.toString()相比使用了短类名
+	 * 
+	 * @see ExceptionUtils#getMessage(Throwable)
+	 */
+	public static String toStringWithShortName(@Nullable Throwable t) {
+		return ExceptionUtils.getMessage(t);
+	}
+
+	/**
+	 * 拼装 短异常类名: 异常信息 <-- RootCause的短异常类名: 异常信息
+	 */
+	public static String toStringWithRootCause(@Nullable Throwable t) {
+		if (t == null) {
+			return StringUtils.EMPTY;
+		}
+
+		final String clsName = ClassUtils.getShortClassName(t, null);
+		final String message = StringUtils.defaultString(t.getMessage());
+		Throwable cause = getRootCause(t);
+
+		StringBuilder sb = new StringBuilder(128).append(clsName).append(": ").append(message);
+		if (cause != t) {
+			sb.append("; <---").append(toStringWithShortName(cause));
+		}
+
+		return sb.toString();
+	}
+
+	/////////// StackTrace 性能优化相关////////
+
+	/**
+	 * from Netty, 为静态异常设置StackTrace.
+	 * 
+	 * 对某些已知且经常抛出的异常, 不需要每次创建异常类并很消耗性能的并生成完整的StackTrace. 此时可使用静态声明的异常.
+	 * 
+	 * 如果异常可能在多个地方抛出,使用本函数设置抛出的类名和方法名.
+	 * 
+	 * <pre>
+	 * private static RuntimeException TIMEOUT_EXCEPTION = ExceptionUtil.setStackTrace(new RuntimeException("Timeout"),
+	 * 		MyClass.class, "mymethod");
+	 * 
+	 * </pre>
+	 */
+	public static <T extends Throwable> T setStackTrace(T exception, Class<?> throwClass, String throwClazz) {
+		exception.setStackTrace(
+				new StackTraceElement[] { new StackTraceElement(throwClass.getName(), throwClazz, null, -1) });
+		return exception;// NOSONAR
+	}
+
+	/**
+	 * 清除StackTrace. 假设StackTrace已生成, 但把它打印出来也有不小的消耗.
+	 * 
+	 * 如果不能控制StackTrace的生成,也不能控制它的打印端(如logger),可用此方法暴力清除Trace.
+	 * 
+	 * 但Cause链依然不能清除, 只能清除每一个Cause的StackTrace.
+	 */
+	public static <T extends Throwable> T clearStackTrace(T exception) {
+		Throwable cause = exception;
+		while (cause != null) {
+			cause.setStackTrace(EMPTY_STACK_TRACE);
+			cause = cause.getCause();
+		}
+		return exception;// NOSONAR
+	}
+
+	/**
+	 * 适用于Message经常变更的异常, 可通过clone()不经过构造函数的构造异常再设定新的异常信息
+	 */
+	public static class CloneableException extends Exception implements Cloneable {
+
+		private static final long serialVersionUID = -6270471689928560417L;
+		protected String message;
+
+		public CloneableException() {
+			super((Throwable) null);
+		}
+
+		public CloneableException(String message) {
+			super((Throwable) null);
+			this.message = message;
+		}
+
+		public CloneableException(String message, Throwable cause) {
+			super(cause);
+			this.message = message;
+		}
+
+		@Override
+		public CloneableException clone() {
+			try {
+				return (CloneableException) super.clone();
+			} catch (CloneNotSupportedException e) {// NOSONAR
+				return null;
+			}
+		}
+
+		public CloneableException clone(String message) {
+			CloneableException newException = this.clone();
+			newException.setMessage(message);
+			return newException;
+		}
+
+		@Override
+		public String getMessage() {
+			return message;
+		}
+
+		public void setMessage(String message) {
+			this.message = message;
+		}
+
+		public CloneableException setStackTrace(Class<?> throwClazz, String throwMethod) {
+			ExceptionUtil.setStackTrace(this, throwClazz, throwMethod);
+			return this;
+		}
+	}
+
+	/**
+	 * 重载fillInStackTrace()方法,不生成StackTrace.
+	 * 
+	 * 适用于Message经常变更,不能使用静态异常时
+	 */
+	public static class CloneableRuntimeException extends RuntimeException implements Cloneable {
+
+		private static final long serialVersionUID = 3984796576627959400L;
+
+		protected String message;
+
+		public CloneableRuntimeException() {
+			super((Throwable) null);
+		}
+
+		public CloneableRuntimeException(String message) {
+			super((Throwable) null);
+			this.message = message;
+		}
+
+		public CloneableRuntimeException(String message, Throwable cause) {
+			super(cause);
+			this.message = message;
+		}
+
+		@Override
+		public CloneableRuntimeException clone() {
+			try {
+				return (CloneableRuntimeException) super.clone();
+			} catch (CloneNotSupportedException e) { // NOSONAR
+				return null;
+			}
+		}
+
+		public CloneableRuntimeException clone(String message) {
+			CloneableRuntimeException newException = this.clone();
+			newException.setMessage(message);
+			return newException;
+		}
+
+		@Override
+		public String getMessage() {
+			return message;
+		}
+
+		public void setMessage(String message) {
+			this.message = message;
+		}
+
+		public CloneableRuntimeException setStackTrace(Class<?> throwClazz, String throwMethod) {
+			ExceptionUtil.setStackTrace(this, throwClazz, throwMethod);
+			return this;
+		}
+	}
+
+	/**
+	 * 自定义一个CheckedException的wrapper
+	 * 
+	 * @author calvin
+	 *
+	 */
+	public static class UncheckedException extends RuntimeException {
+
+		private static final long serialVersionUID = 4140223302171577501L;
+
+		public UncheckedException(Throwable cause) {
+			super(cause);
+		}
+
+		@Override
+		public String getMessage() {
+			return super.getCause().getMessage();
+		}
+	}
+}

+ 117 - 0
src/main/java/com/jeeplus/common/utils/base/MoreValidate.java

@@ -0,0 +1,117 @@
+package com.jeeplus.common.utils.base;
+
+import com.jeeplus.common.utils.base.annotation.Nullable;
+
+/**
+ * 参数校验统一使用Apache Common Lange Validate, 补充一些缺少的.
+ * 
+ * 为什么不用Guava的Preconditions? 无他,
+ * 
+ * 一是少打几个字而已, 二是Validate的方法多,比如noNullElements()判断多个元素都不为空
+ * 
+ * @see com.google.common.math.MathPreconditions
+ * 
+ * @author calvin
+ */
+public class MoreValidate {
+
+	/**
+	 * 校验为正数则返回该数字,否则抛出异常.
+	 */
+	public static int positive(@Nullable String role, int x) {
+		if (x <= 0) {
+			throw new IllegalArgumentException(role + " (" + x + ") must be > 0");
+		}
+		return x;
+	}
+
+	/**
+	 * 校验为正数则返回该数字,否则抛出异常.
+	 */
+	public static Integer positive(@Nullable String role, Integer x) {
+		if (x.intValue() <= 0) {
+			throw new IllegalArgumentException(role + " (" + x + ") must be > 0");
+		}
+		return x;
+	}
+
+	/**
+	 * 校验为正数则返回该数字,否则抛出异常.
+	 */
+	public static long positive(@Nullable String role, long x) {
+		if (x <= 0) {
+			throw new IllegalArgumentException(role + " (" + x + ") must be > 0");
+		}
+		return x;
+	}
+
+	/**
+	 * 校验为正数则返回该数字,否则抛出异常.
+	 */
+	public static Long positive(@Nullable String role, Long x) {
+		if (x.longValue() <= 0) {
+			throw new IllegalArgumentException(role + " (" + x + ") must be > 0");
+		}
+		return x;
+	}
+
+	/**
+	 * 校验为正数则返回该数字,否则抛出异常.
+	 */
+	public static double positive(@Nullable String role, double x) {
+		if (!(x > 0)) { // not x < 0, to work with NaN.
+			throw new IllegalArgumentException(role + " (" + x + ") must be >= 0");
+		}
+		return x;
+	}
+
+	/**
+	 * 校验为正数则返回该数字,否则抛出异常.
+	 */
+	public static int nonNegative(@Nullable String role, int x) {
+		if (x < 0) {
+			throw new IllegalArgumentException(role + " (" + x + ") must be >= 0");
+		}
+		return x;
+	}
+
+	/**
+	 * 校验为正数则返回该数字,否则抛出异常.
+	 */
+	public static Integer nonNegative(@Nullable String role, Integer x) {
+		if (x.intValue() < 0) {
+			throw new IllegalArgumentException(role + " (" + x + ") must be >= 0");
+		}
+		return x;
+	}
+
+	/**
+	 * 校验为正数则返回该数字,否则抛出异常.
+	 */
+	public static long nonNegative(@Nullable String role, long x) {
+		if (x < 0) {
+			throw new IllegalArgumentException(role + " (" + x + ") must be >= 0");
+		}
+		return x;
+	}
+
+	/**
+	 * 校验为正数则返回该数字,否则抛出异常.
+	 */
+	public static Long nonNegative(@Nullable String role, Long x) {
+		if (x.longValue() < 0) {
+			throw new IllegalArgumentException(role + " (" + x + ") must be >= 0");
+		}
+		return x;
+	}
+
+	/**
+	 * 校验为正数则返回该数字,否则抛出异常.
+	 */
+	public static double nonNegative(@Nullable String role, double x) {
+		if (!(x >= 0)) { // not x < 0, to work with NaN.
+			throw new IllegalArgumentException(role + " (" + x + ") must be >= 0");
+		}
+		return x;
+	}
+}

+ 84 - 0
src/main/java/com/jeeplus/common/utils/base/ObjectUtil.java

@@ -0,0 +1,84 @@
+package com.jeeplus.common.utils.base;
+
+import java.util.Arrays;
+
+public class ObjectUtil {
+
+	private static String NULL = "null";
+
+	/**
+	 * 多个对象的HashCode串联
+	 */
+	public static int hashCode(Object... objects) {
+		return Arrays.hashCode(objects);
+	}
+
+	/**
+	 * 对象的toString(),处理了对象为数组和集合的情况.
+	 */
+	public static String toPrettyString(Object value) {
+		if (value == null) {
+			return NULL;
+		}
+
+		Class<?> type = value.getClass();
+
+		if (type.isArray()) {
+			Class componentType = type.getComponentType();
+
+			if (componentType.isPrimitive()) {
+				StringBuilder sb = new StringBuilder();
+
+				if (componentType == int.class) {
+					sb.append(Arrays.toString((int[]) value));
+				} else if (componentType == long.class) {
+					sb.append(Arrays.toString((long[]) value));
+				} else if (componentType == double.class) {
+					sb.append(Arrays.toString((double[]) value));
+				} else if (componentType == float.class) {
+					sb.append(Arrays.toString((float[]) value));
+				} else if (componentType == boolean.class) {
+					sb.append(Arrays.toString((boolean[]) value));
+				} else if (componentType == short.class) {
+					sb.append(Arrays.toString((short[]) value));
+				} else if (componentType == byte.class) {
+					sb.append(Arrays.toString((byte[]) value));
+				} else {
+					throw new IllegalArgumentException("unsupport array type");
+				}
+
+				return sb.toString();
+			} else {
+				StringBuilder sb = new StringBuilder();
+				sb.append('[');
+
+				Object[] array = (Object[]) value;
+				for (int i = 0; i < array.length; i++) {
+					if (i > 0) {
+						sb.append(", ");
+					}
+					sb.append(toPrettyString(array[i]));
+				}
+				sb.append(']');
+				return sb.toString();
+			}
+		} else if (value instanceof Iterable) {
+			Iterable iterable = (Iterable) value;
+			StringBuilder sb = new StringBuilder();
+			sb.append('{');
+			int i = 0;
+			for (Object o : iterable) {
+				if (i > 0) {
+					sb.append(',');
+				}
+				sb.append(toPrettyString(o));
+				i++;
+			}
+			sb.append('}');
+			return sb.toString();
+		}
+
+		return value.toString();
+	}
+
+}

+ 76 - 0
src/main/java/com/jeeplus/common/utils/base/Platforms.java

@@ -0,0 +1,76 @@
+package com.jeeplus.common.utils.base;
+
+import java.io.File;
+import java.lang.management.ManagementFactory;
+
+import org.apache.commons.lang3.SystemUtils;
+
+/**
+ * 关于系统设定,平台信息的变量
+ * 
+ * via Common Lang
+ * 
+ * @author calvin
+ */
+public class Platforms {
+
+	// 文件路径分隔符
+	public static final String FILE_PATH_SEPARATOR = File.separator;
+	public static final char FILE_PATH_SEPARATOR_CHAR = File.separatorChar;
+	public static final char WINDOWS_FILE_PATH_SEPARATOR_CHAR = '\\';
+	public static final char LINUX_FILE_PATH_SEPARATOR_CHAR = '/';
+
+	// ClassPath分隔符
+	public static final String CLASS_PATH_SEPARATOR = File.pathSeparator;
+	public static final char CLASS_PATH_SEPARATOR_CHAR = File.pathSeparatorChar;
+
+	// 换行符, JDK7可使用System.lineSeparator()
+	public static final String LINE_SEPARATOR = SystemUtils.LINE_SEPARATOR;
+
+	// 临时目录
+	public static final String TMP_DIR = SystemUtils.JAVA_IO_TMPDIR;
+	// 当前应用的工作目录
+	public static final String WORKING_DIR = SystemUtils.USER_DIR;
+	// 用户 HOME目录
+	public static final String USER_HOME = SystemUtils.USER_HOME;
+	// Java HOME目录
+	public static final String JAVA_HOME = SystemUtils.JAVA_HOME;
+
+	// Java版本
+	public static final String JAVA_SPECIFICATION_VERSION = SystemUtils.JAVA_SPECIFICATION_VERSION; // e.g. 1.8
+	public static final String JAVA_VERSION = SystemUtils.JAVA_VERSION; // e.g. 1.8.0_102
+	public static final boolean IS_JAVA6 = SystemUtils.IS_JAVA_1_6;
+	public static final boolean IS_JAVA7 = SystemUtils.IS_JAVA_1_7;
+	public static final boolean IS_JAVA8 = SystemUtils.IS_JAVA_1_8;
+	public static final boolean IS_ATLEASET_JAVA6 = IS_JAVA6 || IS_JAVA7 || IS_JAVA8;
+	public static final boolean IS_ATLEASET_JAVA7 = IS_JAVA7 || IS_JAVA8;
+	public static final boolean IS_ATLEASET_JAVA8 = IS_JAVA8;
+
+	// 操作系统类型及版本
+	public static final String OS_NAME = SystemUtils.OS_NAME;
+	public static final String OS_VERSION = SystemUtils.OS_VERSION;
+	public static final String OS_ARCH = SystemUtils.OS_ARCH; // e.g. x86_64
+	public static final boolean IS_LINUX = SystemUtils.IS_OS_LINUX;
+	public static final boolean IS_UNIX = SystemUtils.IS_OS_UNIX;
+	public static final boolean IS_WINDOWS = SystemUtils.IS_OS_WINDOWS;
+
+	/**
+	 * 获得当前进程的PID
+	 * 
+	 * 当失败时返回-1
+	 */
+	public static int getPid() {
+		// format: "pid@hostname"
+		String name = ManagementFactory.getRuntimeMXBean().getName();
+		String[] split = name.split("@");
+		if (split.length != 2) {
+			return -1;
+		}
+
+		try {
+			return Integer.parseInt(split[0]);
+		} catch (Exception e) {
+			return -1;
+		}
+	}
+}

+ 86 - 0
src/main/java/com/jeeplus/common/utils/base/PropertiesUtil.java

@@ -0,0 +1,86 @@
+package com.jeeplus.common.utils.base;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Properties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.jeeplus.common.utils.io.IOUtil;
+import com.jeeplus.common.utils.io.URLResourceUtil;
+import com.jeeplus.common.utils.number.NumberUtil;
+
+/**
+ * 关于Properties的工具类
+ * 
+ * 1. 统一读取Properties
+ * 
+ * 2. 从文件或字符串装载Properties
+ * 
+ * @author calvin
+ */
+public class PropertiesUtil {
+
+	private static final Logger logger = LoggerFactory.getLogger(PropertiesUtil.class);
+
+	/////////////////// 读取Properties ////////////////////
+
+	public static Boolean getBoolean(Properties p, String name, Boolean defaultValue) {
+		return BooleanUtil.toBooleanObject(p.getProperty(name), defaultValue);
+	}
+
+	public static Integer getInt(Properties p, String name, Integer defaultValue) {
+		return NumberUtil.toIntObject(p.getProperty(name), defaultValue);
+	}
+
+	public static Long getLong(Properties p, String name, Long defaultValue) {
+		return NumberUtil.toLongObject(p.getProperty(name), defaultValue);
+	}
+
+	public static Double getDouble(Properties p, String name, Double defaultValue) {
+		return NumberUtil.toDoubleObject(p.getProperty(name), defaultValue);
+	}
+
+	public static String getString(Properties p, String name, String defaultValue) {
+		return p.getProperty(name, defaultValue);
+	}
+
+	/////////// 加载Properties////////
+	/**
+	 * 从文件路径加载properties.
+	 * 
+	 * 路径支持从外部文件或resources文件加载, "file://"或无前缀代表外部文件, "classpath://"代表resources,
+	 */
+	public static Properties loadFromFile(String generalPath) {
+		Properties p = new Properties();
+		InputStream is = null;
+		try {
+			is = URLResourceUtil.asStream(generalPath);
+			p.load(is);
+		} catch (IOException e) {
+			logger.warn("Load property from " + generalPath + " fail ", e);
+		} finally {
+			IOUtil.closeQuietly(is);
+		}
+		return p;
+	}
+
+	/**
+	 * 从字符串内容加载Properties
+	 */
+	public static Properties loadFromString(String content) {
+		Properties p = new Properties();
+		Reader reader = new StringReader(content);
+		try {
+			p.load(reader);
+		} catch (IOException ignored) {
+		} finally {
+			IOUtil.closeQuietly(reader);
+		}
+
+		return p;
+	}
+
+}

+ 241 - 0
src/main/java/com/jeeplus/common/utils/base/SystemPropertiesUtil.java

@@ -0,0 +1,241 @@
+package com.jeeplus.common.utils.base;
+
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.jeeplus.common.utils.number.NumberUtil;
+
+/**
+ * 关于系统Properties的工具类
+ * 
+ * 1. 统一的读取系统变量,其中Boolean.readBoolean的风格不统一,Double则不支持,都进行了扩展.
+ * 
+ * 2. 简单的合并系统变量(-D),环境变量 和默认值,以系统变量优先,在未引入Commons Config时使用.
+ * 
+ * 3. Properties 本质上是一个HashTable,每次读写都会加锁,所以不支持频繁的System.getProperty(name)来检查系统内容变化 因此扩展了一个ListenableProperties,
+ * 在其所关心的属性变化时进行通知.
+ * 
+ * @author calvin
+ */
+public class SystemPropertiesUtil {
+
+	/**
+	 * 读取Boolean类型的系统变量,为空时返回null,代表未设置,而不是Boolean.getBoolean()的false.
+	 */
+	public static Boolean getBoolean(String name) {
+		String stringResult = System.getProperty(name);
+		return BooleanUtil.toBooleanObject(stringResult);
+	}
+
+	/**
+	 * 读取Boolean类型的系统变量,为空时返回默认值, 而不是Boolean.getBoolean()的false.
+	 */
+	public static Boolean getBoolean(String name, Boolean defaultValue) {
+		String stringResult = System.getProperty(name);
+		return BooleanUtil.toBooleanObject(stringResult, defaultValue);
+	}
+
+	/**
+	 * 读取String类型的系统变量,为空时返回null.
+	 */
+	public static String getString(String name) {
+		return System.getProperty(name);
+	}
+
+	/**
+	 * 读取String类型的系统变量,为空时返回默认值
+	 */
+	public static String getString(String name, String defaultValue) {
+		return System.getProperty(name, defaultValue);
+	}
+
+	/**
+	 * 读取Integer类型的系统变量,为空时返回null.
+	 */
+	public static Integer getInteger(String name) {
+		return Integer.getInteger(name);
+	}
+
+	/**
+	 * 读取Integer类型的系统变量,为空时返回默认值
+	 */
+	public static Integer getInteger(String name, Integer defaultValue) {
+		return Integer.getInteger(name, defaultValue);
+	}
+
+	/**
+	 * 读取Long类型的系统变量,为空时返回null.
+	 */
+	public static Long getLong(String name) {
+		return Long.getLong(name);
+	}
+
+	/**
+	 * 读取Integer类型的系统变量,为空时返回默认值
+	 */
+	public static Long getLong(String name, Long defaultValue) {
+		return Long.getLong(name, defaultValue);
+	}
+
+	/**
+	 * 读取Double类型的系统变量,为空时返回null.
+	 */
+	public static Double getDouble(String propertyName) {
+		return NumberUtil.toDoubleObject(System.getProperty(propertyName));
+	}
+
+	/**
+	 * 读取Double类型的系统变量,为空时返回默认值.
+	 */
+	public static Double getDouble(String propertyName, Double defaultValue) {
+		Double propertyValue = NumberUtil.toDoubleObject(System.getProperty(propertyName));
+		return propertyValue != null ? propertyValue : defaultValue;
+	}
+
+	/////////// 简单的合并系统变量(-D),环境变量 和默认值,以系统变量优先.///////////////
+
+	/**
+	 * 合并系统变量(-D),环境变量 和默认值,以系统变量优先
+	 */
+	public static String getString(String propertyName, String envName, String defaultValue) {
+		checkEnvName(envName);
+		String propertyValue = System.getProperty(propertyName);
+		if (propertyValue != null) {
+			return propertyValue;
+		} else {
+			propertyValue = System.getenv(envName);
+			return propertyValue != null ? propertyValue : defaultValue;
+		}
+	}
+
+	/**
+	 * 合并系统变量(-D),环境变量 和默认值,以系统变量优先
+	 */
+	public static Integer getInteger(String propertyName, String envName, Integer defaultValue) {
+		checkEnvName(envName);
+		Integer propertyValue = NumberUtil.toIntObject(System.getProperty(propertyName));
+		if (propertyValue != null) {
+			return propertyValue;
+		} else {
+			propertyValue = NumberUtil.toIntObject(System.getenv(envName));
+			return propertyValue != null ? propertyValue : defaultValue;
+		}
+	}
+
+	/**
+	 * 合并系统变量(-D),环境变量 和默认值,以系统变量优先
+	 */
+	public static Long getLong(String propertyName, String envName, Long defaultValue) {
+		checkEnvName(envName);
+		Long propertyValue = NumberUtil.toLongObject(System.getProperty(propertyName));
+		if (propertyValue != null) {
+			return propertyValue;
+		} else {
+			propertyValue = NumberUtil.toLongObject(System.getenv(envName));
+			return propertyValue != null ? propertyValue : defaultValue;
+		}
+	}
+
+	/**
+	 * 合并系统变量(-D),环境变量 和默认值,以系统变量优先
+	 */
+	public static Double getDouble(String propertyName, String envName, Double defaultValue) {
+		checkEnvName(envName);
+		Double propertyValue = NumberUtil.toDoubleObject(System.getProperty(propertyName));
+		if (propertyValue != null) {
+			return propertyValue;
+		} else {
+			propertyValue = NumberUtil.toDoubleObject(System.getenv(envName));
+			return propertyValue != null ? propertyValue : defaultValue;
+		}
+	}
+
+	/**
+	 * 合并系统变量(-D),环境变量 和默认值,以系统变量优先
+	 */
+	public static Boolean getBoolean(String propertyName, String envName, Boolean defaultValue) {
+		checkEnvName(envName);
+		Boolean propertyValue = BooleanUtil.toBooleanObject(System.getProperty(propertyName), null);
+		if (propertyValue != null) {
+			return propertyValue;
+		} else {
+			propertyValue = BooleanUtil.toBooleanObject(System.getenv(envName), null);
+			return propertyValue != null ? propertyValue : defaultValue;
+		}
+	}
+
+	/////////// ListenableProperties /////////////
+	/**
+	 * Properties 本质上是一个HashTable,每次读写都会加锁,所以不支持频繁的System.getProperty(name)来检查系统内容变化 因此扩展了一个ListenableProperties,
+	 * 在其所关心的属性变化时进行通知.
+	 * 
+	 * @see ListenableProperties
+	 */
+	public static synchronized void registerSystemPropertiesListener(PropertiesListener listener) {
+		Properties currentProperties = System.getProperties();
+
+		if (!(currentProperties instanceof ListenableProperties)) {
+			ListenableProperties newProperties = new ListenableProperties(currentProperties);
+			System.setProperties(newProperties);
+			currentProperties = newProperties;
+		}
+
+		((ListenableProperties) currentProperties).register(listener);
+	}
+
+	/**
+	 * 检查环境变量名不能有'.',在linux下不支持
+	 */
+	private static void checkEnvName(String envName) {
+		if (envName == null || envName.indexOf('.') != -1) {
+			throw new IllegalArgumentException("envName " + envName + " has dot which is not valid");
+		}
+	}
+
+	/**
+	 * Properties 本质上是一个HashTable,每次读写都会加锁,所以不支持频繁的System.getProperty(name)来检查系统内容变化 因此扩展了一个ListenableProperties,
+	 * 在其所关心的属性变化时进行通知.
+	 * 
+	 * @see PropertiesListener
+	 */
+	public static class ListenableProperties extends Properties {
+
+		private static final long serialVersionUID = -8282465702074684324L;
+
+		protected List<PropertiesListener> listeners = new CopyOnWriteArrayList<PropertiesListener>();
+
+		public ListenableProperties(Properties properties) {
+			super(properties);
+		}
+
+		public void register(PropertiesListener listener) {
+			listeners.add(listener);
+		}
+
+		@Override
+		public synchronized Object setProperty(String key, String value) {
+			Object result = put(key, value);
+			for (PropertiesListener listener : listeners) {
+				if (listener.propertyName.equals(key)) {
+					listener.onChange(key, value);
+				}
+			}
+			return result;
+		}
+	}
+
+	/**
+	 * 获取所关心的Properties变更的Listener基类.
+	 */
+	public abstract static class PropertiesListener {
+
+		protected String propertyName;
+
+		public PropertiesListener(String propertyName) {
+			this.propertyName = propertyName;
+		}
+
+		public abstract void onChange(String propertyName, String value);
+	}
+}

+ 5 - 0
src/main/java/com/jeeplus/common/utils/base/annotation/NotNull.java

@@ -0,0 +1,5 @@
+package com.jeeplus.common.utils.base.annotation;
+
+public @interface NotNull {
+
+}

+ 5 - 0
src/main/java/com/jeeplus/common/utils/base/annotation/Nullable.java

@@ -0,0 +1,5 @@
+package com.jeeplus.common.utils.base.annotation;
+
+public @interface Nullable {
+
+}

+ 136 - 0
src/main/java/com/jeeplus/common/utils/collection/ArrayUtil.java

@@ -0,0 +1,136 @@
+package com.jeeplus.common.utils.collection;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import com.jeeplus.common.utils.base.annotation.Nullable;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.ObjectArrays;
+import com.google.common.primitives.Doubles;
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Longs;
+
+/**
+ * 数组工具类.
+ * 
+ * JDK Arrays的其他函数,如sort(), toString() 请直接调用
+ * 
+ * Common Lang ArrayUtils的其他函数,如subarray(),reverse(),indeOf(), 请直接调用
+ */
+public class ArrayUtil {
+
+	/**
+	 * 将传入的数组乱序
+	 */
+	public static <T> T[] shuffle(T[] array) {
+		List<T> list = new ArrayList<T>(array.length);
+		Collections.addAll(list, array);
+		Collections.shuffle(list);
+		return list.toArray(array);
+	}
+
+	/**
+	 * 将传入的数组乱序
+	 */
+	public static <T> T[] shuffle(T[] array, Random random) {
+		List<T> list = new ArrayList<T>(Arrays.asList(array));
+		Collections.shuffle(list, random);
+		return list.toArray(array);
+	}
+
+	/**
+	 * 添加元素到数组头,没有银弹,复制扩容.
+	 */
+	public static <T> T[] concat(@Nullable T element, T[] array) {
+		return ObjectArrays.concat(element, array);
+	}
+
+	/**
+	 * 添加元素到数组末尾,没有银弹,复制扩容.
+	 */
+	public static <T> T[] concat(T[] array, @Nullable T element) {
+		return ObjectArrays.concat(array, element);
+	}
+
+	/**
+	 * 传入类型与大小创建数组.
+	 */
+	public static <T> T[] newArray(Class<T> type, int length) {
+		return (T[]) Array.newInstance(type, length);
+	}
+
+	/**
+	 * list.toArray() 返回的是Object[] 如果要有类型的数组话,就要使用list.toArray(new String[list.size()]),这里对调用做了简化
+	 */
+	public static <T> T[] toArray(List<T> list, Class<T> type) {
+		return list.toArray((T[]) Array.newInstance(type, list.size()));
+	}
+
+	////////////////// guava Array向List的转换 ///////////
+
+	/**
+	 * 原版将数组转换为List.
+	 * 
+	 * 注意转换后的List不能写入, 否则抛出UnsupportedOperationException
+	 * 
+	 * @see Arrays#asList(Object...)
+	 */
+	public static <T> List<T> asList(T... a) {
+		return Arrays.asList(a);
+	}
+
+	/**
+	 * 一个独立元素+一个数组组成新的list,只是一个View,不复制数组内容,而且独立元素在最前.
+	 *
+	 * 
+	 * 注意转换后的List不能写入, 否则抛出UnsupportedOperationException
+	 * 
+	 * @see Lists#asList(Object, Object[])
+	 */
+	public static <E> List<E> asList(E first, E[] rest) {
+		return Lists.asList(first, rest);
+	}
+
+	/**
+	 * Arrays.asList()的加强版, 返回一个底层为原始类型int的List
+	 * 
+	 * 与保存Integer相比节约空间,同时只在读取数据时AutoBoxing.
+	 * 
+	 * @see Arrays#asList(Object...)
+	 * @see Ints#asList(int...)
+	 * 
+	 */
+	public static List<Integer> intAsList(int... backingArray) {
+		return Ints.asList(backingArray);
+	}
+
+	/**
+	 * Arrays.asList()的加强版, 返回一个底层为原始类型long的List
+	 * 
+	 * 与保存Long相比节约空间,同时只在读取数据时AutoBoxing.
+	 * 
+	 * @see Arrays#asList(Object...)
+	 * @see Longs#asList(long...)
+	 */
+	public static List<Long> longAsList(long... backingArray) {
+		return Longs.asList(backingArray);
+	}
+
+	/**
+	 * Arrays.asList()的加强版, 返回一个底层为原始类型double的List
+	 * 
+	 * 与保存Double相比节约空间,同时也避免了AutoBoxing.
+	 * 
+	 * @see Arrays#asList(Object...)
+	 * @see Doubles#asList(double...)
+	 */
+	public static List<Double> doubleAsList(double... backingArray) {
+		return Doubles.asList(backingArray);
+	}
+
+}

+ 181 - 0
src/main/java/com/jeeplus/common/utils/collection/CollectionUtil.java

@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2014 springside.github.io
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *******************************************************************************/
+package com.jeeplus.common.utils.collection;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import com.jeeplus.common.utils.collection.type.Pair;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Ordering;
+
+/**
+ * 通用Collection的工具集
+ * 
+ * 关于List, Map, Queue, Set的特殊工具集,另见特定的Utils.
+ * 
+ * 另JDK中缺少ComparableComparator和NullComparator,直到JDK8才补上。
+ * 
+ * 因此平时请使用guava的Ordering,fluentable的API更好用,可以链式设置nullFirst,nullLast,reverse
+ * 
+ * @see Ordering
+ */
+public class CollectionUtil {
+
+	/**
+	 * 判断是否为空.
+	 */
+	public static boolean isEmpty(Collection<?> collection) {
+		return (collection == null) || collection.isEmpty();
+	}
+
+	/**
+	 * 判断是否不为空.
+	 */
+	public static boolean isNotEmpty(Collection<?> collection) {
+		return (collection != null) && !(collection.isEmpty());
+	}
+
+	/**
+	 * 取得Collection的第一个元素,如果collection为空返回null.
+	 */
+	public static <T> T getFirst(Collection<T> collection) {
+		if (isEmpty(collection)) {
+			return null;
+		}
+		if (collection instanceof List) {
+			return ((List<T>) collection).get(0);
+		}
+		return collection.iterator().next();
+	}
+
+	/**
+	 * 获取Collection的最后一个元素,如果collection为空返回null.
+	 */
+	public static <T> T getLast(Collection<T> collection) {
+		if (isEmpty(collection)) {
+			return null;
+		}
+
+		// 当类型List时,直接取得最后一个元素.
+		if (collection instanceof List) {
+			List<T> list = (List<T>) collection;
+			return list.get(list.size() - 1);
+		}
+
+		return Iterators.getLast(collection.iterator());
+	}
+
+	/**
+	 * 两个集合中的所有元素按顺序相等.
+	 */
+	public static boolean elementsEqual(Iterable<?> iterable1, Iterable<?> iterable2) {
+		return Iterables.elementsEqual(iterable1, iterable2);
+	}
+
+	///////////// 求最大最小值,及Top N, Low N//////////
+	/**
+	 * 返回无序集合中的最小值,使用元素默认排序
+	 */
+	public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll) {
+		return Collections.min(coll);
+	}
+
+	/**
+	 * 返回无序集合中的最小值
+	 */
+	public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp) {
+		return Collections.min(coll, comp);
+	}
+
+	/**
+	 * 返回无序集合中的最大值,使用元素默认排序
+	 */
+	public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) {
+		return Collections.max(coll);
+	}
+
+	/**
+	 * 返回无序集合中的最大值
+	 */
+	public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) {
+		return Collections.max(coll, comp);
+	}
+
+	/**
+	 * 返回无序集合中的最小值和最大值,使用元素默认排序
+	 */
+	public static <T extends Object & Comparable<? super T>> Pair<T, T> minAndMax(Collection<? extends T> coll) {
+		Iterator<? extends T> i = coll.iterator();
+		T minCandidate = i.next();
+		T maxCandidate = minCandidate;
+
+		while (i.hasNext()) {
+			T next = i.next();
+			if (next.compareTo(minCandidate) < 0) {
+				minCandidate = next;
+			} else if (next.compareTo(maxCandidate) > 0) {
+				maxCandidate = next;
+			}
+		}
+		return Pair.of(minCandidate, maxCandidate);
+	}
+
+	/**
+	 * 返回无序集合中的最小值和最大值
+	 */
+	public static <T> Pair<T, T> minAndMax(Collection<? extends T> coll, Comparator<? super T> comp) {
+
+		Iterator<? extends T> i = coll.iterator();
+		T minCandidate = i.next();
+		T maxCandidate = minCandidate;
+
+		while (i.hasNext()) {
+			T next = i.next();
+			if (comp.compare(next, minCandidate) < 0) {
+				minCandidate = next;
+			} else if (comp.compare(next, maxCandidate) > 0) {
+				maxCandidate = next;
+			}
+		}
+
+		return Pair.of(minCandidate, maxCandidate);
+	}
+
+	/**
+	 * 排序最高的N个对象, guava已优化.
+	 */
+	public static <T extends Comparable> List<T> topN(Iterable<T> coll, int n) {
+		return Ordering.natural().greatestOf(coll, n);
+	}
+
+	/**
+	 * 排序最高的N个对象, guava已优化.
+	 */
+	public static <T extends Comparable> List<T> topN(Iterable<T> coll, int n, Comparator<? super T> comp) {
+		return Ordering.from(comp).greatestOf(coll, n);
+	}
+
+	/**
+	 * 排序最低的N个对象, guava已优化.
+	 */
+	public static <T extends Comparable> List<T> bottomN(Iterable<T> coll, int n) {
+		return Ordering.natural().leastOf(coll, n);
+	}
+
+	/**
+	 * 排序最低的N个对象, guava已优化.
+	 */
+	public static <T extends Comparable> List<T> bottomN(Iterable<T> coll, int n, Comparator<? super T> comp) {
+		return Ordering.from(comp).leastOf(coll, n);
+	}
+
+}

+ 372 - 0
src/main/java/com/jeeplus/common/utils/collection/ListUtil.java

@@ -0,0 +1,372 @@
+package com.jeeplus.common.utils.collection;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.jeeplus.common.utils.collection.type.SortedArrayList;
+
+import com.google.common.collect.Lists;
+
+/**
+ * 关于List的工具集合.
+ * 
+ * 1. 常用函数(如是否为空,sort/binarySearch/shuffle/reverse(via JDK Collection)
+ * 
+ * 2. 各种构造函数(from guava and JDK Collection)
+ * 
+ * 3. 各种扩展List类型的创建函数
+ * 
+ * 5. 集合运算:交集,并集, 差集, 补集,from Commons Collection,但对其不合理的地方做了修正
+ * 
+ * @author calvin
+ */
+@SuppressWarnings("unchecked")
+public class ListUtil {
+
+	/**
+	 * 判断是否为空.
+	 */
+	public static boolean isEmpty(List<?> list) {
+		return (list == null) || list.isEmpty();
+	}
+
+	/**
+	 * 判断是否不为空.
+	 */
+	public static boolean isNotEmpty(List<?> list) {
+		return (list != null) && !(list.isEmpty());
+	}
+
+	/**
+	 * 获取第一个元素, 如果List为空返回 null.
+	 */
+	public static <T> T getFirst(List<T> list) {
+		if (isEmpty(list)) {
+			return null;
+		}
+		return list.get(0);
+	}
+
+	/**
+	 * 获取最后一个元素,如果List为空返回null.
+	 */
+	public static <T> T getLast(List<T> list) {
+		if (isEmpty(list)) {
+			return null;
+		}
+
+		return list.get(list.size() - 1);
+	}
+
+	///////////////// from Guava的构造函数///////////////////
+	/**
+	 * 根据等号左边的类型,构造类型正确的ArrayList.
+	 * 
+	 * @see Lists#newArrayList()
+	 */
+	public static <T> ArrayList<T> newArrayList() {
+		return new ArrayList<T>();
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的ArrayList, 并初始化元素.
+	 * 
+	 * @see Lists#newArrayList(Object...)
+	 */
+	public static <T> ArrayList<T> newArrayList(T... elements) {
+		return Lists.newArrayList(elements);
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的ArrayList, 并初始化元素.
+	 * 
+	 * @see Lists#newArrayList(Iterable)
+	 */
+	public static <T> ArrayList<T> newArrayList(Iterable<T> elements) {
+		return Lists.newArrayList(elements);
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的ArrayList, 并初始化数组大小.
+	 * 
+	 * @see Lists#newArrayListWithCapacity(int)
+	 */
+	public static <T> ArrayList<T> newArrayListWithCapacity(int initSize) {
+		return new ArrayList<T>(initSize);
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的LinkedList.
+	 * 
+	 * @see Lists#newLinkedList()
+	 */
+	public static <T> LinkedList<T> newLinkedList() {
+		return new LinkedList<T>();
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的LinkedList.
+	 * 
+	 * @see Lists#newLinkedList()
+	 */
+	public static <T> LinkedList<T> newLinkedList(Iterable<? extends T> elements) {
+		return Lists.newLinkedList(elements);
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的CopyOnWriteArrayList.
+	 * 
+	 * @see Lists#newCopyOnWriteArrayList()
+	 */
+	public static <T> CopyOnWriteArrayList<T> newCopyOnWriteArrayList() {
+		return new CopyOnWriteArrayList<T>();
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型转换的CopyOnWriteArrayList, 并初始化元素.
+	 */
+	public static <T> CopyOnWriteArrayList<T> newCopyOnWriteArrayList(T... elements) {
+		return new CopyOnWriteArrayList<T>(elements);
+	}
+
+	////////////// 特别类型的List//////////////
+
+	/**
+	 * 构造排序的ArrayList.
+	 * 
+	 * from Jodd的新类型,插入时排序,但指定插入index的方法如add(index,element)不支持
+	 */
+	@SuppressWarnings("rawtypes")
+	public static <T extends Comparable> SortedArrayList<T> createSortedArrayList() {
+		return new SortedArrayList<T>();
+	}
+
+	/**
+	 * 构造排序的ArrayList.
+	 * 
+	 * from Jodd的新类型,插入时排序,但指定插入index的方法如add(index,element)不支持
+	 */
+	public static <T> SortedArrayList<T> createSortedArrayList(Comparator<? super T> c) {
+		return new SortedArrayList<T>(c);
+	}
+
+	///////////////// from JDK Collections的常用构造函数 ///////////////////
+
+	/**
+	 * 返回一个空的结构特殊的List,节约空间.
+	 * 
+	 * 注意返回的List不可写, 写入会抛出UnsupportedOperationException.
+	 * 
+	 * @see Collections#emptyList()
+	 */
+	public static final <T> List<T> emptyList() {
+		return (List<T>) Collections.EMPTY_LIST;
+	}
+
+	/**
+	 * 如果list为null,转化为一个安全的空List.
+	 * 
+	 * 注意返回的List不可写, 写入会抛出UnsupportedOperationException.
+	 * 
+	 * @see Collections#emptyList()
+	 */
+	public static <T> List<T> emptyListIfNull(final List<T> list) {
+		return list == null ? (List<T>) Collections.EMPTY_LIST : list;
+	}
+
+	/**
+	 * 返回只含一个元素但结构特殊的List,节约空间.
+	 * 
+	 * 注意返回的List不可写, 写入会抛出UnsupportedOperationException.
+	 * 
+	 * @see Collections#singletonList(Object)
+	 */
+	public static <T> List<T> singletonList(T o) {
+		return Collections.singletonList(o);
+	}
+
+	/**
+	 * 返回包装后不可修改的List.
+	 * 
+	 * 如果尝试写入会抛出UnsupportedOperationException.
+	 * 
+	 * @see Collections#unmodifiableList(List)
+	 */
+	public static <T> List<T> unmodifiableList(List<? extends T> list) {
+		return Collections.unmodifiableList(list);
+	}
+
+	/**
+	 * 返回包装后同步的List,所有方法都会被synchronized原语同步.
+	 * 
+	 * 用于CopyOnWriteArrayList与 ArrayDequeue均不符合的场景
+	 */
+	public static <T> List<T> synchronizedList(List<T> list) {
+		return Collections.synchronizedList(list);
+	}
+
+	///////////////// from JDK Collections的常用函数 ///////////////////
+
+	/**
+	 * 升序排序, 采用JDK认为最优的排序算法, 使用元素自身的compareTo()方法
+	 * 
+	 * @see Collections#sort(List)
+	 */
+	public static <T extends Comparable<? super T>> void sort(List<T> list) {
+		Collections.sort(list);
+	}
+
+	/**
+	 * 倒序排序, 采用JDK认为最优的排序算法,使用元素自身的compareTo()方法
+	 * 
+	 * @see Collections#sort(List)
+	 */
+	public static <T extends Comparable<? super T>> void sortReverse(List<T> list) {
+		Collections.sort(list, Collections.reverseOrder());
+	}
+
+	/**
+	 * 升序排序, 采用JDK认为最优的排序算法, 使用Comparetor.
+	 * 
+	 * @see Collections#sort(List, Comparator)
+	 */
+	public static <T> void sort(List<T> list, Comparator<? super T> c) {
+		Collections.sort(list, c);
+	}
+
+	/**
+	 * 倒序排序, 采用JDK认为最优的排序算法, 使用Comparetor
+	 * 
+	 * @see Collections#sort(List, Comparator)
+	 */
+	public static <T> void sortReverse(List<T> list, Comparator<? super T> c) {
+		Collections.sort(list, Collections.reverseOrder(c));
+	}
+
+	/**
+	 * 二分法快速查找对象, 使用Comparable对象自身的比较.
+	 * 
+	 * list必须已按升序排序.
+	 * 
+	 * 如果不存在,返回一个负数,代表如果要插入这个对象,应该插入的位置
+	 * 
+	 * @see Collections#binarySearch(List, Object)
+	 */
+	public static <T> int binarySearch(List<? extends Comparable<? super T>> sortedList, T key) {
+		return Collections.binarySearch(sortedList, key);
+	}
+
+	/**
+	 * 二分法快速查找对象,使用Comparator.
+	 * 
+	 * list必须已按升序排序.
+	 * 
+	 * 如果不存在,返回一个负数,代表如果要插入这个对象,应该插入的位置
+	 * 
+	 * @see Collections#binarySearch(List, Object, Comparator)
+	 */
+	public static <T> int binarySearch(List<? extends T> sortedList, T key, Comparator<? super T> c) {
+		return Collections.binarySearch(sortedList, key, c);
+	}
+
+	/**
+	 * 随机乱序,使用默认的Random.
+	 * 
+	 * @see Collections#shuffle(List)
+	 */
+	public static void shuffle(List<?> list) {
+		Collections.shuffle(list);
+	}
+
+	/**
+	 * 返回一个倒转顺序访问的List,仅仅是一个倒序的View,不会实际多生成一个List
+	 * 
+	 * @see Lists#reverse(List)
+	 */
+	public static <T> List<T> reverse(final List<T> list) {
+		return Lists.reverse(list);
+	}
+
+	/**
+	 * 随机乱序,使用传入的Random.
+	 * 
+	 * @see Collections#shuffle(List, Random)
+	 */
+	public static void shuffle(List<?> list, Random rnd) {
+		Collections.shuffle(list, rnd);
+	}
+
+	///////////////// 集合运算 ///////////////////
+
+	/**
+	 * list1,list2的并集(在list1或list2中的对象),产生新List
+	 * 
+	 * 对比Apache Common Collection4 ListUtils, 优化了初始大小
+	 */
+	public static <E> List<E> union(final List<? extends E> list1, final List<? extends E> list2) {
+		final List<E> result = new ArrayList<E>(list1.size() + list2.size());
+		result.addAll(list1);
+		result.addAll(list2);
+		return result;
+	}
+
+	/**
+	 * list1, list2的交集(同时在list1和list2的对象),产生新List
+	 * 
+	 * from Apache Common Collection4 ListUtils,但其做了不合理的去重,因此重新改为性能较低但不去重的版本
+	 * 
+	 * 与List.retainAll()相比,考虑了的List中相同元素出现的次数, 如"a"在list1出现两次,而在list2中只出现一次,则交集里会保留一个"a".
+	 */
+	public static <T> List<T> intersection(final List<? extends T> list1, final List<? extends T> list2) {
+		List<? extends T> smaller = list1;
+		List<? extends T> larger = list2;
+		if (list1.size() > list2.size()) {
+			smaller = list2;
+			larger = list1;
+		}
+
+		// 克隆一个可修改的副本
+		List<T> newSmaller = new ArrayList<T>(smaller);
+		List<T> result = new ArrayList<T>(smaller.size());
+		for (final T e : larger) {
+			if (newSmaller.contains(e)) {
+				result.add(e);
+				newSmaller.remove(e);
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * list1, list2的差集(在list1,不在list2中的对象),产生新List.
+	 * 
+	 * 与List.removeAll()相比,会计算元素出现的次数,如"a"在list1出现两次,而在list2中只出现一次,则差集里会保留一个"a".
+	 */
+	public static <T> List<T> difference(final List<? extends T> list1, final List<? extends T> list2) {
+		final List<T> result = new ArrayList<T>(list1);
+		final Iterator<? extends T> iterator = list2.iterator();
+
+		while (iterator.hasNext()) {
+			result.remove(iterator.next());
+		}
+
+		return result;
+	}
+
+	/**
+	 * list1, list2的补集(在list1或list2中,但不在交集中的对象,又叫反交集)产生新List.
+	 * 
+	 * from Apache Common Collection4 ListUtils,但其并集-交集时,没有对交集*2,所以做了修改
+	 */
+	public static <T> List<T> disjoint(final List<? extends T> list1, final List<? extends T> list2) {
+		List<T> intersection = intersection(list1, list2);
+		List<T> towIntersection = union(intersection, intersection);
+		return difference(union(list1, list2), towIntersection);
+	}
+}

+ 421 - 0
src/main/java/com/jeeplus/common/utils/collection/MapUtil.java

@@ -0,0 +1,421 @@
+package com.jeeplus.common.utils.collection;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import org.apache.commons.lang3.mutable.MutableInt;
+import org.apache.commons.lang3.mutable.MutableLong;
+import com.jeeplus.common.utils.base.Platforms;
+import com.jeeplus.common.utils.base.annotation.NotNull;
+import com.jeeplus.common.utils.base.annotation.Nullable;
+import com.jeeplus.common.utils.collection.type.primitive.IntObjectHashMap;
+import com.jeeplus.common.utils.collection.type.primitive.LongObjectHashMap;
+import com.jeeplus.common.utils.concurrent.jsr166e.ConcurrentHashMapV8;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ConcurrentHashMultiset;
+import com.google.common.collect.MapDifference;
+import com.google.common.collect.MapMaker;
+import com.google.common.collect.Maps;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.SortedSetMultimap;
+import com.google.common.collect.TreeRangeMap;
+import com.google.common.util.concurrent.AtomicLongMap;
+
+/**
+ * 关于Map的工具集合,
+ * 
+ * 1. 常用函数(如是否为空)
+ * 
+ * 2. 对于并发Map,增加putIfAbsent(返回最终值版), createIfAbsent这两个重要函数(from Common Lang)
+ * 
+ * 3. 便捷的构造函数(via guava,Java Collections,并增加了用数组,List等方式初始化Map的函数)
+ * 
+ * 4. 特殊的类型,包括WeakConcurrentHashMap, IntObjectHashMap, MapCounter, MultiKeyMap, RangeMap
+ * 
+ * 
+ * 参考文章:《高性能场景下,Map家族的优化使用建议》 http://calvin1978.blogcn.com/articles/hashmap.html
+ * 
+ * @author calvin
+ */
+@SuppressWarnings("unchecked")
+public class MapUtil {
+
+	/**
+	 * 判断是否为空.
+	 */
+	public static boolean isEmpty(final Map<?, ?> map) {
+		return (map == null) || map.isEmpty();
+	}
+
+	/**
+	 * 判断是否为空.
+	 */
+	public static boolean isNotEmpty(final Map<?, ?> map) {
+		return (map != null) && !map.isEmpty();
+	}
+
+	/**
+	 * ConcurrentMap的putIfAbsent()返回之前的Value,此函数封装返回最终存储在Map中的Value
+	 * 
+	 * @see org.apache.commons.lang3.concurrent.ConcurrentUtils#putIfAbsent(ConcurrentMap, Object, Object)
+	 */
+	public static <K, V> V putIfAbsentWithFinalValue(@NotNull final ConcurrentMap<K, V> map, final K key,
+			final V value) {
+		final V result = map.putIfAbsent(key, value);
+		return result != null ? result : value;
+	}
+
+	/**
+	 * 如果Key不存在则创建,返回最后存储在Map中的Value.
+	 * 
+	 * 如果创建对象有一定成本, 直接使用PutIfAbsent可能重复浪费,则使用此类,传入回调的ConcurrentInitializer
+	 * 
+	 * @see org.apache.commons.lang3.concurrent.ConcurrentUtils#createIfAbsent(ConcurrentMap, Object,
+	 * org.apache.commons.lang3.concurrent.ConcurrentInitializer)
+	 */
+	public static <K, V> V createIfAbsent(@NotNull final ConcurrentMap<K, V> map, final K key,
+			@NotNull final ValueCreator<? extends V> creator) {
+		final V value = map.get(key);
+		if (value == null) {
+			return putIfAbsentWithFinalValue(map, key, creator.get());
+		}
+		return value;
+	}
+
+	/**
+	 * 创建Value值的回调函数
+	 * 
+	 * @see MapUtil#createIfAbsent(ConcurrentMap, Object, ValueCreator)
+	 */
+	public interface ValueCreator<T> {
+		/**
+		 * 创建对象
+		 */
+		T get();
+	}
+
+	///////////////// from Guava的构造函数///////////////////
+
+	/**
+	 * 根据等号左边的类型, 构造类型正确的HashMap.
+	 * 
+	 * 未初始化数组大小, 默认为16个桶.
+	 * 
+	 * @see Maps#newHashMap()
+	 */
+	public static <K, V> HashMap<K, V> newHashMap() {
+		return new HashMap<K, V>();
+	}
+
+	/**
+	 * 根据等号左边的类型, 构造类型正确的HashMap.
+	 * 
+	 * 注意HashMap中有0.75的加载因子的影响, 需要进行运算后才能正确初始化HashMap的大小.
+	 * 
+	 * 加载因子也是HashMap中减少Hash冲突的重要一环,如果读写频繁,总记录数不多的Map,可以比默认值0.75进一步降低,建议0.5
+	 * 
+	 * @see Maps#newHashMap(int)
+	 */
+	public static <K, V> HashMap<K, V> newHashMapWithCapacity(int expectedSize, float loadFactor) {
+		int finalSize = (int) ((double) expectedSize / loadFactor + 1.0F);
+		return new HashMap<K, V>(finalSize, loadFactor);
+	}
+
+	/**
+	 * 根据等号左边的类型, 构造类型正确的HashMap.
+	 * 
+	 * 同时初始化第一个元素
+	 */
+	public static <K, V> HashMap<K, V> newHashMap(final K key, final V value) {
+		HashMap<K, V> map = new HashMap<K, V>();
+		map.put(key, value);
+		return map;
+	}
+
+	/**
+	 * 根据等号左边的类型, 构造类型正确的HashMap.
+	 * 
+	 * 同时初始化元素.
+	 */
+	public static <K, V> HashMap<K, V> newHashMap(@NotNull final K[] keys, @NotNull final V[] values) {
+		if (keys.length != values.length) {
+			throw new IllegalArgumentException(
+					"keys.length is " + keys.length + " but values.length is " + values.length);
+		}
+
+		HashMap<K, V> map = new HashMap<K, V>();
+
+		for (int i = 0; i < keys.length; i++) {
+			map.put(keys[i], values[i]);
+		}
+
+		return map;
+	}
+
+	/**
+	 * 根据等号左边的类型, 构造类型正确的HashMap.
+	 * 
+	 * 同时初始化元素.
+	 */
+	public static <K, V> HashMap<K, V> newHashMap(@NotNull final List<K> keys, @NotNull final List<V> values) {
+		if (keys.size() != values.size()) {
+			throw new IllegalArgumentException("keys.size is " + keys.size() + " but values.size is " + values.size());
+		}
+
+		HashMap<K, V> map = new HashMap<K, V>();
+		Iterator<K> keyIt = keys.iterator();
+		Iterator<V> valueIt = values.iterator();
+
+		while (keyIt.hasNext()) {
+			map.put(keyIt.next(), valueIt.next());
+		}
+
+		return map;
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的TreeMap.
+	 * 
+	 * @see Maps#newTreeMap()
+	 */
+	@SuppressWarnings("rawtypes")
+	public static <K extends Comparable, V> TreeMap<K, V> newSortedMap() {
+		return new TreeMap<K, V>();
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的TreeMap.
+	 * 
+	 * @see Maps#newTreeMap(Comparator)
+	 */
+	public static <C, K extends C, V> TreeMap<K, V> newSortedMap(@Nullable Comparator<C> comparator) {
+		return Maps.newTreeMap(comparator);
+	}
+
+	/**
+	 * 相比HashMap,当key是枚举类时, 性能与空间占用俱佳.
+	 */
+	public static <K extends Enum<K>, V> EnumMap<K, V> newEnumMap(@NotNull Class<K> type) {
+		return new EnumMap<K, V>(Preconditions.checkNotNull(type));
+	}
+
+	/**
+	 * JDK8下,ConcurrentHashMap已不再需要设置loadFactor, concurrencyLevel和initialCapacity.
+	 * 
+	 * 如果JDK8,使用原生ConcurrentHashMap,否则使用移植版
+	 */
+	public static <K, V> ConcurrentMap<K, V> newConcurrentHashMap() {
+		if (Platforms.IS_ATLEASET_JAVA8) {
+			return new ConcurrentHashMap<K, V>();
+		} else {
+			return new ConcurrentHashMapV8<K, V>();
+		}
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的ConcurrentSkipListMap.
+	 */
+	public static <K, V> ConcurrentSkipListMap<K, V> newConcurrentSortedMap() {
+		return new ConcurrentSkipListMap<K, V>();
+	}
+
+	///////////////// from Guava的特别Map //////////////////////
+
+	/**
+	 * 创建Key为弱引用的ConcurrentMap,Key对象可被回收.
+	 * 
+	 * JDK没有WeakHashMap的并发实现, 由Guava提供
+	 */
+	public static <K, V> ConcurrentMap<K, V> createWeakKeyConcurrentHashMap(int initialCapacity, int concurrencyLevel) {
+		return new MapMaker().weakKeys().initialCapacity(initialCapacity).concurrencyLevel(concurrencyLevel).makeMap();
+	}
+
+	/**
+	 * 创建Value为弱引用的ConcurrentMap,Value对象可被回收.
+	 * 
+	 * JDK没有WeakHashMap的并发实现, 由Guava提供
+	 */
+	public static <K, V> ConcurrentMap<K, V> createWeakValueConcurrentHashMap(int initialCapacity,
+			int concurrencyLevel) {
+		return new MapMaker().weakValues().initialCapacity(initialCapacity).concurrencyLevel(concurrencyLevel)
+				.makeMap();
+	}
+
+	/**
+	 * 创建移植自Netty的key为int的优化HashMap
+	 * 
+	 * @param initialCapacity 建议为16
+	 * @param loadFactor 建议为0.5
+	 */
+	public static <V> IntObjectHashMap<V> createIntObjectHashMap(int initialCapacity, float loadFactor) {
+		return new IntObjectHashMap<V>(initialCapacity, loadFactor);
+	}
+
+	/**
+	 * 创建移植自Netty的key为long的优化HashMap
+	 * 
+	 * @param initialCapacity 建议为16
+	 * @param loadFactor 建议为0.5
+	 */
+	public static <V> LongObjectHashMap<V> createLongObjectHashMap(int initialCapacity, float loadFactor) {
+		return new LongObjectHashMap<V>(initialCapacity, loadFactor);
+	}
+
+	/**
+	 * 创建值为可更改的Integer的HashMap. 可更改的Integer在更改时不需要重新创建Integer对象,节约了内存
+	 * 
+	 * @param initialCapacity 建议为16
+	 * @param loadFactor 建议为0.5
+	 */
+	public static <K> HashMap<K, MutableInt> createMutableIntValueHashMap(int initialCapacity, float loadFactor) {
+		return new HashMap<K, MutableInt>(initialCapacity, loadFactor);
+	}
+
+	/**
+	 * 创建值为可更改的Long的HashMap. 可更改的Long在更改时不需要重新创建Long对象,节约了内存
+	 * 
+	 * @param initialCapacity 建议为16
+	 * @param loadFactor 建议为0.5
+	 */
+	public static <K> HashMap<K, MutableLong> createMutableLongValueHashMap(int initialCapacity, float loadFactor) {
+		return new HashMap<K, MutableLong>(initialCapacity, loadFactor);
+	}
+
+	/**
+	 * 以Guava的AtomicLongMap,实现线程安全的HashMap<E,AtomicLong>结构的Counter
+	 */
+	public static <E> AtomicLongMap<E> createConcurrentMapCounter() {
+		return AtomicLongMap.create();
+	}
+
+	/**
+	 * 以Guava的MultiSet,实现线程安全的HashMap<E,Integer>结构的Counter
+	 */
+	public static <E> ConcurrentHashMultiset<E> createConcurrentMapCounter(Iterable<? extends E> elements) {
+		return ConcurrentHashMultiset.create(elements);
+	}
+
+	/**
+	 * 以Guava的MultiMap,实现的HashMap<E,List<V>>结构的一个Key对应多个值的map.
+	 * 
+	 * 注意非线程安全, MultiMap无线程安全的实现.
+	 * 
+	 * 另有其他结构存储values的MultiMap,请自行参考MultimapBuilder使用.
+	 * 
+	 * @param expectedKeys 默认为16
+	 * @param expectedValuesPerKey 默认为3
+	 */
+	public static <K, V> ArrayListMultimap<K, V> createListValueMap(int expectedKeys, int expectedValuesPerKey) {
+		return ArrayListMultimap.create(expectedKeys, expectedValuesPerKey);
+	}
+
+	/**
+	 * 以Guava的MultiMap,实现的HashMap<E,TreeSet<V>>结构的一个Key对应多个值的map.
+	 * 
+	 * 注意非线程安全, MultiMap无线程安全的实现.
+	 * 
+	 * 另有其他结构存储values的MultiMap,请自行参考MultimapBuilder使用.
+	 */
+	public static <K, V extends Comparable> SortedSetMultimap<K, V> createSortedSetValueMap() {
+		return MultimapBuilder.hashKeys().treeSetValues().build();
+	}
+
+	/**
+	 * 以Guava的MultiMap,实现的HashMap<E,TreeSet<V>>结构的一个Key对应多个值的map.
+	 * 
+	 * 注意非线程安全, MultiMap无线程安全的实现.
+	 * 
+	 * 另有其他结构存储values的MultiMap,请自行参考MultimapBuilder使用.
+	 */
+	public static <K, V> SortedSetMultimap<K, V> createSortedSetValueMap(Comparator<V> comparator) {
+		return (SortedSetMultimap<K, V>) MultimapBuilder.hashKeys().treeSetValues(comparator);
+	}
+
+	/**
+	 * 以Guava TreeRangeMap实现的, 一段范围的Key指向同一个Value的Map
+	 */
+	@SuppressWarnings("rawtypes")
+	public static <K extends Comparable, V> TreeRangeMap<K, V> createRangeMap() {
+		return TreeRangeMap.create();
+	}
+
+	///////////////// from JDK Collections的常用构造函数 ///////////////////
+
+	/**
+	 * 返回一个空的结构特殊的Map,节约空间.
+	 * 
+	 * 注意返回的Map不可写, 写入会抛出UnsupportedOperationException.
+	 * 
+	 * @see Collections#emptyMap()
+	 */
+	public static final <K, V> Map<K, V> emptyMap() {
+		return (Map<K, V>) Collections.EMPTY_MAP;
+	}
+
+	/**
+	 * 如果map为null,转化为一个安全的空Map.
+	 * 
+	 * 注意返回的Map不可写, 写入会抛出UnsupportedOperationException.
+	 * 
+	 * @see Collections#emptyMap()
+	 */
+	public static <K, V> Map<K, V> emptyMapIfNull(final Map<K, V> map) {
+		return map == null ? (Map<K, V>) Collections.EMPTY_MAP : map;
+	}
+
+	/**
+	 * 返回一个只含一个元素但结构特殊的Map,节约空间.
+	 * 
+	 * 注意返回的Map不可写, 写入会抛出UnsupportedOperationException.
+	 * 
+	 * @see Collections#singletonMap(Object, Object)
+	 */
+	public static <K, V> Map<K, V> singletonMap(final K key, final V value) {
+		return Collections.singletonMap(key, value);
+	}
+
+	/**
+	 * 返回包装后不可修改的Map.
+	 * 
+	 * 如果尝试修改,会抛出UnsupportedOperationException
+	 * 
+	 * @see Collections#unmodifiableMap(Map)
+	 */
+	public static <K, V> Map<K, V> unmodifiableMap(final Map<? extends K, ? extends V> m) {
+		return Collections.unmodifiableMap(m);
+	}
+
+	/**
+	 * 返回包装后不可修改的有序Map.
+	 * 
+	 * @see Collections#unmodifiableSortedMap(SortedMap)
+	 */
+	public static <K, V> SortedMap<K, V> unmodifiableSortedMap(final SortedMap<K, ? extends V> m) {
+		return Collections.unmodifiableSortedMap(m);
+	}
+
+	//////// Map的集合操作 //////
+
+	/**
+	 * 对两个Map进行比较,返回MapDifference,然后各种妙用.
+	 * 
+	 * 包括key的差集,key的交集,以及key相同但value不同的元素。
+	 */
+	public static <K, V> MapDifference<K, V> difference(Map<? extends K, ? extends V> left,
+			Map<? extends K, ? extends V> right) {
+		return Maps.difference(left, right);
+	}
+
+}

+ 128 - 0
src/main/java/com/jeeplus/common/utils/collection/QueueUtil.java

@@ -0,0 +1,128 @@
+package com.jeeplus.common.utils.collection;
+
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import com.google.common.collect.EvictingQueue;
+
+/**
+ * Queue工具集.
+ * 
+ * 1.各种Queue,Dequeue的创建
+ * 
+ * 2.特殊类型Queue:LIFO的Stack, LRU的Queue
+ */
+public class QueueUtil {
+
+	/**
+	 * 创建ArrayDeque (JDK无ArrayQueue)
+	 * 
+	 * 需设置初始长度,默认为16,数组满时成倍扩容
+	 */
+	public static <E> ArrayDeque<E> newArrayDeque(int initSize) {
+		return new ArrayDeque<E>(initSize);
+	}
+
+	/**
+	 * 创建LinkedDeque (LinkedList实现了Deque接口)
+	 */
+	public static <E> LinkedList<E> newLinkedDeque() {
+		return new LinkedList<E>();
+	}
+
+	/**
+	 * 创建无阻塞情况下,性能最优的并发队列
+	 */
+	public static <E> ConcurrentLinkedQueue<E> newConcurrentNonBlockingQueue() {
+		return new ConcurrentLinkedQueue<E>();
+	}
+
+	/**
+	 * 创建无阻塞情况下,性能最优的并发双端队列
+	 */
+	public static <E> Deque<E> newConcurrentNonBlockingDeque() {
+		return new java.util.concurrent.ConcurrentLinkedDeque<E>();
+	}
+
+	/**
+	 * 创建并发阻塞情况下,长度不受限的队列.
+	 * 
+	 * 长度不受限,即队列不会因为满而阻塞,但会因为空而阻塞.
+	 */
+	public static <E> LinkedBlockingQueue<E> newBlockingUnlimitQueue() {
+		return new LinkedBlockingQueue<E>();
+	}
+
+	/**
+	 * 创建并发阻塞情况下,长度不受限的双端队列.
+	 * 
+	 * 长度不受限,即队列不会因为满而阻塞,但会因为空而阻塞.
+	 */
+	public static <E> LinkedBlockingDeque<E> newBlockingUnlimitDeque() {
+		return new LinkedBlockingDeque<E>();
+	}
+
+	/**
+	 * 创建并发阻塞情况下,长度受限,更节约内存,但共用一把锁的队列(无双端队列实现).
+	 */
+	public static <E> ArrayBlockingQueue<E> newArrayBlockingQueue(int capacity) {
+		return new ArrayBlockingQueue<E>(capacity);
+	}
+
+	/**
+	 * 创建并发阻塞情况下,长度受限,头队尾两把锁, 但使用更多内存的队列.
+	 */
+	public static <E> LinkedBlockingQueue<E> newLinkedBlockingQeque(int capacity) {
+		return new LinkedBlockingQueue<E>(capacity);
+	}
+
+	/**
+	 * 创建并发阻塞情况下,长度受限,头队尾两把锁, 但使用更多内存的双端队列.
+	 */
+	public static <E> LinkedBlockingDeque<E> newBlockingDeque(int capacity) {
+		return new LinkedBlockingDeque<E>(capacity);
+	}
+
+	//////////////// 特殊类型Queue:Stack ///////////
+
+	/**
+	 * 支持后进先出的栈,用ArrayDeque实现, 经过Collections#asLifoQueue()转换顺序
+	 * 
+	 * 需设置初始长度,默认为16,数组满时成倍扩容
+	 * 
+	 * @see Collections#asLifoQueue()
+	 */
+	public static <E> Queue<E> createStack(int initSize) {
+		return Collections.asLifoQueue(new ArrayDeque<E>(initSize));
+	}
+
+	/**
+	 * 支持后进先出的并发栈,用ConcurrentLinkedDeque实现,经过Collections#asLifoQueue()转换顺序
+	 * 
+	 * 另对于BlockingQueue接口, JDK暂无Lifo倒转实现,因此只能直接使用未调转顺序的LinkedBlockingDeque
+	 * 
+	 * @see Collections#asLifoQueue()
+	 */
+	public static <E> Queue<E> createConcurrentStack() {
+		return (Queue<E>) Collections.asLifoQueue(newConcurrentNonBlockingDeque());
+	}
+
+	//////////////// 特殊类型Queue:LRUQueue ///////////
+
+	/**
+	 * LRUQueue, 如果Queue已满,则删除最旧的元素.
+	 * 
+	 * 内部实现是ArrayDeque
+	 */
+	public static <E> EvictingQueue<E> createLRUQueue(int maxSize) {
+		return EvictingQueue.create(maxSize);
+	}
+
+}

+ 178 - 0
src/main/java/com/jeeplus/common/utils/collection/SetUtil.java

@@ -0,0 +1,178 @@
+package com.jeeplus.common.utils.collection;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import com.jeeplus.common.utils.base.annotation.Nullable;
+import com.jeeplus.common.utils.collection.type.ConcurrentHashSet;
+
+import com.google.common.collect.Sets;
+
+/**
+ * 关于Set的工具集合.
+ * 
+ * 1. 各种Set的创建函数, 包括ConcurrentHashSet
+ * 
+ * 2. 集合运算函数(交集,并集等,from guava)
+ */
+public class SetUtil {
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的HashSet.
+	 * 
+	 * @see Sets#newHashSet()
+	 */
+	public static <T> HashSet<T> newHashSet() {
+		return new HashSet<T>();
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的HashSet, 并初始化元素.
+	 * 
+	 * @see Sets#newHashSet(Object...)
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> HashSet<T> newHashSet(T... elements) {
+		return Sets.newHashSet(elements);
+	}
+
+	/**
+	 * HashSet涉及HashMap大小,因此建议在构造时传入需要初始的集合,其他如TreeSet不需要.
+	 * 
+	 * @see Sets#newHashSet(Iterable)
+	 */
+	public static <T> HashSet<T> newHashSet(Iterable<? extends T> elements) {
+		return Sets.newHashSet(elements);
+	}
+
+	/**
+	 * 创建HashSet并设置初始大小,因为HashSet内部是HashMap,会计算LoadFactor后的真实大小.
+	 * 
+	 * @see Sets#newHashSetWithExpectedSize(int)
+	 */
+	public static <T> HashSet<T> newHashSetWithCapacity(int expectedSize) {
+		return Sets.newHashSetWithExpectedSize(expectedSize);
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的TreeSet, 通过实现了Comparable的元素自身进行排序.
+	 * 
+	 * @see Sets#newTreeSet()
+	 */
+	@SuppressWarnings("rawtypes")
+	public static <T extends Comparable> TreeSet<T> newSortedSet() {
+		return new TreeSet<T>();
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的TreeSet, 并设置comparator.
+	 * 
+	 * @see Sets#newTreeSet(Comparator)
+	 */
+	public static <T> TreeSet<T> newSortedSet(@Nullable Comparator<? super T> comparator) {
+		return Sets.newTreeSet(comparator);
+	}
+
+	/**
+	 * 根据等号左边的类型,构造类型正确的ConcurrentHashSet
+	 */
+	public static <T> ConcurrentHashSet<T> newConcurrentHashSet() {
+		return new ConcurrentHashSet<T>();
+	}
+
+	///////////////// from JDK Collections的常用构造函数 ///////////////////
+
+	/**
+	 * 返回一个空的结构特殊的Set,节约空间.
+	 * 
+	 * 注意返回的Set不可写, 写入会抛出UnsupportedOperationException.
+	 * 
+	 * @see Collections#emptySet()
+	 */
+	public static final <T> Set<T> emptySet() {
+		return Collections.emptySet();
+	}
+
+	/**
+	 * 如果set为null,转化为一个安全的空Set.
+	 * 
+	 * 注意返回的Set不可写, 写入会抛出UnsupportedOperationException.
+	 * 
+	 * @see Collections#emptySet()
+	 */
+	public static <T> Set<T> emptySetIfNull(final Set<T> set) {
+		return set == null ? (Set<T>) Collections.EMPTY_SET : set;
+	}
+
+	/**
+	 * 返回只含一个元素但结构特殊的Set,节约空间.
+	 * 
+	 * 注意返回的Set不可写, 写入会抛出UnsupportedOperationException.
+	 * 
+	 * @see Collections#singleton(Object)
+	 */
+	public static final <T> Set<T> singletonSet(T o) {
+		return Collections.singleton(o);
+	}
+
+	/**
+	 * 返回包装后不可修改的Set.
+	 * 
+	 * 如果尝试修改,会抛出UnsupportedOperationException
+	 * 
+	 * @see Collections#unmodifiableSet(Set)
+	 */
+	public static <T> Set<T> unmodifiableSet(Set<? extends T> s) {
+		return Collections.unmodifiableSet(s);
+	}
+
+	/**
+	 * 从Map构造Set的大杀器, 可以用来制造各种Set
+	 * 
+	 * @see Collections#newSetFromMap(Map)
+	 */
+	public static <T> Set<T> newSetFromMap(Map<T, Boolean> map) {
+		return Collections.newSetFromMap(map);
+	}
+
+	//////////////// from guava的集合运算函数/////////////
+	/**
+	 * set1, set2的并集(在set1或set2的对象)的只读view,不复制产生新的Set对象.
+	 * 
+	 * 如果尝试写入该View会抛出UnsupportedOperationException
+	 */
+	public static <E> Set<E> unionView(final Set<? extends E> set1, final Set<? extends E> set2) {
+		return Sets.union(set1, set2);
+	}
+
+	/**
+	 * set1, set2的交集(同时在set1和set2的对象)的只读view,不复制产生新的Set对象.
+	 * 
+	 * 如果尝试写入该View会抛出UnsupportedOperationException
+	 */
+	public static <E> Set<E> intersectionView(final Set<E> set1, final Set<?> set2) {
+		return Sets.intersection(set1, set2);
+	}
+
+	/**
+	 * set1, set2的差集(在set1,不在set2中的对象)的只读view,不复制产生新的Set对象.
+	 * 
+	 * 如果尝试写入该View会抛出UnsupportedOperationException
+	 */
+	public static <E> Set<E> differenceView(final Set<E> set1, final Set<?> set2) {
+		return Sets.difference(set1, set2);
+	}
+
+	/**
+	 * set1, set2的补集(在set1或set2中,但不在交集中的对象,又叫反交集)的只读view,不复制产生新的Set对象.
+	 * 
+	 * 如果尝试写入该View会抛出UnsupportedOperationException
+	 */
+	public static <E> Set<E> disjointView(final Set<? extends E> set1, final Set<? extends E> set2) {
+		return Sets.symmetricDifference(set1, set2);
+	}
+}

+ 90 - 0
src/main/java/com/jeeplus/common/utils/collection/type/ConcurrentHashSet.java

@@ -0,0 +1,90 @@
+package com.jeeplus.common.utils.collection.type;
+
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import com.jeeplus.common.utils.collection.MapUtil;
+
+/**
+ * JDK并没有提供ConcurrenHashSet,考虑到JDK的HashSet也是基于HashMap实现的,因此ConcurrenHashSet也由ConcurrenHashMap完成。
+ * 
+ * 虽然也可以通过Collections.newSetFromMap(new ConcurrentHashMap()),
+ * 
+ * 但声明一个单独的类型,阅读代码时能更清晰的知道set的并发友好性,代码来自JDK的SetFromMap,去除JDK8接口.
+ */
+public class ConcurrentHashSet<E> extends AbstractSet<E> implements Set<E>, java.io.Serializable {
+
+	private static final long serialVersionUID = -8672117787651310382L;
+
+	private final Map<E, Boolean> m;
+
+	private transient Set<E> s; // Its keySet
+
+	public ConcurrentHashSet() {
+		m = MapUtil.newConcurrentHashMap();
+		s = m.keySet();
+	}
+
+	public void clear() {
+		m.clear();
+	}
+
+	public int size() {
+		return m.size();
+	}
+
+	public boolean isEmpty() {
+		return m.isEmpty();
+	}
+
+	public boolean contains(Object o) {
+		return m.containsKey(o);
+	}
+
+	public boolean remove(Object o) {
+		return m.remove(o) != null;
+	}
+
+	public boolean add(E e) {
+		return m.put(e, Boolean.TRUE) == null;
+	}
+
+	public Iterator<E> iterator() {
+		return s.iterator();
+	}
+
+	public Object[] toArray() {
+		return s.toArray();
+	}
+
+	public <T> T[] toArray(T[] a) {
+		return s.toArray(a);
+	}
+
+	public String toString() {
+		return s.toString();
+	}
+
+	public int hashCode() {
+		return s.hashCode();
+	}
+
+	public boolean equals(Object o) {
+		return o == this || s.equals(o);
+	}
+
+	public boolean containsAll(Collection<?> c) {
+		return s.containsAll(c);
+	}
+
+	public boolean removeAll(Collection<?> c) {
+		return s.removeAll(c);
+	}
+
+	public boolean retainAll(Collection<?> c) {
+		return s.retainAll(c);
+	}
+}

+ 87 - 0
src/main/java/com/jeeplus/common/utils/collection/type/Pair.java

@@ -0,0 +1,87 @@
+package com.jeeplus.common.utils.collection.type;
+
+import com.jeeplus.common.utils.base.annotation.Nullable;
+
+/**
+ * 引入一个简简单单的Pair, 用于返回值返回两个元素.
+ * 
+ * from Twitter Common
+ */
+public class Pair<A, B> {
+
+	@Nullable
+	private final A first;
+	@Nullable
+	private final B second;
+
+	/**
+	 * Creates a new pair.
+	 *
+	 * @param first The first value.
+	 * @param second The second value.
+	 */
+	public Pair(@Nullable A first, @Nullable B second) {
+		this.first = first;
+		this.second = second;
+	}
+
+	@Nullable
+	public A getFirst() {
+		return first;
+	}
+
+	@Nullable
+	public B getSecond() {
+		return second;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((first == null) ? 0 : first.hashCode());
+		result = prime * result + ((second == null) ? 0 : second.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (obj == null) {
+			return false;
+		}
+		if (getClass() != obj.getClass()) {
+			return false;
+		}
+		Pair other = (Pair) obj;
+		if (first == null) {
+			if (other.first != null) {
+				return false;
+			}
+		} else if (!first.equals(other.first)) {
+			return false;
+		}
+		if (second == null) {
+			if (other.second != null) {
+				return false;
+			}
+		} else if (!second.equals(other.second)) {
+			return false;
+		}
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return "Pair [first=" + first + ", second=" + second + "]";
+	}
+
+	/**
+	 * 根据等号左边的泛型,自动构造合适的Pair
+	 */
+	public static <A, B> Pair<A, B> of(@Nullable A a, @Nullable B b) {
+		return new Pair<A, B>(a, b);
+	}
+}

+ 185 - 0
src/main/java/com/jeeplus/common/utils/collection/type/SortedArrayList.java

@@ -0,0 +1,185 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+package com.jeeplus.common.utils.collection.type;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+
+/**
+ * 从Jodd整体复制,部分指定了index的操作不支持,如 add(index, element)
+ * 
+ * An extension of <code>ArrayList</code> that insures that all of the items
+ * added are sorted. <b>This breaks original list contract!</b>.
+ * A binary search method is used to provide a quick way to
+ * auto sort this list.Note: Not all methods for adding and
+ * removing elements are supported.
+ */
+public class SortedArrayList<E> extends ArrayList<E> {
+
+	private static final long serialVersionUID = -8301136559614447593L;
+
+	protected final Comparator<? super E> comparator;
+
+	/**
+	 * Constructs a new <code>SortedArrayList</code>.
+	 */
+	public SortedArrayList(Comparator<? super E> c) {
+		comparator = c;
+	}
+
+	/**
+	 * Constructs a new <code>SortedArrayList</code> expecting
+	 * elements are comparable.
+	 */
+	public SortedArrayList() {
+		comparator = null;
+	}
+
+	/**
+	 * Constructs a new <code>SortedArrayList</code> expecting
+	 * elements are comparable.
+	 */
+	public SortedArrayList(Collection<? extends E> c) {
+		comparator = null;
+		addAll(c);
+	}
+
+	/**
+	 * Returns comparator assigned to this collection, if such exist.
+	 */
+	public Comparator getComparator() {
+		return comparator;
+	}
+
+	// ---------------------------------------------------------------- override
+
+	/**
+	 * Adds an Object to sorted list. Object is inserted at correct place, found
+	 * using binary search. If the same item exist, it will be put to the end of
+	 * the range.
+	 * <p>
+	 * This method breaks original list contract since objects are not
+	 * added at the list end, but in sorted manner.
+	 */
+	@Override
+	public boolean add(E o) {
+		int idx = 0;
+		if (!isEmpty()) {
+			idx = findInsertionPoint(o);
+		}
+		super.add(idx, o);
+		return true;
+	}
+
+	/**
+	 * Add all of the elements in the given collection to this list.
+	 */
+	@Override
+	public boolean addAll(Collection<? extends E> c) {
+		Iterator<? extends E> i = c.iterator();
+		boolean changed = false;
+		while (i.hasNext()) {
+			boolean ret = add(i.next());
+			if (!changed) {
+				changed = ret;
+			}
+		}
+		return changed;
+	}
+
+	/**
+	 * Finds the index at which object should be inserted.
+	 */
+	public int findInsertionPoint(E o) {
+		return findInsertionPoint(o, 0, size() - 1);
+	}
+
+	// ---------------------------------------------------------------- unsupported methods
+
+	/**
+	 * @throws UnsupportedOperationException This method not supported.
+	 */
+	@Override
+	@Deprecated
+	public void add(int index, E element) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException This method not supported.
+	 */
+	@Override
+	@Deprecated
+	public E set(int index, E element) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException This method not supported.
+	 */
+	@Override
+	@Deprecated
+	public boolean addAll(int index, Collection<? extends E> c) {
+		throw new UnsupportedOperationException();
+	}
+
+
+	// ---------------------------------------------------------------- sorting
+
+	/**
+	 * Compares two keys using the correct comparison method for this
+	 * collection.
+	 */
+	@SuppressWarnings( {"unchecked"})
+	protected int compare(E k1, E k2) {
+		if (comparator == null) {
+			return ((Comparable) k1).compareTo(k2);
+		}
+		return comparator.compare(k1, k2);
+	}
+
+	/**
+	 * Conducts a binary search to find the index where Object
+	 * should be inserted.
+	 */
+	protected int findInsertionPoint(E o, int low, int high) {
+
+		while (low <= high) {
+			int mid = (low + high) >>> 1;
+			int delta = compare(get(mid), o);
+
+			if (delta > 0) {
+				high = mid - 1;
+			} else {
+				low = mid + 1;
+			}
+		}
+
+		return low;
+	}
+
+}

+ 725 - 0
src/main/java/com/jeeplus/common/utils/collection/type/primitive/IntObjectHashMap.java

@@ -0,0 +1,725 @@
+/*
+ * Copyright 2014 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.jeeplus.common.utils.collection.type.primitive;
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import com.jeeplus.common.utils.number.MathUtil;
+
+/**
+ * 移植Netty 4.1.6的Key为原子类型的集合类, 在数据结构上与HashMap不一样,空间占用与读写性能俱比原来更优.
+ * 
+ * 原子类型集合类有多个实现,选择Netty是因为有在实战中使用.
+ * 
+ * A hash map implementation of {@link IntObjectMap} that uses open addressing for keys. To minimize the memory
+ * footprint, this class uses open addressing rather than chaining. Collisions are resolved using linear probing.
+ * Deletions implement compaction, so cost of remove can approach O(N) for full maps, which makes a small loadFactor
+ * recommended.
+ *
+ * @param <V> The value type stored in the map.
+ */
+public class IntObjectHashMap<V> implements IntObjectMap<V> {
+
+	/** Default initial capacity. Used if not specified in the constructor */
+	public static final int DEFAULT_CAPACITY = 8;
+
+	/** Default load factor. Used if not specified in the constructor */
+	public static final float DEFAULT_LOAD_FACTOR = 0.5f;
+
+	/**
+	 * Placeholder for null values, so we can use the actual null to mean available. (Better than using a placeholder
+	 * for available: less references for GC processing.)
+	 */
+	private static final Object NULL_VALUE = new Object();
+
+	/** The maximum number of elements allowed without allocating more space. */
+	private int maxSize;
+
+	/** The load factor for the map. Used to calculate {@link #maxSize}. */
+	private final float loadFactor;
+
+	private int[] keys;
+	private V[] values;
+	private int size;
+	private int mask;
+
+	private final Set<Integer> keySet = new KeySet();
+	private final Set<Entry<Integer, V>> entrySet = new EntrySet();
+	private final Iterable<PrimitiveEntry<V>> entries = new Iterable<PrimitiveEntry<V>>() {
+		@Override
+		public Iterator<PrimitiveEntry<V>> iterator() {
+			return new PrimitiveIterator();
+		}
+	};
+
+	public IntObjectHashMap() {
+		this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
+	}
+
+	public IntObjectHashMap(int initialCapacity) {
+		this(initialCapacity, DEFAULT_LOAD_FACTOR);
+	}
+
+	public IntObjectHashMap(int initialCapacity, float loadFactor) {
+		if (loadFactor <= 0.0f || loadFactor > 1.0f) {
+			// Cannot exceed 1 because we can never store more than capacity elements;
+			// using a bigger loadFactor would trigger rehashing before the desired load is reached.
+			throw new IllegalArgumentException("loadFactor must be > 0 and <= 1");
+		}
+
+		this.loadFactor = loadFactor;
+
+		// Adjust the initial capacity if necessary.
+		int capacity = MathUtil.nextPowerOfTwo(initialCapacity);
+		mask = capacity - 1;
+
+		// Allocate the arrays.
+		keys = new int[capacity];
+		@SuppressWarnings({ "unchecked" })
+		V[] temp = (V[]) new Object[capacity];
+		values = temp;
+
+		// Initialize the maximum size value.
+		maxSize = calcMaxSize(capacity);
+	}
+
+	private static <T> T toExternal(T value) {
+		return value == NULL_VALUE ? null : value;
+	}
+
+	@SuppressWarnings("unchecked")
+	private static <T> T toInternal(T value) {
+		return value == null ? (T) NULL_VALUE : value;
+	}
+
+	@Override
+	public V get(int key) {
+		int index = indexOf(key);
+		return index == -1 ? null : toExternal(values[index]);
+	}
+
+	@Override
+	public V put(int key, V value) {
+		int startIndex = hashIndex(key);
+		int index = startIndex;
+
+		for (;;) {
+			if (values[index] == null) {
+				// Found empty slot, use it.
+				keys[index] = key;
+				values[index] = toInternal(value);
+				growSize();
+				return null;
+			}
+			if (keys[index] == key) {
+				// Found existing entry with this key, just replace the value.
+				V previousValue = values[index];
+				values[index] = toInternal(value);
+				return toExternal(previousValue);
+			}
+
+			// Conflict, keep probing ...
+			if ((index = probeNext(index)) == startIndex) {
+				// Can only happen if the map was full at MAX_ARRAY_SIZE and couldn't grow.
+				throw new IllegalStateException("Unable to insert");
+			}
+		}
+	}
+
+	@Override
+	public void putAll(Map<? extends Integer, ? extends V> sourceMap) {
+		if (sourceMap instanceof IntObjectHashMap) {
+			// Optimization - iterate through the arrays.
+			@SuppressWarnings("unchecked")
+			IntObjectHashMap<V> source = (IntObjectHashMap<V>) sourceMap;
+			for (int i = 0; i < source.values.length; ++i) {
+				V sourceValue = source.values[i];
+				if (sourceValue != null) {
+					put(source.keys[i], sourceValue);
+				}
+			}
+			return;
+		}
+
+		// Otherwise, just add each entry.
+		for (Entry<? extends Integer, ? extends V> entry : sourceMap.entrySet()) {
+			put(entry.getKey(), entry.getValue());
+		}
+	}
+
+	@Override
+	public V remove(int key) {
+		int index = indexOf(key);
+		if (index == -1) {
+			return null;
+		}
+
+		V prev = values[index];
+		removeAt(index);
+		return toExternal(prev);
+	}
+
+	@Override
+	public int size() {
+		return size;
+	}
+
+	@Override
+	public boolean isEmpty() {
+		return size == 0;
+	}
+
+	@Override
+	public void clear() {
+		Arrays.fill(keys, 0);
+		Arrays.fill(values, null);
+		size = 0;
+	}
+
+	@Override
+	public boolean containsKey(int key) {
+		return indexOf(key) >= 0;
+	}
+
+	@Override
+	public boolean containsValue(Object value) {
+		@SuppressWarnings("unchecked")
+		V v1 = toInternal((V) value);
+		for (V v2 : values) {
+			// The map supports null values; this will be matched as NULL_VALUE.equals(NULL_VALUE).
+			if (v2 != null && v2.equals(v1)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public Iterable<PrimitiveEntry<V>> entries() {
+		return entries;
+	}
+
+	@Override
+	public Collection<V> values() {
+		return new AbstractCollection<V>() {
+			@Override
+			public Iterator<V> iterator() {
+				return new Iterator<V>() {
+					final PrimitiveIterator iter = new PrimitiveIterator();
+
+					@Override
+					public boolean hasNext() {
+						return iter.hasNext();
+					}
+
+					@Override
+					public V next() {
+						return iter.next().value();
+					}
+
+					@Override
+					public void remove() {
+						throw new UnsupportedOperationException();
+					}
+				};
+			}
+
+			@Override
+			public int size() {
+				return size;
+			}
+		};
+	}
+
+	@Override
+	public int hashCode() {
+		// Hashcode is based on all non-zero, valid keys. We have to scan the whole keys
+		// array, which may have different lengths for two maps of same size(), so the
+		// capacity cannot be used as input for hashing but the size can.
+		int hash = size;
+		for (int key : keys) {
+			// 0 can be a valid key or unused slot, but won't impact the hashcode in either case.
+			// This way we can use a cheap loop without conditionals, or hard-to-unroll operations,
+			// or the devastatingly bad memory locality of visiting value objects.
+			// Also, it's important to use a hash function that does not depend on the ordering
+			// of terms, only their values; since the map is an unordered collection and
+			// entries can end up in different positions in different maps that have the same
+			// elements, but with different history of puts/removes, due to conflicts.
+			hash ^= hashCode(key);
+		}
+		return hash;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (!(obj instanceof IntObjectMap)) {
+			return false;
+		}
+		@SuppressWarnings("rawtypes")
+		IntObjectMap other = (IntObjectMap) obj;
+		if (size != other.size()) {
+			return false;
+		}
+		for (int i = 0; i < values.length; ++i) {
+			V value = values[i];
+			if (value != null) {
+				int key = keys[i];
+				Object otherValue = other.get(key);
+				if (value == NULL_VALUE) {
+					if (otherValue != null) {
+						return false;
+					}
+				} else if (!value.equals(otherValue)) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+	@Override
+	public boolean containsKey(Object key) {
+		return containsKey(objectToKey(key));
+	}
+
+	@Override
+	public V get(Object key) {
+		return get(objectToKey(key));
+	}
+
+	@Override
+	public V put(Integer key, V value) {
+		return put(objectToKey(key), value);
+	}
+
+	@Override
+	public V remove(Object key) {
+		return remove(objectToKey(key));
+	}
+
+	@Override
+	public Set<Integer> keySet() {
+		return keySet;
+	}
+
+	@Override
+	public Set<Entry<Integer, V>> entrySet() {
+		return entrySet;
+	}
+
+	private int objectToKey(Object key) {
+		return ((Integer) key).intValue();
+	}
+
+	/**
+	 * Locates the index for the given key. This method probes using double hashing.
+	 *
+	 * @param key the key for an entry in the map.
+	 * @return the index where the key was found, or {@code -1} if no entry is found for that key.
+	 */
+	private int indexOf(int key) {
+		int startIndex = hashIndex(key);
+		int index = startIndex;
+
+		for (;;) {
+			if (values[index] == null) {
+				// It's available, so no chance that this value exists anywhere in the map.
+				return -1;
+			}
+			if (key == keys[index]) {
+				return index;
+			}
+
+			// Conflict, keep probing ...
+			if ((index = probeNext(index)) == startIndex) {
+				return -1;
+			}
+		}
+	}
+
+	/**
+	 * Returns the hashed index for the given key.
+	 */
+	private int hashIndex(int key) {
+		// The array lengths are always a power of two, so we can use a bitmask to stay inside the array bounds.
+		return hashCode(key) & mask;
+	}
+
+	/**
+	 * Returns the hash code for the key.
+	 */
+	private static int hashCode(int key) {
+		return key;
+	}
+
+	/**
+	 * Get the next sequential index after {@code index} and wraps if necessary.
+	 */
+	private int probeNext(int index) {
+		// The array lengths are always a power of two, so we can use a bitmask to stay inside the array bounds.
+		return (index + 1) & mask;
+	}
+
+	/**
+	 * Grows the map size after an insertion. If necessary, performs a rehash of the map.
+	 */
+	private void growSize() {
+		size++;
+
+		if (size > maxSize) {
+			if (keys.length == Integer.MAX_VALUE) {
+				throw new IllegalStateException("Max capacity reached at size=" + size);
+			}
+
+			// Double the capacity.
+			rehash(keys.length << 1);
+		}
+	}
+
+	/**
+	 * Removes entry at the given index position. Also performs opportunistic, incremental rehashing if necessary to not
+	 * break conflict chains.
+	 *
+	 * @param index the index position of the element to remove.
+	 * @return {@code true} if the next item was moved back. {@code false} otherwise.
+	 */
+	private boolean removeAt(final int index) {
+		--size;
+		// Clearing the key is not strictly necessary (for GC like in a regular collection),
+		// but recommended for security. The memory location is still fresh in the cache anyway.
+		keys[index] = 0;
+		values[index] = null;
+
+		// In the interval from index to the next available entry, the arrays may have entries
+		// that are displaced from their base position due to prior conflicts. Iterate these
+		// entries and move them back if possible, optimizing future lookups.
+		// Knuth Section 6.4 Algorithm R, also used by the JDK's IdentityHashMap.
+
+		boolean movedBack = false;
+		int nextFree = index;
+		for (int i = probeNext(index); values[i] != null; i = probeNext(i)) {
+			int bucket = hashIndex(keys[i]);
+			if (i < bucket && (bucket <= nextFree || nextFree <= i) || bucket <= nextFree && nextFree <= i) {
+				// Move the displaced entry "back" to the first available position.
+				keys[nextFree] = keys[i];
+				values[nextFree] = values[i];
+				movedBack = true;
+				// Put the first entry after the displaced entry
+				keys[i] = 0;
+				values[i] = null;
+				nextFree = i;
+			}
+		}
+		return movedBack;
+	}
+
+	/**
+	 * Calculates the maximum size allowed before rehashing.
+	 */
+	private int calcMaxSize(int capacity) {
+		// Clip the upper bound so that there will always be at least one available slot.
+		int upperBound = capacity - 1;
+		return Math.min(upperBound, (int) (capacity * loadFactor));
+	}
+
+	/**
+	 * Rehashes the map for the given capacity.
+	 *
+	 * @param newCapacity the new capacity for the map.
+	 */
+	private void rehash(int newCapacity) {
+		int[] oldKeys = keys;
+		V[] oldVals = values;
+
+		keys = new int[newCapacity];
+		@SuppressWarnings({ "unchecked" })
+		V[] temp = (V[]) new Object[newCapacity];
+		values = temp;
+
+		maxSize = calcMaxSize(newCapacity);
+		mask = newCapacity - 1;
+
+		// Insert to the new arrays.
+		for (int i = 0; i < oldVals.length; ++i) {
+			V oldVal = oldVals[i];
+			if (oldVal != null) {
+				// Inlined put(), but much simpler: we don't need to worry about
+				// duplicated keys, growing/rehashing, or failing to insert.
+				int oldKey = oldKeys[i];
+				int index = hashIndex(oldKey);
+
+				for (;;) {
+					if (values[index] == null) {
+						keys[index] = oldKey;
+						values[index] = oldVal;
+						break;
+					}
+
+					// Conflict, keep probing. Can wrap around, but never reaches startIndex again.
+					index = probeNext(index);
+				}
+			}
+		}
+	}
+
+	@Override
+	public String toString() {
+		if (isEmpty()) {
+			return "{}";
+		}
+		StringBuilder sb = new StringBuilder(4 * size);
+		sb.append('{');
+		boolean first = true;
+		for (int i = 0; i < values.length; ++i) {
+			V value = values[i];
+			if (value != null) {
+				if (!first) {
+					sb.append(", ");
+				}
+				sb.append(keyToString(keys[i])).append('=').append(value == this ? "(this Map)" : toExternal(value));
+				first = false;
+			}
+		}
+		return sb.append('}').toString();
+	}
+
+	/**
+	 * Helper method called by {@link #toString()} in order to convert a single map key into a string. This is protected
+	 * to allow subclasses to override the appearance of a given key.
+	 */
+	protected String keyToString(int key) {
+		return Integer.toString(key);
+	}
+
+	/**
+	 * Set implementation for iterating over the entries of the map.
+	 */
+	private final class EntrySet extends AbstractSet<Entry<Integer, V>> {
+		@Override
+		public Iterator<Entry<Integer, V>> iterator() {
+			return new MapIterator();
+		}
+
+		@Override
+		public int size() {
+			return IntObjectHashMap.this.size();
+		}
+	}
+
+	/**
+	 * Set implementation for iterating over the keys.
+	 */
+	private final class KeySet extends AbstractSet<Integer> {
+		@Override
+		public int size() {
+			return IntObjectHashMap.this.size();
+		}
+
+		@Override
+		public boolean contains(Object o) {
+			return IntObjectHashMap.this.containsKey(o);
+		}
+
+		@Override
+		public boolean remove(Object o) {
+			return IntObjectHashMap.this.remove(o) != null;
+		}
+
+		@Override
+		public boolean retainAll(Collection<?> retainedKeys) {
+			boolean changed = false;
+			for (Iterator<PrimitiveEntry<V>> iter = entries().iterator(); iter.hasNext();) {
+				PrimitiveEntry<V> entry = iter.next();
+				if (!retainedKeys.contains(entry.key())) {
+					changed = true;
+					iter.remove();
+				}
+			}
+			return changed;
+		}
+
+		@Override
+		public void clear() {
+			IntObjectHashMap.this.clear();
+		}
+
+		@Override
+		public Iterator<Integer> iterator() {
+			return new Iterator<Integer>() {
+				private final Iterator<Entry<Integer, V>> iter = entrySet.iterator();
+
+				@Override
+				public boolean hasNext() {
+					return iter.hasNext();
+				}
+
+				@Override
+				public Integer next() {
+					return iter.next().getKey();
+				}
+
+				@Override
+				public void remove() {
+					iter.remove();
+				}
+			};
+		}
+	}
+
+	/**
+	 * Iterator over primitive entries. Entry key/values are overwritten by each call to {@link #next()}.
+	 */
+	private final class PrimitiveIterator implements Iterator<PrimitiveEntry<V>>, PrimitiveEntry<V> {
+		private int prevIndex = -1;
+		private int nextIndex = -1;
+		private int entryIndex = -1;
+
+		private void scanNext() {
+			for (;;) {
+				if (++nextIndex == values.length || values[nextIndex] != null) {
+					break;
+				}
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			if (nextIndex == -1) {
+				scanNext();
+			}
+			return nextIndex < keys.length;
+		}
+
+		@Override
+		public PrimitiveEntry<V> next() {
+			if (!hasNext()) {
+				throw new NoSuchElementException();
+			}
+
+			prevIndex = nextIndex;
+			scanNext();
+
+			// Always return the same Entry object, just change its index each time.
+			entryIndex = prevIndex;
+			return this;
+		}
+
+		@Override
+		public void remove() {
+			if (prevIndex < 0) {
+				throw new IllegalStateException("next must be called before each remove.");
+			}
+			if (removeAt(prevIndex)) {
+				// removeAt may move elements "back" in the array if they have been displaced because their spot in the
+				// array was occupied when they were inserted. If this occurs then the nextIndex is now invalid and
+				// should instead point to the prevIndex which now holds an element which was "moved back".
+				nextIndex = prevIndex;
+			}
+			prevIndex = -1;
+		}
+
+		// Entry implementation. Since this implementation uses a single Entry, we coalesce that
+		// into the Iterator object (potentially making loop optimization much easier).
+
+		@Override
+		public int key() {
+			return keys[entryIndex];
+		}
+
+		@Override
+		public V value() {
+			return toExternal(values[entryIndex]);
+		}
+
+		@Override
+		public void setValue(V value) {
+			values[entryIndex] = toInternal(value);
+		}
+	}
+
+	/**
+	 * Iterator used by the {@link Map} interface.
+	 */
+	private final class MapIterator implements Iterator<Entry<Integer, V>> {
+		private final PrimitiveIterator iter = new PrimitiveIterator();
+
+		@Override
+		public boolean hasNext() {
+			return iter.hasNext();
+		}
+
+		@Override
+		public Entry<Integer, V> next() {
+			if (!hasNext()) {
+				throw new NoSuchElementException();
+			}
+
+			iter.next();
+
+			return new MapEntry(iter.entryIndex);
+		}
+
+		@Override
+		public void remove() {
+			iter.remove();
+		}
+	}
+
+	/**
+	 * A single entry in the map.
+	 */
+	final class MapEntry implements Entry<Integer, V> {
+		private final int entryIndex;
+
+		MapEntry(int entryIndex) {
+			this.entryIndex = entryIndex;
+		}
+
+		@Override
+		public Integer getKey() {
+			verifyExists();
+			return keys[entryIndex];
+		}
+
+		@Override
+		public V getValue() {
+			verifyExists();
+			return toExternal(values[entryIndex]);
+		}
+
+		@Override
+		public V setValue(V value) {
+			verifyExists();
+			V prevValue = toExternal(values[entryIndex]);
+			values[entryIndex] = toInternal(value);
+			return prevValue;
+		}
+
+		private void verifyExists() {
+			if (values[entryIndex] == null) {
+				throw new IllegalStateException("The map entry has been removed");
+			}
+		}
+	}
+}

+ 84 - 0
src/main/java/com/jeeplus/common/utils/collection/type/primitive/IntObjectMap.java

@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.jeeplus.common.utils.collection.type.primitive;
+import java.util.Map;
+
+
+/**
+ * Interface for a primitive map that uses {@code int}s as keys.
+ *
+ * @param <V> the value type stored in the map.
+ */
+public interface IntObjectMap<V> extends Map<Integer, V> {
+
+    /**
+     * A primitive entry in the map, provided by the iterator from {@link #entries()}
+     *
+     * @param <V> the value type stored in the map.
+     */
+    interface PrimitiveEntry<V> {
+        /**
+         * Gets the key for this entry.
+         */
+        int key();
+
+        /**
+         * Gets the value for this entry.
+         */
+        V value();
+
+        /**
+         * Sets the value for this entry.
+         */
+        void setValue(V value);
+    }
+
+    /**
+     * Gets the value in the map with the specified key.
+     *
+     * @param key the key whose associated value is to be returned.
+     * @return the value or {@code null} if the key was not found in the map.
+     */
+    V get(int key);
+
+    /**
+     * Puts the given entry into the map.
+     *
+     * @param key the key of the entry.
+     * @param value the value of the entry.
+     * @return the previous value for this key or {@code null} if there was no previous mapping.
+     */
+    V put(int key, V value);
+
+    /**
+     * Removes the entry with the specified key.
+     *
+     * @param key the key for the entry to be removed from this map.
+     * @return the previous value for the key, or {@code null} if there was no mapping.
+     */
+    V remove(int key);
+
+    /**
+     * Gets an iterable to traverse over the primitive entries contained in this map. As an optimization,
+     * the {@link PrimitiveEntry}s returned by the {@link Iterator} may change as the {@link Iterator}
+     * progresses. The caller should not rely on {@link PrimitiveEntry} key/value stability.
+     */
+    Iterable<PrimitiveEntry<V>> entries();
+
+    /**
+     * Indicates whether or not this map contains a value for the specified key.
+     */
+    boolean containsKey(int key);
+}

+ 728 - 0
src/main/java/com/jeeplus/common/utils/collection/type/primitive/LongObjectHashMap.java

@@ -0,0 +1,728 @@
+/*
+ * Copyright 2014 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.jeeplus.common.utils.collection.type.primitive;
+
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import com.jeeplus.common.utils.number.MathUtil;
+
+/**
+ * 移植Netty 4.1.6的Key为原子类型的集合类, 在数据结构上与HashMap不一样,空间占用与读写性能俱比原来更优.
+ * 
+ * 原子类型集合类有多个实现,选择Netty是因为有在实战中使用.
+ * 
+ * A hash map implementation of {@link LongObjectMap} that uses open addressing for keys.
+ * To minimize the memory footprint, this class uses open addressing rather than chaining.
+ * Collisions are resolved using linear probing. Deletions implement compaction, so cost of
+ * remove can approach O(N) for full maps, which makes a small loadFactor recommended.
+ *
+ * @param <V> The value type stored in the map.
+ */
+public class LongObjectHashMap<V> implements LongObjectMap<V> {
+
+    /** Default initial capacity. Used if not specified in the constructor */
+    public static final int DEFAULT_CAPACITY = 8;
+
+    /** Default load factor. Used if not specified in the constructor */
+    public static final float DEFAULT_LOAD_FACTOR = 0.5f;
+
+    /**
+     * Placeholder for null values, so we can use the actual null to mean available.
+     * (Better than using a placeholder for available: less references for GC processing.)
+     */
+    private static final Object NULL_VALUE = new Object();
+
+    /** The maximum number of elements allowed without allocating more space. */
+    private int maxSize;
+
+    /** The load factor for the map. Used to calculate {@link #maxSize}. */
+    private final float loadFactor;
+
+    private long[] keys;
+    private V[] values;
+    private int size;
+    private int mask;
+
+    private final Set<Long> keySet = new KeySet();
+    private final Set<Entry<Long, V>> entrySet = new EntrySet();
+    private final Iterable<PrimitiveEntry<V>> entries = new Iterable<PrimitiveEntry<V>>() {
+        @Override
+        public Iterator<PrimitiveEntry<V>> iterator() {
+            return new PrimitiveIterator();
+        }
+    };
+
+    public LongObjectHashMap() {
+        this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
+    }
+
+    public LongObjectHashMap(int initialCapacity) {
+        this(initialCapacity, DEFAULT_LOAD_FACTOR);
+    }
+
+    public LongObjectHashMap(int initialCapacity, float loadFactor) {
+        if (loadFactor <= 0.0f || loadFactor > 1.0f) {
+            // Cannot exceed 1 because we can never store more than capacity elements;
+            // using a bigger loadFactor would trigger rehashing before the desired load is reached.
+            throw new IllegalArgumentException("loadFactor must be > 0 and <= 1");
+        }
+
+        this.loadFactor = loadFactor;
+
+        // Adjust the initial capacity if necessary.
+        int capacity = MathUtil.nextPowerOfTwo(initialCapacity);
+        mask = capacity - 1;
+
+        // Allocate the arrays.
+        keys = new long[capacity];
+        @SuppressWarnings({ "unchecked" })
+        V[] temp = (V[]) new Object[capacity];
+        values = temp;
+
+        // Initialize the maximum size value.
+        maxSize = calcMaxSize(capacity);
+    }
+
+    private static <T> T toExternal(T value) {
+        return value == NULL_VALUE ? null : value;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> T toInternal(T value) {
+        return value == null ? (T) NULL_VALUE : value;
+    }
+
+    @Override
+    public V get(long key) {
+        int index = indexOf(key);
+        return index == -1 ? null : toExternal(values[index]);
+    }
+
+    @Override
+    public V put(long key, V value) {
+        int startIndex = hashIndex(key);
+        int index = startIndex;
+
+        for (;;) {
+            if (values[index] == null) {
+                // Found empty slot, use it.
+                keys[index] = key;
+                values[index] = toInternal(value);
+                growSize();
+                return null;
+            }
+            if (keys[index] == key) {
+                // Found existing entry with this key, just replace the value.
+                V previousValue = values[index];
+                values[index] = toInternal(value);
+                return toExternal(previousValue);
+            }
+
+            // Conflict, keep probing ...
+            if ((index = probeNext(index)) == startIndex) {
+                // Can only happen if the map was full at MAX_ARRAY_SIZE and couldn't grow.
+                throw new IllegalStateException("Unable to insert");
+            }
+        }
+    }
+
+    @Override
+    public void putAll(Map<? extends Long, ? extends V> sourceMap) {
+        if (sourceMap instanceof LongObjectHashMap) {
+            // Optimization - iterate through the arrays.
+            @SuppressWarnings("unchecked")
+            LongObjectHashMap<V> source = (LongObjectHashMap<V>) sourceMap;
+            for (int i = 0; i < source.values.length; ++i) {
+                V sourceValue = source.values[i];
+                if (sourceValue != null) {
+                    put(source.keys[i], sourceValue);
+                }
+            }
+            return;
+        }
+
+        // Otherwise, just add each entry.
+        for (Entry<? extends Long, ? extends V> entry : sourceMap.entrySet()) {
+            put(entry.getKey(), entry.getValue());
+        }
+    }
+
+    @Override
+    public V remove(long key) {
+        int index = indexOf(key);
+        if (index == -1) {
+            return null;
+        }
+
+        V prev = values[index];
+        removeAt(index);
+        return toExternal(prev);
+    }
+
+    @Override
+    public int size() {
+        return size;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    @Override
+    public void clear() {
+        Arrays.fill(keys, 0);
+        Arrays.fill(values, null);
+        size = 0;
+    }
+
+    @Override
+    public boolean containsKey(long key) {
+        return indexOf(key) >= 0;
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        @SuppressWarnings("unchecked")
+        V v1 = toInternal((V) value);
+        for (V v2 : values) {
+            // The map supports null values; this will be matched as NULL_VALUE.equals(NULL_VALUE).
+            if (v2 != null && v2.equals(v1)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public Iterable<PrimitiveEntry<V>> entries() {
+        return entries;
+    }
+
+    @Override
+    public Collection<V> values() {
+        return new AbstractCollection<V>() {
+            @Override
+            public Iterator<V> iterator() {
+                return new Iterator<V>() {
+                    final PrimitiveIterator iter = new PrimitiveIterator();
+
+                    @Override
+                    public boolean hasNext() {
+                        return iter.hasNext();
+                    }
+
+                    @Override
+                    public V next() {
+                        return iter.next().value();
+                    }
+
+                    @Override
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+
+            @Override
+            public int size() {
+                return size;
+            }
+        };
+    }
+
+    @Override
+    public int hashCode() {
+        // Hashcode is based on all non-zero, valid keys. We have to scan the whole keys
+        // array, which may have different lengths for two maps of same size(), so the
+        // capacity cannot be used as input for hashing but the size can.
+        int hash = size;
+        for (long key : keys) {
+            // 0 can be a valid key or unused slot, but won't impact the hashcode in either case.
+            // This way we can use a cheap loop without conditionals, or hard-to-unroll operations,
+            // or the devastatingly bad memory locality of visiting value objects.
+            // Also, it's important to use a hash function that does not depend on the ordering
+            // of terms, only their values; since the map is an unordered collection and
+            // entries can end up in different positions in different maps that have the same
+            // elements, but with different history of puts/removes, due to conflicts.
+            hash ^= hashCode(key);
+        }
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof LongObjectMap)) {
+            return false;
+        }
+        @SuppressWarnings("rawtypes")
+        LongObjectMap other = (LongObjectMap) obj;
+        if (size != other.size()) {
+            return false;
+        }
+        for (int i = 0; i < values.length; ++i) {
+            V value = values[i];
+            if (value != null) {
+                long key = keys[i];
+                Object otherValue = other.get(key);
+                if (value == NULL_VALUE) {
+                    if (otherValue != null) {
+                        return false;
+                    }
+                } else if (!value.equals(otherValue)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return containsKey(objectToKey(key));
+    }
+
+    @Override
+    public V get(Object key) {
+        return get(objectToKey(key));
+    }
+
+    @Override
+    public V put(Long key, V value) {
+        return put(objectToKey(key), value);
+    }
+
+    @Override
+    public V remove(Object key) {
+        return remove(objectToKey(key));
+    }
+
+    @Override
+    public Set<Long> keySet() {
+        return keySet;
+    }
+
+    @Override
+    public Set<Entry<Long, V>> entrySet() {
+        return entrySet;
+    }
+
+    private long objectToKey(Object key) {
+        return ((Long) key).longValue();
+    }
+
+    /**
+     * Locates the index for the given key. This method probes using double hashing.
+     *
+     * @param key the key for an entry in the map.
+     * @return the index where the key was found, or {@code -1} if no entry is found for that key.
+     */
+    private int indexOf(long key) {
+        int startIndex = hashIndex(key);
+        int index = startIndex;
+
+        for (;;) {
+            if (values[index] == null) {
+                // It's available, so no chance that this value exists anywhere in the map.
+                return -1;
+            }
+            if (key == keys[index]) {
+                return index;
+            }
+
+            // Conflict, keep probing ...
+            if ((index = probeNext(index)) == startIndex) {
+                return -1;
+            }
+        }
+    }
+
+    /**
+     * Returns the hashed index for the given key.
+     */
+    private int hashIndex(long key) {
+        // The array lengths are always a power of two, so we can use a bitmask to stay inside the array bounds.
+        return hashCode(key) & mask;
+    }
+
+    /**
+     * Returns the hash code for the key.
+     */
+    private static int hashCode(long key) {
+       return (int) (key ^ (key >>> 32));
+    }
+
+    /**
+     * Get the next sequential index after {@code index} and wraps if necessary.
+     */
+    private int probeNext(int index) {
+        // The array lengths are always a power of two, so we can use a bitmask to stay inside the array bounds.
+        return (index + 1) & mask;
+    }
+
+    /**
+     * Grows the map size after an insertion. If necessary, performs a rehash of the map.
+     */
+    private void growSize() {
+        size++;
+
+        if (size > maxSize) {
+            if(keys.length == Integer.MAX_VALUE) {
+                throw new IllegalStateException("Max capacity reached at size=" + size);
+            }
+
+            // Double the capacity.
+            rehash(keys.length << 1);
+        }
+    }
+
+    /**
+     * Removes entry at the given index position. Also performs opportunistic, incremental rehashing
+     * if necessary to not break conflict chains.
+     *
+     * @param index the index position of the element to remove.
+     * @return {@code true} if the next item was moved back. {@code false} otherwise.
+     */
+    private boolean removeAt(final int index) {
+        --size;
+        // Clearing the key is not strictly necessary (for GC like in a regular collection),
+        // but recommended for security. The memory location is still fresh in the cache anyway.
+        keys[index] = 0;
+        values[index] = null;
+
+        // In the interval from index to the next available entry, the arrays may have entries
+        // that are displaced from their base position due to prior conflicts. Iterate these
+        // entries and move them back if possible, optimizing future lookups.
+        // Knuth Section 6.4 Algorithm R, also used by the JDK's IdentityHashMap.
+
+        boolean movedBack = false;
+        int nextFree = index;
+        for (int i = probeNext(index); values[i] != null; i = probeNext(i)) {
+            int bucket = hashIndex(keys[i]);
+            if (i < bucket && (bucket <= nextFree || nextFree <= i) ||
+                bucket <= nextFree && nextFree <= i) {
+                // Move the displaced entry "back" to the first available position.
+                keys[nextFree] = keys[i];
+                values[nextFree] = values[i];
+                movedBack = true;
+                // Put the first entry after the displaced entry
+                keys[i] = 0;
+                values[i] = null;
+                nextFree = i;
+            }
+        }
+        return movedBack;
+    }
+
+    /**
+     * Calculates the maximum size allowed before rehashing.
+     */
+    private int calcMaxSize(int capacity) {
+        // Clip the upper bound so that there will always be at least one available slot.
+        int upperBound = capacity - 1;
+        return Math.min(upperBound, (int) (capacity * loadFactor));
+    }
+
+    /**
+     * Rehashes the map for the given capacity.
+     *
+     * @param newCapacity the new capacity for the map.
+     */
+    private void rehash(int newCapacity) {
+        long[] oldKeys = keys;
+        V[] oldVals = values;
+
+        keys = new long[newCapacity];
+        @SuppressWarnings({ "unchecked"})
+        V[] temp = (V[]) new Object[newCapacity];
+        values = temp;
+
+        maxSize = calcMaxSize(newCapacity);
+        mask = newCapacity - 1;
+
+        // Insert to the new arrays.
+        for (int i = 0; i < oldVals.length; ++i) {
+            V oldVal = oldVals[i];
+            if (oldVal != null) {
+                // Inlined put(), but much simpler: we don't need to worry about
+                // duplicated keys, growing/rehashing, or failing to insert.
+                long oldKey = oldKeys[i];
+                int index = hashIndex(oldKey);
+
+                for (;;) {
+                    if (values[index] == null) {
+                        keys[index] = oldKey;
+                        values[index] = oldVal;
+                        break;
+                    }
+
+                    // Conflict, keep probing. Can wrap around, but never reaches startIndex again.
+                    index = probeNext(index);
+                }
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        if (isEmpty()) {
+            return "{}";
+        }
+        StringBuilder sb = new StringBuilder(4 * size);
+        sb.append('{');
+        boolean first = true;
+        for (int i = 0; i < values.length; ++i) {
+            V value = values[i];
+            if (value != null) {
+                if (!first) {
+                    sb.append(", ");
+                }
+                sb.append(keyToString(keys[i])).append('=').append(value == this ? "(this Map)" :
+                    toExternal(value));
+                first = false;
+            }
+        }
+        return sb.append('}').toString();
+    }
+
+    /**
+     * Helper method called by {@link #toString()} in order to convert a single map key into a string.
+     * This is protected to allow subclasses to override the appearance of a given key.
+     */
+    protected String keyToString(long key) {
+        return Long.toString(key);
+    }
+
+    /**
+     * Set implementation for iterating over the entries of the map.
+     */
+    private final class EntrySet extends AbstractSet<Entry<Long, V>> {
+        @Override
+        public Iterator<Entry<Long, V>> iterator() {
+            return new MapIterator();
+        }
+
+        @Override
+        public int size() {
+            return LongObjectHashMap.this.size();
+        }
+    }
+
+    /**
+     * Set implementation for iterating over the keys.
+     */
+    private final class KeySet extends AbstractSet<Long> {
+        @Override
+        public int size() {
+            return LongObjectHashMap.this.size();
+        }
+
+        @Override
+        public boolean contains(Object o) {
+            return LongObjectHashMap.this.containsKey(o);
+        }
+
+        @Override
+        public boolean remove(Object o) {
+            return LongObjectHashMap.this.remove(o) != null;
+        }
+
+        @Override
+        public boolean retainAll(Collection<?> retainedKeys) {
+            boolean changed = false;
+            for(Iterator<PrimitiveEntry<V>> iter = entries().iterator(); iter.hasNext(); ) {
+                PrimitiveEntry<V> entry = iter.next();
+                if (!retainedKeys.contains(entry.key())) {
+                    changed = true;
+                    iter.remove();
+                }
+            }
+            return changed;
+        }
+
+        @Override
+        public void clear() {
+            LongObjectHashMap.this.clear();
+        }
+
+        @Override
+        public Iterator<Long> iterator() {
+            return new Iterator<Long>() {
+                private final Iterator<Entry<Long, V>> iter = entrySet.iterator();
+
+                @Override
+                public boolean hasNext() {
+                    return iter.hasNext();
+                }
+
+                @Override
+                public Long next() {
+                    return iter.next().getKey();
+                }
+
+                @Override
+                public void remove() {
+                    iter.remove();
+                }
+            };
+        }
+    }
+
+    /**
+     * Iterator over primitive entries. Entry key/values are overwritten by each call to {@link #next()}.
+     */
+    private final class PrimitiveIterator implements Iterator<PrimitiveEntry<V>>, PrimitiveEntry<V> {
+        private int prevIndex = -1;
+        private int nextIndex = -1;
+        private int entryIndex = -1;
+
+        private void scanNext() {
+            for (;;) {
+                if (++nextIndex == values.length || values[nextIndex] != null) {
+                    break;
+                }
+            }
+        }
+
+        @Override
+        public boolean hasNext() {
+            if (nextIndex == -1) {
+                scanNext();
+            }
+            return nextIndex < keys.length;
+        }
+
+        @Override
+        public PrimitiveEntry<V> next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+
+            prevIndex = nextIndex;
+            scanNext();
+
+            // Always return the same Entry object, just change its index each time.
+            entryIndex = prevIndex;
+            return this;
+        }
+
+        @Override
+        public void remove() {
+            if (prevIndex < 0) {
+                throw new IllegalStateException("next must be called before each remove.");
+            }
+            if (removeAt(prevIndex)) {
+                // removeAt may move elements "back" in the array if they have been displaced because their spot in the
+                // array was occupied when they were inserted. If this occurs then the nextIndex is now invalid and
+                // should instead point to the prevIndex which now holds an element which was "moved back".
+                nextIndex = prevIndex;
+            }
+            prevIndex = -1;
+        }
+
+        // Entry implementation. Since this implementation uses a single Entry, we coalesce that
+        // into the Iterator object (potentially making loop optimization much easier).
+
+        @Override
+        public long key() {
+            return keys[entryIndex];
+        }
+
+        @Override
+        public V value() {
+            return toExternal(values[entryIndex]);
+        }
+
+        @Override
+        public void setValue(V value) {
+            values[entryIndex] = toInternal(value);
+        }
+    }
+
+    /**
+     * Iterator used by the {@link Map} interface.
+     */
+    private final class MapIterator implements Iterator<Entry<Long, V>> {
+        private final PrimitiveIterator iter = new PrimitiveIterator();
+
+        @Override
+        public boolean hasNext() {
+            return iter.hasNext();
+        }
+
+        @Override
+        public Entry<Long, V> next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+
+            iter.next();
+
+            return new MapEntry(iter.entryIndex);
+        }
+
+        @Override
+        public void remove() {
+            iter.remove();
+        }
+    }
+
+    /**
+     * A single entry in the map.
+     */
+    final class MapEntry implements Entry<Long, V> {
+        private final int entryIndex;
+
+        MapEntry(int entryIndex) {
+            this.entryIndex = entryIndex;
+        }
+
+        @Override
+        public Long getKey() {
+            verifyExists();
+            return keys[entryIndex];
+        }
+
+        @Override
+        public V getValue() {
+            verifyExists();
+            return toExternal(values[entryIndex]);
+        }
+
+        @Override
+        public V setValue(V value) {
+            verifyExists();
+            V prevValue = toExternal(values[entryIndex]);
+            values[entryIndex] = toInternal(value);
+            return prevValue;
+        }
+
+        private void verifyExists() {
+            if (values[entryIndex] == null) {
+                throw new IllegalStateException("The map entry has been removed");
+            }
+        }
+    }
+}

+ 84 - 0
src/main/java/com/jeeplus/common/utils/collection/type/primitive/LongObjectMap.java

@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.jeeplus.common.utils.collection.type.primitive;
+
+import java.util.Map;
+
+/**
+ * Interface for a primitive map that uses {@code long}s as keys.
+ *
+ * @param <V> the value type stored in the map.
+ */
+public interface LongObjectMap<V> extends Map<Long, V> {
+
+    /**
+     * A primitive entry in the map, provided by the iterator from {@link #entries()}
+     *
+     * @param <V> the value type stored in the map.
+     */
+    interface PrimitiveEntry<V> {
+        /**
+         * Gets the key for this entry.
+         */
+        long key();
+
+        /**
+         * Gets the value for this entry.
+         */
+        V value();
+
+        /**
+         * Sets the value for this entry.
+         */
+        void setValue(V value);
+    }
+
+    /**
+     * Gets the value in the map with the specified key.
+     *
+     * @param key the key whose associated value is to be returned.
+     * @return the value or {@code null} if the key was not found in the map.
+     */
+    V get(long key);
+
+    /**
+     * Puts the given entry into the map.
+     *
+     * @param key the key of the entry.
+     * @param value the value of the entry.
+     * @return the previous value for this key or {@code null} if there was no previous mapping.
+     */
+    V put(long key, V value);
+
+    /**
+     * Removes the entry with the specified key.
+     *
+     * @param key the key for the entry to be removed from this map.
+     * @return the previous value for the key, or {@code null} if there was no mapping.
+     */
+    V remove(long key);
+
+    /**
+     * Gets an iterable to traverse over the primitive entries contained in this map. As an optimization,
+     * the {@link PrimitiveEntry}s returned by the {@link Iterator} may change as the {@link Iterator}
+     * progresses. The caller should not rely on {@link PrimitiveEntry} key/value stability.
+     */
+    Iterable<PrimitiveEntry<V>> entries();
+
+    /**
+     * Indicates whether or not this map contains a value for the specified key.
+     */
+    boolean containsKey(long key);
+}

+ 139 - 0
src/main/java/com/jeeplus/common/utils/concurrent/BasicFuture.java

@@ -0,0 +1,139 @@
+/*
+ * ==================================================================== Licensed to the Apache Software Foundation (ASF)
+ * under one or more contributor license agreements. See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many individuals on behalf of the Apache Software
+ * Foundation. For more information on the Apache Software Foundation, please see <http://www.apache.org/>.
+ *
+ */
+package com.jeeplus.common.utils.concurrent;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.lang3.Validate;
+
+/**
+ * 从Apache HttpClient 4.2 移植,一个Future实现类的基本框架.
+ */
+public abstract class BasicFuture<T> implements Future<T> {
+
+	private volatile boolean completed;
+	private volatile boolean cancelled;
+	private volatile T result;
+	private volatile Exception ex;
+
+	@Override
+	public boolean isCancelled() {
+		return this.cancelled;
+	}
+
+	@Override
+	public boolean isDone() {
+		return this.completed;
+	}
+
+	@Override
+	public synchronized T get() throws InterruptedException, ExecutionException {
+		while (!this.completed) {
+			wait();
+		}
+		return getResult();
+	}
+
+	@Override
+	public synchronized T get(final long timeout, final TimeUnit unit)
+			throws InterruptedException, ExecutionException, TimeoutException {
+		Validate.notNull(unit, "Time unit");
+		final long msecs = unit.toMillis(timeout);
+		final long startTime = (msecs <= 0) ? 0 : System.currentTimeMillis();
+		long waitTime = msecs;
+		if (this.completed) {
+			return getResult();
+		} else if (waitTime <= 0) {
+			throw new TimeoutException();
+		} else {
+			for (;;) {
+				wait(waitTime);
+				if (this.completed) {
+					return getResult();
+				} else {
+					waitTime = msecs - (System.currentTimeMillis() - startTime);
+					if (waitTime <= 0) {
+						throw new TimeoutException();
+					}
+				}
+			}
+		}
+	}
+
+	private T getResult() throws ExecutionException {
+		if (this.ex != null) {
+			throw new ExecutionException(this.ex);
+		}
+		return this.result;
+	}
+
+	public boolean completed(final T result) {
+		synchronized (this) {
+			if (this.completed) {
+				return false;
+			}
+			this.completed = true;
+			this.result = result;
+			notifyAll();
+		}
+		onCompleted(result);
+
+		return true;
+	}
+
+	public boolean failed(final Exception exception) {
+		synchronized (this) {
+			if (this.completed) {
+				return false;
+			}
+			this.completed = true;
+			this.ex = exception;
+			notifyAll();
+		}
+
+		onFailed(exception);
+
+		return true;
+	}
+
+	@Override
+	public boolean cancel(final boolean mayInterruptIfRunning) {
+		synchronized (this) {
+			if (this.completed) {
+				return false;
+			}
+			this.completed = true;
+			this.cancelled = true;
+			notifyAll();
+		}
+
+		onCancelled();
+
+		return true;
+	}
+
+	protected abstract void onCompleted(T result);
+
+	protected abstract void onFailed(Exception ex);
+
+	protected abstract void onCancelled();
+}

+ 54 - 0
src/main/java/com/jeeplus/common/utils/concurrent/ConcurrentTools.java

@@ -0,0 +1,54 @@
+package com.jeeplus.common.utils.concurrent;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import com.jeeplus.common.utils.concurrent.jsr166e.LongAdder;
+import com.jeeplus.common.utils.concurrent.throttle.Sampler;
+
+import com.google.common.util.concurrent.RateLimiter;
+
+public class ConcurrentTools {
+
+	/**
+	 * 返回没有激烈CAS冲突的LongAdder, 并发的+1将在不同的Counter里进行,只在取值时将多个Counter求和.
+	 * 
+	 * 为了保持JDK版本兼容性,统一采用移植版
+	 */
+	public static LongAdder longAdder() {
+		return new LongAdder();
+	}
+
+	/**
+	 * 返回CountDownLatch
+	 */
+	public static CountDownLatch countDownLatch(int count) {
+		return new CountDownLatch(count);
+	}
+
+	/**
+	 * 返回CyclicBarrier
+	 */
+	public static CyclicBarrier cyclicBarrier(int count) {
+		return new CyclicBarrier(count);
+	}
+
+	/////////// 限流采样 //////
+	/**
+	 * 返回漏桶算法的RateLimiter
+	 * 
+	 * @permitsPerSecond 期望的QPS, RateLimiter将QPS平滑到毫秒级别上,但有蓄水及桶外预借的能力.
+	 */
+	public static RateLimiter rateLimiter(int permitsPerSecond) {
+		return RateLimiter.create(permitsPerSecond);
+	}
+
+	/**
+	 * 返回采样器.
+	 * 
+	 * @param selectPercent 采样率,在0-100 之间,可以有小数位
+	 */
+	public static Sampler sampler(double selectPercent) {
+		return Sampler.create(selectPercent);
+	}
+}

+ 121 - 0
src/main/java/com/jeeplus/common/utils/concurrent/ThreadDumpper.java

@@ -0,0 +1,121 @@
+package com.jeeplus.common.utils.concurrent;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 由程序触发的ThreadDump,打印到日志中.
+ * 
+ * 因为ThreadDump本身会造成JVM停顿,所以加上了开关和最少间隔时间的选项(默认不限制)
+ * 
+ * 因为ThreadInfo的toString()最多只会打印8层的StackTrace,所以加上了最大打印层数的选项.(默认为8)
+ * 
+ * @author calvin
+ */
+public class ThreadDumpper {
+
+	private static final int DEFAULT_MAX_STACK_LEVEL = 8;
+
+	private static Logger logger = LoggerFactory.getLogger(ThreadDumpper.class);
+
+	private boolean enable = true; // 快速关闭该功能
+	private long leastIntervalMills = 0; // 每次打印ThreadDump的最小时间间隔,单位为毫秒
+	private int maxStackLevel = DEFAULT_MAX_STACK_LEVEL; // 打印StackTrace的最大深度
+
+	private volatile Long lastThreadDumpTime = 0L;
+
+	public ThreadDumpper() {
+	}
+
+	public ThreadDumpper(long leastIntervalMills, int maxStackLevel) {
+		this.leastIntervalMills = leastIntervalMills;
+		this.maxStackLevel = maxStackLevel;
+	}
+
+	/**
+	 * 符合条件则打印线程栈.
+	 */
+	public void threadDumpIfNeed() {
+		threadDumpIfNeed(null);
+	}
+
+	/**
+	 * 符合条件则打印线程栈.
+	 * 
+	 * @param reasonMsg 发生ThreadDump的原因
+	 */
+	public void threadDumpIfNeed(String reasonMsg) {
+		if (!enable) {
+			return;
+		}
+
+		synchronized (this) {
+			if (System.currentTimeMillis() - lastThreadDumpTime < leastIntervalMills) {
+				return;
+			} else {
+				lastThreadDumpTime = System.currentTimeMillis();
+			}
+		}
+
+		logger.info("Thread dump by ThreadDumpper" + (reasonMsg != null ? (" for " + reasonMsg) : ""));
+
+		Map<Thread, StackTraceElement[]> threads = Thread.getAllStackTraces();
+		// 两条日志间的时间间隔,是VM被thread dump堵塞的时间.
+		logger.info("Finish the threads snapshot");
+
+		StringBuilder sb = new StringBuilder(8192 * 20).append("\n");
+
+		for (Entry<Thread, StackTraceElement[]> entry : threads.entrySet()) {
+			dumpThreadInfo(entry.getKey(), entry.getValue(), sb);
+		}
+		logger.info(sb.toString());
+
+	}
+
+	/**
+	 * 打印全部的stack,重新实现threadInfo的toString()函数,因为默认最多只打印8层的stack. 同时,不再打印lockedMonitors和lockedSynchronizers.
+	 */
+	private String dumpThreadInfo(Thread thread, StackTraceElement[] stackTrace, StringBuilder sb) {
+		sb.append("\"").append(thread.getName()).append("\" Id=").append(thread.getId()).append(' ')
+				.append(thread.getState());
+		sb.append('\n');
+		int i = 0;
+		for (; i < Math.min(maxStackLevel, stackTrace.length); i++) {
+			StackTraceElement ste = stackTrace[i];
+			sb.append("\tat ").append(ste.toString()).append('\n');
+		}
+		if (i < stackTrace.length) {
+			sb.append("\t...").append('\n');
+		}
+
+		sb.append('\n');
+		return sb.toString();
+	}
+
+	/**
+	 * 快速关闭打印
+	 */
+	public void setEnable(boolean enable) {
+		this.enable = enable;
+	}
+
+	/**
+	 * 打印ThreadDump的最小时间间隔,单位为秒,默认为0不限制.
+	 */
+	public void setLeastInterval(int leastIntervalSeconds) {
+		synchronized (this) {
+			this.leastIntervalMills = TimeUnit.SECONDS.toMillis(leastIntervalSeconds);
+		}
+	}
+
+	/**
+	 * 打印StackTrace的最大深度, 默认为8
+	 */
+	public void setMaxStackLevel(int maxStackLevel) {
+		this.maxStackLevel = maxStackLevel;
+	}
+}

+ 55 - 0
src/main/java/com/jeeplus/common/utils/concurrent/ThreadLocalContext.java

@@ -0,0 +1,55 @@
+package com.jeeplus.common.utils.concurrent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 存储于ThreadLocal的Map, 用于存储上下文.<br/>
+ * 
+ * 但HashMap<String,Object>的存储其实较为低效,在高性能场景下可改为EnumMap<br/>
+ * 
+ * 1.先定义枚举类,列举所有可能的Key<br/>
+ * 2.替换contextMap的创建语句,见下例<br/>
+ * 3.修改put()/get()中key的类型<br/>
+ * 
+ * <pre>
+ * private static ThreadLocal<Map<MyEnum, Object>> contextMap = new ThreadLocal<Map<MyEnum, Object>>() {
+ * 	&#64;Override
+ * 	protected Map<MyEnum, Object> initialValue() {
+ * 		return new EnumMap<MyEnum, Object>(MyEnum.class);
+ * 	}
+ * };
+ * </pre>
+ */
+public class ThreadLocalContext {
+
+	private static ThreadLocal<Map<String, Object>> contextMap = new ThreadLocal<Map<String, Object>>() {
+		@Override
+		protected Map<String, Object> initialValue() {
+			// 降低loadFactory减少冲突
+			return new HashMap<String, Object>(16, 0.5f);
+		}
+	};
+
+	/**
+	 * 放入ThreadLocal的上下文信息.
+	 */
+	public static void put(String key, Object value) {
+		contextMap.get().put(key, value);
+	}
+
+	/**
+	 * 取出ThreadLocal的上下文信息.
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> T get(String key) {
+		return (T) (contextMap.get().get(key));
+	}
+
+	/**
+	 * 清理ThreadLocal的Context内容.
+	 */
+	public static void reset() {
+		contextMap.get().clear();
+	}
+}

+ 100 - 0
src/main/java/com/jeeplus/common/utils/concurrent/ThreadUtil.java

@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2014 springside.github.io
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *******************************************************************************/
+package com.jeeplus.common.utils.concurrent;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 线程相关工具类.
+ * 
+ * 1. 处理了InterruptedException的sleep
+ */
+public class ThreadUtil {
+
+	/**
+	 * sleep等待, 单位为毫秒, 已捕捉并处理InterruptedException.
+	 */
+	public static void sleep(long durationMillis) {
+		try {
+			Thread.sleep(durationMillis);
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+		}
+	}
+
+	/**
+	 * sleep等待,已捕捉并处理InterruptedException.
+	 */
+	public static void sleep(long duration, TimeUnit unit) {
+		try {
+			Thread.sleep(unit.toMillis(duration));
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+		}
+	}
+
+	/**
+	 * 纯粹为了提醒下处理InterruptedException的正确方式,除非你是在写不可中断的任务.
+	 */
+	public static void handleInterruptedException() {
+		Thread.currentThread().interrupt();
+	}
+
+	/**
+	 * 通过StackTrace,获得调用者的类名.
+	 */
+	public static String getCallerClass() {
+		StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
+		if (stacktrace.length >= 4) {
+			StackTraceElement element = stacktrace[3];
+			return element.getClassName();
+		} else {
+			return StringUtils.EMPTY;
+		}
+	}
+
+	/**
+	 * 通过StackTrace,获得调用者的"类名.方法名()"
+	 */
+	public static String getCallerMethod() {
+		StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
+		if (stacktrace.length >= 4) {
+			StackTraceElement element = stacktrace[3];
+			return element.getClassName() + '.' + element.getMethodName() + "()";
+		} else {
+			return StringUtils.EMPTY;
+		}
+	}
+
+	/**
+	 * 通过StackTrace,获得调用者的类名.
+	 */
+	public static String getCurrentClass() {
+		StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
+		if (stacktrace.length >= 3) {
+			StackTraceElement element = stacktrace[2];
+			return element.getClassName();
+		} else {
+			return StringUtils.EMPTY;
+		}
+	}
+
+	/**
+	 * 通过StackTrace,获得当前方法的"类名.方法名()"
+	 */
+	public static String getCurrentMethod() {
+		StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
+		if (stacktrace.length >= 3) {
+			StackTraceElement element = stacktrace[2];
+			return element.getClassName() + '.' + element.getMethodName() + "()";
+		} else {
+			return StringUtils.EMPTY;
+		}
+	}
+
+}

+ 6310 - 0
src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/ConcurrentHashMapV8.java

@@ -0,0 +1,6310 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package com.jeeplus.common.utils.concurrent.jsr166e;
+
+import java.io.ObjectStreamField;
+import java.io.Serializable;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.LockSupport;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/RecursiveTask.java 1.126
+ * 
+ * A hash table supporting full concurrency of retrievals and
+ * high expected concurrency for updates. This class obeys the
+ * same functional specification as {@link Hashtable}, and
+ * includes versions of methods corresponding to each method of
+ * {@code Hashtable}. However, even though all operations are
+ * thread-safe, retrieval operations do <em>not</em> entail locking,
+ * and there is <em>not</em> any support for locking the entire table
+ * in a way that prevents all access.  This class is fully
+ * interoperable with {@code Hashtable} in programs that rely on its
+ * thread safety but not on its synchronization details.
+ *
+ * <p>Retrieval operations (including {@code get}) generally do not
+ * block, so may overlap with update operations (including {@code put}
+ * and {@code remove}). Retrievals reflect the results of the most
+ * recently <em>completed</em> update operations holding upon their
+ * onset. (More formally, an update operation for a given key bears a
+ * <em>happens-before</em> relation with any (non-null) retrieval for
+ * that key reporting the updated value.)  For aggregate operations
+ * such as {@code putAll} and {@code clear}, concurrent retrievals may
+ * reflect insertion or removal of only some entries.  Similarly,
+ * Iterators and Enumerations return elements reflecting the state of
+ * the hash table at some point at or since the creation of the
+ * iterator/enumeration.  They do <em>not</em> throw {@link
+ * ConcurrentModificationException}.  However, iterators are designed
+ * to be used by only one thread at a time.  Bear in mind that the
+ * results of aggregate status methods including {@code size}, {@code
+ * isEmpty}, and {@code containsValue} are typically useful only when
+ * a map is not undergoing concurrent updates in other threads.
+ * Otherwise the results of these methods reflect transient states
+ * that may be adequate for monitoring or estimation purposes, but not
+ * for program control.
+ *
+ * <p>The table is dynamically expanded when there are too many
+ * collisions (i.e., keys that have distinct hash codes but fall into
+ * the same slot modulo the table size), with the expected average
+ * effect of maintaining roughly two bins per mapping (corresponding
+ * to a 0.75 load factor threshold for resizing). There may be much
+ * variance around this average as mappings are added and removed, but
+ * overall, this maintains a commonly accepted time/space tradeoff for
+ * hash tables.  However, resizing this or any other kind of hash
+ * table may be a relatively slow operation. When possible, it is a
+ * good idea to provide a size estimate as an optional {@code
+ * initialCapacity} constructor argument. An additional optional
+ * {@code loadFactor} constructor argument provides a further means of
+ * customizing initial table capacity by specifying the table density
+ * to be used in calculating the amount of space to allocate for the
+ * given number of elements.  Also, for compatibility with previous
+ * versions of this class, constructors may optionally specify an
+ * expected {@code concurrencyLevel} as an additional hint for
+ * internal sizing.  Note that using many keys with exactly the same
+ * {@code hashCode()} is a sure way to slow down performance of any
+ * hash table. To ameliorate impact, when keys are {@link Comparable},
+ * this class may use comparison order among keys to help break ties.
+ *
+ * <p>A {@link Set} projection of a ConcurrentHashMapV8 may be created
+ * (using {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed
+ * (using {@link #keySet(Object)} when only keys are of interest, and the
+ * mapped values are (perhaps transiently) not used or all take the
+ * same mapping value.
+ *
+ * <p>This class and its views and iterators implement all of the
+ * <em>optional</em> methods of the {@link Map} and {@link Iterator}
+ * interfaces.
+ *
+ * <p>Like {@link Hashtable} but unlike {@link HashMap}, this class
+ * does <em>not</em> allow {@code null} to be used as a key or value.
+ *
+ * <p>ConcurrentHashMapV8s support a set of sequential and parallel bulk
+ * operations that are designed
+ * to be safely, and often sensibly, applied even with maps that are
+ * being concurrently updated by other threads; for example, when
+ * computing a snapshot summary of the values in a shared registry.
+ * There are three kinds of operation, each with four forms, accepting
+ * functions with Keys, Values, Entries, and (Key, Value) arguments
+ * and/or return values. Because the elements of a ConcurrentHashMapV8
+ * are not ordered in any particular way, and may be processed in
+ * different orders in different parallel executions, the correctness
+ * of supplied functions should not depend on any ordering, or on any
+ * other objects or values that may transiently change while
+ * computation is in progress; and except for forEach actions, should
+ * ideally be side-effect-free. Bulk operations on {@link Entry}
+ * objects do not support method {@code setValue}.
+ *
+ * <ul>
+ * <li>forEach: Perform a given action on each element.
+ * A variant form applies a given transformation on each element
+ * before performing the action.
+ *
+ * <li>search: Return the first available non-null result of
+ * applying a given function on each element; skipping further
+ * search when a result is found.
+ *
+ * <li>reduce: Accumulate each element.  The supplied reduction
+ * function cannot rely on ordering (more formally, it should be
+ * both associative and commutative).  There are five variants:
+ *
+ * <ul>
+ *
+ * <li>Plain reductions. (There is not a form of this method for
+ * (key, value) function arguments since there is no corresponding
+ * return type.)
+ *
+ * <li>Mapped reductions that accumulate the results of a given
+ * function applied to each element.
+ *
+ * <li>Reductions to scalar doubles, longs, and ints, using a
+ * given basis value.
+ *
+ * </ul>
+ * </ul>
+ *
+ * <p>These bulk operations accept a {@code parallelismThreshold}
+ * argument. Methods proceed sequentially if the current map size is
+ * estimated to be less than the given threshold. Using a value of
+ * {@code Long.MAX_VALUE} suppresses all parallelism.  Using a value
+ * of {@code 1} results in maximal parallelism by partitioning into
+ * enough subtasks to fully utilize the {@link
+ * ForkJoinPool#commonPool()} that is used for all parallel
+ * computations. Normally, you would initially choose one of these
+ * extreme values, and then measure performance of using in-between
+ * values that trade off overhead versus throughput.
+ *
+ * <p>The concurrency properties of bulk operations follow
+ * from those of ConcurrentHashMapV8: Any non-null result returned
+ * from {@code get(key)} and related access methods bears a
+ * happens-before relation with the associated insertion or
+ * update.  The result of any bulk operation reflects the
+ * composition of these per-element relations (but is not
+ * necessarily atomic with respect to the map as a whole unless it
+ * is somehow known to be quiescent).  Conversely, because keys
+ * and values in the map are never null, null serves as a reliable
+ * atomic indicator of the current lack of any result.  To
+ * maintain this property, null serves as an implicit basis for
+ * all non-scalar reduction operations. For the double, long, and
+ * int versions, the basis should be one that, when combined with
+ * any other value, returns that other value (more formally, it
+ * should be the identity element for the reduction). Most common
+ * reductions have these properties; for example, computing a sum
+ * with basis 0 or a minimum with basis MAX_VALUE.
+ *
+ * <p>Search and transformation functions provided as arguments
+ * should similarly return null to indicate the lack of any result
+ * (in which case it is not used). In the case of mapped
+ * reductions, this also enables transformations to serve as
+ * filters, returning null (or, in the case of primitive
+ * specializations, the identity basis) if the element should not
+ * be combined. You can create compound transformations and
+ * filterings by composing them yourself under this "null means
+ * there is nothing there now" rule before using them in search or
+ * reduce operations.
+ *
+ * <p>Methods accepting and/or returning Entry arguments maintain
+ * key-value associations. They may be useful for example when
+ * finding the key for the greatest value. Note that "plain" Entry
+ * arguments can be supplied using {@code new
+ * AbstractMap.SimpleEntry(k,v)}.
+ *
+ * <p>Bulk operations may complete abruptly, throwing an
+ * exception encountered in the application of a supplied
+ * function. Bear in mind when handling such exceptions that other
+ * concurrently executing functions could also have thrown
+ * exceptions, or would have done so if the first exception had
+ * not occurred.
+ *
+ * <p>Speedups for parallel compared to sequential forms are common
+ * but not guaranteed.  Parallel operations involving brief functions
+ * on small maps may execute more slowly than sequential forms if the
+ * underlying work to parallelize the computation is more expensive
+ * than the computation itself.  Similarly, parallelization may not
+ * lead to much actual parallelism if all processors are busy
+ * performing unrelated tasks.
+ *
+ * <p>All arguments to all task methods must be non-null.
+ *
+ * <p><em>jsr166e note: During transition, this class
+ * uses nested functional interfaces with different names but the
+ * same forms as those expected for JDK8.</em>
+ *
+ * <p>This class is a member of the
+ * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * Java Collections Framework</a>.
+ *
+ * @since 1.5
+ * @author Doug Lea
+ * @param <K> the type of keys maintained by this map
+ * @param <V> the type of mapped values
+ */
+public class ConcurrentHashMapV8<K,V> extends AbstractMap<K,V>
+    implements ConcurrentMap<K,V>, Serializable {
+    private static final long serialVersionUID = 7249069246763182397L;
+
+    /**
+     * An object for traversing and partitioning elements of a source.
+     * This interface provides a subset of the functionality of JDK8
+     * java.util.Spliterator.
+     */
+    public static interface ConcurrentHashMapSpliterator<T> {
+        /**
+         * If possible, returns a new spliterator covering
+         * approximately one half of the elements, which will not be
+         * covered by this spliterator. Returns null if cannot be
+         * split.
+         */
+        ConcurrentHashMapSpliterator<T> trySplit();
+        /**
+         * Returns an estimate of the number of elements covered by
+         * this Spliterator.
+         */
+        long estimateSize();
+
+        /** Applies the action to each untraversed element */
+        void forEachRemaining(Action<? super T> action);
+        /** If an element remains, applies the action and returns true. */
+        boolean tryAdvance(Action<? super T> action);
+    }
+
+    // Sams
+    /** Interface describing a void action of one argument */
+    public interface Action<A> { void apply(A a); }
+    /** Interface describing a void action of two arguments */
+    public interface BiAction<A,B> { void apply(A a, B b); }
+    /** Interface describing a function of one argument */
+    public interface Fun<A,T> { T apply(A a); }
+    /** Interface describing a function of two arguments */
+    public interface BiFun<A,B,T> { T apply(A a, B b); }
+    /** Interface describing a function mapping its argument to a double */
+    public interface ObjectToDouble<A> { double apply(A a); }
+    /** Interface describing a function mapping its argument to a long */
+    public interface ObjectToLong<A> { long apply(A a); }
+    /** Interface describing a function mapping its argument to an int */
+    public interface ObjectToInt<A> {int apply(A a); }
+    /** Interface describing a function mapping two arguments to a double */
+    public interface ObjectByObjectToDouble<A,B> { double apply(A a, B b); }
+    /** Interface describing a function mapping two arguments to a long */
+    public interface ObjectByObjectToLong<A,B> { long apply(A a, B b); }
+    /** Interface describing a function mapping two arguments to an int */
+    public interface ObjectByObjectToInt<A,B> {int apply(A a, B b); }
+    /** Interface describing a function mapping two doubles to a double */
+    public interface DoubleByDoubleToDouble { double apply(double a, double b); }
+    /** Interface describing a function mapping two longs to a long */
+    public interface LongByLongToLong { long apply(long a, long b); }
+    /** Interface describing a function mapping two ints to an int */
+    public interface IntByIntToInt { int apply(int a, int b); }
+
+
+    /*
+     * Overview:
+     *
+     * The primary design goal of this hash table is to maintain
+     * concurrent readability (typically method get(), but also
+     * iterators and related methods) while minimizing update
+     * contention. Secondary goals are to keep space consumption about
+     * the same or better than java.util.HashMap, and to support high
+     * initial insertion rates on an empty table by many threads.
+     *
+     * This map usually acts as a binned (bucketed) hash table.  Each
+     * key-value mapping is held in a Node.  Most nodes are instances
+     * of the basic Node class with hash, key, value, and next
+     * fields. However, various subclasses exist: TreeNodes are
+     * arranged in balanced trees, not lists.  TreeBins hold the roots
+     * of sets of TreeNodes. ForwardingNodes are placed at the heads
+     * of bins during resizing. ReservationNodes are used as
+     * placeholders while establishing values in computeIfAbsent and
+     * related methods.  The types TreeBin, ForwardingNode, and
+     * ReservationNode do not hold normal user keys, values, or
+     * hashes, and are readily distinguishable during search etc
+     * because they have negative hash fields and null key and value
+     * fields. (These special nodes are either uncommon or transient,
+     * so the impact of carrying around some unused fields is
+     * insignificant.)
+     *
+     * The table is lazily initialized to a power-of-two size upon the
+     * first insertion.  Each bin in the table normally contains a
+     * list of Nodes (most often, the list has only zero or one Node).
+     * Table accesses require volatile/atomic reads, writes, and
+     * CASes.  Because there is no other way to arrange this without
+     * adding further indirections, we use intrinsics
+     * (sun.misc.Unsafe) operations.
+     *
+     * We use the top (sign) bit of Node hash fields for control
+     * purposes -- it is available anyway because of addressing
+     * constraints.  Nodes with negative hash fields are specially
+     * handled or ignored in map methods.
+     *
+     * Insertion (via put or its variants) of the first node in an
+     * empty bin is performed by just CASing it to the bin.  This is
+     * by far the most common case for put operations under most
+     * key/hash distributions.  Other update operations (insert,
+     * delete, and replace) require locks.  We do not want to waste
+     * the space required to associate a distinct lock object with
+     * each bin, so instead use the first node of a bin list itself as
+     * a lock. Locking support for these locks relies on builtin
+     * "synchronized" monitors.
+     *
+     * Using the first node of a list as a lock does not by itself
+     * suffice though: When a node is locked, any update must first
+     * validate that it is still the first node after locking it, and
+     * retry if not. Because new nodes are always appended to lists,
+     * once a node is first in a bin, it remains first until deleted
+     * or the bin becomes invalidated (upon resizing).
+     *
+     * The main disadvantage of per-bin locks is that other update
+     * operations on other nodes in a bin list protected by the same
+     * lock can stall, for example when user equals() or mapping
+     * functions take a long time.  However, statistically, under
+     * random hash codes, this is not a common problem.  Ideally, the
+     * frequency of nodes in bins follows a Poisson distribution
+     * (http://en.wikipedia.org/wiki/Poisson_distribution) with a
+     * parameter of about 0.5 on average, given the resizing threshold
+     * of 0.75, although with a large variance because of resizing
+     * granularity. Ignoring variance, the expected occurrences of
+     * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The
+     * first values are:
+     *
+     * 0:    0.60653066
+     * 1:    0.30326533
+     * 2:    0.07581633
+     * 3:    0.01263606
+     * 4:    0.00157952
+     * 5:    0.00015795
+     * 6:    0.00001316
+     * 7:    0.00000094
+     * 8:    0.00000006
+     * more: less than 1 in ten million
+     *
+     * Lock contention probability for two threads accessing distinct
+     * elements is roughly 1 / (8 * #elements) under random hashes.
+     *
+     * Actual hash code distributions encountered in practice
+     * sometimes deviate significantly from uniform randomness.  This
+     * includes the case when N > (1<<30), so some keys MUST collide.
+     * Similarly for dumb or hostile usages in which multiple keys are
+     * designed to have identical hash codes or ones that differs only
+     * in masked-out high bits. So we use a secondary strategy that
+     * applies when the number of nodes in a bin exceeds a
+     * threshold. These TreeBins use a balanced tree to hold nodes (a
+     * specialized form of red-black trees), bounding search time to
+     * O(log N).  Each search step in a TreeBin is at least twice as
+     * slow as in a regular list, but given that N cannot exceed
+     * (1<<64) (before running out of addresses) this bounds search
+     * steps, lock hold times, etc, to reasonable constants (roughly
+     * 100 nodes inspected per operation worst case) so long as keys
+     * are Comparable (which is very common -- String, Long, etc).
+     * TreeBin nodes (TreeNodes) also maintain the same "next"
+     * traversal pointers as regular nodes, so can be traversed in
+     * iterators in the same way.
+     *
+     * The table is resized when occupancy exceeds a percentage
+     * threshold (nominally, 0.75, but see below).  Any thread
+     * noticing an overfull bin may assist in resizing after the
+     * initiating thread allocates and sets up the replacement array.
+     * However, rather than stalling, these other threads may proceed
+     * with insertions etc.  The use of TreeBins shields us from the
+     * worst case effects of overfilling while resizes are in
+     * progress.  Resizing proceeds by transferring bins, one by one,
+     * from the table to the next table. However, threads claim small
+     * blocks of indices to transfer (via field transferIndex) before
+     * doing so, reducing contention.  A generation stamp in field
+     * sizeCtl ensures that resizings do not overlap. Because we are
+     * using power-of-two expansion, the elements from each bin must
+     * either stay at same index, or move with a power of two
+     * offset. We eliminate unnecessary node creation by catching
+     * cases where old nodes can be reused because their next fields
+     * won't change.  On average, only about one-sixth of them need
+     * cloning when a table doubles. The nodes they replace will be
+     * garbage collectable as soon as they are no longer referenced by
+     * any reader thread that may be in the midst of concurrently
+     * traversing table.  Upon transfer, the old table bin contains
+     * only a special forwarding node (with hash field "MOVED") that
+     * contains the next table as its key. On encountering a
+     * forwarding node, access and update operations restart, using
+     * the new table.
+     *
+     * Each bin transfer requires its bin lock, which can stall
+     * waiting for locks while resizing. However, because other
+     * threads can join in and help resize rather than contend for
+     * locks, average aggregate waits become shorter as resizing
+     * progresses.  The transfer operation must also ensure that all
+     * accessible bins in both the old and new table are usable by any
+     * traversal.  This is arranged in part by proceeding from the
+     * last bin (table.length - 1) up towards the first.  Upon seeing
+     * a forwarding node, traversals (see class Traverser) arrange to
+     * move to the new table without revisiting nodes.  To ensure that
+     * no intervening nodes are skipped even when moved out of order,
+     * a stack (see class TableStack) is created on first encounter of
+     * a forwarding node during a traversal, to maintain its place if
+     * later processing the current table. The need for these
+     * save/restore mechanics is relatively rare, but when one
+     * forwarding node is encountered, typically many more will be.
+     * So Traversers use a simple caching scheme to avoid creating so
+     * many new TableStack nodes. (Thanks to Peter Levart for
+     * suggesting use of a stack here.)
+     *
+     * The traversal scheme also applies to partial traversals of
+     * ranges of bins (via an alternate Traverser constructor)
+     * to support partitioned aggregate operations.  Also, read-only
+     * operations give up if ever forwarded to a null table, which
+     * provides support for shutdown-style clearing, which is also not
+     * currently implemented.
+     *
+     * Lazy table initialization minimizes footprint until first use,
+     * and also avoids resizings when the first operation is from a
+     * putAll, constructor with map argument, or deserialization.
+     * These cases attempt to override the initial capacity settings,
+     * but harmlessly fail to take effect in cases of races.
+     *
+     * The element count is maintained using a specialization of
+     * LongAdder. We need to incorporate a specialization rather than
+     * just use a LongAdder in order to access implicit
+     * contention-sensing that leads to creation of multiple
+     * CounterCells.  The counter mechanics avoid contention on
+     * updates but can encounter cache thrashing if read too
+     * frequently during concurrent access. To avoid reading so often,
+     * resizing under contention is attempted only upon adding to a
+     * bin already holding two or more nodes. Under uniform hash
+     * distributions, the probability of this occurring at threshold
+     * is around 13%, meaning that only about 1 in 8 puts check
+     * threshold (and after resizing, many fewer do so).
+     *
+     * TreeBins use a special form of comparison for search and
+     * related operations (which is the main reason we cannot use
+     * existing collections such as TreeMaps). TreeBins contain
+     * Comparable elements, but may contain others, as well as
+     * elements that are Comparable but not necessarily Comparable for
+     * the same T, so we cannot invoke compareTo among them. To handle
+     * this, the tree is ordered primarily by hash value, then by
+     * Comparable.compareTo order if applicable.  On lookup at a node,
+     * if elements are not comparable or compare as 0 then both left
+     * and right children may need to be searched in the case of tied
+     * hash values. (This corresponds to the full list search that
+     * would be necessary if all elements were non-Comparable and had
+     * tied hashes.) On insertion, to keep a total ordering (or as
+     * close as is required here) across rebalancings, we compare
+     * classes and identityHashCodes as tie-breakers. The red-black
+     * balancing code is updated from pre-jdk-collections
+     * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java)
+     * based in turn on Cormen, Leiserson, and Rivest "Introduction to
+     * Algorithms" (CLR).
+     *
+     * TreeBins also require an additional locking mechanism.  While
+     * list traversal is always possible by readers even during
+     * updates, tree traversal is not, mainly because of tree-rotations
+     * that may change the root node and/or its linkages.  TreeBins
+     * include a simple read-write lock mechanism parasitic on the
+     * main bin-synchronization strategy: Structural adjustments
+     * associated with an insertion or removal are already bin-locked
+     * (and so cannot conflict with other writers) but must wait for
+     * ongoing readers to finish. Since there can be only one such
+     * waiter, we use a simple scheme using a single "waiter" field to
+     * block writers.  However, readers need never block.  If the root
+     * lock is held, they proceed along the slow traversal path (via
+     * next-pointers) until the lock becomes available or the list is
+     * exhausted, whichever comes first. These cases are not fast, but
+     * maximize aggregate expected throughput.
+     *
+     * Maintaining API and serialization compatibility with previous
+     * versions of this class introduces several oddities. Mainly: We
+     * leave untouched but unused constructor arguments referring to
+     * concurrencyLevel. We accept a loadFactor constructor argument,
+     * but apply it only to initial table capacity (which is the only
+     * time that we can guarantee to honor it.) We also declare an
+     * unused "Segment" class that is instantiated in minimal form
+     * only when serializing.
+     *
+     * Also, solely for compatibility with previous versions of this
+     * class, it extends AbstractMap, even though all of its methods
+     * are overridden, so it is just useless baggage.
+     *
+     * This file is organized to make things a little easier to follow
+     * while reading than they might otherwise: First the main static
+     * declarations and utilities, then fields, then main public
+     * methods (with a few factorings of multiple public methods into
+     * internal ones), then sizing methods, trees, traversers, and
+     * bulk operations.
+     */
+
+    /* ---------------- Constants -------------- */
+
+    /**
+     * The largest possible table capacity.  This value must be
+     * exactly 1<<30 to stay within Java array allocation and indexing
+     * bounds for power of two table sizes, and is further required
+     * because the top two bits of 32bit hash fields are used for
+     * control purposes.
+     */
+    private static final int MAXIMUM_CAPACITY = 1 << 30;
+
+    /**
+     * The default initial table capacity.  Must be a power of 2
+     * (i.e., at least 1) and at most MAXIMUM_CAPACITY.
+     */
+    private static final int DEFAULT_CAPACITY = 16;
+
+    /**
+     * The largest possible (non-power of two) array size.
+     * Needed by toArray and related methods.
+     */
+    static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+
+    /**
+     * The default concurrency level for this table. Unused but
+     * defined for compatibility with previous versions of this class.
+     */
+    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
+
+    /**
+     * The load factor for this table. Overrides of this value in
+     * constructors affect only the initial table capacity.  The
+     * actual floating point value isn't normally used -- it is
+     * simpler to use expressions such as {@code n - (n >>> 2)} for
+     * the associated resizing threshold.
+     */
+    private static final float LOAD_FACTOR = 0.75f;
+
+    /**
+     * The bin count threshold for using a tree rather than list for a
+     * bin.  Bins are converted to trees when adding an element to a
+     * bin with at least this many nodes. The value must be greater
+     * than 2, and should be at least 8 to mesh with assumptions in
+     * tree removal about conversion back to plain bins upon
+     * shrinkage.
+     */
+    static final int TREEIFY_THRESHOLD = 8;
+
+    /**
+     * The bin count threshold for untreeifying a (split) bin during a
+     * resize operation. Should be less than TREEIFY_THRESHOLD, and at
+     * most 6 to mesh with shrinkage detection under removal.
+     */
+    static final int UNTREEIFY_THRESHOLD = 6;
+
+    /**
+     * The smallest table capacity for which bins may be treeified.
+     * (Otherwise the table is resized if too many nodes in a bin.)
+     * The value should be at least 4 * TREEIFY_THRESHOLD to avoid
+     * conflicts between resizing and treeification thresholds.
+     */
+    static final int MIN_TREEIFY_CAPACITY = 64;
+
+    /**
+     * Minimum number of rebinnings per transfer step. Ranges are
+     * subdivided to allow multiple resizer threads.  This value
+     * serves as a lower bound to avoid resizers encountering
+     * excessive memory contention.  The value should be at least
+     * DEFAULT_CAPACITY.
+     */
+    private static final int MIN_TRANSFER_STRIDE = 16;
+
+    /**
+     * The number of bits used for generation stamp in sizeCtl.
+     * Must be at least 6 for 32bit arrays.
+     */
+    private static int RESIZE_STAMP_BITS = 16;
+
+    /**
+     * The maximum number of threads that can help resize.
+     * Must fit in 32 - RESIZE_STAMP_BITS bits.
+     */
+    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
+
+    /**
+     * The bit shift for recording size stamp in sizeCtl.
+     */
+    private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
+
+    /*
+     * Encodings for Node hash fields. See above for explanation.
+     */
+    static final int MOVED     = -1; // hash for forwarding nodes
+    static final int TREEBIN   = -2; // hash for roots of trees
+    static final int RESERVED  = -3; // hash for transient reservations
+    static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
+
+    /** Number of CPUS, to place bounds on some sizings */
+    static final int NCPU = Runtime.getRuntime().availableProcessors();
+
+    /** For serialization compatibility. */
+    private static final ObjectStreamField[] serialPersistentFields = {
+        new ObjectStreamField("segments", Segment[].class),
+        new ObjectStreamField("segmentMask", Integer.TYPE),
+        new ObjectStreamField("segmentShift", Integer.TYPE)
+    };
+
+    /* ---------------- Nodes -------------- */
+
+    /**
+     * Key-value entry.  This class is never exported out as a
+     * user-mutable Map.Entry (i.e., one supporting setValue; see
+     * MapEntry below), but can be used for read-only traversals used
+     * in bulk tasks.  Subclasses of Node with a negative hash field
+     * are special, and contain null keys and values (but are never
+     * exported).  Otherwise, keys and vals are never null.
+     */
+    static class Node<K,V> implements Entry<K,V> {
+        final int hash;
+        final K key;
+        volatile V val;
+        volatile Node<K,V> next;
+
+        Node(int hash, K key, V val, Node<K,V> next) {
+            this.hash = hash;
+            this.key = key;
+            this.val = val;
+            this.next = next;
+        }
+
+        public final K getKey()     { return key; }
+        public final V getValue()   { return val; }
+        public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
+        public final String toString() { return key + "=" + val; }
+        public final V setValue(V value) {
+            throw new UnsupportedOperationException();
+        }
+
+        public final boolean equals(Object o) {
+            Object k, v, u; Entry<?,?> e;
+            return ((o instanceof Map.Entry) &&
+                    (k = (e = (Entry<?,?>)o).getKey()) != null &&
+                    (v = e.getValue()) != null &&
+                    (k == key || k.equals(key)) &&
+                    (v == (u = val) || v.equals(u)));
+        }
+
+        /**
+         * Virtualized support for map.get(); overridden in subclasses.
+         */
+        Node<K,V> find(int h, Object k) {
+            Node<K,V> e = this;
+            if (k != null) {
+                do {
+                    K ek;
+                    if (e.hash == h &&
+                        ((ek = e.key) == k || (ek != null && k.equals(ek))))
+                        return e;
+                } while ((e = e.next) != null);
+            }
+            return null;
+        }
+    }
+
+    /* ---------------- Static utilities -------------- */
+
+    /**
+     * Spreads (XORs) higher bits of hash to lower and also forces top
+     * bit to 0. Because the table uses power-of-two masking, sets of
+     * hashes that vary only in bits above the current mask will
+     * always collide. (Among known examples are sets of Float keys
+     * holding consecutive whole numbers in small tables.)  So we
+     * apply a transform that spreads the impact of higher bits
+     * downward. There is a tradeoff between speed, utility, and
+     * quality of bit-spreading. Because many common sets of hashes
+     * are already reasonably distributed (so don't benefit from
+     * spreading), and because we use trees to handle large sets of
+     * collisions in bins, we just XOR some shifted bits in the
+     * cheapest possible way to reduce systematic lossage, as well as
+     * to incorporate impact of the highest bits that would otherwise
+     * never be used in index calculations because of table bounds.
+     */
+    static final int spread(int h) {
+        return (h ^ (h >>> 16)) & HASH_BITS;
+    }
+
+    /**
+     * Returns a power of two table size for the given desired capacity.
+     * See Hackers Delight, sec 3.2
+     */
+    private static final int tableSizeFor(int c) {
+        int n = c - 1;
+        n |= n >>> 1;
+        n |= n >>> 2;
+        n |= n >>> 4;
+        n |= n >>> 8;
+        n |= n >>> 16;
+        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+    }
+
+    /**
+     * Returns x's Class if it is of the form "class C implements
+     * Comparable<C>", else null.
+     */
+    static Class<?> comparableClassFor(Object x) {
+        if (x instanceof Comparable) {
+            Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
+            if ((c = x.getClass()) == String.class) // bypass checks
+                return c;
+            if ((ts = c.getGenericInterfaces()) != null) {
+                for (int i = 0; i < ts.length; ++i) {
+                    if (((t = ts[i]) instanceof ParameterizedType) &&
+                        ((p = (ParameterizedType)t).getRawType() ==
+                         Comparable.class) &&
+                        (as = p.getActualTypeArguments()) != null &&
+                        as.length == 1 && as[0] == c) // type arg is c
+                        return c;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns k.compareTo(x) if x matches kc (k's screened comparable
+     * class), else 0.
+     */
+    @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
+    static int compareComparables(Class<?> kc, Object k, Object x) {
+        return (x == null || x.getClass() != kc ? 0 :
+                ((Comparable)k).compareTo(x));
+    }
+
+    /* ---------------- Table element access -------------- */
+
+    /*
+     * Volatile access methods are used for table elements as well as
+     * elements of in-progress next table while resizing.  All uses of
+     * the tab arguments must be null checked by callers.  All callers
+     * also paranoically precheck that tab's length is not zero (or an
+     * equivalent check), thus ensuring that any index argument taking
+     * the form of a hash value anded with (length - 1) is a valid
+     * index.  Note that, to be correct wrt arbitrary concurrency
+     * errors by users, these checks must operate on local variables,
+     * which accounts for some odd-looking inline assignments below.
+     * Note that calls to setTabAt always occur within locked regions,
+     * and so in principle require only release ordering, not
+     * full volatile semantics, but are currently coded as volatile
+     * writes to be conservative.
+     */
+
+    @SuppressWarnings("unchecked")
+    static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
+        return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
+    }
+
+    static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
+                                        Node<K,V> c, Node<K,V> v) {
+        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
+    }
+
+    static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
+        U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
+    }
+
+    /* ---------------- Fields -------------- */
+
+    /**
+     * The array of bins. Lazily initialized upon first insertion.
+     * Size is always a power of two. Accessed directly by iterators.
+     */
+    transient volatile Node<K,V>[] table;
+
+    /**
+     * The next table to use; non-null only while resizing.
+     */
+    private transient volatile Node<K,V>[] nextTable;
+
+    /**
+     * Base counter value, used mainly when there is no contention,
+     * but also as a fallback during table initialization
+     * races. Updated via CAS.
+     */
+    private transient volatile long baseCount;
+
+    /**
+     * Table initialization and resizing control.  When negative, the
+     * table is being initialized or resized: -1 for initialization,
+     * else -(1 + the number of active resizing threads).  Otherwise,
+     * when table is null, holds the initial table size to use upon
+     * creation, or 0 for default. After initialization, holds the
+     * next element count value upon which to resize the table.
+     */
+    private transient volatile int sizeCtl;
+
+    /**
+     * The next table index (plus one) to split while resizing.
+     */
+    private transient volatile int transferIndex;
+
+    /**
+     * Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
+     */
+    private transient volatile int cellsBusy;
+
+    /**
+     * Table of counter cells. When non-null, size is a power of 2.
+     */
+    private transient volatile CounterCell[] counterCells;
+
+    // views
+    private transient KeySetView<K,V> keySet;
+    private transient ValuesView<K,V> values;
+    private transient EntrySetView<K,V> entrySet;
+
+
+    /* ---------------- Public operations -------------- */
+
+    /**
+     * Creates a new, empty map with the default initial table size (16).
+     */
+    public ConcurrentHashMapV8() {
+    }
+
+    /**
+     * Creates a new, empty map with an initial table size
+     * accommodating the specified number of elements without the need
+     * to dynamically resize.
+     *
+     * @param initialCapacity The implementation performs internal
+     * sizing to accommodate this many elements.
+     * @throws IllegalArgumentException if the initial capacity of
+     * elements is negative
+     */
+    public ConcurrentHashMapV8(int initialCapacity) {
+        if (initialCapacity < 0)
+            throw new IllegalArgumentException();
+        int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
+                   MAXIMUM_CAPACITY :
+                   tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
+        this.sizeCtl = cap;
+    }
+
+    /**
+     * Creates a new map with the same mappings as the given map.
+     *
+     * @param m the map
+     */
+    public ConcurrentHashMapV8(Map<? extends K, ? extends V> m) {
+        this.sizeCtl = DEFAULT_CAPACITY;
+        putAll(m);
+    }
+
+    /**
+     * Creates a new, empty map with an initial table size based on
+     * the given number of elements ({@code initialCapacity}) and
+     * initial table density ({@code loadFactor}).
+     *
+     * @param initialCapacity the initial capacity. The implementation
+     * performs internal sizing to accommodate this many elements,
+     * given the specified load factor.
+     * @param loadFactor the load factor (table density) for
+     * establishing the initial table size
+     * @throws IllegalArgumentException if the initial capacity of
+     * elements is negative or the load factor is nonpositive
+     *
+     * @since 1.6
+     */
+    public ConcurrentHashMapV8(int initialCapacity, float loadFactor) {
+        this(initialCapacity, loadFactor, 1);
+    }
+
+    /**
+     * Creates a new, empty map with an initial table size based on
+     * the given number of elements ({@code initialCapacity}), table
+     * density ({@code loadFactor}), and number of concurrently
+     * updating threads ({@code concurrencyLevel}).
+     *
+     * @param initialCapacity the initial capacity. The implementation
+     * performs internal sizing to accommodate this many elements,
+     * given the specified load factor.
+     * @param loadFactor the load factor (table density) for
+     * establishing the initial table size
+     * @param concurrencyLevel the estimated number of concurrently
+     * updating threads. The implementation may use this value as
+     * a sizing hint.
+     * @throws IllegalArgumentException if the initial capacity is
+     * negative or the load factor or concurrencyLevel are
+     * nonpositive
+     */
+    public ConcurrentHashMapV8(int initialCapacity,
+                             float loadFactor, int concurrencyLevel) {
+        if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
+            throw new IllegalArgumentException();
+        if (initialCapacity < concurrencyLevel)   // Use at least as many bins
+            initialCapacity = concurrencyLevel;   // as estimated threads
+        long size = (long)(1.0 + (long)initialCapacity / loadFactor);
+        int cap = (size >= (long)MAXIMUM_CAPACITY) ?
+            MAXIMUM_CAPACITY : tableSizeFor((int)size);
+        this.sizeCtl = cap;
+    }
+
+    // Original (since JDK1.2) Map methods
+
+    /**
+     * {@inheritDoc}
+     */
+    public int size() {
+        long n = sumCount();
+        return ((n < 0L) ? 0 :
+                (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
+                (int)n);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isEmpty() {
+        return sumCount() <= 0L; // ignore transient negative values
+    }
+
+    /**
+     * Returns the value to which the specified key is mapped,
+     * or {@code null} if this map contains no mapping for the key.
+     *
+     * <p>More formally, if this map contains a mapping from a key
+     * {@code k} to a value {@code v} such that {@code key.equals(k)},
+     * then this method returns {@code v}; otherwise it returns
+     * {@code null}.  (There can be at most one such mapping.)
+     *
+     * @throws NullPointerException if the specified key is null
+     */
+    public V get(Object key) {
+        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
+        int h = spread(key.hashCode());
+        if ((tab = table) != null && (n = tab.length) > 0 &&
+            (e = tabAt(tab, (n - 1) & h)) != null) {
+            if ((eh = e.hash) == h) {
+                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
+                    return e.val;
+            }
+            else if (eh < 0)
+                return (p = e.find(h, key)) != null ? p.val : null;
+            while ((e = e.next) != null) {
+                if (e.hash == h &&
+                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
+                    return e.val;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Tests if the specified object is a key in this table.
+     *
+     * @param  key possible key
+     * @return {@code true} if and only if the specified object
+     *         is a key in this table, as determined by the
+     *         {@code equals} method; {@code false} otherwise
+     * @throws NullPointerException if the specified key is null
+     */
+    public boolean containsKey(Object key) {
+        return get(key) != null;
+    }
+
+    /**
+     * Returns {@code true} if this map maps one or more keys to the
+     * specified value. Note: This method may require a full traversal
+     * of the map, and is much slower than method {@code containsKey}.
+     *
+     * @param value value whose presence in this map is to be tested
+     * @return {@code true} if this map maps one or more keys to the
+     *         specified value
+     * @throws NullPointerException if the specified value is null
+     */
+    public boolean containsValue(Object value) {
+        if (value == null)
+            throw new NullPointerException();
+        Node<K,V>[] t;
+        if ((t = table) != null) {
+            Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+            for (Node<K,V> p; (p = it.advance()) != null; ) {
+                V v;
+                if ((v = p.val) == value || (v != null && value.equals(v)))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Maps the specified key to the specified value in this table.
+     * Neither the key nor the value can be null.
+     *
+     * <p>The value can be retrieved by calling the {@code get} method
+     * with a key that is equal to the original key.
+     *
+     * @param key key with which the specified value is to be associated
+     * @param value value to be associated with the specified key
+     * @return the previous value associated with {@code key}, or
+     *         {@code null} if there was no mapping for {@code key}
+     * @throws NullPointerException if the specified key or value is null
+     */
+    public V put(K key, V value) {
+        return putVal(key, value, false);
+    }
+
+    /** Implementation for put and putIfAbsent */
+    final V putVal(K key, V value, boolean onlyIfAbsent) {
+        if (key == null || value == null) throw new NullPointerException();
+        int hash = spread(key.hashCode());
+        int binCount = 0;
+        for (Node<K,V>[] tab = table;;) {
+            Node<K,V> f; int n, i, fh;
+            if (tab == null || (n = tab.length) == 0)
+                tab = initTable();
+            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
+                if (casTabAt(tab, i, null,
+                             new Node<K,V>(hash, key, value, null)))
+                    break;                   // no lock when adding to empty bin
+            }
+            else if ((fh = f.hash) == MOVED)
+                tab = helpTransfer(tab, f);
+            else {
+                V oldVal = null;
+                synchronized (f) {
+                    if (tabAt(tab, i) == f) {
+                        if (fh >= 0) {
+                            binCount = 1;
+                            for (Node<K,V> e = f;; ++binCount) {
+                                K ek;
+                                if (e.hash == hash &&
+                                    ((ek = e.key) == key ||
+                                     (ek != null && key.equals(ek)))) {
+                                    oldVal = e.val;
+                                    if (!onlyIfAbsent)
+                                        e.val = value;
+                                    break;
+                                }
+                                Node<K,V> pred = e;
+                                if ((e = e.next) == null) {
+                                    pred.next = new Node<K,V>(hash, key,
+                                                              value, null);
+                                    break;
+                                }
+                            }
+                        }
+                        else if (f instanceof TreeBin) {
+                            Node<K,V> p;
+                            binCount = 2;
+                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
+                                                           value)) != null) {
+                                oldVal = p.val;
+                                if (!onlyIfAbsent)
+                                    p.val = value;
+                            }
+                        }
+                    }
+                }
+                if (binCount != 0) {
+                    if (binCount >= TREEIFY_THRESHOLD)
+                        treeifyBin(tab, i);
+                    if (oldVal != null)
+                        return oldVal;
+                    break;
+                }
+            }
+        }
+        addCount(1L, binCount);
+        return null;
+    }
+
+    /**
+     * Copies all of the mappings from the specified map to this one.
+     * These mappings replace any mappings that this map had for any of the
+     * keys currently in the specified map.
+     *
+     * @param m mappings to be stored in this map
+     */
+    public void putAll(Map<? extends K, ? extends V> m) {
+        tryPresize(m.size());
+        for (Entry<? extends K, ? extends V> e : m.entrySet())
+            putVal(e.getKey(), e.getValue(), false);
+    }
+
+    /**
+     * Removes the key (and its corresponding value) from this map.
+     * This method does nothing if the key is not in the map.
+     *
+     * @param  key the key that needs to be removed
+     * @return the previous value associated with {@code key}, or
+     *         {@code null} if there was no mapping for {@code key}
+     * @throws NullPointerException if the specified key is null
+     */
+    public V remove(Object key) {
+        return replaceNode(key, null, null);
+    }
+
+    /**
+     * Implementation for the four public remove/replace methods:
+     * Replaces node value with v, conditional upon match of cv if
+     * non-null.  If resulting value is null, delete.
+     */
+    final V replaceNode(Object key, V value, Object cv) {
+        int hash = spread(key.hashCode());
+        for (Node<K,V>[] tab = table;;) {
+            Node<K,V> f; int n, i, fh;
+            if (tab == null || (n = tab.length) == 0 ||
+                (f = tabAt(tab, i = (n - 1) & hash)) == null)
+                break;
+            else if ((fh = f.hash) == MOVED)
+                tab = helpTransfer(tab, f);
+            else {
+                V oldVal = null;
+                boolean validated = false;
+                synchronized (f) {
+                    if (tabAt(tab, i) == f) {
+                        if (fh >= 0) {
+                            validated = true;
+                            for (Node<K,V> e = f, pred = null;;) {
+                                K ek;
+                                if (e.hash == hash &&
+                                    ((ek = e.key) == key ||
+                                     (ek != null && key.equals(ek)))) {
+                                    V ev = e.val;
+                                    if (cv == null || cv == ev ||
+                                        (ev != null && cv.equals(ev))) {
+                                        oldVal = ev;
+                                        if (value != null)
+                                            e.val = value;
+                                        else if (pred != null)
+                                            pred.next = e.next;
+                                        else
+                                            setTabAt(tab, i, e.next);
+                                    }
+                                    break;
+                                }
+                                pred = e;
+                                if ((e = e.next) == null)
+                                    break;
+                            }
+                        }
+                        else if (f instanceof TreeBin) {
+                            validated = true;
+                            TreeBin<K,V> t = (TreeBin<K,V>)f;
+                            TreeNode<K,V> r, p;
+                            if ((r = t.root) != null &&
+                                (p = r.findTreeNode(hash, key, null)) != null) {
+                                V pv = p.val;
+                                if (cv == null || cv == pv ||
+                                    (pv != null && cv.equals(pv))) {
+                                    oldVal = pv;
+                                    if (value != null)
+                                        p.val = value;
+                                    else if (t.removeTreeNode(p))
+                                        setTabAt(tab, i, untreeify(t.first));
+                                }
+                            }
+                        }
+                    }
+                }
+                if (validated) {
+                    if (oldVal != null) {
+                        if (value == null)
+                            addCount(-1L, -1);
+                        return oldVal;
+                    }
+                    break;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Removes all of the mappings from this map.
+     */
+    public void clear() {
+        long delta = 0L; // negative number of deletions
+        int i = 0;
+        Node<K,V>[] tab = table;
+        while (tab != null && i < tab.length) {
+            int fh;
+            Node<K,V> f = tabAt(tab, i);
+            if (f == null)
+                ++i;
+            else if ((fh = f.hash) == MOVED) {
+                tab = helpTransfer(tab, f);
+                i = 0; // restart
+            }
+            else {
+                synchronized (f) {
+                    if (tabAt(tab, i) == f) {
+                        Node<K,V> p = (fh >= 0 ? f :
+                                       (f instanceof TreeBin) ?
+                                       ((TreeBin<K,V>)f).first : null);
+                        while (p != null) {
+                            --delta;
+                            p = p.next;
+                        }
+                        setTabAt(tab, i++, null);
+                    }
+                }
+            }
+        }
+        if (delta != 0L)
+            addCount(delta, -1);
+    }
+
+    /**
+     * Returns a {@link Set} view of the keys contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa. The set supports element
+     * removal, which removes the corresponding mapping from this map,
+     * via the {@code Iterator.remove}, {@code Set.remove},
+     * {@code removeAll}, {@code retainAll}, and {@code clear}
+     * operations.  It does not support the {@code add} or
+     * {@code addAll} operations.
+     *
+     * <p>The view's {@code iterator} is a "weakly consistent" iterator
+     * that will never throw {@link ConcurrentModificationException},
+     * and guarantees to traverse elements as they existed upon
+     * construction of the iterator, and may (but is not guaranteed to)
+     * reflect any modifications subsequent to construction.
+     *
+     * @return the set view
+     */
+    public KeySetView<K,V> keySet() {
+        KeySetView<K,V> ks;
+        return (ks = keySet) != null ? ks : (keySet = new KeySetView<K,V>(this, null));
+    }
+
+    /**
+     * Returns a {@link Collection} view of the values contained in this map.
+     * The collection is backed by the map, so changes to the map are
+     * reflected in the collection, and vice-versa.  The collection
+     * supports element removal, which removes the corresponding
+     * mapping from this map, via the {@code Iterator.remove},
+     * {@code Collection.remove}, {@code removeAll},
+     * {@code retainAll}, and {@code clear} operations.  It does not
+     * support the {@code add} or {@code addAll} operations.
+     *
+     * <p>The view's {@code iterator} is a "weakly consistent" iterator
+     * that will never throw {@link ConcurrentModificationException},
+     * and guarantees to traverse elements as they existed upon
+     * construction of the iterator, and may (but is not guaranteed to)
+     * reflect any modifications subsequent to construction.
+     *
+     * @return the collection view
+     */
+    public Collection<V> values() {
+        ValuesView<K,V> vs;
+        return (vs = values) != null ? vs : (values = new ValuesView<K,V>(this));
+    }
+
+    /**
+     * Returns a {@link Set} view of the mappings contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa.  The set supports element
+     * removal, which removes the corresponding mapping from the map,
+     * via the {@code Iterator.remove}, {@code Set.remove},
+     * {@code removeAll}, {@code retainAll}, and {@code clear}
+     * operations.
+     *
+     * <p>The view's {@code iterator} is a "weakly consistent" iterator
+     * that will never throw {@link ConcurrentModificationException},
+     * and guarantees to traverse elements as they existed upon
+     * construction of the iterator, and may (but is not guaranteed to)
+     * reflect any modifications subsequent to construction.
+     *
+     * @return the set view
+     */
+    public Set<Entry<K,V>> entrySet() {
+        EntrySetView<K,V> es;
+        return (es = entrySet) != null ? es : (entrySet = new EntrySetView<K,V>(this));
+    }
+
+    /**
+     * Returns the hash code value for this {@link Map}, i.e.,
+     * the sum of, for each key-value pair in the map,
+     * {@code key.hashCode() ^ value.hashCode()}.
+     *
+     * @return the hash code value for this map
+     */
+    public int hashCode() {
+        int h = 0;
+        Node<K,V>[] t;
+        if ((t = table) != null) {
+            Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+            for (Node<K,V> p; (p = it.advance()) != null; )
+                h += p.key.hashCode() ^ p.val.hashCode();
+        }
+        return h;
+    }
+
+    /**
+     * Returns a string representation of this map.  The string
+     * representation consists of a list of key-value mappings (in no
+     * particular order) enclosed in braces ("{@code {}}").  Adjacent
+     * mappings are separated by the characters {@code ", "} (comma
+     * and space).  Each key-value mapping is rendered as the key
+     * followed by an equals sign ("{@code =}") followed by the
+     * associated value.
+     *
+     * @return a string representation of this map
+     */
+    public String toString() {
+        Node<K,V>[] t;
+        int f = (t = table) == null ? 0 : t.length;
+        Traverser<K,V> it = new Traverser<K,V>(t, f, 0, f);
+        StringBuilder sb = new StringBuilder();
+        sb.append('{');
+        Node<K,V> p;
+        if ((p = it.advance()) != null) {
+            for (;;) {
+                K k = p.key;
+                V v = p.val;
+                sb.append(k == this ? "(this Map)" : k);
+                sb.append('=');
+                sb.append(v == this ? "(this Map)" : v);
+                if ((p = it.advance()) == null)
+                    break;
+                sb.append(',').append(' ');
+            }
+        }
+        return sb.append('}').toString();
+    }
+
+    /**
+     * Compares the specified object with this map for equality.
+     * Returns {@code true} if the given object is a map with the same
+     * mappings as this map.  This operation may return misleading
+     * results if either map is concurrently modified during execution
+     * of this method.
+     *
+     * @param o object to be compared for equality with this map
+     * @return {@code true} if the specified object is equal to this map
+     */
+    public boolean equals(Object o) {
+        if (o != this) {
+            if (!(o instanceof Map))
+                return false;
+            Map<?,?> m = (Map<?,?>) o;
+            Node<K,V>[] t;
+            int f = (t = table) == null ? 0 : t.length;
+            Traverser<K,V> it = new Traverser<K,V>(t, f, 0, f);
+            for (Node<K,V> p; (p = it.advance()) != null; ) {
+                V val = p.val;
+                Object v = m.get(p.key);
+                if (v == null || (v != val && !v.equals(val)))
+                    return false;
+            }
+            for (Entry<?,?> e : m.entrySet()) {
+                Object mk, mv, v;
+                if ((mk = e.getKey()) == null ||
+                    (mv = e.getValue()) == null ||
+                    (v = get(mk)) == null ||
+                    (mv != v && !mv.equals(v)))
+                    return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Stripped-down version of helper class used in previous version,
+     * declared for the sake of serialization compatibility
+     */
+    static class Segment<K,V> extends ReentrantLock implements Serializable {
+        private static final long serialVersionUID = 2249069246763182397L;
+        final float loadFactor;
+        Segment(float lf) { this.loadFactor = lf; }
+    }
+
+    /**
+     * Saves the state of the {@code ConcurrentHashMapV8} instance to a
+     * stream (i.e., serializes it).
+     * @param s the stream
+     * @throws java.io.IOException if an I/O error occurs
+     * @serialData
+     * the key (Object) and value (Object)
+     * for each key-value mapping, followed by a null pair.
+     * The key-value mappings are emitted in no particular order.
+     */
+    private void writeObject(java.io.ObjectOutputStream s)
+        throws java.io.IOException {
+        // For serialization compatibility
+        // Emulate segment calculation from previous version of this class
+        int sshift = 0;
+        int ssize = 1;
+        while (ssize < DEFAULT_CONCURRENCY_LEVEL) {
+            ++sshift;
+            ssize <<= 1;
+        }
+        int segmentShift = 32 - sshift;
+        int segmentMask = ssize - 1;
+        @SuppressWarnings("unchecked") Segment<K,V>[] segments = (Segment<K,V>[])
+            new Segment<?,?>[DEFAULT_CONCURRENCY_LEVEL];
+        for (int i = 0; i < segments.length; ++i)
+            segments[i] = new Segment<K,V>(LOAD_FACTOR);
+        s.putFields().put("segments", segments);
+        s.putFields().put("segmentShift", segmentShift);
+        s.putFields().put("segmentMask", segmentMask);
+        s.writeFields();
+
+        Node<K,V>[] t;
+        if ((t = table) != null) {
+            Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+            for (Node<K,V> p; (p = it.advance()) != null; ) {
+                s.writeObject(p.key);
+                s.writeObject(p.val);
+            }
+        }
+        s.writeObject(null);
+        s.writeObject(null);
+        segments = null; // throw away
+    }
+
+    /**
+     * Reconstitutes the instance from a stream (that is, deserializes it).
+     * @param s the stream
+     * @throws ClassNotFoundException if the class of a serialized object
+     *         could not be found
+     * @throws java.io.IOException if an I/O error occurs
+     */
+    private void readObject(java.io.ObjectInputStream s)
+        throws java.io.IOException, ClassNotFoundException {
+        /*
+         * To improve performance in typical cases, we create nodes
+         * while reading, then place in table once size is known.
+         * However, we must also validate uniqueness and deal with
+         * overpopulated bins while doing so, which requires
+         * specialized versions of putVal mechanics.
+         */
+        sizeCtl = -1; // force exclusion for table construction
+        s.defaultReadObject();
+        long size = 0L;
+        Node<K,V> p = null;
+        for (;;) {
+            @SuppressWarnings("unchecked") K k = (K) s.readObject();
+            @SuppressWarnings("unchecked") V v = (V) s.readObject();
+            if (k != null && v != null) {
+                p = new Node<K,V>(spread(k.hashCode()), k, v, p);
+                ++size;
+            }
+            else
+                break;
+        }
+        if (size == 0L)
+            sizeCtl = 0;
+        else {
+            int n;
+            if (size >= (long)(MAXIMUM_CAPACITY >>> 1))
+                n = MAXIMUM_CAPACITY;
+            else {
+                int sz = (int)size;
+                n = tableSizeFor(sz + (sz >>> 1) + 1);
+            }
+            @SuppressWarnings("unchecked")
+                Node<K,V>[] tab = (Node<K,V>[])new Node<?,?>[n];
+            int mask = n - 1;
+            long added = 0L;
+            while (p != null) {
+                boolean insertAtFront;
+                Node<K,V> next = p.next, first;
+                int h = p.hash, j = h & mask;
+                if ((first = tabAt(tab, j)) == null)
+                    insertAtFront = true;
+                else {
+                    K k = p.key;
+                    if (first.hash < 0) {
+                        TreeBin<K,V> t = (TreeBin<K,V>)first;
+                        if (t.putTreeVal(h, k, p.val) == null)
+                            ++added;
+                        insertAtFront = false;
+                    }
+                    else {
+                        int binCount = 0;
+                        insertAtFront = true;
+                        Node<K,V> q; K qk;
+                        for (q = first; q != null; q = q.next) {
+                            if (q.hash == h &&
+                                ((qk = q.key) == k ||
+                                 (qk != null && k.equals(qk)))) {
+                                insertAtFront = false;
+                                break;
+                            }
+                            ++binCount;
+                        }
+                        if (insertAtFront && binCount >= TREEIFY_THRESHOLD) {
+                            insertAtFront = false;
+                            ++added;
+                            p.next = first;
+                            TreeNode<K,V> hd = null, tl = null;
+                            for (q = p; q != null; q = q.next) {
+                                TreeNode<K,V> t = new TreeNode<K,V>
+                                    (q.hash, q.key, q.val, null, null);
+                                if ((t.prev = tl) == null)
+                                    hd = t;
+                                else
+                                    tl.next = t;
+                                tl = t;
+                            }
+                            setTabAt(tab, j, new TreeBin<K,V>(hd));
+                        }
+                    }
+                }
+                if (insertAtFront) {
+                    ++added;
+                    p.next = first;
+                    setTabAt(tab, j, p);
+                }
+                p = next;
+            }
+            table = tab;
+            sizeCtl = n - (n >>> 2);
+            baseCount = added;
+        }
+    }
+
+    // ConcurrentMap methods
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return the previous value associated with the specified key,
+     *         or {@code null} if there was no mapping for the key
+     * @throws NullPointerException if the specified key or value is null
+     */
+    public V putIfAbsent(K key, V value) {
+        return putVal(key, value, true);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws NullPointerException if the specified key is null
+     */
+    public boolean remove(Object key, Object value) {
+        if (key == null)
+            throw new NullPointerException();
+        return value != null && replaceNode(key, null, value) != null;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws NullPointerException if any of the arguments are null
+     */
+    public boolean replace(K key, V oldValue, V newValue) {
+        if (key == null || oldValue == null || newValue == null)
+            throw new NullPointerException();
+        return replaceNode(key, newValue, oldValue) != null;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return the previous value associated with the specified key,
+     *         or {@code null} if there was no mapping for the key
+     * @throws NullPointerException if the specified key or value is null
+     */
+    public V replace(K key, V value) {
+        if (key == null || value == null)
+            throw new NullPointerException();
+        return replaceNode(key, value, null);
+    }
+
+    // Overrides of JDK8+ Map extension method defaults
+
+    /**
+     * Returns the value to which the specified key is mapped, or the
+     * given default value if this map contains no mapping for the
+     * key.
+     *
+     * @param key the key whose associated value is to be returned
+     * @param defaultValue the value to return if this map contains
+     * no mapping for the given key
+     * @return the mapping for the key, if present; else the default value
+     * @throws NullPointerException if the specified key is null
+     */
+    public V getOrDefault(Object key, V defaultValue) {
+        V v;
+        return (v = get(key)) == null ? defaultValue : v;
+    }
+
+    public void forEach(BiAction<? super K, ? super V> action) {
+        if (action == null) throw new NullPointerException();
+        Node<K,V>[] t;
+        if ((t = table) != null) {
+            Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+            for (Node<K,V> p; (p = it.advance()) != null; ) {
+                action.apply(p.key, p.val);
+            }
+        }
+    }
+
+    public void replaceAll(BiFun<? super K, ? super V, ? extends V> function) {
+        if (function == null) throw new NullPointerException();
+        Node<K,V>[] t;
+        if ((t = table) != null) {
+            Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+            for (Node<K,V> p; (p = it.advance()) != null; ) {
+                V oldValue = p.val;
+                for (K key = p.key;;) {
+                    V newValue = function.apply(key, oldValue);
+                    if (newValue == null)
+                        throw new NullPointerException();
+                    if (replaceNode(key, newValue, oldValue) != null ||
+                        (oldValue = get(key)) == null)
+                        break;
+                }
+            }
+        }
+    }
+
+    /**
+     * If the specified key is not already associated with a value,
+     * attempts to compute its value using the given mapping function
+     * and enters it into this map unless {@code null}.  The entire
+     * method invocation is performed atomically, so the function is
+     * applied at most once per key.  Some attempted update operations
+     * on this map by other threads may be blocked while computation
+     * is in progress, so the computation should be short and simple,
+     * and must not attempt to update any other mappings of this map.
+     *
+     * @param key key with which the specified value is to be associated
+     * @param mappingFunction the function to compute a value
+     * @return the current (existing or computed) value associated with
+     *         the specified key, or null if the computed value is null
+     * @throws NullPointerException if the specified key or mappingFunction
+     *         is null
+     * @throws IllegalStateException if the computation detectably
+     *         attempts a recursive update to this map that would
+     *         otherwise never complete
+     * @throws RuntimeException or Error if the mappingFunction does so,
+     *         in which case the mapping is left unestablished
+     */
+    public V computeIfAbsent(K key, Fun<? super K, ? extends V> mappingFunction) {
+        if (key == null || mappingFunction == null)
+            throw new NullPointerException();
+        int h = spread(key.hashCode());
+        V val = null;
+        int binCount = 0;
+        for (Node<K,V>[] tab = table;;) {
+            Node<K,V> f; int n, i, fh;
+            if (tab == null || (n = tab.length) == 0)
+                tab = initTable();
+            else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
+                Node<K,V> r = new ReservationNode<K,V>();
+                synchronized (r) {
+                    if (casTabAt(tab, i, null, r)) {
+                        binCount = 1;
+                        Node<K,V> node = null;
+                        try {
+                            if ((val = mappingFunction.apply(key)) != null)
+                                node = new Node<K,V>(h, key, val, null);
+                        } finally {
+                            setTabAt(tab, i, node);
+                        }
+                    }
+                }
+                if (binCount != 0)
+                    break;
+            }
+            else if ((fh = f.hash) == MOVED)
+                tab = helpTransfer(tab, f);
+            else {
+                boolean added = false;
+                synchronized (f) {
+                    if (tabAt(tab, i) == f) {
+                        if (fh >= 0) {
+                            binCount = 1;
+                            for (Node<K,V> e = f;; ++binCount) {
+                                K ek; V ev;
+                                if (e.hash == h &&
+                                    ((ek = e.key) == key ||
+                                     (ek != null && key.equals(ek)))) {
+                                    val = e.val;
+                                    break;
+                                }
+                                Node<K,V> pred = e;
+                                if ((e = e.next) == null) {
+                                    if ((val = mappingFunction.apply(key)) != null) {
+                                        added = true;
+                                        pred.next = new Node<K,V>(h, key, val, null);
+                                    }
+                                    break;
+                                }
+                            }
+                        }
+                        else if (f instanceof TreeBin) {
+                            binCount = 2;
+                            TreeBin<K,V> t = (TreeBin<K,V>)f;
+                            TreeNode<K,V> r, p;
+                            if ((r = t.root) != null &&
+                                (p = r.findTreeNode(h, key, null)) != null)
+                                val = p.val;
+                            else if ((val = mappingFunction.apply(key)) != null) {
+                                added = true;
+                                t.putTreeVal(h, key, val);
+                            }
+                        }
+                    }
+                }
+                if (binCount != 0) {
+                    if (binCount >= TREEIFY_THRESHOLD)
+                        treeifyBin(tab, i);
+                    if (!added)
+                        return val;
+                    break;
+                }
+            }
+        }
+        if (val != null)
+            addCount(1L, binCount);
+        return val;
+    }
+
+    /**
+     * If the value for the specified key is present, attempts to
+     * compute a new mapping given the key and its current mapped
+     * value.  The entire method invocation is performed atomically.
+     * Some attempted update operations on this map by other threads
+     * may be blocked while computation is in progress, so the
+     * computation should be short and simple, and must not attempt to
+     * update any other mappings of this map.
+     *
+     * @param key key with which a value may be associated
+     * @param remappingFunction the function to compute a value
+     * @return the new value associated with the specified key, or null if none
+     * @throws NullPointerException if the specified key or remappingFunction
+     *         is null
+     * @throws IllegalStateException if the computation detectably
+     *         attempts a recursive update to this map that would
+     *         otherwise never complete
+     * @throws RuntimeException or Error if the remappingFunction does so,
+     *         in which case the mapping is unchanged
+     */
+    public V computeIfPresent(K key, BiFun<? super K, ? super V, ? extends V> remappingFunction) {
+        if (key == null || remappingFunction == null)
+            throw new NullPointerException();
+        int h = spread(key.hashCode());
+        V val = null;
+        int delta = 0;
+        int binCount = 0;
+        for (Node<K,V>[] tab = table;;) {
+            Node<K,V> f; int n, i, fh;
+            if (tab == null || (n = tab.length) == 0)
+                tab = initTable();
+            else if ((f = tabAt(tab, i = (n - 1) & h)) == null)
+                break;
+            else if ((fh = f.hash) == MOVED)
+                tab = helpTransfer(tab, f);
+            else {
+                synchronized (f) {
+                    if (tabAt(tab, i) == f) {
+                        if (fh >= 0) {
+                            binCount = 1;
+                            for (Node<K,V> e = f, pred = null;; ++binCount) {
+                                K ek;
+                                if (e.hash == h &&
+                                    ((ek = e.key) == key ||
+                                     (ek != null && key.equals(ek)))) {
+                                    val = remappingFunction.apply(key, e.val);
+                                    if (val != null)
+                                        e.val = val;
+                                    else {
+                                        delta = -1;
+                                        Node<K,V> en = e.next;
+                                        if (pred != null)
+                                            pred.next = en;
+                                        else
+                                            setTabAt(tab, i, en);
+                                    }
+                                    break;
+                                }
+                                pred = e;
+                                if ((e = e.next) == null)
+                                    break;
+                            }
+                        }
+                        else if (f instanceof TreeBin) {
+                            binCount = 2;
+                            TreeBin<K,V> t = (TreeBin<K,V>)f;
+                            TreeNode<K,V> r, p;
+                            if ((r = t.root) != null &&
+                                (p = r.findTreeNode(h, key, null)) != null) {
+                                val = remappingFunction.apply(key, p.val);
+                                if (val != null)
+                                    p.val = val;
+                                else {
+                                    delta = -1;
+                                    if (t.removeTreeNode(p))
+                                        setTabAt(tab, i, untreeify(t.first));
+                                }
+                            }
+                        }
+                    }
+                }
+                if (binCount != 0)
+                    break;
+            }
+        }
+        if (delta != 0)
+            addCount((long)delta, binCount);
+        return val;
+    }
+
+    /**
+     * Attempts to compute a mapping for the specified key and its
+     * current mapped value (or {@code null} if there is no current
+     * mapping). The entire method invocation is performed atomically.
+     * Some attempted update operations on this map by other threads
+     * may be blocked while computation is in progress, so the
+     * computation should be short and simple, and must not attempt to
+     * update any other mappings of this Map.
+     *
+     * @param key key with which the specified value is to be associated
+     * @param remappingFunction the function to compute a value
+     * @return the new value associated with the specified key, or null if none
+     * @throws NullPointerException if the specified key or remappingFunction
+     *         is null
+     * @throws IllegalStateException if the computation detectably
+     *         attempts a recursive update to this map that would
+     *         otherwise never complete
+     * @throws RuntimeException or Error if the remappingFunction does so,
+     *         in which case the mapping is unchanged
+     */
+    public V compute(K key,
+                     BiFun<? super K, ? super V, ? extends V> remappingFunction) {
+        if (key == null || remappingFunction == null)
+            throw new NullPointerException();
+        int h = spread(key.hashCode());
+        V val = null;
+        int delta = 0;
+        int binCount = 0;
+        for (Node<K,V>[] tab = table;;) {
+            Node<K,V> f; int n, i, fh;
+            if (tab == null || (n = tab.length) == 0)
+                tab = initTable();
+            else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
+                Node<K,V> r = new ReservationNode<K,V>();
+                synchronized (r) {
+                    if (casTabAt(tab, i, null, r)) {
+                        binCount = 1;
+                        Node<K,V> node = null;
+                        try {
+                            if ((val = remappingFunction.apply(key, null)) != null) {
+                                delta = 1;
+                                node = new Node<K,V>(h, key, val, null);
+                            }
+                        } finally {
+                            setTabAt(tab, i, node);
+                        }
+                    }
+                }
+                if (binCount != 0)
+                    break;
+            }
+            else if ((fh = f.hash) == MOVED)
+                tab = helpTransfer(tab, f);
+            else {
+                synchronized (f) {
+                    if (tabAt(tab, i) == f) {
+                        if (fh >= 0) {
+                            binCount = 1;
+                            for (Node<K,V> e = f, pred = null;; ++binCount) {
+                                K ek;
+                                if (e.hash == h &&
+                                    ((ek = e.key) == key ||
+                                     (ek != null && key.equals(ek)))) {
+                                    val = remappingFunction.apply(key, e.val);
+                                    if (val != null)
+                                        e.val = val;
+                                    else {
+                                        delta = -1;
+                                        Node<K,V> en = e.next;
+                                        if (pred != null)
+                                            pred.next = en;
+                                        else
+                                            setTabAt(tab, i, en);
+                                    }
+                                    break;
+                                }
+                                pred = e;
+                                if ((e = e.next) == null) {
+                                    val = remappingFunction.apply(key, null);
+                                    if (val != null) {
+                                        delta = 1;
+                                        pred.next =
+                                            new Node<K,V>(h, key, val, null);
+                                    }
+                                    break;
+                                }
+                            }
+                        }
+                        else if (f instanceof TreeBin) {
+                            binCount = 1;
+                            TreeBin<K,V> t = (TreeBin<K,V>)f;
+                            TreeNode<K,V> r, p;
+                            if ((r = t.root) != null)
+                                p = r.findTreeNode(h, key, null);
+                            else
+                                p = null;
+                            V pv = (p == null) ? null : p.val;
+                            val = remappingFunction.apply(key, pv);
+                            if (val != null) {
+                                if (p != null)
+                                    p.val = val;
+                                else {
+                                    delta = 1;
+                                    t.putTreeVal(h, key, val);
+                                }
+                            }
+                            else if (p != null) {
+                                delta = -1;
+                                if (t.removeTreeNode(p))
+                                    setTabAt(tab, i, untreeify(t.first));
+                            }
+                        }
+                    }
+                }
+                if (binCount != 0) {
+                    if (binCount >= TREEIFY_THRESHOLD)
+                        treeifyBin(tab, i);
+                    break;
+                }
+            }
+        }
+        if (delta != 0)
+            addCount((long)delta, binCount);
+        return val;
+    }
+
+    /**
+     * If the specified key is not already associated with a
+     * (non-null) value, associates it with the given value.
+     * Otherwise, replaces the value with the results of the given
+     * remapping function, or removes if {@code null}. The entire
+     * method invocation is performed atomically.  Some attempted
+     * update operations on this map by other threads may be blocked
+     * while computation is in progress, so the computation should be
+     * short and simple, and must not attempt to update any other
+     * mappings of this Map.
+     *
+     * @param key key with which the specified value is to be associated
+     * @param value the value to use if absent
+     * @param remappingFunction the function to recompute a value if present
+     * @return the new value associated with the specified key, or null if none
+     * @throws NullPointerException if the specified key or the
+     *         remappingFunction is null
+     * @throws RuntimeException or Error if the remappingFunction does so,
+     *         in which case the mapping is unchanged
+     */
+    public V merge(K key, V value, BiFun<? super V, ? super V, ? extends V> remappingFunction) {
+        if (key == null || value == null || remappingFunction == null)
+            throw new NullPointerException();
+        int h = spread(key.hashCode());
+        V val = null;
+        int delta = 0;
+        int binCount = 0;
+        for (Node<K,V>[] tab = table;;) {
+            Node<K,V> f; int n, i, fh;
+            if (tab == null || (n = tab.length) == 0)
+                tab = initTable();
+            else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
+                if (casTabAt(tab, i, null, new Node<K,V>(h, key, value, null))) {
+                    delta = 1;
+                    val = value;
+                    break;
+                }
+            }
+            else if ((fh = f.hash) == MOVED)
+                tab = helpTransfer(tab, f);
+            else {
+                synchronized (f) {
+                    if (tabAt(tab, i) == f) {
+                        if (fh >= 0) {
+                            binCount = 1;
+                            for (Node<K,V> e = f, pred = null;; ++binCount) {
+                                K ek;
+                                if (e.hash == h &&
+                                    ((ek = e.key) == key ||
+                                     (ek != null && key.equals(ek)))) {
+                                    val = remappingFunction.apply(e.val, value);
+                                    if (val != null)
+                                        e.val = val;
+                                    else {
+                                        delta = -1;
+                                        Node<K,V> en = e.next;
+                                        if (pred != null)
+                                            pred.next = en;
+                                        else
+                                            setTabAt(tab, i, en);
+                                    }
+                                    break;
+                                }
+                                pred = e;
+                                if ((e = e.next) == null) {
+                                    delta = 1;
+                                    val = value;
+                                    pred.next =
+                                        new Node<K,V>(h, key, val, null);
+                                    break;
+                                }
+                            }
+                        }
+                        else if (f instanceof TreeBin) {
+                            binCount = 2;
+                            TreeBin<K,V> t = (TreeBin<K,V>)f;
+                            TreeNode<K,V> r = t.root;
+                            TreeNode<K,V> p = (r == null) ? null :
+                                r.findTreeNode(h, key, null);
+                            val = (p == null) ? value :
+                                remappingFunction.apply(p.val, value);
+                            if (val != null) {
+                                if (p != null)
+                                    p.val = val;
+                                else {
+                                    delta = 1;
+                                    t.putTreeVal(h, key, val);
+                                }
+                            }
+                            else if (p != null) {
+                                delta = -1;
+                                if (t.removeTreeNode(p))
+                                    setTabAt(tab, i, untreeify(t.first));
+                            }
+                        }
+                    }
+                }
+                if (binCount != 0) {
+                    if (binCount >= TREEIFY_THRESHOLD)
+                        treeifyBin(tab, i);
+                    break;
+                }
+            }
+        }
+        if (delta != 0)
+            addCount((long)delta, binCount);
+        return val;
+    }
+
+    // Hashtable legacy methods
+
+    /**
+     * Legacy method testing if some key maps into the specified value
+     * in this table.  This method is identical in functionality to
+     * {@link #containsValue(Object)}, and exists solely to ensure
+     * full compatibility with class {@link Hashtable},
+     * which supported this method prior to introduction of the
+     * Java Collections framework.
+     *
+     * @param  value a value to search for
+     * @return {@code true} if and only if some key maps to the
+     *         {@code value} argument in this table as
+     *         determined by the {@code equals} method;
+     *         {@code false} otherwise
+     * @throws NullPointerException if the specified value is null
+     */
+    @Deprecated public boolean contains(Object value) {
+        return containsValue(value);
+    }
+
+    /**
+     * Returns an enumeration of the keys in this table.
+     *
+     * @return an enumeration of the keys in this table
+     * @see #keySet()
+     */
+    public Enumeration<K> keys() {
+        Node<K,V>[] t;
+        int f = (t = table) == null ? 0 : t.length;
+        return new KeyIterator<K,V>(t, f, 0, f, this);
+    }
+
+    /**
+     * Returns an enumeration of the values in this table.
+     *
+     * @return an enumeration of the values in this table
+     * @see #values()
+     */
+    public Enumeration<V> elements() {
+        Node<K,V>[] t;
+        int f = (t = table) == null ? 0 : t.length;
+        return new ValueIterator<K,V>(t, f, 0, f, this);
+    }
+
+    // ConcurrentHashMapV8-only methods
+
+    /**
+     * Returns the number of mappings. This method should be used
+     * instead of {@link #size} because a ConcurrentHashMapV8 may
+     * contain more mappings than can be represented as an int. The
+     * value returned is an estimate; the actual count may differ if
+     * there are concurrent insertions or removals.
+     *
+     * @return the number of mappings
+     * @since 1.8
+     */
+    public long mappingCount() {
+        long n = sumCount();
+        return (n < 0L) ? 0L : n; // ignore transient negative values
+    }
+
+    /**
+     * Creates a new {@link Set} backed by a ConcurrentHashMapV8
+     * from the given type to {@code Boolean.TRUE}.
+     *
+     * @return the new set
+     * @since 1.8
+     */
+    public static <K> KeySetView<K,Boolean> newKeySet() {
+        return new KeySetView<K,Boolean>
+            (new ConcurrentHashMapV8<K,Boolean>(), Boolean.TRUE);
+    }
+
+    /**
+     * Creates a new {@link Set} backed by a ConcurrentHashMapV8
+     * from the given type to {@code Boolean.TRUE}.
+     *
+     * @param initialCapacity The implementation performs internal
+     * sizing to accommodate this many elements.
+     * @return the new set
+     * @throws IllegalArgumentException if the initial capacity of
+     * elements is negative
+     * @since 1.8
+     */
+    public static <K> KeySetView<K,Boolean> newKeySet(int initialCapacity) {
+        return new KeySetView<K,Boolean>
+            (new ConcurrentHashMapV8<K,Boolean>(initialCapacity), Boolean.TRUE);
+    }
+
+    /**
+     * Returns a {@link Set} view of the keys in this map, using the
+     * given common mapped value for any additions (i.e., {@link
+     * Collection#add} and {@link Collection#addAll(Collection)}).
+     * This is of course only appropriate if it is acceptable to use
+     * the same value for all additions from this view.
+     *
+     * @param mappedValue the mapped value to use for any additions
+     * @return the set view
+     * @throws NullPointerException if the mappedValue is null
+     */
+    public KeySetView<K,V> keySet(V mappedValue) {
+        if (mappedValue == null)
+            throw new NullPointerException();
+        return new KeySetView<K,V>(this, mappedValue);
+    }
+
+    /* ---------------- Special Nodes -------------- */
+
+    /**
+     * A node inserted at head of bins during transfer operations.
+     */
+    static final class ForwardingNode<K,V> extends Node<K,V> {
+        final Node<K,V>[] nextTable;
+        ForwardingNode(Node<K,V>[] tab) {
+            super(MOVED, null, null, null);
+            this.nextTable = tab;
+        }
+
+        Node<K,V> find(int h, Object k) {
+            // loop to avoid arbitrarily deep recursion on forwarding nodes
+            outer: for (Node<K,V>[] tab = nextTable;;) {
+                Node<K,V> e; int n;
+                if (k == null || tab == null || (n = tab.length) == 0 ||
+                    (e = tabAt(tab, (n - 1) & h)) == null)
+                    return null;
+                for (;;) {
+                    int eh; K ek;
+                    if ((eh = e.hash) == h &&
+                        ((ek = e.key) == k || (ek != null && k.equals(ek))))
+                        return e;
+                    if (eh < 0) {
+                        if (e instanceof ForwardingNode) {
+                            tab = ((ForwardingNode<K,V>)e).nextTable;
+                            continue outer;
+                        }
+                        else
+                            return e.find(h, k);
+                    }
+                    if ((e = e.next) == null)
+                        return null;
+                }
+            }
+        }
+    }
+
+    /**
+     * A place-holder node used in computeIfAbsent and compute
+     */
+    static final class ReservationNode<K,V> extends Node<K,V> {
+        ReservationNode() {
+            super(RESERVED, null, null, null);
+        }
+
+        Node<K,V> find(int h, Object k) {
+            return null;
+        }
+    }
+
+    /* ---------------- Table Initialization and Resizing -------------- */
+
+    /**
+     * Returns the stamp bits for resizing a table of size n.
+     * Must be negative when shifted left by RESIZE_STAMP_SHIFT.
+     */
+    static final int resizeStamp(int n) {
+        return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
+    }
+
+    /**
+     * Initializes table, using the size recorded in sizeCtl.
+     */
+    private final Node<K,V>[] initTable() {
+        Node<K,V>[] tab; int sc;
+        while ((tab = table) == null || tab.length == 0) {
+            if ((sc = sizeCtl) < 0)
+                Thread.yield(); // lost initialization race; just spin
+            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
+                try {
+                    if ((tab = table) == null || tab.length == 0) {
+                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
+                        @SuppressWarnings("unchecked")
+                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
+                        table = tab = nt;
+                        sc = n - (n >>> 2);
+                    }
+                } finally {
+                    sizeCtl = sc;
+                }
+                break;
+            }
+        }
+        return tab;
+    }
+
+    /**
+     * Adds to count, and if table is too small and not already
+     * resizing, initiates transfer. If already resizing, helps
+     * perform transfer if work is available.  Rechecks occupancy
+     * after a transfer to see if another resize is already needed
+     * because resizings are lagging additions.
+     *
+     * @param x the count to add
+     * @param check if <0, don't check resize, if <= 1 only check if uncontended
+     */
+    private final void addCount(long x, int check) {
+        CounterCell[] as; long b, s;
+        if ((as = counterCells) != null ||
+            !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
+            CounterHashCode hc; CounterCell a; long v; int m;
+            boolean uncontended = true;
+            if ((hc = threadCounterHashCode.get()) == null ||
+                as == null || (m = as.length - 1) < 0 ||
+                (a = as[m & hc.code]) == null ||
+                !(uncontended =
+                  U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
+                fullAddCount(x, hc, uncontended);
+                return;
+            }
+            if (check <= 1)
+                return;
+            s = sumCount();
+        }
+        if (check >= 0) {
+            Node<K,V>[] tab, nt; int n, sc;
+            while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
+                   (n = tab.length) < MAXIMUM_CAPACITY) {
+                int rs = resizeStamp(n);
+                if (sc < 0) {
+                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
+                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
+                        transferIndex <= 0)
+                        break;
+                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
+                        transfer(tab, nt);
+                }
+                else if (U.compareAndSwapInt(this, SIZECTL, sc,
+                                             (rs << RESIZE_STAMP_SHIFT) + 2))
+                    transfer(tab, null);
+                s = sumCount();
+            }
+        }
+    }
+
+    /**
+     * Helps transfer if a resize is in progress.
+     */
+    final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
+        Node<K,V>[] nextTab; int sc;
+        if (tab != null && (f instanceof ForwardingNode) &&
+            (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
+            int rs = resizeStamp(tab.length);
+            while (nextTab == nextTable && table == tab &&
+                   (sc = sizeCtl) < 0) {
+                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
+                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
+                    break;
+                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
+                    transfer(tab, nextTab);
+                    break;
+                }
+            }
+            return nextTab;
+        }
+        return table;
+    }
+
+    /**
+     * Tries to presize table to accommodate the given number of elements.
+     *
+     * @param size number of elements (doesn't need to be perfectly accurate)
+     */
+    private final void tryPresize(int size) {
+        int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :
+            tableSizeFor(size + (size >>> 1) + 1);
+        int sc;
+        while ((sc = sizeCtl) >= 0) {
+            Node<K,V>[] tab = table; int n;
+            if (tab == null || (n = tab.length) == 0) {
+                n = (sc > c) ? sc : c;
+                if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
+                    try {
+                        if (table == tab) {
+                            @SuppressWarnings("unchecked")
+                            Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
+                            table = nt;
+                            sc = n - (n >>> 2);
+                        }
+                    } finally {
+                        sizeCtl = sc;
+                    }
+                }
+            }
+            else if (c <= sc || n >= MAXIMUM_CAPACITY)
+                break;
+            else if (tab == table) {
+                int rs = resizeStamp(n);
+                if (sc < 0) {
+                    Node<K,V>[] nt;
+                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
+                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
+                        transferIndex <= 0)
+                        break;
+                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
+                        transfer(tab, nt);
+                }
+                else if (U.compareAndSwapInt(this, SIZECTL, sc,
+                                             (rs << RESIZE_STAMP_SHIFT) + 2))
+                    transfer(tab, null);
+            }
+        }
+    }
+
+    /**
+     * Moves and/or copies the nodes in each bin to new table. See
+     * above for explanation.
+     */
+    private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
+        int n = tab.length, stride;
+        if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
+            stride = MIN_TRANSFER_STRIDE; // subdivide range
+        if (nextTab == null) {            // initiating
+            try {
+                @SuppressWarnings("unchecked")
+                Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
+                nextTab = nt;
+            } catch (Throwable ex) {      // try to cope with OOME
+                sizeCtl = Integer.MAX_VALUE;
+                return;
+            }
+            nextTable = nextTab;
+            transferIndex = n;
+        }
+        int nextn = nextTab.length;
+        ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
+        boolean advance = true;
+        boolean finishing = false; // to ensure sweep before committing nextTab
+        for (int i = 0, bound = 0;;) {
+            Node<K,V> f; int fh;
+            while (advance) {
+                int nextIndex, nextBound;
+                if (--i >= bound || finishing)
+                    advance = false;
+                else if ((nextIndex = transferIndex) <= 0) {
+                    i = -1;
+                    advance = false;
+                }
+                else if (U.compareAndSwapInt
+                         (this, TRANSFERINDEX, nextIndex,
+                          nextBound = (nextIndex > stride ?
+                                       nextIndex - stride : 0))) {
+                    bound = nextBound;
+                    i = nextIndex - 1;
+                    advance = false;
+                }
+            }
+            if (i < 0 || i >= n || i + n >= nextn) {
+                int sc;
+                if (finishing) {
+                    nextTable = null;
+                    table = nextTab;
+                    sizeCtl = (n << 1) - (n >>> 1);
+                    return;
+                }
+                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
+                    if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
+                        return;
+                    finishing = advance = true;
+                    i = n; // recheck before commit
+                }
+            }
+            else if ((f = tabAt(tab, i)) == null)
+                advance = casTabAt(tab, i, null, fwd);
+            else if ((fh = f.hash) == MOVED)
+                advance = true; // already processed
+            else {
+                synchronized (f) {
+                    if (tabAt(tab, i) == f) {
+                        Node<K,V> ln, hn;
+                        if (fh >= 0) {
+                            int runBit = fh & n;
+                            Node<K,V> lastRun = f;
+                            for (Node<K,V> p = f.next; p != null; p = p.next) {
+                                int b = p.hash & n;
+                                if (b != runBit) {
+                                    runBit = b;
+                                    lastRun = p;
+                                }
+                            }
+                            if (runBit == 0) {
+                                ln = lastRun;
+                                hn = null;
+                            }
+                            else {
+                                hn = lastRun;
+                                ln = null;
+                            }
+                            for (Node<K,V> p = f; p != lastRun; p = p.next) {
+                                int ph = p.hash; K pk = p.key; V pv = p.val;
+                                if ((ph & n) == 0)
+                                    ln = new Node<K,V>(ph, pk, pv, ln);
+                                else
+                                    hn = new Node<K,V>(ph, pk, pv, hn);
+                            }
+                            setTabAt(nextTab, i, ln);
+                            setTabAt(nextTab, i + n, hn);
+                            setTabAt(tab, i, fwd);
+                            advance = true;
+                        }
+                        else if (f instanceof TreeBin) {
+                            TreeBin<K,V> t = (TreeBin<K,V>)f;
+                            TreeNode<K,V> lo = null, loTail = null;
+                            TreeNode<K,V> hi = null, hiTail = null;
+                            int lc = 0, hc = 0;
+                            for (Node<K,V> e = t.first; e != null; e = e.next) {
+                                int h = e.hash;
+                                TreeNode<K,V> p = new TreeNode<K,V>
+                                    (h, e.key, e.val, null, null);
+                                if ((h & n) == 0) {
+                                    if ((p.prev = loTail) == null)
+                                        lo = p;
+                                    else
+                                        loTail.next = p;
+                                    loTail = p;
+                                    ++lc;
+                                }
+                                else {
+                                    if ((p.prev = hiTail) == null)
+                                        hi = p;
+                                    else
+                                        hiTail.next = p;
+                                    hiTail = p;
+                                    ++hc;
+                                }
+                            }
+                            ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
+                                (hc != 0) ? new TreeBin<K,V>(lo) : t;
+                            hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
+                                (lc != 0) ? new TreeBin<K,V>(hi) : t;
+                            setTabAt(nextTab, i, ln);
+                            setTabAt(nextTab, i + n, hn);
+                            setTabAt(tab, i, fwd);
+                            advance = true;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /* ---------------- Conversion from/to TreeBins -------------- */
+
+    /**
+     * Replaces all linked nodes in bin at given index unless table is
+     * too small, in which case resizes instead.
+     */
+    private final void treeifyBin(Node<K,V>[] tab, int index) {
+        Node<K,V> b; int n, sc;
+        if (tab != null) {
+            if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
+                tryPresize(n << 1);
+            else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
+                synchronized (b) {
+                    if (tabAt(tab, index) == b) {
+                        TreeNode<K,V> hd = null, tl = null;
+                        for (Node<K,V> e = b; e != null; e = e.next) {
+                            TreeNode<K,V> p =
+                                new TreeNode<K,V>(e.hash, e.key, e.val,
+                                                  null, null);
+                            if ((p.prev = tl) == null)
+                                hd = p;
+                            else
+                                tl.next = p;
+                            tl = p;
+                        }
+                        setTabAt(tab, index, new TreeBin<K,V>(hd));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns a list of non-TreeNodes replacing those in given list.
+     */
+    static <K,V> Node<K,V> untreeify(Node<K,V> b) {
+        Node<K,V> hd = null, tl = null;
+        for (Node<K,V> q = b; q != null; q = q.next) {
+            Node<K,V> p = new Node<K,V>(q.hash, q.key, q.val, null);
+            if (tl == null)
+                hd = p;
+            else
+                tl.next = p;
+            tl = p;
+        }
+        return hd;
+    }
+
+    /* ---------------- TreeNodes -------------- */
+
+    /**
+     * Nodes for use in TreeBins
+     */
+    static final class TreeNode<K,V> extends Node<K,V> {
+        TreeNode<K,V> parent;  // red-black tree links
+        TreeNode<K,V> left;
+        TreeNode<K,V> right;
+        TreeNode<K,V> prev;    // needed to unlink next upon deletion
+        boolean red;
+
+        TreeNode(int hash, K key, V val, Node<K,V> next,
+                 TreeNode<K,V> parent) {
+            super(hash, key, val, next);
+            this.parent = parent;
+        }
+
+        Node<K,V> find(int h, Object k) {
+            return findTreeNode(h, k, null);
+        }
+
+        /**
+         * Returns the TreeNode (or null if not found) for the given key
+         * starting at given root.
+         */
+        final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
+            if (k != null) {
+                TreeNode<K,V> p = this;
+                do {
+                    int ph, dir; K pk; TreeNode<K,V> q;
+                    TreeNode<K,V> pl = p.left, pr = p.right;
+                    if ((ph = p.hash) > h)
+                        p = pl;
+                    else if (ph < h)
+                        p = pr;
+                    else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
+                        return p;
+                    else if (pl == null)
+                        p = pr;
+                    else if (pr == null)
+                        p = pl;
+                    else if ((kc != null ||
+                              (kc = comparableClassFor(k)) != null) &&
+                             (dir = compareComparables(kc, k, pk)) != 0)
+                        p = (dir < 0) ? pl : pr;
+                    else if ((q = pr.findTreeNode(h, k, kc)) != null)
+                        return q;
+                    else
+                        p = pl;
+                } while (p != null);
+            }
+            return null;
+        }
+    }
+
+    /* ---------------- TreeBins -------------- */
+
+    /**
+     * TreeNodes used at the heads of bins. TreeBins do not hold user
+     * keys or values, but instead point to list of TreeNodes and
+     * their root. They also maintain a parasitic read-write lock
+     * forcing writers (who hold bin lock) to wait for readers (who do
+     * not) to complete before tree restructuring operations.
+     */
+    static final class TreeBin<K,V> extends Node<K,V> {
+        TreeNode<K,V> root;
+        volatile TreeNode<K,V> first;
+        volatile Thread waiter;
+        volatile int lockState;
+        // values for lockState
+        static final int WRITER = 1; // set while holding write lock
+        static final int WAITER = 2; // set when waiting for write lock
+        static final int READER = 4; // increment value for setting read lock
+
+        /**
+         * Tie-breaking utility for ordering insertions when equal
+         * hashCodes and non-comparable. We don't require a total
+         * order, just a consistent insertion rule to maintain
+         * equivalence across rebalancings. Tie-breaking further than
+         * necessary simplifies testing a bit.
+         */
+        static int tieBreakOrder(Object a, Object b) {
+            int d;
+            if (a == null || b == null ||
+                (d = a.getClass().getName().
+                 compareTo(b.getClass().getName())) == 0)
+                d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
+                     -1 : 1);
+            return d;
+        }
+
+        /**
+         * Creates bin with initial set of nodes headed by b.
+         */
+        TreeBin(TreeNode<K,V> b) {
+            super(TREEBIN, null, null, null);
+            this.first = b;
+            TreeNode<K,V> r = null;
+            for (TreeNode<K,V> x = b, next; x != null; x = next) {
+                next = (TreeNode<K,V>)x.next;
+                x.left = x.right = null;
+                if (r == null) {
+                    x.parent = null;
+                    x.red = false;
+                    r = x;
+                }
+                else {
+                    K k = x.key;
+                    int h = x.hash;
+                    Class<?> kc = null;
+                    for (TreeNode<K,V> p = r;;) {
+                        int dir, ph;
+                        K pk = p.key;
+                        if ((ph = p.hash) > h)
+                            dir = -1;
+                        else if (ph < h)
+                            dir = 1;
+                        else if ((kc == null &&
+                                  (kc = comparableClassFor(k)) == null) ||
+                                 (dir = compareComparables(kc, k, pk)) == 0)
+                            dir = tieBreakOrder(k, pk);
+                            TreeNode<K,V> xp = p;
+                        if ((p = (dir <= 0) ? p.left : p.right) == null) {
+                            x.parent = xp;
+                            if (dir <= 0)
+                                xp.left = x;
+                            else
+                                xp.right = x;
+                            r = balanceInsertion(r, x);
+                            break;
+                        }
+                    }
+                }
+            }
+            this.root = r;
+            assert checkInvariants(root);
+        }
+
+        /**
+         * Acquires write lock for tree restructuring.
+         */
+        private final void lockRoot() {
+            if (!U.compareAndSwapInt(this, LOCKSTATE, 0, WRITER))
+                contendedLock(); // offload to separate method
+        }
+
+        /**
+         * Releases write lock for tree restructuring.
+         */
+        private final void unlockRoot() {
+            lockState = 0;
+        }
+
+        /**
+         * Possibly blocks awaiting root lock.
+         */
+        private final void contendedLock() {
+            boolean waiting = false;
+            for (int s;;) {
+                if (((s = lockState) & ~WAITER) == 0) {
+                    if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) {
+                        if (waiting)
+                            waiter = null;
+                        return;
+                    }
+                }
+                else if ((s & WAITER) == 0) {
+                    if (U.compareAndSwapInt(this, LOCKSTATE, s, s | WAITER)) {
+                        waiting = true;
+                        waiter = Thread.currentThread();
+                    }
+                }
+                else if (waiting)
+                    LockSupport.park(this);
+            }
+        }
+
+        /**
+         * Returns matching node or null if none. Tries to search
+         * using tree comparisons from root, but continues linear
+         * search when lock not available.
+         */
+        final Node<K,V> find(int h, Object k) {
+            if (k != null) {
+                for (Node<K,V> e = first; e != null; ) {
+                    int s; K ek;
+                    if (((s = lockState) & (WAITER|WRITER)) != 0) {
+                        if (e.hash == h &&
+                            ((ek = e.key) == k || (ek != null && k.equals(ek))))
+                            return e;
+                        e = e.next;
+                    }
+                    else if (U.compareAndSwapInt(this, LOCKSTATE, s,
+                                                 s + READER)) {
+                        TreeNode<K,V> r, p;
+                        try {
+                            p = ((r = root) == null ? null :
+                                 r.findTreeNode(h, k, null));
+                        } finally {
+                            Thread w;
+                            int ls;
+                            do {} while (!U.compareAndSwapInt
+                                         (this, LOCKSTATE,
+                                          ls = lockState, ls - READER));
+                            if (ls == (READER|WAITER) && (w = waiter) != null)
+                                LockSupport.unpark(w);
+                        }
+                        return p;
+                    }
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Finds or adds a node.
+         * @return null if added
+         */
+        final TreeNode<K,V> putTreeVal(int h, K k, V v) {
+            Class<?> kc = null;
+            boolean searched = false;
+            for (TreeNode<K,V> p = root;;) {
+                int dir, ph; K pk;
+                if (p == null) {
+                    first = root = new TreeNode<K,V>(h, k, v, null, null);
+                    break;
+                }
+                else if ((ph = p.hash) > h)
+                    dir = -1;
+                else if (ph < h)
+                    dir = 1;
+                else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
+                    return p;
+                else if ((kc == null &&
+                          (kc = comparableClassFor(k)) == null) ||
+                         (dir = compareComparables(kc, k, pk)) == 0) {
+                    if (!searched) {
+                        TreeNode<K,V> q, ch;
+                        searched = true;
+                        if (((ch = p.left) != null &&
+                             (q = ch.findTreeNode(h, k, kc)) != null) ||
+                            ((ch = p.right) != null &&
+                             (q = ch.findTreeNode(h, k, kc)) != null))
+                            return q;
+                    }
+                    dir = tieBreakOrder(k, pk);
+                }
+
+                TreeNode<K,V> xp = p;
+                if ((p = (dir <= 0) ? p.left : p.right) == null) {
+                    TreeNode<K,V> x, f = first;
+                    first = x = new TreeNode<K,V>(h, k, v, f, xp);
+                    if (f != null)
+                        f.prev = x;
+                    if (dir <= 0)
+                        xp.left = x;
+                    else
+                        xp.right = x;
+                    if (!xp.red)
+                        x.red = true;
+                    else {
+                        lockRoot();
+                        try {
+                            root = balanceInsertion(root, x);
+                        } finally {
+                            unlockRoot();
+                        }
+                    }
+                    break;
+                }
+            }
+            assert checkInvariants(root);
+            return null;
+        }
+
+        /**
+         * Removes the given node, that must be present before this
+         * call.  This is messier than typical red-black deletion code
+         * because we cannot swap the contents of an interior node
+         * with a leaf successor that is pinned by "next" pointers
+         * that are accessible independently of lock. So instead we
+         * swap the tree linkages.
+         *
+         * @return true if now too small, so should be untreeified
+         */
+        final boolean removeTreeNode(TreeNode<K,V> p) {
+            TreeNode<K,V> next = (TreeNode<K,V>)p.next;
+            TreeNode<K,V> pred = p.prev;  // unlink traversal pointers
+            TreeNode<K,V> r, rl;
+            if (pred == null)
+                first = next;
+            else
+                pred.next = next;
+            if (next != null)
+                next.prev = pred;
+            if (first == null) {
+                root = null;
+                return true;
+            }
+            if ((r = root) == null || r.right == null || // too small
+                (rl = r.left) == null || rl.left == null)
+                return true;
+            lockRoot();
+            try {
+                TreeNode<K,V> replacement;
+                TreeNode<K,V> pl = p.left;
+                TreeNode<K,V> pr = p.right;
+                if (pl != null && pr != null) {
+                    TreeNode<K,V> s = pr, sl;
+                    while ((sl = s.left) != null) // find successor
+                        s = sl;
+                    boolean c = s.red; s.red = p.red; p.red = c; // swap colors
+                    TreeNode<K,V> sr = s.right;
+                    TreeNode<K,V> pp = p.parent;
+                    if (s == pr) { // p was s's direct parent
+                        p.parent = s;
+                        s.right = p;
+                    }
+                    else {
+                        TreeNode<K,V> sp = s.parent;
+                        if ((p.parent = sp) != null) {
+                            if (s == sp.left)
+                                sp.left = p;
+                            else
+                                sp.right = p;
+                        }
+                        if ((s.right = pr) != null)
+                            pr.parent = s;
+                    }
+                    p.left = null;
+                    if ((p.right = sr) != null)
+                        sr.parent = p;
+                    if ((s.left = pl) != null)
+                        pl.parent = s;
+                    if ((s.parent = pp) == null)
+                        r = s;
+                    else if (p == pp.left)
+                        pp.left = s;
+                    else
+                        pp.right = s;
+                    if (sr != null)
+                        replacement = sr;
+                    else
+                        replacement = p;
+                }
+                else if (pl != null)
+                    replacement = pl;
+                else if (pr != null)
+                    replacement = pr;
+                else
+                    replacement = p;
+                if (replacement != p) {
+                    TreeNode<K,V> pp = replacement.parent = p.parent;
+                    if (pp == null)
+                        r = replacement;
+                    else if (p == pp.left)
+                        pp.left = replacement;
+                    else
+                        pp.right = replacement;
+                    p.left = p.right = p.parent = null;
+                }
+
+                root = (p.red) ? r : balanceDeletion(r, replacement);
+
+                if (p == replacement) {  // detach pointers
+                    TreeNode<K,V> pp;
+                    if ((pp = p.parent) != null) {
+                        if (p == pp.left)
+                            pp.left = null;
+                        else if (p == pp.right)
+                            pp.right = null;
+                        p.parent = null;
+                    }
+                }
+            } finally {
+                unlockRoot();
+            }
+            assert checkInvariants(root);
+            return false;
+        }
+
+        /* ------------------------------------------------------------ */
+        // Red-black tree methods, all adapted from CLR
+
+        static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
+                                              TreeNode<K,V> p) {
+            TreeNode<K,V> r, pp, rl;
+            if (p != null && (r = p.right) != null) {
+                if ((rl = p.right = r.left) != null)
+                    rl.parent = p;
+                if ((pp = r.parent = p.parent) == null)
+                    (root = r).red = false;
+                else if (pp.left == p)
+                    pp.left = r;
+                else
+                    pp.right = r;
+                r.left = p;
+                p.parent = r;
+            }
+            return root;
+        }
+
+        static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
+                                               TreeNode<K,V> p) {
+            TreeNode<K,V> l, pp, lr;
+            if (p != null && (l = p.left) != null) {
+                if ((lr = p.left = l.right) != null)
+                    lr.parent = p;
+                if ((pp = l.parent = p.parent) == null)
+                    (root = l).red = false;
+                else if (pp.right == p)
+                    pp.right = l;
+                else
+                    pp.left = l;
+                l.right = p;
+                p.parent = l;
+            }
+            return root;
+        }
+
+        static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
+                                                    TreeNode<K,V> x) {
+            x.red = true;
+            for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
+                if ((xp = x.parent) == null) {
+                    x.red = false;
+                    return x;
+                }
+                else if (!xp.red || (xpp = xp.parent) == null)
+                    return root;
+                if (xp == (xppl = xpp.left)) {
+                    if ((xppr = xpp.right) != null && xppr.red) {
+                        xppr.red = false;
+                        xp.red = false;
+                        xpp.red = true;
+                        x = xpp;
+                    }
+                    else {
+                        if (x == xp.right) {
+                            root = rotateLeft(root, x = xp);
+                            xpp = (xp = x.parent) == null ? null : xp.parent;
+                        }
+                        if (xp != null) {
+                            xp.red = false;
+                            if (xpp != null) {
+                                xpp.red = true;
+                                root = rotateRight(root, xpp);
+                            }
+                        }
+                    }
+                }
+                else {
+                    if (xppl != null && xppl.red) {
+                        xppl.red = false;
+                        xp.red = false;
+                        xpp.red = true;
+                        x = xpp;
+                    }
+                    else {
+                        if (x == xp.left) {
+                            root = rotateRight(root, x = xp);
+                            xpp = (xp = x.parent) == null ? null : xp.parent;
+                        }
+                        if (xp != null) {
+                            xp.red = false;
+                            if (xpp != null) {
+                                xpp.red = true;
+                                root = rotateLeft(root, xpp);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
+                                                   TreeNode<K,V> x) {
+            for (TreeNode<K,V> xp, xpl, xpr;;) {
+                if (x == null || x == root)
+                    return root;
+                else if ((xp = x.parent) == null) {
+                    x.red = false;
+                    return x;
+                }
+                else if (x.red) {
+                    x.red = false;
+                    return root;
+                }
+                else if ((xpl = xp.left) == x) {
+                    if ((xpr = xp.right) != null && xpr.red) {
+                        xpr.red = false;
+                        xp.red = true;
+                        root = rotateLeft(root, xp);
+                        xpr = (xp = x.parent) == null ? null : xp.right;
+                    }
+                    if (xpr == null)
+                        x = xp;
+                    else {
+                        TreeNode<K,V> sl = xpr.left, sr = xpr.right;
+                        if ((sr == null || !sr.red) &&
+                            (sl == null || !sl.red)) {
+                            xpr.red = true;
+                            x = xp;
+                        }
+                        else {
+                            if (sr == null || !sr.red) {
+                                if (sl != null)
+                                    sl.red = false;
+                                xpr.red = true;
+                                root = rotateRight(root, xpr);
+                                xpr = (xp = x.parent) == null ?
+                                    null : xp.right;
+                            }
+                            if (xpr != null) {
+                                xpr.red = (xp == null) ? false : xp.red;
+                                if ((sr = xpr.right) != null)
+                                    sr.red = false;
+                            }
+                            if (xp != null) {
+                                xp.red = false;
+                                root = rotateLeft(root, xp);
+                            }
+                            x = root;
+                        }
+                    }
+                }
+                else { // symmetric
+                    if (xpl != null && xpl.red) {
+                        xpl.red = false;
+                        xp.red = true;
+                        root = rotateRight(root, xp);
+                        xpl = (xp = x.parent) == null ? null : xp.left;
+                    }
+                    if (xpl == null)
+                        x = xp;
+                    else {
+                        TreeNode<K,V> sl = xpl.left, sr = xpl.right;
+                        if ((sl == null || !sl.red) &&
+                            (sr == null || !sr.red)) {
+                            xpl.red = true;
+                            x = xp;
+                        }
+                        else {
+                            if (sl == null || !sl.red) {
+                                if (sr != null)
+                                    sr.red = false;
+                                xpl.red = true;
+                                root = rotateLeft(root, xpl);
+                                xpl = (xp = x.parent) == null ?
+                                    null : xp.left;
+                            }
+                            if (xpl != null) {
+                                xpl.red = (xp == null) ? false : xp.red;
+                                if ((sl = xpl.left) != null)
+                                    sl.red = false;
+                            }
+                            if (xp != null) {
+                                xp.red = false;
+                                root = rotateRight(root, xp);
+                            }
+                            x = root;
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Recursive invariant check
+         */
+        static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
+            TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
+                tb = t.prev, tn = (TreeNode<K,V>)t.next;
+            if (tb != null && tb.next != t)
+                return false;
+            if (tn != null && tn.prev != t)
+                return false;
+            if (tp != null && t != tp.left && t != tp.right)
+                return false;
+            if (tl != null && (tl.parent != t || tl.hash > t.hash))
+                return false;
+            if (tr != null && (tr.parent != t || tr.hash < t.hash))
+                return false;
+            if (t.red && tl != null && tl.red && tr != null && tr.red)
+                return false;
+            if (tl != null && !checkInvariants(tl))
+                return false;
+            if (tr != null && !checkInvariants(tr))
+                return false;
+            return true;
+        }
+
+        private static final sun.misc.Unsafe U;
+        private static final long LOCKSTATE;
+        static {
+            try {
+                U = getUnsafe();
+                Class<?> k = TreeBin.class;
+                LOCKSTATE = U.objectFieldOffset
+                    (k.getDeclaredField("lockState"));
+            } catch (Exception e) {
+                throw new Error(e);
+            }
+        }
+    }
+
+    /* ----------------Table Traversal -------------- */
+
+    /**
+     * Records the table, its length, and current traversal index for a
+     * traverser that must process a region of a forwarded table before
+     * proceeding with current table.
+     */
+    static final class TableStack<K,V> {
+        int length;
+        int index;
+        Node<K,V>[] tab;
+        TableStack<K,V> next;
+    }
+
+    /**
+     * Encapsulates traversal for methods such as containsValue; also
+     * serves as a base class for other iterators and spliterators.
+     *
+     * Method advance visits once each still-valid node that was
+     * reachable upon iterator construction. It might miss some that
+     * were added to a bin after the bin was visited, which is OK wrt
+     * consistency guarantees. Maintaining this property in the face
+     * of possible ongoing resizes requires a fair amount of
+     * bookkeeping state that is difficult to optimize away amidst
+     * volatile accesses.  Even so, traversal maintains reasonable
+     * throughput.
+     *
+     * Normally, iteration proceeds bin-by-bin traversing lists.
+     * However, if the table has been resized, then all future steps
+     * must traverse both the bin at the current index as well as at
+     * (index + baseSize); and so on for further resizings. To
+     * paranoically cope with potential sharing by users of iterators
+     * across threads, iteration terminates if a bounds checks fails
+     * for a table read.
+     */
+    static class Traverser<K,V> {
+        Node<K,V>[] tab;        // current table; updated if resized
+        Node<K,V> next;         // the next entry to use
+        TableStack<K,V> stack, spare; // to save/restore on ForwardingNodes
+        int index;              // index of bin to use next
+        int baseIndex;          // current index of initial table
+        int baseLimit;          // index bound for initial table
+        final int baseSize;     // initial table size
+
+        Traverser(Node<K,V>[] tab, int size, int index, int limit) {
+            this.tab = tab;
+            this.baseSize = size;
+            this.baseIndex = this.index = index;
+            this.baseLimit = limit;
+            this.next = null;
+        }
+
+        /**
+         * Advances if possible, returning next valid node, or null if none.
+         */
+        final Node<K,V> advance() {
+            Node<K,V> e;
+            if ((e = next) != null)
+                e = e.next;
+            for (;;) {
+                Node<K,V>[] t; int i, n;  // must use locals in checks
+                if (e != null)
+                    return next = e;
+                if (baseIndex >= baseLimit || (t = tab) == null ||
+                    (n = t.length) <= (i = index) || i < 0)
+                    return next = null;
+                if ((e = tabAt(t, i)) != null && e.hash < 0) {
+                    if (e instanceof ForwardingNode) {
+                        tab = ((ForwardingNode<K,V>)e).nextTable;
+                        e = null;
+                        pushState(t, i, n);
+                        continue;
+                    }
+                    else if (e instanceof TreeBin)
+                        e = ((TreeBin<K,V>)e).first;
+                    else
+                        e = null;
+                }
+                if (stack != null)
+                    recoverState(n);
+                else if ((index = i + baseSize) >= n)
+                    index = ++baseIndex; // visit upper slots if present
+            }
+        }
+
+        /**
+         * Saves traversal state upon encountering a forwarding node.
+         */
+        private void pushState(Node<K,V>[] t, int i, int n) {
+            TableStack<K,V> s = spare;  // reuse if possible
+            if (s != null)
+                spare = s.next;
+            else
+                s = new TableStack<K,V>();
+            s.tab = t;
+            s.length = n;
+            s.index = i;
+            s.next = stack;
+            stack = s;
+        }
+
+        /**
+         * Possibly pops traversal state.
+         *
+         * @param n length of current table
+         */
+        private void recoverState(int n) {
+            TableStack<K,V> s; int len;
+            while ((s = stack) != null && (index += (len = s.length)) >= n) {
+                n = len;
+                index = s.index;
+                tab = s.tab;
+                s.tab = null;
+                TableStack<K,V> next = s.next;
+                s.next = spare; // save for reuse
+                stack = next;
+                spare = s;
+            }
+            if (s == null && (index += baseSize) >= n)
+                index = ++baseIndex;
+        }
+    }
+
+    /**
+     * Base of key, value, and entry Iterators. Adds fields to
+     * Traverser to support iterator.remove.
+     */
+    static class BaseIterator<K,V> extends Traverser<K,V> {
+        final ConcurrentHashMapV8<K,V> map;
+        Node<K,V> lastReturned;
+        BaseIterator(Node<K,V>[] tab, int size, int index, int limit,
+                    ConcurrentHashMapV8<K,V> map) {
+            super(tab, size, index, limit);
+            this.map = map;
+            advance();
+        }
+
+        public final boolean hasNext() { return next != null; }
+        public final boolean hasMoreElements() { return next != null; }
+
+        public final void remove() {
+            Node<K,V> p;
+            if ((p = lastReturned) == null)
+                throw new IllegalStateException();
+            lastReturned = null;
+            map.replaceNode(p.key, null, null);
+        }
+    }
+
+    static final class KeyIterator<K,V> extends BaseIterator<K,V>
+        implements Iterator<K>, Enumeration<K> {
+        KeyIterator(Node<K,V>[] tab, int index, int size, int limit,
+                    ConcurrentHashMapV8<K,V> map) {
+            super(tab, index, size, limit, map);
+        }
+
+        public final K next() {
+            Node<K,V> p;
+            if ((p = next) == null)
+                throw new NoSuchElementException();
+            K k = p.key;
+            lastReturned = p;
+            advance();
+            return k;
+        }
+
+        public final K nextElement() { return next(); }
+    }
+
+    static final class ValueIterator<K,V> extends BaseIterator<K,V>
+        implements Iterator<V>, Enumeration<V> {
+        ValueIterator(Node<K,V>[] tab, int index, int size, int limit,
+                      ConcurrentHashMapV8<K,V> map) {
+            super(tab, index, size, limit, map);
+        }
+
+        public final V next() {
+            Node<K,V> p;
+            if ((p = next) == null)
+                throw new NoSuchElementException();
+            V v = p.val;
+            lastReturned = p;
+            advance();
+            return v;
+        }
+
+        public final V nextElement() { return next(); }
+    }
+
+    static final class EntryIterator<K,V> extends BaseIterator<K,V>
+        implements Iterator<Entry<K,V>> {
+        EntryIterator(Node<K,V>[] tab, int index, int size, int limit,
+                      ConcurrentHashMapV8<K,V> map) {
+            super(tab, index, size, limit, map);
+        }
+
+        public final Entry<K,V> next() {
+            Node<K,V> p;
+            if ((p = next) == null)
+                throw new NoSuchElementException();
+            K k = p.key;
+            V v = p.val;
+            lastReturned = p;
+            advance();
+            return new MapEntry<K,V>(k, v, map);
+        }
+    }
+
+    /**
+     * Exported Entry for EntryIterator
+     */
+    static final class MapEntry<K,V> implements Entry<K,V> {
+        final K key; // non-null
+        V val;       // non-null
+        final ConcurrentHashMapV8<K,V> map;
+        MapEntry(K key, V val, ConcurrentHashMapV8<K,V> map) {
+            this.key = key;
+            this.val = val;
+            this.map = map;
+        }
+        public K getKey()        { return key; }
+        public V getValue()      { return val; }
+        public int hashCode()    { return key.hashCode() ^ val.hashCode(); }
+        public String toString() { return key + "=" + val; }
+
+        public boolean equals(Object o) {
+            Object k, v; Entry<?,?> e;
+            return ((o instanceof Map.Entry) &&
+                    (k = (e = (Entry<?,?>)o).getKey()) != null &&
+                    (v = e.getValue()) != null &&
+                    (k == key || k.equals(key)) &&
+                    (v == val || v.equals(val)));
+        }
+
+        /**
+         * Sets our entry's value and writes through to the map. The
+         * value to return is somewhat arbitrary here. Since we do not
+         * necessarily track asynchronous changes, the most recent
+         * "previous" value could be different from what we return (or
+         * could even have been removed, in which case the put will
+         * re-establish). We do not and cannot guarantee more.
+         */
+        public V setValue(V value) {
+            if (value == null) throw new NullPointerException();
+            V v = val;
+            val = value;
+            map.put(key, value);
+            return v;
+        }
+    }
+
+    static final class KeySpliterator<K,V> extends Traverser<K,V>
+        implements ConcurrentHashMapSpliterator<K> {
+        long est;               // size estimate
+        KeySpliterator(Node<K,V>[] tab, int size, int index, int limit,
+                       long est) {
+            super(tab, size, index, limit);
+            this.est = est;
+        }
+
+        public ConcurrentHashMapSpliterator<K> trySplit() {
+            int i, f, h;
+            return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null :
+                new KeySpliterator<K,V>(tab, baseSize, baseLimit = h,
+                                        f, est >>>= 1);
+        }
+
+        public void forEachRemaining(Action<? super K> action) {
+            if (action == null) throw new NullPointerException();
+            for (Node<K,V> p; (p = advance()) != null;)
+                action.apply(p.key);
+        }
+
+        public boolean tryAdvance(Action<? super K> action) {
+            if (action == null) throw new NullPointerException();
+            Node<K,V> p;
+            if ((p = advance()) == null)
+                return false;
+            action.apply(p.key);
+            return true;
+        }
+
+        public long estimateSize() { return est; }
+
+    }
+
+    static final class ValueSpliterator<K,V> extends Traverser<K,V>
+        implements ConcurrentHashMapSpliterator<V> {
+        long est;               // size estimate
+        ValueSpliterator(Node<K,V>[] tab, int size, int index, int limit,
+                         long est) {
+            super(tab, size, index, limit);
+            this.est = est;
+        }
+
+        public ConcurrentHashMapSpliterator<V> trySplit() {
+            int i, f, h;
+            return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null :
+                new ValueSpliterator<K,V>(tab, baseSize, baseLimit = h,
+                                          f, est >>>= 1);
+        }
+
+        public void forEachRemaining(Action<? super V> action) {
+            if (action == null) throw new NullPointerException();
+            for (Node<K,V> p; (p = advance()) != null;)
+                action.apply(p.val);
+        }
+
+        public boolean tryAdvance(Action<? super V> action) {
+            if (action == null) throw new NullPointerException();
+            Node<K,V> p;
+            if ((p = advance()) == null)
+                return false;
+            action.apply(p.val);
+            return true;
+        }
+
+        public long estimateSize() { return est; }
+
+    }
+
+    static final class EntrySpliterator<K,V> extends Traverser<K,V>
+        implements ConcurrentHashMapSpliterator<Entry<K,V>> {
+        final ConcurrentHashMapV8<K,V> map; // To export MapEntry
+        long est;               // size estimate
+        EntrySpliterator(Node<K,V>[] tab, int size, int index, int limit,
+                         long est, ConcurrentHashMapV8<K,V> map) {
+            super(tab, size, index, limit);
+            this.map = map;
+            this.est = est;
+        }
+
+        public ConcurrentHashMapSpliterator<Entry<K,V>> trySplit() {
+            int i, f, h;
+            return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null :
+                new EntrySpliterator<K,V>(tab, baseSize, baseLimit = h,
+                                          f, est >>>= 1, map);
+        }
+
+        public void forEachRemaining(Action<? super Entry<K,V>> action) {
+            if (action == null) throw new NullPointerException();
+            for (Node<K,V> p; (p = advance()) != null; )
+                action.apply(new MapEntry<K,V>(p.key, p.val, map));
+        }
+
+        public boolean tryAdvance(Action<? super Entry<K,V>> action) {
+            if (action == null) throw new NullPointerException();
+            Node<K,V> p;
+            if ((p = advance()) == null)
+                return false;
+            action.apply(new MapEntry<K,V>(p.key, p.val, map));
+            return true;
+        }
+
+        public long estimateSize() { return est; }
+
+    }
+
+    // Parallel bulk operations
+
+    /**
+     * Computes initial batch value for bulk tasks. The returned value
+     * is approximately exp2 of the number of times (minus one) to
+     * split task by two before executing leaf action. This value is
+     * faster to compute and more convenient to use as a guide to
+     * splitting than is the depth, since it is used while dividing by
+     * two anyway.
+     */
+    final int batchFor(long b) {
+        long n;
+        if (b == Long.MAX_VALUE || (n = sumCount()) <= 1L || n < b)
+            return 0;
+        int sp = ForkJoinPool.getCommonPoolParallelism() << 2; // slack of 4
+        return (b <= 0L || (n /= b) >= sp) ? sp : (int)n;
+    }
+
+    /**
+     * Performs the given action for each (key, value).
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param action the action
+     * @since 1.8
+     */
+    public void forEach(long parallelismThreshold,
+                        BiAction<? super K,? super V> action) {
+        if (action == null) throw new NullPointerException();
+        new ForEachMappingTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             action).invoke();
+    }
+
+    /**
+     * Performs the given action for each non-null transformation
+     * of each (key, value).
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element, or null if there is no transformation (in
+     * which case the action is not applied)
+     * @param action the action
+     * @since 1.8
+     */
+    public <U> void forEach(long parallelismThreshold,
+                            BiFun<? super K, ? super V, ? extends U> transformer,
+                            Action<? super U> action) {
+        if (transformer == null || action == null)
+            throw new NullPointerException();
+        new ForEachTransformedMappingTask<K,V,U>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             transformer, action).invoke();
+    }
+
+    /**
+     * Returns a non-null result from applying the given search
+     * function on each (key, value), or null if none.  Upon
+     * success, further element processing is suppressed and the
+     * results of any other parallel invocations of the search
+     * function are ignored.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param searchFunction a function returning a non-null
+     * result on success, else null
+     * @return a non-null result from applying the given search
+     * function on each (key, value), or null if none
+     * @since 1.8
+     */
+    public <U> U search(long parallelismThreshold,
+                        BiFun<? super K, ? super V, ? extends U> searchFunction) {
+        if (searchFunction == null) throw new NullPointerException();
+        return new SearchMappingsTask<K,V,U>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             searchFunction, new AtomicReference<U>()).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all (key, value) pairs using the given reducer to
+     * combine values, or null if none.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element, or null if there is no transformation (in
+     * which case it is not combined)
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all (key, value) pairs
+     * @since 1.8
+     */
+    public <U> U reduce(long parallelismThreshold,
+                        BiFun<? super K, ? super V, ? extends U> transformer,
+                        BiFun<? super U, ? super U, ? extends U> reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceMappingsTask<K,V,U>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all (key, value) pairs using the given reducer to
+     * combine values, and the given basis as an identity value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element
+     * @param basis the identity (initial default value) for the reduction
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all (key, value) pairs
+     * @since 1.8
+     */
+    public double reduceToDouble(long parallelismThreshold,
+                                 ObjectByObjectToDouble<? super K, ? super V> transformer,
+                                 double basis,
+                                 DoubleByDoubleToDouble reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceMappingsToDoubleTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, basis, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all (key, value) pairs using the given reducer to
+     * combine values, and the given basis as an identity value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element
+     * @param basis the identity (initial default value) for the reduction
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all (key, value) pairs
+     * @since 1.8
+     */
+    public long reduceToLong(long parallelismThreshold,
+                             ObjectByObjectToLong<? super K, ? super V> transformer,
+                             long basis,
+                             LongByLongToLong reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceMappingsToLongTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, basis, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all (key, value) pairs using the given reducer to
+     * combine values, and the given basis as an identity value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element
+     * @param basis the identity (initial default value) for the reduction
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all (key, value) pairs
+     * @since 1.8
+     */
+    public int reduceToInt(long parallelismThreshold,
+                           ObjectByObjectToInt<? super K, ? super V> transformer,
+                           int basis,
+                           IntByIntToInt reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceMappingsToIntTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, basis, reducer).invoke();
+    }
+
+    /**
+     * Performs the given action for each key.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param action the action
+     * @since 1.8
+     */
+    public void forEachKey(long parallelismThreshold,
+                           Action<? super K> action) {
+        if (action == null) throw new NullPointerException();
+        new ForEachKeyTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             action).invoke();
+    }
+
+    /**
+     * Performs the given action for each non-null transformation
+     * of each key.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element, or null if there is no transformation (in
+     * which case the action is not applied)
+     * @param action the action
+     * @since 1.8
+     */
+    public <U> void forEachKey(long parallelismThreshold,
+                               Fun<? super K, ? extends U> transformer,
+                               Action<? super U> action) {
+        if (transformer == null || action == null)
+            throw new NullPointerException();
+        new ForEachTransformedKeyTask<K,V,U>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             transformer, action).invoke();
+    }
+
+    /**
+     * Returns a non-null result from applying the given search
+     * function on each key, or null if none. Upon success,
+     * further element processing is suppressed and the results of
+     * any other parallel invocations of the search function are
+     * ignored.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param searchFunction a function returning a non-null
+     * result on success, else null
+     * @return a non-null result from applying the given search
+     * function on each key, or null if none
+     * @since 1.8
+     */
+    public <U> U searchKeys(long parallelismThreshold,
+                            Fun<? super K, ? extends U> searchFunction) {
+        if (searchFunction == null) throw new NullPointerException();
+        return new SearchKeysTask<K,V,U>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             searchFunction, new AtomicReference<U>()).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating all keys using the given
+     * reducer to combine values, or null if none.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating all keys using the given
+     * reducer to combine values, or null if none
+     * @since 1.8
+     */
+    public K reduceKeys(long parallelismThreshold,
+                        BiFun<? super K, ? super K, ? extends K> reducer) {
+        if (reducer == null) throw new NullPointerException();
+        return new ReduceKeysTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all keys using the given reducer to combine values, or
+     * null if none.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element, or null if there is no transformation (in
+     * which case it is not combined)
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all keys
+     * @since 1.8
+     */
+    public <U> U reduceKeys(long parallelismThreshold,
+                            Fun<? super K, ? extends U> transformer,
+         BiFun<? super U, ? super U, ? extends U> reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceKeysTask<K,V,U>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all keys using the given reducer to combine values, and
+     * the given basis as an identity value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element
+     * @param basis the identity (initial default value) for the reduction
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all keys
+     * @since 1.8
+     */
+    public double reduceKeysToDouble(long parallelismThreshold,
+                                     ObjectToDouble<? super K> transformer,
+                                     double basis,
+                                     DoubleByDoubleToDouble reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceKeysToDoubleTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, basis, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all keys using the given reducer to combine values, and
+     * the given basis as an identity value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element
+     * @param basis the identity (initial default value) for the reduction
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all keys
+     * @since 1.8
+     */
+    public long reduceKeysToLong(long parallelismThreshold,
+                                 ObjectToLong<? super K> transformer,
+                                 long basis,
+                                 LongByLongToLong reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceKeysToLongTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, basis, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all keys using the given reducer to combine values, and
+     * the given basis as an identity value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element
+     * @param basis the identity (initial default value) for the reduction
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all keys
+     * @since 1.8
+     */
+    public int reduceKeysToInt(long parallelismThreshold,
+                               ObjectToInt<? super K> transformer,
+                               int basis,
+                               IntByIntToInt reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceKeysToIntTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, basis, reducer).invoke();
+    }
+
+    /**
+     * Performs the given action for each value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param action the action
+     * @since 1.8
+     */
+    public void forEachValue(long parallelismThreshold,
+                             Action<? super V> action) {
+        if (action == null)
+            throw new NullPointerException();
+        new ForEachValueTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             action).invoke();
+    }
+
+    /**
+     * Performs the given action for each non-null transformation
+     * of each value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element, or null if there is no transformation (in
+     * which case the action is not applied)
+     * @param action the action
+     * @since 1.8
+     */
+    public <U> void forEachValue(long parallelismThreshold,
+                                 Fun<? super V, ? extends U> transformer,
+                                 Action<? super U> action) {
+        if (transformer == null || action == null)
+            throw new NullPointerException();
+        new ForEachTransformedValueTask<K,V,U>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             transformer, action).invoke();
+    }
+
+    /**
+     * Returns a non-null result from applying the given search
+     * function on each value, or null if none.  Upon success,
+     * further element processing is suppressed and the results of
+     * any other parallel invocations of the search function are
+     * ignored.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param searchFunction a function returning a non-null
+     * result on success, else null
+     * @return a non-null result from applying the given search
+     * function on each value, or null if none
+     * @since 1.8
+     */
+    public <U> U searchValues(long parallelismThreshold,
+                              Fun<? super V, ? extends U> searchFunction) {
+        if (searchFunction == null) throw new NullPointerException();
+        return new SearchValuesTask<K,V,U>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             searchFunction, new AtomicReference<U>()).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating all values using the
+     * given reducer to combine values, or null if none.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating all values
+     * @since 1.8
+     */
+    public V reduceValues(long parallelismThreshold,
+                          BiFun<? super V, ? super V, ? extends V> reducer) {
+        if (reducer == null) throw new NullPointerException();
+        return new ReduceValuesTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all values using the given reducer to combine values, or
+     * null if none.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element, or null if there is no transformation (in
+     * which case it is not combined)
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all values
+     * @since 1.8
+     */
+    public <U> U reduceValues(long parallelismThreshold,
+                              Fun<? super V, ? extends U> transformer,
+                              BiFun<? super U, ? super U, ? extends U> reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceValuesTask<K,V,U>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all values using the given reducer to combine values,
+     * and the given basis as an identity value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element
+     * @param basis the identity (initial default value) for the reduction
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all values
+     * @since 1.8
+     */
+    public double reduceValuesToDouble(long parallelismThreshold,
+                                       ObjectToDouble<? super V> transformer,
+                                       double basis,
+                                       DoubleByDoubleToDouble reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceValuesToDoubleTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, basis, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all values using the given reducer to combine values,
+     * and the given basis as an identity value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element
+     * @param basis the identity (initial default value) for the reduction
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all values
+     * @since 1.8
+     */
+    public long reduceValuesToLong(long parallelismThreshold,
+                                   ObjectToLong<? super V> transformer,
+                                   long basis,
+                                   LongByLongToLong reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceValuesToLongTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, basis, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all values using the given reducer to combine values,
+     * and the given basis as an identity value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element
+     * @param basis the identity (initial default value) for the reduction
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all values
+     * @since 1.8
+     */
+    public int reduceValuesToInt(long parallelismThreshold,
+                                 ObjectToInt<? super V> transformer,
+                                 int basis,
+                                 IntByIntToInt reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceValuesToIntTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, basis, reducer).invoke();
+    }
+
+    /**
+     * Performs the given action for each entry.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param action the action
+     * @since 1.8
+     */
+    public void forEachEntry(long parallelismThreshold,
+                             Action<? super Entry<K,V>> action) {
+        if (action == null) throw new NullPointerException();
+        new ForEachEntryTask<K,V>(null, batchFor(parallelismThreshold), 0, 0, table,
+                                  action).invoke();
+    }
+
+    /**
+     * Performs the given action for each non-null transformation
+     * of each entry.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element, or null if there is no transformation (in
+     * which case the action is not applied)
+     * @param action the action
+     * @since 1.8
+     */
+    public <U> void forEachEntry(long parallelismThreshold,
+                                 Fun<Entry<K,V>, ? extends U> transformer,
+                                 Action<? super U> action) {
+        if (transformer == null || action == null)
+            throw new NullPointerException();
+        new ForEachTransformedEntryTask<K,V,U>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             transformer, action).invoke();
+    }
+
+    /**
+     * Returns a non-null result from applying the given search
+     * function on each entry, or null if none.  Upon success,
+     * further element processing is suppressed and the results of
+     * any other parallel invocations of the search function are
+     * ignored.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param searchFunction a function returning a non-null
+     * result on success, else null
+     * @return a non-null result from applying the given search
+     * function on each entry, or null if none
+     * @since 1.8
+     */
+    public <U> U searchEntries(long parallelismThreshold,
+                               Fun<Entry<K,V>, ? extends U> searchFunction) {
+        if (searchFunction == null) throw new NullPointerException();
+        return new SearchEntriesTask<K,V,U>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             searchFunction, new AtomicReference<U>()).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating all entries using the
+     * given reducer to combine values, or null if none.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating all entries
+     * @since 1.8
+     */
+    public Entry<K,V> reduceEntries(long parallelismThreshold,
+                                        BiFun<Entry<K,V>, Entry<K,V>, ? extends Entry<K,V>> reducer) {
+        if (reducer == null) throw new NullPointerException();
+        return new ReduceEntriesTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all entries using the given reducer to combine values,
+     * or null if none.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element, or null if there is no transformation (in
+     * which case it is not combined)
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all entries
+     * @since 1.8
+     */
+    public <U> U reduceEntries(long parallelismThreshold,
+                               Fun<Entry<K,V>, ? extends U> transformer,
+                               BiFun<? super U, ? super U, ? extends U> reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceEntriesTask<K,V,U>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all entries using the given reducer to combine values,
+     * and the given basis as an identity value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element
+     * @param basis the identity (initial default value) for the reduction
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all entries
+     * @since 1.8
+     */
+    public double reduceEntriesToDouble(long parallelismThreshold,
+                                        ObjectToDouble<Entry<K,V>> transformer,
+                                        double basis,
+                                        DoubleByDoubleToDouble reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceEntriesToDoubleTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, basis, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all entries using the given reducer to combine values,
+     * and the given basis as an identity value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element
+     * @param basis the identity (initial default value) for the reduction
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all entries
+     * @since 1.8
+     */
+    public long reduceEntriesToLong(long parallelismThreshold,
+                                    ObjectToLong<Entry<K,V>> transformer,
+                                    long basis,
+                                    LongByLongToLong reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceEntriesToLongTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, basis, reducer).invoke();
+    }
+
+    /**
+     * Returns the result of accumulating the given transformation
+     * of all entries using the given reducer to combine values,
+     * and the given basis as an identity value.
+     *
+     * @param parallelismThreshold the (estimated) number of elements
+     * needed for this operation to be executed in parallel
+     * @param transformer a function returning the transformation
+     * for an element
+     * @param basis the identity (initial default value) for the reduction
+     * @param reducer a commutative associative combining function
+     * @return the result of accumulating the given transformation
+     * of all entries
+     * @since 1.8
+     */
+    public int reduceEntriesToInt(long parallelismThreshold,
+                                  ObjectToInt<Entry<K,V>> transformer,
+                                  int basis,
+                                  IntByIntToInt reducer) {
+        if (transformer == null || reducer == null)
+            throw new NullPointerException();
+        return new MapReduceEntriesToIntTask<K,V>
+            (null, batchFor(parallelismThreshold), 0, 0, table,
+             null, transformer, basis, reducer).invoke();
+    }
+
+
+    /* ----------------Views -------------- */
+
+    /**
+     * Base class for views.
+     */
+    abstract static class CollectionView<K,V,E>
+        implements Collection<E>, Serializable {
+        private static final long serialVersionUID = 7249069246763182397L;
+        final ConcurrentHashMapV8<K,V> map;
+        CollectionView(ConcurrentHashMapV8<K,V> map)  { this.map = map; }
+
+        /**
+         * Returns the map backing this view.
+         *
+         * @return the map backing this view
+         */
+        public ConcurrentHashMapV8<K,V> getMap() { return map; }
+
+        /**
+         * Removes all of the elements from this view, by removing all
+         * the mappings from the map backing this view.
+         */
+        public final void clear()      { map.clear(); }
+        public final int size()        { return map.size(); }
+        public final boolean isEmpty() { return map.isEmpty(); }
+
+        // implementations below rely on concrete classes supplying these
+        // abstract methods
+        /**
+         * Returns a "weakly consistent" iterator that will never
+         * throw {@link ConcurrentModificationException}, and
+         * guarantees to traverse elements as they existed upon
+         * construction of the iterator, and may (but is not
+         * guaranteed to) reflect any modifications subsequent to
+         * construction.
+         */
+        public abstract Iterator<E> iterator();
+        public abstract boolean contains(Object o);
+        public abstract boolean remove(Object o);
+
+        private static final String oomeMsg = "Required array size too large";
+
+        public final Object[] toArray() {
+            long sz = map.mappingCount();
+            if (sz > MAX_ARRAY_SIZE)
+                throw new OutOfMemoryError(oomeMsg);
+            int n = (int)sz;
+            Object[] r = new Object[n];
+            int i = 0;
+            for (E e : this) {
+                if (i == n) {
+                    if (n >= MAX_ARRAY_SIZE)
+                        throw new OutOfMemoryError(oomeMsg);
+                    if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1)
+                        n = MAX_ARRAY_SIZE;
+                    else
+                        n += (n >>> 1) + 1;
+                    r = Arrays.copyOf(r, n);
+                }
+                r[i++] = e;
+            }
+            return (i == n) ? r : Arrays.copyOf(r, i);
+        }
+
+        @SuppressWarnings("unchecked")
+        public final <T> T[] toArray(T[] a) {
+            long sz = map.mappingCount();
+            if (sz > MAX_ARRAY_SIZE)
+                throw new OutOfMemoryError(oomeMsg);
+            int m = (int)sz;
+            T[] r = (a.length >= m) ? a :
+                (T[])java.lang.reflect.Array
+                .newInstance(a.getClass().getComponentType(), m);
+            int n = r.length;
+            int i = 0;
+            for (E e : this) {
+                if (i == n) {
+                    if (n >= MAX_ARRAY_SIZE)
+                        throw new OutOfMemoryError(oomeMsg);
+                    if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1)
+                        n = MAX_ARRAY_SIZE;
+                    else
+                        n += (n >>> 1) + 1;
+                    r = Arrays.copyOf(r, n);
+                }
+                r[i++] = (T)e;
+            }
+            if (a == r && i < n) {
+                r[i] = null; // null-terminate
+                return r;
+            }
+            return (i == n) ? r : Arrays.copyOf(r, i);
+        }
+
+        /**
+         * Returns a string representation of this collection.
+         * The string representation consists of the string representations
+         * of the collection's elements in the order they are returned by
+         * its iterator, enclosed in square brackets ({@code "[]"}).
+         * Adjacent elements are separated by the characters {@code ", "}
+         * (comma and space).  Elements are converted to strings as by
+         * {@link String#valueOf(Object)}.
+         *
+         * @return a string representation of this collection
+         */
+        public final String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append('[');
+            Iterator<E> it = iterator();
+            if (it.hasNext()) {
+                for (;;) {
+                    Object e = it.next();
+                    sb.append(e == this ? "(this Collection)" : e);
+                    if (!it.hasNext())
+                        break;
+                    sb.append(',').append(' ');
+                }
+            }
+            return sb.append(']').toString();
+        }
+
+        public final boolean containsAll(Collection<?> c) {
+            if (c != this) {
+                for (Object e : c) {
+                    if (e == null || !contains(e))
+                        return false;
+                }
+            }
+            return true;
+        }
+
+        public final boolean removeAll(Collection<?> c) {
+            boolean modified = false;
+            for (Iterator<E> it = iterator(); it.hasNext();) {
+                if (c.contains(it.next())) {
+                    it.remove();
+                    modified = true;
+                }
+            }
+            return modified;
+        }
+
+        public final boolean retainAll(Collection<?> c) {
+            boolean modified = false;
+            for (Iterator<E> it = iterator(); it.hasNext();) {
+                if (!c.contains(it.next())) {
+                    it.remove();
+                    modified = true;
+                }
+            }
+            return modified;
+        }
+
+    }
+
+    /**
+     * A view of a ConcurrentHashMapV8 as a {@link Set} of keys, in
+     * which additions may optionally be enabled by mapping to a
+     * common value.  This class cannot be directly instantiated.
+     * See {@link #keySet() keySet()},
+     * {@link #keySet(Object) keySet(V)},
+     * {@link #newKeySet() newKeySet()},
+     * {@link #newKeySet(int) newKeySet(int)}.
+     *
+     * @since 1.8
+     */
+    public static class KeySetView<K,V> extends CollectionView<K,V,K>
+        implements Set<K>, Serializable {
+        private static final long serialVersionUID = 7249069246763182397L;
+        private final V value;
+        KeySetView(ConcurrentHashMapV8<K,V> map, V value) {  // non-public
+            super(map);
+            this.value = value;
+        }
+
+        /**
+         * Returns the default mapped value for additions,
+         * or {@code null} if additions are not supported.
+         *
+         * @return the default mapped value for additions, or {@code null}
+         * if not supported
+         */
+        public V getMappedValue() { return value; }
+
+        /**
+         * {@inheritDoc}
+         * @throws NullPointerException if the specified key is null
+         */
+        public boolean contains(Object o) { return map.containsKey(o); }
+
+        /**
+         * Removes the key from this map view, by removing the key (and its
+         * corresponding value) from the backing map.  This method does
+         * nothing if the key is not in the map.
+         *
+         * @param  o the key to be removed from the backing map
+         * @return {@code true} if the backing map contained the specified key
+         * @throws NullPointerException if the specified key is null
+         */
+        public boolean remove(Object o) { return map.remove(o) != null; }
+
+        /**
+         * @return an iterator over the keys of the backing map
+         */
+        public Iterator<K> iterator() {
+            Node<K,V>[] t;
+            ConcurrentHashMapV8<K,V> m = map;
+            int f = (t = m.table) == null ? 0 : t.length;
+            return new KeyIterator<K,V>(t, f, 0, f, m);
+        }
+
+        /**
+         * Adds the specified key to this set view by mapping the key to
+         * the default mapped value in the backing map, if defined.
+         *
+         * @param e key to be added
+         * @return {@code true} if this set changed as a result of the call
+         * @throws NullPointerException if the specified key is null
+         * @throws UnsupportedOperationException if no default mapped value
+         * for additions was provided
+         */
+        public boolean add(K e) {
+            V v;
+            if ((v = value) == null)
+                throw new UnsupportedOperationException();
+            return map.putVal(e, v, true) == null;
+        }
+
+        /**
+         * Adds all of the elements in the specified collection to this set,
+         * as if by calling {@link #add} on each one.
+         *
+         * @param c the elements to be inserted into this set
+         * @return {@code true} if this set changed as a result of the call
+         * @throws NullPointerException if the collection or any of its
+         * elements are {@code null}
+         * @throws UnsupportedOperationException if no default mapped value
+         * for additions was provided
+         */
+        public boolean addAll(Collection<? extends K> c) {
+            boolean added = false;
+            V v;
+            if ((v = value) == null)
+                throw new UnsupportedOperationException();
+            for (K e : c) {
+                if (map.putVal(e, v, true) == null)
+                    added = true;
+            }
+            return added;
+        }
+
+        public int hashCode() {
+            int h = 0;
+            for (K e : this)
+                h += e.hashCode();
+            return h;
+        }
+
+        public boolean equals(Object o) {
+            Set<?> c;
+            return ((o instanceof Set) &&
+                    ((c = (Set<?>)o) == this ||
+                     (containsAll(c) && c.containsAll(this))));
+        }
+
+        //calvin: change the name to avoid conflict in JDK8
+        public ConcurrentHashMapSpliterator<K> spliterator2() {
+            Node<K,V>[] t;
+            ConcurrentHashMapV8<K,V> m = map;
+            long n = m.sumCount();
+            int f = (t = m.table) == null ? 0 : t.length;
+            return new KeySpliterator<K,V>(t, f, 0, f, n < 0L ? 0L : n);
+        }
+
+        public void forEach(Action<? super K> action) {
+            if (action == null) throw new NullPointerException();
+            Node<K,V>[] t;
+            if ((t = map.table) != null) {
+                Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+                for (Node<K,V> p; (p = it.advance()) != null; )
+                    action.apply(p.key);
+            }
+        }
+    }
+
+    /**
+     * A view of a ConcurrentHashMapV8 as a {@link Collection} of
+     * values, in which additions are disabled. This class cannot be
+     * directly instantiated. See {@link #values()}.
+     */
+    static final class ValuesView<K,V> extends CollectionView<K,V,V>
+        implements Collection<V>, Serializable {
+        private static final long serialVersionUID = 2249069246763182397L;
+        ValuesView(ConcurrentHashMapV8<K,V> map) { super(map); }
+        public final boolean contains(Object o) {
+            return map.containsValue(o);
+        }
+
+        public final boolean remove(Object o) {
+            if (o != null) {
+                for (Iterator<V> it = iterator(); it.hasNext();) {
+                    if (o.equals(it.next())) {
+                        it.remove();
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        public final Iterator<V> iterator() {
+            ConcurrentHashMapV8<K,V> m = map;
+            Node<K,V>[] t;
+            int f = (t = m.table) == null ? 0 : t.length;
+            return new ValueIterator<K,V>(t, f, 0, f, m);
+        }
+
+        public final boolean add(V e) {
+            throw new UnsupportedOperationException();
+        }
+        public final boolean addAll(Collection<? extends V> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        //calvin: change the name to avoid conflict in JDK8
+        public ConcurrentHashMapSpliterator<V> spliterator2() {
+            Node<K,V>[] t;
+            ConcurrentHashMapV8<K,V> m = map;
+            long n = m.sumCount();
+            int f = (t = m.table) == null ? 0 : t.length;
+            return new ValueSpliterator<K,V>(t, f, 0, f, n < 0L ? 0L : n);
+        }
+
+        public void forEach(Action<? super V> action) {
+            if (action == null) throw new NullPointerException();
+            Node<K,V>[] t;
+            if ((t = map.table) != null) {
+                Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+                for (Node<K,V> p; (p = it.advance()) != null; )
+                    action.apply(p.val);
+            }
+        }
+    }
+
+    /**
+     * A view of a ConcurrentHashMapV8 as a {@link Set} of (key, value)
+     * entries.  This class cannot be directly instantiated. See
+     * {@link #entrySet()}.
+     */
+    static final class EntrySetView<K,V> extends CollectionView<K,V,Entry<K,V>>
+        implements Set<Entry<K,V>>, Serializable {
+        private static final long serialVersionUID = 2249069246763182397L;
+        EntrySetView(ConcurrentHashMapV8<K,V> map) { super(map); }
+
+        public boolean contains(Object o) {
+            Object k, v, r; Entry<?,?> e;
+            return ((o instanceof Map.Entry) &&
+                    (k = (e = (Entry<?,?>)o).getKey()) != null &&
+                    (r = map.get(k)) != null &&
+                    (v = e.getValue()) != null &&
+                    (v == r || v.equals(r)));
+        }
+
+        public boolean remove(Object o) {
+            Object k, v; Entry<?,?> e;
+            return ((o instanceof Map.Entry) &&
+                    (k = (e = (Entry<?,?>)o).getKey()) != null &&
+                    (v = e.getValue()) != null &&
+                    map.remove(k, v));
+        }
+
+        /**
+         * @return an iterator over the entries of the backing map
+         */
+        public Iterator<Entry<K,V>> iterator() {
+            ConcurrentHashMapV8<K,V> m = map;
+            Node<K,V>[] t;
+            int f = (t = m.table) == null ? 0 : t.length;
+            return new EntryIterator<K,V>(t, f, 0, f, m);
+        }
+
+        public boolean add(Entry<K,V> e) {
+            return map.putVal(e.getKey(), e.getValue(), false) == null;
+        }
+
+        public boolean addAll(Collection<? extends Entry<K,V>> c) {
+            boolean added = false;
+            for (Entry<K,V> e : c) {
+                if (add(e))
+                    added = true;
+            }
+            return added;
+        }
+
+        public final int hashCode() {
+            int h = 0;
+            Node<K,V>[] t;
+            if ((t = map.table) != null) {
+                Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+                for (Node<K,V> p; (p = it.advance()) != null; ) {
+                    h += p.hashCode();
+                }
+            }
+            return h;
+        }
+
+        public final boolean equals(Object o) {
+            Set<?> c;
+            return ((o instanceof Set) &&
+                    ((c = (Set<?>)o) == this ||
+                     (containsAll(c) && c.containsAll(this))));
+        }
+
+        //calvin: change the name to avoid conflict in JDK8
+        public ConcurrentHashMapSpliterator<Entry<K,V>> spliterator2() {
+            Node<K,V>[] t;
+            ConcurrentHashMapV8<K,V> m = map;
+            long n = m.sumCount();
+            int f = (t = m.table) == null ? 0 : t.length;
+            return new EntrySpliterator<K,V>(t, f, 0, f, n < 0L ? 0L : n, m);
+        }
+
+        public void forEach(Action<? super Entry<K,V>> action) {
+            if (action == null) throw new NullPointerException();
+            Node<K,V>[] t;
+            if ((t = map.table) != null) {
+                Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+                for (Node<K,V> p; (p = it.advance()) != null; )
+                    action.apply(new MapEntry<K,V>(p.key, p.val, map));
+            }
+        }
+
+    }
+
+    // -------------------------------------------------------
+
+    /**
+     * Base class for bulk tasks. Repeats some fields and code from
+     * class Traverser, because we need to subclass CountedCompleter.
+     */
+    abstract static class BulkTask<K,V,R> extends CountedCompleter<R> {
+        Node<K,V>[] tab;        // same as Traverser
+        Node<K,V> next;
+        int index;
+        int baseIndex;
+        int baseLimit;
+        final int baseSize;
+        int batch;              // split control
+
+        BulkTask(BulkTask<K,V,?> par, int b, int i, int f, Node<K,V>[] t) {
+            super(par);
+            this.batch = b;
+            this.index = this.baseIndex = i;
+            if ((this.tab = t) == null)
+                this.baseSize = this.baseLimit = 0;
+            else if (par == null)
+                this.baseSize = this.baseLimit = t.length;
+            else {
+                this.baseLimit = f;
+                this.baseSize = par.baseSize;
+            }
+        }
+
+        /**
+         * Same as Traverser version
+         */
+        final Node<K,V> advance() {
+            Node<K,V> e;
+            if ((e = next) != null)
+                e = e.next;
+            for (;;) {
+                Node<K,V>[] t; int i, n; K ek;  // must use locals in checks
+                if (e != null)
+                    return next = e;
+                if (baseIndex >= baseLimit || (t = tab) == null ||
+                    (n = t.length) <= (i = index) || i < 0)
+                    return next = null;
+                if ((e = tabAt(t, index)) != null && e.hash < 0) {
+                    if (e instanceof ForwardingNode) {
+                        tab = ((ForwardingNode<K,V>)e).nextTable;
+                        e = null;
+                        continue;
+                    }
+                    else if (e instanceof TreeBin)
+                        e = ((TreeBin<K,V>)e).first;
+                    else
+                        e = null;
+                }
+                if ((index += baseSize) >= n)
+                    index = ++baseIndex;    // visit upper slots if present
+            }
+        }
+    }
+
+    /*
+     * Task classes. Coded in a regular but ugly format/style to
+     * simplify checks that each variant differs in the right way from
+     * others. The null screenings exist because compilers cannot tell
+     * that we've already null-checked task arguments, so we force
+     * simplest hoisted bypass to help avoid convoluted traps.
+     */
+    @SuppressWarnings("serial")
+    static final class ForEachKeyTask<K,V>
+        extends BulkTask<K,V,Void> {
+        final Action<? super K> action;
+        ForEachKeyTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             Action<? super K> action) {
+            super(p, b, i, f, t);
+            this.action = action;
+        }
+        public final void compute() {
+            final Action<? super K> action;
+            if ((action = this.action) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    new ForEachKeyTask<K,V>
+                        (this, batch >>>= 1, baseLimit = h, f, tab,
+                         action).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null;)
+                    action.apply(p.key);
+                propagateCompletion();
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class ForEachValueTask<K,V>
+        extends BulkTask<K,V,Void> {
+        final Action<? super V> action;
+        ForEachValueTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             Action<? super V> action) {
+            super(p, b, i, f, t);
+            this.action = action;
+        }
+        public final void compute() {
+            final Action<? super V> action;
+            if ((action = this.action) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    new ForEachValueTask<K,V>
+                        (this, batch >>>= 1, baseLimit = h, f, tab,
+                         action).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null;)
+                    action.apply(p.val);
+                propagateCompletion();
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class ForEachEntryTask<K,V>
+        extends BulkTask<K,V,Void> {
+        final Action<? super Entry<K,V>> action;
+        ForEachEntryTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             Action<? super Entry<K,V>> action) {
+            super(p, b, i, f, t);
+            this.action = action;
+        }
+        public final void compute() {
+            final Action<? super Entry<K,V>> action;
+            if ((action = this.action) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    new ForEachEntryTask<K,V>
+                        (this, batch >>>= 1, baseLimit = h, f, tab,
+                         action).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    action.apply(p);
+                propagateCompletion();
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class ForEachMappingTask<K,V>
+        extends BulkTask<K,V,Void> {
+        final BiAction<? super K, ? super V> action;
+        ForEachMappingTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             BiAction<? super K,? super V> action) {
+            super(p, b, i, f, t);
+            this.action = action;
+        }
+        public final void compute() {
+            final BiAction<? super K, ? super V> action;
+            if ((action = this.action) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    new ForEachMappingTask<K,V>
+                        (this, batch >>>= 1, baseLimit = h, f, tab,
+                         action).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    action.apply(p.key, p.val);
+                propagateCompletion();
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class ForEachTransformedKeyTask<K,V,U>
+        extends BulkTask<K,V,Void> {
+        final Fun<? super K, ? extends U> transformer;
+        final Action<? super U> action;
+        ForEachTransformedKeyTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             Fun<? super K, ? extends U> transformer, Action<? super U> action) {
+            super(p, b, i, f, t);
+            this.transformer = transformer; this.action = action;
+        }
+        public final void compute() {
+            final Fun<? super K, ? extends U> transformer;
+            final Action<? super U> action;
+            if ((transformer = this.transformer) != null &&
+                (action = this.action) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    new ForEachTransformedKeyTask<K,V,U>
+                        (this, batch >>>= 1, baseLimit = h, f, tab,
+                         transformer, action).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; ) {
+                    U u;
+                    if ((u = transformer.apply(p.key)) != null)
+                        action.apply(u);
+                }
+                propagateCompletion();
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class ForEachTransformedValueTask<K,V,U>
+        extends BulkTask<K,V,Void> {
+        final Fun<? super V, ? extends U> transformer;
+        final Action<? super U> action;
+        ForEachTransformedValueTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             Fun<? super V, ? extends U> transformer, Action<? super U> action) {
+            super(p, b, i, f, t);
+            this.transformer = transformer; this.action = action;
+        }
+        public final void compute() {
+            final Fun<? super V, ? extends U> transformer;
+            final Action<? super U> action;
+            if ((transformer = this.transformer) != null &&
+                (action = this.action) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    new ForEachTransformedValueTask<K,V,U>
+                        (this, batch >>>= 1, baseLimit = h, f, tab,
+                         transformer, action).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; ) {
+                    U u;
+                    if ((u = transformer.apply(p.val)) != null)
+                        action.apply(u);
+                }
+                propagateCompletion();
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class ForEachTransformedEntryTask<K,V,U>
+        extends BulkTask<K,V,Void> {
+        final Fun<Entry<K,V>, ? extends U> transformer;
+        final Action<? super U> action;
+        ForEachTransformedEntryTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             Fun<Entry<K,V>, ? extends U> transformer, Action<? super U> action) {
+            super(p, b, i, f, t);
+            this.transformer = transformer; this.action = action;
+        }
+        public final void compute() {
+            final Fun<Entry<K,V>, ? extends U> transformer;
+            final Action<? super U> action;
+            if ((transformer = this.transformer) != null &&
+                (action = this.action) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    new ForEachTransformedEntryTask<K,V,U>
+                        (this, batch >>>= 1, baseLimit = h, f, tab,
+                         transformer, action).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; ) {
+                    U u;
+                    if ((u = transformer.apply(p)) != null)
+                        action.apply(u);
+                }
+                propagateCompletion();
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class ForEachTransformedMappingTask<K,V,U>
+        extends BulkTask<K,V,Void> {
+        final BiFun<? super K, ? super V, ? extends U> transformer;
+        final Action<? super U> action;
+        ForEachTransformedMappingTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             BiFun<? super K, ? super V, ? extends U> transformer,
+             Action<? super U> action) {
+            super(p, b, i, f, t);
+            this.transformer = transformer; this.action = action;
+        }
+        public final void compute() {
+            final BiFun<? super K, ? super V, ? extends U> transformer;
+            final Action<? super U> action;
+            if ((transformer = this.transformer) != null &&
+                (action = this.action) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    new ForEachTransformedMappingTask<K,V,U>
+                        (this, batch >>>= 1, baseLimit = h, f, tab,
+                         transformer, action).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; ) {
+                    U u;
+                    if ((u = transformer.apply(p.key, p.val)) != null)
+                        action.apply(u);
+                }
+                propagateCompletion();
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class SearchKeysTask<K,V,U>
+        extends BulkTask<K,V,U> {
+        final Fun<? super K, ? extends U> searchFunction;
+        final AtomicReference<U> result;
+        SearchKeysTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             Fun<? super K, ? extends U> searchFunction,
+             AtomicReference<U> result) {
+            super(p, b, i, f, t);
+            this.searchFunction = searchFunction; this.result = result;
+        }
+        public final U getRawResult() { return result.get(); }
+        public final void compute() {
+            final Fun<? super K, ? extends U> searchFunction;
+            final AtomicReference<U> result;
+            if ((searchFunction = this.searchFunction) != null &&
+                (result = this.result) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    if (result.get() != null)
+                        return;
+                    addToPendingCount(1);
+                    new SearchKeysTask<K,V,U>
+                        (this, batch >>>= 1, baseLimit = h, f, tab,
+                         searchFunction, result).fork();
+                }
+                while (result.get() == null) {
+                    U u;
+                    Node<K,V> p;
+                    if ((p = advance()) == null) {
+                        propagateCompletion();
+                        break;
+                    }
+                    if ((u = searchFunction.apply(p.key)) != null) {
+                        if (result.compareAndSet(null, u))
+                            quietlyCompleteRoot();
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class SearchValuesTask<K,V,U>
+        extends BulkTask<K,V,U> {
+        final Fun<? super V, ? extends U> searchFunction;
+        final AtomicReference<U> result;
+        SearchValuesTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             Fun<? super V, ? extends U> searchFunction,
+             AtomicReference<U> result) {
+            super(p, b, i, f, t);
+            this.searchFunction = searchFunction; this.result = result;
+        }
+        public final U getRawResult() { return result.get(); }
+        public final void compute() {
+            final Fun<? super V, ? extends U> searchFunction;
+            final AtomicReference<U> result;
+            if ((searchFunction = this.searchFunction) != null &&
+                (result = this.result) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    if (result.get() != null)
+                        return;
+                    addToPendingCount(1);
+                    new SearchValuesTask<K,V,U>
+                        (this, batch >>>= 1, baseLimit = h, f, tab,
+                         searchFunction, result).fork();
+                }
+                while (result.get() == null) {
+                    U u;
+                    Node<K,V> p;
+                    if ((p = advance()) == null) {
+                        propagateCompletion();
+                        break;
+                    }
+                    if ((u = searchFunction.apply(p.val)) != null) {
+                        if (result.compareAndSet(null, u))
+                            quietlyCompleteRoot();
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class SearchEntriesTask<K,V,U>
+        extends BulkTask<K,V,U> {
+        final Fun<Entry<K,V>, ? extends U> searchFunction;
+        final AtomicReference<U> result;
+        SearchEntriesTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             Fun<Entry<K,V>, ? extends U> searchFunction,
+             AtomicReference<U> result) {
+            super(p, b, i, f, t);
+            this.searchFunction = searchFunction; this.result = result;
+        }
+        public final U getRawResult() { return result.get(); }
+        public final void compute() {
+            final Fun<Entry<K,V>, ? extends U> searchFunction;
+            final AtomicReference<U> result;
+            if ((searchFunction = this.searchFunction) != null &&
+                (result = this.result) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    if (result.get() != null)
+                        return;
+                    addToPendingCount(1);
+                    new SearchEntriesTask<K,V,U>
+                        (this, batch >>>= 1, baseLimit = h, f, tab,
+                         searchFunction, result).fork();
+                }
+                while (result.get() == null) {
+                    U u;
+                    Node<K,V> p;
+                    if ((p = advance()) == null) {
+                        propagateCompletion();
+                        break;
+                    }
+                    if ((u = searchFunction.apply(p)) != null) {
+                        if (result.compareAndSet(null, u))
+                            quietlyCompleteRoot();
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class SearchMappingsTask<K,V,U>
+        extends BulkTask<K,V,U> {
+        final BiFun<? super K, ? super V, ? extends U> searchFunction;
+        final AtomicReference<U> result;
+        SearchMappingsTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             BiFun<? super K, ? super V, ? extends U> searchFunction,
+             AtomicReference<U> result) {
+            super(p, b, i, f, t);
+            this.searchFunction = searchFunction; this.result = result;
+        }
+        public final U getRawResult() { return result.get(); }
+        public final void compute() {
+            final BiFun<? super K, ? super V, ? extends U> searchFunction;
+            final AtomicReference<U> result;
+            if ((searchFunction = this.searchFunction) != null &&
+                (result = this.result) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    if (result.get() != null)
+                        return;
+                    addToPendingCount(1);
+                    new SearchMappingsTask<K,V,U>
+                        (this, batch >>>= 1, baseLimit = h, f, tab,
+                         searchFunction, result).fork();
+                }
+                while (result.get() == null) {
+                    U u;
+                    Node<K,V> p;
+                    if ((p = advance()) == null) {
+                        propagateCompletion();
+                        break;
+                    }
+                    if ((u = searchFunction.apply(p.key, p.val)) != null) {
+                        if (result.compareAndSet(null, u))
+                            quietlyCompleteRoot();
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class ReduceKeysTask<K,V>
+        extends BulkTask<K,V,K> {
+        final BiFun<? super K, ? super K, ? extends K> reducer;
+        K result;
+        ReduceKeysTask<K,V> rights, nextRight;
+        ReduceKeysTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             ReduceKeysTask<K,V> nextRight,
+             BiFun<? super K, ? super K, ? extends K> reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.reducer = reducer;
+        }
+        public final K getRawResult() { return result; }
+        public final void compute() {
+            final BiFun<? super K, ? super K, ? extends K> reducer;
+            if ((reducer = this.reducer) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new ReduceKeysTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, reducer)).fork();
+                }
+                K r = null;
+                for (Node<K,V> p; (p = advance()) != null; ) {
+                    K u = p.key;
+                    r = (r == null) ? u : u == null ? r : reducer.apply(r, u);
+                }
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") ReduceKeysTask<K,V>
+                        t = (ReduceKeysTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        K tr, sr;
+                        if ((sr = s.result) != null)
+                            t.result = (((tr = t.result) == null) ? sr :
+                                        reducer.apply(tr, sr));
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class ReduceValuesTask<K,V>
+        extends BulkTask<K,V,V> {
+        final BiFun<? super V, ? super V, ? extends V> reducer;
+        V result;
+        ReduceValuesTask<K,V> rights, nextRight;
+        ReduceValuesTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             ReduceValuesTask<K,V> nextRight,
+             BiFun<? super V, ? super V, ? extends V> reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.reducer = reducer;
+        }
+        public final V getRawResult() { return result; }
+        public final void compute() {
+            final BiFun<? super V, ? super V, ? extends V> reducer;
+            if ((reducer = this.reducer) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new ReduceValuesTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, reducer)).fork();
+                }
+                V r = null;
+                for (Node<K,V> p; (p = advance()) != null; ) {
+                    V v = p.val;
+                    r = (r == null) ? v : reducer.apply(r, v);
+                }
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") ReduceValuesTask<K,V>
+                        t = (ReduceValuesTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        V tr, sr;
+                        if ((sr = s.result) != null)
+                            t.result = (((tr = t.result) == null) ? sr :
+                                        reducer.apply(tr, sr));
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class ReduceEntriesTask<K,V>
+        extends BulkTask<K,V,Entry<K,V>> {
+        final BiFun<Entry<K,V>, Entry<K,V>, ? extends Entry<K,V>> reducer;
+        Entry<K,V> result;
+        ReduceEntriesTask<K,V> rights, nextRight;
+        ReduceEntriesTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             ReduceEntriesTask<K,V> nextRight,
+             BiFun<Entry<K,V>, Entry<K,V>, ? extends Entry<K,V>> reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.reducer = reducer;
+        }
+        public final Entry<K,V> getRawResult() { return result; }
+        public final void compute() {
+            final BiFun<Entry<K,V>, Entry<K,V>, ? extends Entry<K,V>> reducer;
+            if ((reducer = this.reducer) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new ReduceEntriesTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, reducer)).fork();
+                }
+                Entry<K,V> r = null;
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = (r == null) ? p : reducer.apply(r, p);
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") ReduceEntriesTask<K,V>
+                        t = (ReduceEntriesTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        Entry<K,V> tr, sr;
+                        if ((sr = s.result) != null)
+                            t.result = (((tr = t.result) == null) ? sr :
+                                        reducer.apply(tr, sr));
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceKeysTask<K,V,U>
+        extends BulkTask<K,V,U> {
+        final Fun<? super K, ? extends U> transformer;
+        final BiFun<? super U, ? super U, ? extends U> reducer;
+        U result;
+        MapReduceKeysTask<K,V,U> rights, nextRight;
+        MapReduceKeysTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceKeysTask<K,V,U> nextRight,
+             Fun<? super K, ? extends U> transformer,
+             BiFun<? super U, ? super U, ? extends U> reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.reducer = reducer;
+        }
+        public final U getRawResult() { return result; }
+        public final void compute() {
+            final Fun<? super K, ? extends U> transformer;
+            final BiFun<? super U, ? super U, ? extends U> reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceKeysTask<K,V,U>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, reducer)).fork();
+                }
+                U r = null;
+                for (Node<K,V> p; (p = advance()) != null; ) {
+                    U u;
+                    if ((u = transformer.apply(p.key)) != null)
+                        r = (r == null) ? u : reducer.apply(r, u);
+                }
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceKeysTask<K,V,U>
+                        t = (MapReduceKeysTask<K,V,U>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        U tr, sr;
+                        if ((sr = s.result) != null)
+                            t.result = (((tr = t.result) == null) ? sr :
+                                        reducer.apply(tr, sr));
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceValuesTask<K,V,U>
+        extends BulkTask<K,V,U> {
+        final Fun<? super V, ? extends U> transformer;
+        final BiFun<? super U, ? super U, ? extends U> reducer;
+        U result;
+        MapReduceValuesTask<K,V,U> rights, nextRight;
+        MapReduceValuesTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceValuesTask<K,V,U> nextRight,
+             Fun<? super V, ? extends U> transformer,
+             BiFun<? super U, ? super U, ? extends U> reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.reducer = reducer;
+        }
+        public final U getRawResult() { return result; }
+        public final void compute() {
+            final Fun<? super V, ? extends U> transformer;
+            final BiFun<? super U, ? super U, ? extends U> reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceValuesTask<K,V,U>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, reducer)).fork();
+                }
+                U r = null;
+                for (Node<K,V> p; (p = advance()) != null; ) {
+                    U u;
+                    if ((u = transformer.apply(p.val)) != null)
+                        r = (r == null) ? u : reducer.apply(r, u);
+                }
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceValuesTask<K,V,U>
+                        t = (MapReduceValuesTask<K,V,U>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        U tr, sr;
+                        if ((sr = s.result) != null)
+                            t.result = (((tr = t.result) == null) ? sr :
+                                        reducer.apply(tr, sr));
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceEntriesTask<K,V,U>
+        extends BulkTask<K,V,U> {
+        final Fun<Entry<K,V>, ? extends U> transformer;
+        final BiFun<? super U, ? super U, ? extends U> reducer;
+        U result;
+        MapReduceEntriesTask<K,V,U> rights, nextRight;
+        MapReduceEntriesTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceEntriesTask<K,V,U> nextRight,
+             Fun<Entry<K,V>, ? extends U> transformer,
+             BiFun<? super U, ? super U, ? extends U> reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.reducer = reducer;
+        }
+        public final U getRawResult() { return result; }
+        public final void compute() {
+            final Fun<Entry<K,V>, ? extends U> transformer;
+            final BiFun<? super U, ? super U, ? extends U> reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceEntriesTask<K,V,U>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, reducer)).fork();
+                }
+                U r = null;
+                for (Node<K,V> p; (p = advance()) != null; ) {
+                    U u;
+                    if ((u = transformer.apply(p)) != null)
+                        r = (r == null) ? u : reducer.apply(r, u);
+                }
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceEntriesTask<K,V,U>
+                        t = (MapReduceEntriesTask<K,V,U>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        U tr, sr;
+                        if ((sr = s.result) != null)
+                            t.result = (((tr = t.result) == null) ? sr :
+                                        reducer.apply(tr, sr));
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceMappingsTask<K,V,U>
+        extends BulkTask<K,V,U> {
+        final BiFun<? super K, ? super V, ? extends U> transformer;
+        final BiFun<? super U, ? super U, ? extends U> reducer;
+        U result;
+        MapReduceMappingsTask<K,V,U> rights, nextRight;
+        MapReduceMappingsTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceMappingsTask<K,V,U> nextRight,
+             BiFun<? super K, ? super V, ? extends U> transformer,
+             BiFun<? super U, ? super U, ? extends U> reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.reducer = reducer;
+        }
+        public final U getRawResult() { return result; }
+        public final void compute() {
+            final BiFun<? super K, ? super V, ? extends U> transformer;
+            final BiFun<? super U, ? super U, ? extends U> reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceMappingsTask<K,V,U>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, reducer)).fork();
+                }
+                U r = null;
+                for (Node<K,V> p; (p = advance()) != null; ) {
+                    U u;
+                    if ((u = transformer.apply(p.key, p.val)) != null)
+                        r = (r == null) ? u : reducer.apply(r, u);
+                }
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceMappingsTask<K,V,U>
+                        t = (MapReduceMappingsTask<K,V,U>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        U tr, sr;
+                        if ((sr = s.result) != null)
+                            t.result = (((tr = t.result) == null) ? sr :
+                                        reducer.apply(tr, sr));
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceKeysToDoubleTask<K,V>
+        extends BulkTask<K,V,Double> {
+        final ObjectToDouble<? super K> transformer;
+        final DoubleByDoubleToDouble reducer;
+        final double basis;
+        double result;
+        MapReduceKeysToDoubleTask<K,V> rights, nextRight;
+        MapReduceKeysToDoubleTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceKeysToDoubleTask<K,V> nextRight,
+             ObjectToDouble<? super K> transformer,
+             double basis,
+             DoubleByDoubleToDouble reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.basis = basis; this.reducer = reducer;
+        }
+        public final Double getRawResult() { return result; }
+        public final void compute() {
+            final ObjectToDouble<? super K> transformer;
+            final DoubleByDoubleToDouble reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                double r = this.basis;
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceKeysToDoubleTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, r, reducer)).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = reducer.apply(r, transformer.apply(p.key));
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceKeysToDoubleTask<K,V>
+                        t = (MapReduceKeysToDoubleTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        t.result = reducer.apply(t.result, s.result);
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceValuesToDoubleTask<K,V>
+        extends BulkTask<K,V,Double> {
+        final ObjectToDouble<? super V> transformer;
+        final DoubleByDoubleToDouble reducer;
+        final double basis;
+        double result;
+        MapReduceValuesToDoubleTask<K,V> rights, nextRight;
+        MapReduceValuesToDoubleTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceValuesToDoubleTask<K,V> nextRight,
+             ObjectToDouble<? super V> transformer,
+             double basis,
+             DoubleByDoubleToDouble reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.basis = basis; this.reducer = reducer;
+        }
+        public final Double getRawResult() { return result; }
+        public final void compute() {
+            final ObjectToDouble<? super V> transformer;
+            final DoubleByDoubleToDouble reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                double r = this.basis;
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceValuesToDoubleTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, r, reducer)).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = reducer.apply(r, transformer.apply(p.val));
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceValuesToDoubleTask<K,V>
+                        t = (MapReduceValuesToDoubleTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        t.result = reducer.apply(t.result, s.result);
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceEntriesToDoubleTask<K,V>
+        extends BulkTask<K,V,Double> {
+        final ObjectToDouble<Entry<K,V>> transformer;
+        final DoubleByDoubleToDouble reducer;
+        final double basis;
+        double result;
+        MapReduceEntriesToDoubleTask<K,V> rights, nextRight;
+        MapReduceEntriesToDoubleTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceEntriesToDoubleTask<K,V> nextRight,
+             ObjectToDouble<Entry<K,V>> transformer,
+             double basis,
+             DoubleByDoubleToDouble reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.basis = basis; this.reducer = reducer;
+        }
+        public final Double getRawResult() { return result; }
+        public final void compute() {
+            final ObjectToDouble<Entry<K,V>> transformer;
+            final DoubleByDoubleToDouble reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                double r = this.basis;
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceEntriesToDoubleTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, r, reducer)).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = reducer.apply(r, transformer.apply(p));
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceEntriesToDoubleTask<K,V>
+                        t = (MapReduceEntriesToDoubleTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        t.result = reducer.apply(t.result, s.result);
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceMappingsToDoubleTask<K,V>
+        extends BulkTask<K,V,Double> {
+        final ObjectByObjectToDouble<? super K, ? super V> transformer;
+        final DoubleByDoubleToDouble reducer;
+        final double basis;
+        double result;
+        MapReduceMappingsToDoubleTask<K,V> rights, nextRight;
+        MapReduceMappingsToDoubleTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceMappingsToDoubleTask<K,V> nextRight,
+             ObjectByObjectToDouble<? super K, ? super V> transformer,
+             double basis,
+             DoubleByDoubleToDouble reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.basis = basis; this.reducer = reducer;
+        }
+        public final Double getRawResult() { return result; }
+        public final void compute() {
+            final ObjectByObjectToDouble<? super K, ? super V> transformer;
+            final DoubleByDoubleToDouble reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                double r = this.basis;
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceMappingsToDoubleTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, r, reducer)).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = reducer.apply(r, transformer.apply(p.key, p.val));
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceMappingsToDoubleTask<K,V>
+                        t = (MapReduceMappingsToDoubleTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        t.result = reducer.apply(t.result, s.result);
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceKeysToLongTask<K,V>
+        extends BulkTask<K,V,Long> {
+        final ObjectToLong<? super K> transformer;
+        final LongByLongToLong reducer;
+        final long basis;
+        long result;
+        MapReduceKeysToLongTask<K,V> rights, nextRight;
+        MapReduceKeysToLongTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceKeysToLongTask<K,V> nextRight,
+             ObjectToLong<? super K> transformer,
+             long basis,
+             LongByLongToLong reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.basis = basis; this.reducer = reducer;
+        }
+        public final Long getRawResult() { return result; }
+        public final void compute() {
+            final ObjectToLong<? super K> transformer;
+            final LongByLongToLong reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                long r = this.basis;
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceKeysToLongTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, r, reducer)).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = reducer.apply(r, transformer.apply(p.key));
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceKeysToLongTask<K,V>
+                        t = (MapReduceKeysToLongTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        t.result = reducer.apply(t.result, s.result);
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceValuesToLongTask<K,V>
+        extends BulkTask<K,V,Long> {
+        final ObjectToLong<? super V> transformer;
+        final LongByLongToLong reducer;
+        final long basis;
+        long result;
+        MapReduceValuesToLongTask<K,V> rights, nextRight;
+        MapReduceValuesToLongTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceValuesToLongTask<K,V> nextRight,
+             ObjectToLong<? super V> transformer,
+             long basis,
+             LongByLongToLong reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.basis = basis; this.reducer = reducer;
+        }
+        public final Long getRawResult() { return result; }
+        public final void compute() {
+            final ObjectToLong<? super V> transformer;
+            final LongByLongToLong reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                long r = this.basis;
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceValuesToLongTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, r, reducer)).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = reducer.apply(r, transformer.apply(p.val));
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceValuesToLongTask<K,V>
+                        t = (MapReduceValuesToLongTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        t.result = reducer.apply(t.result, s.result);
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceEntriesToLongTask<K,V>
+        extends BulkTask<K,V,Long> {
+        final ObjectToLong<Entry<K,V>> transformer;
+        final LongByLongToLong reducer;
+        final long basis;
+        long result;
+        MapReduceEntriesToLongTask<K,V> rights, nextRight;
+        MapReduceEntriesToLongTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceEntriesToLongTask<K,V> nextRight,
+             ObjectToLong<Entry<K,V>> transformer,
+             long basis,
+             LongByLongToLong reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.basis = basis; this.reducer = reducer;
+        }
+        public final Long getRawResult() { return result; }
+        public final void compute() {
+            final ObjectToLong<Entry<K,V>> transformer;
+            final LongByLongToLong reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                long r = this.basis;
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceEntriesToLongTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, r, reducer)).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = reducer.apply(r, transformer.apply(p));
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceEntriesToLongTask<K,V>
+                        t = (MapReduceEntriesToLongTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        t.result = reducer.apply(t.result, s.result);
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceMappingsToLongTask<K,V>
+        extends BulkTask<K,V,Long> {
+        final ObjectByObjectToLong<? super K, ? super V> transformer;
+        final LongByLongToLong reducer;
+        final long basis;
+        long result;
+        MapReduceMappingsToLongTask<K,V> rights, nextRight;
+        MapReduceMappingsToLongTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceMappingsToLongTask<K,V> nextRight,
+             ObjectByObjectToLong<? super K, ? super V> transformer,
+             long basis,
+             LongByLongToLong reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.basis = basis; this.reducer = reducer;
+        }
+        public final Long getRawResult() { return result; }
+        public final void compute() {
+            final ObjectByObjectToLong<? super K, ? super V> transformer;
+            final LongByLongToLong reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                long r = this.basis;
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceMappingsToLongTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, r, reducer)).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = reducer.apply(r, transformer.apply(p.key, p.val));
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceMappingsToLongTask<K,V>
+                        t = (MapReduceMappingsToLongTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        t.result = reducer.apply(t.result, s.result);
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceKeysToIntTask<K,V>
+        extends BulkTask<K,V,Integer> {
+        final ObjectToInt<? super K> transformer;
+        final IntByIntToInt reducer;
+        final int basis;
+        int result;
+        MapReduceKeysToIntTask<K,V> rights, nextRight;
+        MapReduceKeysToIntTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceKeysToIntTask<K,V> nextRight,
+             ObjectToInt<? super K> transformer,
+             int basis,
+             IntByIntToInt reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.basis = basis; this.reducer = reducer;
+        }
+        public final Integer getRawResult() { return result; }
+        public final void compute() {
+            final ObjectToInt<? super K> transformer;
+            final IntByIntToInt reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                int r = this.basis;
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceKeysToIntTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, r, reducer)).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = reducer.apply(r, transformer.apply(p.key));
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceKeysToIntTask<K,V>
+                        t = (MapReduceKeysToIntTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        t.result = reducer.apply(t.result, s.result);
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceValuesToIntTask<K,V>
+        extends BulkTask<K,V,Integer> {
+        final ObjectToInt<? super V> transformer;
+        final IntByIntToInt reducer;
+        final int basis;
+        int result;
+        MapReduceValuesToIntTask<K,V> rights, nextRight;
+        MapReduceValuesToIntTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceValuesToIntTask<K,V> nextRight,
+             ObjectToInt<? super V> transformer,
+             int basis,
+             IntByIntToInt reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.basis = basis; this.reducer = reducer;
+        }
+        public final Integer getRawResult() { return result; }
+        public final void compute() {
+            final ObjectToInt<? super V> transformer;
+            final IntByIntToInt reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                int r = this.basis;
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceValuesToIntTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, r, reducer)).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = reducer.apply(r, transformer.apply(p.val));
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceValuesToIntTask<K,V>
+                        t = (MapReduceValuesToIntTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        t.result = reducer.apply(t.result, s.result);
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceEntriesToIntTask<K,V>
+        extends BulkTask<K,V,Integer> {
+        final ObjectToInt<Entry<K,V>> transformer;
+        final IntByIntToInt reducer;
+        final int basis;
+        int result;
+        MapReduceEntriesToIntTask<K,V> rights, nextRight;
+        MapReduceEntriesToIntTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceEntriesToIntTask<K,V> nextRight,
+             ObjectToInt<Entry<K,V>> transformer,
+             int basis,
+             IntByIntToInt reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.basis = basis; this.reducer = reducer;
+        }
+        public final Integer getRawResult() { return result; }
+        public final void compute() {
+            final ObjectToInt<Entry<K,V>> transformer;
+            final IntByIntToInt reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                int r = this.basis;
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceEntriesToIntTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, r, reducer)).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = reducer.apply(r, transformer.apply(p));
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceEntriesToIntTask<K,V>
+                        t = (MapReduceEntriesToIntTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        t.result = reducer.apply(t.result, s.result);
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static final class MapReduceMappingsToIntTask<K,V>
+        extends BulkTask<K,V,Integer> {
+        final ObjectByObjectToInt<? super K, ? super V> transformer;
+        final IntByIntToInt reducer;
+        final int basis;
+        int result;
+        MapReduceMappingsToIntTask<K,V> rights, nextRight;
+        MapReduceMappingsToIntTask
+            (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+             MapReduceMappingsToIntTask<K,V> nextRight,
+             ObjectByObjectToInt<? super K, ? super V> transformer,
+             int basis,
+             IntByIntToInt reducer) {
+            super(p, b, i, f, t); this.nextRight = nextRight;
+            this.transformer = transformer;
+            this.basis = basis; this.reducer = reducer;
+        }
+        public final Integer getRawResult() { return result; }
+        public final void compute() {
+            final ObjectByObjectToInt<? super K, ? super V> transformer;
+            final IntByIntToInt reducer;
+            if ((transformer = this.transformer) != null &&
+                (reducer = this.reducer) != null) {
+                int r = this.basis;
+                for (int i = baseIndex, f, h; batch > 0 &&
+                         (h = ((f = baseLimit) + i) >>> 1) > i;) {
+                    addToPendingCount(1);
+                    (rights = new MapReduceMappingsToIntTask<K,V>
+                     (this, batch >>>= 1, baseLimit = h, f, tab,
+                      rights, transformer, r, reducer)).fork();
+                }
+                for (Node<K,V> p; (p = advance()) != null; )
+                    r = reducer.apply(r, transformer.apply(p.key, p.val));
+                result = r;
+                CountedCompleter<?> c;
+                for (c = firstComplete(); c != null; c = c.nextComplete()) {
+                    @SuppressWarnings("unchecked") MapReduceMappingsToIntTask<K,V>
+                        t = (MapReduceMappingsToIntTask<K,V>)c,
+                        s = t.rights;
+                    while (s != null) {
+                        t.result = reducer.apply(t.result, s.result);
+                        s = t.rights = s.nextRight;
+                    }
+                }
+            }
+        }
+    }
+
+    /* ---------------- Counters -------------- */
+
+    // Adapted from LongAdder and Striped64.
+    // See their internal docs for explanation.
+
+    // A padded cell for distributing counts
+    static final class CounterCell {
+        volatile long p0, p1, p2, p3, p4, p5, p6;
+        volatile long value;
+        volatile long q0, q1, q2, q3, q4, q5, q6;
+        CounterCell(long x) { value = x; }
+    }
+
+    /**
+     * Holder for the thread-local hash code determining which
+     * CounterCell to use. The code is initialized via the
+     * counterHashCodeGenerator, but may be moved upon collisions.
+     */
+    static final class CounterHashCode {
+        int code;
+    }
+
+    /**
+     * Generates initial value for per-thread CounterHashCodes.
+     */
+    static final AtomicInteger counterHashCodeGenerator = new AtomicInteger();
+
+    /**
+     * Increment for counterHashCodeGenerator. See class ThreadLocal
+     * for explanation.
+     */
+    static final int SEED_INCREMENT = 0x61c88647;
+
+    /**
+     * Per-thread counter hash codes. Shared across all instances.
+     */
+    static final ThreadLocal<CounterHashCode> threadCounterHashCode =
+        new ThreadLocal<CounterHashCode>();
+
+
+    final long sumCount() {
+        CounterCell[] as = counterCells; CounterCell a;
+        long sum = baseCount;
+        if (as != null) {
+            for (int i = 0; i < as.length; ++i) {
+                if ((a = as[i]) != null)
+                    sum += a.value;
+            }
+        }
+        return sum;
+    }
+
+    // See LongAdder version for explanation
+    private final void fullAddCount(long x, CounterHashCode hc,
+                                    boolean wasUncontended) {
+        int h;
+        if (hc == null) {
+            hc = new CounterHashCode();
+            int s = counterHashCodeGenerator.addAndGet(SEED_INCREMENT);
+            h = hc.code = (s == 0) ? 1 : s; // Avoid zero
+            threadCounterHashCode.set(hc);
+        }
+        else
+            h = hc.code;
+        boolean collide = false;                // True if last slot nonempty
+        for (;;) {
+            CounterCell[] as; CounterCell a; int n; long v;
+            if ((as = counterCells) != null && (n = as.length) > 0) {
+                if ((a = as[(n - 1) & h]) == null) {
+                    if (cellsBusy == 0) {            // Try to attach new Cell
+                        CounterCell r = new CounterCell(x); // Optimistic create
+                        if (cellsBusy == 0 &&
+                            U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
+                            boolean created = false;
+                            try {               // Recheck under lock
+                                CounterCell[] rs; int m, j;
+                                if ((rs = counterCells) != null &&
+                                    (m = rs.length) > 0 &&
+                                    rs[j = (m - 1) & h] == null) {
+                                    rs[j] = r;
+                                    created = true;
+                                }
+                            } finally {
+                                cellsBusy = 0;
+                            }
+                            if (created)
+                                break;
+                            continue;           // Slot is now non-empty
+                        }
+                    }
+                    collide = false;
+                }
+                else if (!wasUncontended)       // CAS already known to fail
+                    wasUncontended = true;      // Continue after rehash
+                else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
+                    break;
+                else if (counterCells != as || n >= NCPU)
+                    collide = false;            // At max size or stale
+                else if (!collide)
+                    collide = true;
+                else if (cellsBusy == 0 &&
+                         U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
+                    try {
+                        if (counterCells == as) {// Expand table unless stale
+                            CounterCell[] rs = new CounterCell[n << 1];
+                            for (int i = 0; i < n; ++i)
+                                rs[i] = as[i];
+                            counterCells = rs;
+                        }
+                    } finally {
+                        cellsBusy = 0;
+                    }
+                    collide = false;
+                    continue;                   // Retry with expanded table
+                }
+                h ^= h << 13;                   // Rehash
+                h ^= h >>> 17;
+                h ^= h << 5;
+            }
+            else if (cellsBusy == 0 && counterCells == as &&
+                     U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
+                boolean init = false;
+                try {                           // Initialize table
+                    if (counterCells == as) {
+                        CounterCell[] rs = new CounterCell[2];
+                        rs[h & 1] = new CounterCell(x);
+                        counterCells = rs;
+                        init = true;
+                    }
+                } finally {
+                    cellsBusy = 0;
+                }
+                if (init)
+                    break;
+            }
+            else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
+                break;                          // Fall back on using base
+        }
+        hc.code = h;                            // Record index for next time
+    }
+
+    // Unsafe mechanics
+    private static final sun.misc.Unsafe U;
+    private static final long SIZECTL;
+    private static final long TRANSFERINDEX;
+    private static final long BASECOUNT;
+    private static final long CELLSBUSY;
+    private static final long CELLVALUE;
+    private static final long ABASE;
+    private static final int ASHIFT;
+
+    static {
+        try {
+            U = getUnsafe();
+            Class<?> k = ConcurrentHashMapV8.class;
+            SIZECTL = U.objectFieldOffset
+                (k.getDeclaredField("sizeCtl"));
+            TRANSFERINDEX = U.objectFieldOffset
+                (k.getDeclaredField("transferIndex"));
+            BASECOUNT = U.objectFieldOffset
+                (k.getDeclaredField("baseCount"));
+            CELLSBUSY = U.objectFieldOffset
+                (k.getDeclaredField("cellsBusy"));
+            Class<?> ck = CounterCell.class;
+            CELLVALUE = U.objectFieldOffset
+                (ck.getDeclaredField("value"));
+            Class<?> ak = Node[].class;
+            ABASE = U.arrayBaseOffset(ak);
+            int scale = U.arrayIndexScale(ak);
+            if ((scale & (scale - 1)) != 0)
+                throw new Error("data type scale not a power of two");
+            ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
+
+    /**
+     * Returns a sun.misc.Unsafe.  Suitable for use in a 3rd party package.
+     * Replace with a simple call to Unsafe.getUnsafe when integrating
+     * into a jdk.
+     *
+     * @return a sun.misc.Unsafe
+     */
+    private static sun.misc.Unsafe getUnsafe() {
+        try {
+            return sun.misc.Unsafe.getUnsafe();
+        } catch (SecurityException tryReflectionInstead) {}
+        try {
+            return java.security.AccessController.doPrivileged
+            (new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() {
+                public sun.misc.Unsafe run() throws Exception {
+                    Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
+                    for (java.lang.reflect.Field f : k.getDeclaredFields()) {
+                        f.setAccessible(true);
+                        Object x = f.get(null);
+                        if (k.isInstance(x))
+                            return k.cast(x);
+                    }
+                    throw new NoSuchFieldError("the Unsafe");
+                }});
+        } catch (java.security.PrivilegedActionException e) {
+            throw new RuntimeException("Could not initialize intrinsics",
+                                       e.getCause());
+        }
+    }
+}
+

+ 753 - 0
src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/CountedCompleter.java

@@ -0,0 +1,753 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package com.jeeplus.common.utils.concurrent.jsr166e;
+
+
+/**
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/CountedCompleter.java 1.31
+ * 
+ * A {@link ForkJoinTask} with a completion action performed when
+ * triggered and there are no remaining pending actions.
+ * CountedCompleters are in general more robust in the
+ * presence of subtask stalls and blockage than are other forms of
+ * ForkJoinTasks, but are less intuitive to program.  Uses of
+ * CountedCompleter are similar to those of other completion based
+ * components (such as {@link java.nio.channels.CompletionHandler})
+ * except that multiple <em>pending</em> completions may be necessary
+ * to trigger the completion action {@link #onCompletion(CountedCompleter)},
+ * not just one.
+ * Unless initialized otherwise, the {@linkplain #getPendingCount pending
+ * count} starts at zero, but may be (atomically) changed using
+ * methods {@link #setPendingCount}, {@link #addToPendingCount}, and
+ * {@link #compareAndSetPendingCount}. Upon invocation of {@link
+ * #tryComplete}, if the pending action count is nonzero, it is
+ * decremented; otherwise, the completion action is performed, and if
+ * this completer itself has a completer, the process is continued
+ * with its completer.  As is the case with related synchronization
+ * components such as {@link java.util.concurrent.Phaser Phaser} and
+ * {@link java.util.concurrent.Semaphore Semaphore}, these methods
+ * affect only internal counts; they do not establish any further
+ * internal bookkeeping. In particular, the identities of pending
+ * tasks are not maintained. As illustrated below, you can create
+ * subclasses that do record some or all pending tasks or their
+ * results when needed.  As illustrated below, utility methods
+ * supporting customization of completion traversals are also
+ * provided. However, because CountedCompleters provide only basic
+ * synchronization mechanisms, it may be useful to create further
+ * abstract subclasses that maintain linkages, fields, and additional
+ * support methods appropriate for a set of related usages.
+ *
+ * <p>A concrete CountedCompleter class must define method {@link
+ * #compute}, that should in most cases (as illustrated below), invoke
+ * {@code tryComplete()} once before returning. The class may also
+ * optionally override method {@link #onCompletion(CountedCompleter)}
+ * to perform an action upon normal completion, and method
+ * {@link #onExceptionalCompletion(Throwable, CountedCompleter)} to
+ * perform an action upon any exception.
+ *
+ * <p>CountedCompleters most often do not bear results, in which case
+ * they are normally declared as {@code CountedCompleter<Void>}, and
+ * will always return {@code null} as a result value.  In other cases,
+ * you should override method {@link #getRawResult} to provide a
+ * result from {@code join(), invoke()}, and related methods.  In
+ * general, this method should return the value of a field (or a
+ * function of one or more fields) of the CountedCompleter object that
+ * holds the result upon completion. Method {@link #setRawResult} by
+ * default plays no role in CountedCompleters.  It is possible, but
+ * rarely applicable, to override this method to maintain other
+ * objects or fields holding result data.
+ *
+ * <p>A CountedCompleter that does not itself have a completer (i.e.,
+ * one for which {@link #getCompleter} returns {@code null}) can be
+ * used as a regular ForkJoinTask with this added functionality.
+ * However, any completer that in turn has another completer serves
+ * only as an internal helper for other computations, so its own task
+ * status (as reported in methods such as {@link ForkJoinTask#isDone})
+ * is arbitrary; this status changes only upon explicit invocations of
+ * {@link #complete}, {@link ForkJoinTask#cancel},
+ * {@link ForkJoinTask#completeExceptionally(Throwable)} or upon
+ * exceptional completion of method {@code compute}. Upon any
+ * exceptional completion, the exception may be relayed to a task's
+ * completer (and its completer, and so on), if one exists and it has
+ * not otherwise already completed. Similarly, cancelling an internal
+ * CountedCompleter has only a local effect on that completer, so is
+ * not often useful.
+ *
+ * <p><b>Sample Usages.</b>
+ *
+ * <p><b>Parallel recursive decomposition.</b> CountedCompleters may
+ * be arranged in trees similar to those often used with {@link
+ * RecursiveAction}s, although the constructions involved in setting
+ * them up typically vary. Here, the completer of each task is its
+ * parent in the computation tree. Even though they entail a bit more
+ * bookkeeping, CountedCompleters may be better choices when applying
+ * a possibly time-consuming operation (that cannot be further
+ * subdivided) to each element of an array or collection; especially
+ * when the operation takes a significantly different amount of time
+ * to complete for some elements than others, either because of
+ * intrinsic variation (for example I/O) or auxiliary effects such as
+ * garbage collection.  Because CountedCompleters provide their own
+ * continuations, other threads need not block waiting to perform
+ * them.
+ *
+ * <p>For example, here is an initial version of a class that uses
+ * divide-by-two recursive decomposition to divide work into single
+ * pieces (leaf tasks). Even when work is split into individual calls,
+ * tree-based techniques are usually preferable to directly forking
+ * leaf tasks, because they reduce inter-thread communication and
+ * improve load balancing. In the recursive case, the second of each
+ * pair of subtasks to finish triggers completion of its parent
+ * (because no result combination is performed, the default no-op
+ * implementation of method {@code onCompletion} is not overridden).
+ * A static utility method sets up the base task and invokes it
+ * (here, implicitly using the {@link ForkJoinPool#commonPool()}).
+ *
+ * <pre> {@code
+ * class MyOperation<E> { void apply(E e) { ... }  }
+ *
+ * class ForEach<E> extends CountedCompleter<Void> {
+ *
+ *   public static <E> void forEach(E[] array, MyOperation<E> op) {
+ *     new ForEach<E>(null, array, op, 0, array.length).invoke();
+ *   }
+ *
+ *   final E[] array; final MyOperation<E> op; final int lo, hi;
+ *   ForEach(CountedCompleter<?> p, E[] array, MyOperation<E> op, int lo, int hi) {
+ *     super(p);
+ *     this.array = array; this.op = op; this.lo = lo; this.hi = hi;
+ *   }
+ *
+ *   public void compute() { // version 1
+ *     if (hi - lo >= 2) {
+ *       int mid = (lo + hi) >>> 1;
+ *       setPendingCount(2); // must set pending count before fork
+ *       new ForEach(this, array, op, mid, hi).fork(); // right child
+ *       new ForEach(this, array, op, lo, mid).fork(); // left child
+ *     }
+ *     else if (hi > lo)
+ *       op.apply(array[lo]);
+ *     tryComplete();
+ *   }
+ * }}</pre>
+ *
+ * This design can be improved by noticing that in the recursive case,
+ * the task has nothing to do after forking its right task, so can
+ * directly invoke its left task before returning. (This is an analog
+ * of tail recursion removal.)  Also, because the task returns upon
+ * executing its left task (rather than falling through to invoke
+ * {@code tryComplete}) the pending count is set to one:
+ *
+ * <pre> {@code
+ * class ForEach<E> ...
+ *   public void compute() { // version 2
+ *     if (hi - lo >= 2) {
+ *       int mid = (lo + hi) >>> 1;
+ *       setPendingCount(1); // only one pending
+ *       new ForEach(this, array, op, mid, hi).fork(); // right child
+ *       new ForEach(this, array, op, lo, mid).compute(); // direct invoke
+ *     }
+ *     else {
+ *       if (hi > lo)
+ *         op.apply(array[lo]);
+ *       tryComplete();
+ *     }
+ *   }
+ * }</pre>
+ *
+ * As a further improvement, notice that the left task need not even exist.
+ * Instead of creating a new one, we can iterate using the original task,
+ * and add a pending count for each fork.  Additionally, because no task
+ * in this tree implements an {@link #onCompletion(CountedCompleter)} method,
+ * {@code tryComplete()} can be replaced with {@link #propagateCompletion}.
+ *
+ * <pre> {@code
+ * class ForEach<E> ...
+ *   public void compute() { // version 3
+ *     int l = lo,  h = hi;
+ *     while (h - l >= 2) {
+ *       int mid = (l + h) >>> 1;
+ *       addToPendingCount(1);
+ *       new ForEach(this, array, op, mid, h).fork(); // right child
+ *       h = mid;
+ *     }
+ *     if (h > l)
+ *       op.apply(array[l]);
+ *     propagateCompletion();
+ *   }
+ * }</pre>
+ *
+ * Additional improvements of such classes might entail precomputing
+ * pending counts so that they can be established in constructors,
+ * specializing classes for leaf steps, subdividing by say, four,
+ * instead of two per iteration, and using an adaptive threshold
+ * instead of always subdividing down to single elements.
+ *
+ * <p><b>Searching.</b> A tree of CountedCompleters can search for a
+ * value or property in different parts of a data structure, and
+ * report a result in an {@link
+ * java.util.concurrent.atomic.AtomicReference AtomicReference} as
+ * soon as one is found. The others can poll the result to avoid
+ * unnecessary work. (You could additionally {@linkplain #cancel
+ * cancel} other tasks, but it is usually simpler and more efficient
+ * to just let them notice that the result is set and if so skip
+ * further processing.)  Illustrating again with an array using full
+ * partitioning (again, in practice, leaf tasks will almost always
+ * process more than one element):
+ *
+ * <pre> {@code
+ * class Searcher<E> extends CountedCompleter<E> {
+ *   final E[] array; final AtomicReference<E> result; final int lo, hi;
+ *   Searcher(CountedCompleter<?> p, E[] array, AtomicReference<E> result, int lo, int hi) {
+ *     super(p);
+ *     this.array = array; this.result = result; this.lo = lo; this.hi = hi;
+ *   }
+ *   public E getRawResult() { return result.get(); }
+ *   public void compute() { // similar to ForEach version 3
+ *     int l = lo,  h = hi;
+ *     while (result.get() == null && h >= l) {
+ *       if (h - l >= 2) {
+ *         int mid = (l + h) >>> 1;
+ *         addToPendingCount(1);
+ *         new Searcher(this, array, result, mid, h).fork();
+ *         h = mid;
+ *       }
+ *       else {
+ *         E x = array[l];
+ *         if (matches(x) && result.compareAndSet(null, x))
+ *           quietlyCompleteRoot(); // root task is now joinable
+ *         break;
+ *       }
+ *     }
+ *     tryComplete(); // normally complete whether or not found
+ *   }
+ *   boolean matches(E e) { ... } // return true if found
+ *
+ *   public static <E> E search(E[] array) {
+ *       return new Searcher<E>(null, array, new AtomicReference<E>(), 0, array.length).invoke();
+ *   }
+ * }}</pre>
+ *
+ * In this example, as well as others in which tasks have no other
+ * effects except to compareAndSet a common result, the trailing
+ * unconditional invocation of {@code tryComplete} could be made
+ * conditional ({@code if (result.get() == null) tryComplete();})
+ * because no further bookkeeping is required to manage completions
+ * once the root task completes.
+ *
+ * <p><b>Recording subtasks.</b> CountedCompleter tasks that combine
+ * results of multiple subtasks usually need to access these results
+ * in method {@link #onCompletion(CountedCompleter)}. As illustrated in the following
+ * class (that performs a simplified form of map-reduce where mappings
+ * and reductions are all of type {@code E}), one way to do this in
+ * divide and conquer designs is to have each subtask record its
+ * sibling, so that it can be accessed in method {@code onCompletion}.
+ * This technique applies to reductions in which the order of
+ * combining left and right results does not matter; ordered
+ * reductions require explicit left/right designations.  Variants of
+ * other streamlinings seen in the above examples may also apply.
+ *
+ * <pre> {@code
+ * class MyMapper<E> { E apply(E v) {  ...  } }
+ * class MyReducer<E> { E apply(E x, E y) {  ...  } }
+ * class MapReducer<E> extends CountedCompleter<E> {
+ *   final E[] array; final MyMapper<E> mapper;
+ *   final MyReducer<E> reducer; final int lo, hi;
+ *   MapReducer<E> sibling;
+ *   E result;
+ *   MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
+ *              MyReducer<E> reducer, int lo, int hi) {
+ *     super(p);
+ *     this.array = array; this.mapper = mapper;
+ *     this.reducer = reducer; this.lo = lo; this.hi = hi;
+ *   }
+ *   public void compute() {
+ *     if (hi - lo >= 2) {
+ *       int mid = (lo + hi) >>> 1;
+ *       MapReducer<E> left = new MapReducer(this, array, mapper, reducer, lo, mid);
+ *       MapReducer<E> right = new MapReducer(this, array, mapper, reducer, mid, hi);
+ *       left.sibling = right;
+ *       right.sibling = left;
+ *       setPendingCount(1); // only right is pending
+ *       right.fork();
+ *       left.compute();     // directly execute left
+ *     }
+ *     else {
+ *       if (hi > lo)
+ *           result = mapper.apply(array[lo]);
+ *       tryComplete();
+ *     }
+ *   }
+ *   public void onCompletion(CountedCompleter<?> caller) {
+ *     if (caller != this) {
+ *       MapReducer<E> child = (MapReducer<E>)caller;
+ *       MapReducer<E> sib = child.sibling;
+ *       if (sib == null || sib.result == null)
+ *         result = child.result;
+ *       else
+ *         result = reducer.apply(child.result, sib.result);
+ *     }
+ *   }
+ *   public E getRawResult() { return result; }
+ *
+ *   public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
+ *     return new MapReducer<E>(null, array, mapper, reducer,
+ *                              0, array.length).invoke();
+ *   }
+ * }}</pre>
+ *
+ * Here, method {@code onCompletion} takes a form common to many
+ * completion designs that combine results. This callback-style method
+ * is triggered once per task, in either of the two different contexts
+ * in which the pending count is, or becomes, zero: (1) by a task
+ * itself, if its pending count is zero upon invocation of {@code
+ * tryComplete}, or (2) by any of its subtasks when they complete and
+ * decrement the pending count to zero. The {@code caller} argument
+ * distinguishes cases.  Most often, when the caller is {@code this},
+ * no action is necessary. Otherwise the caller argument can be used
+ * (usually via a cast) to supply a value (and/or links to other
+ * values) to be combined.  Assuming proper use of pending counts, the
+ * actions inside {@code onCompletion} occur (once) upon completion of
+ * a task and its subtasks. No additional synchronization is required
+ * within this method to ensure thread safety of accesses to fields of
+ * this task or other completed tasks.
+ *
+ * <p><b>Completion Traversals</b>. If using {@code onCompletion} to
+ * process completions is inapplicable or inconvenient, you can use
+ * methods {@link #firstComplete} and {@link #nextComplete} to create
+ * custom traversals.  For example, to define a MapReducer that only
+ * splits out right-hand tasks in the form of the third ForEach
+ * example, the completions must cooperatively reduce along
+ * unexhausted subtask links, which can be done as follows:
+ *
+ * <pre> {@code
+ * class MapReducer<E> extends CountedCompleter<E> { // version 2
+ *   final E[] array; final MyMapper<E> mapper;
+ *   final MyReducer<E> reducer; final int lo, hi;
+ *   MapReducer<E> forks, next; // record subtask forks in list
+ *   E result;
+ *   MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
+ *              MyReducer<E> reducer, int lo, int hi, MapReducer<E> next) {
+ *     super(p);
+ *     this.array = array; this.mapper = mapper;
+ *     this.reducer = reducer; this.lo = lo; this.hi = hi;
+ *     this.next = next;
+ *   }
+ *   public void compute() {
+ *     int l = lo,  h = hi;
+ *     while (h - l >= 2) {
+ *       int mid = (l + h) >>> 1;
+ *       addToPendingCount(1);
+ *       (forks = new MapReducer(this, array, mapper, reducer, mid, h, forks)).fork();
+ *       h = mid;
+ *     }
+ *     if (h > l)
+ *       result = mapper.apply(array[l]);
+ *     // process completions by reducing along and advancing subtask links
+ *     for (CountedCompleter<?> c = firstComplete(); c != null; c = c.nextComplete()) {
+ *       for (MapReducer t = (MapReducer)c, s = t.forks;  s != null; s = t.forks = s.next)
+ *         t.result = reducer.apply(t.result, s.result);
+ *     }
+ *   }
+ *   public E getRawResult() { return result; }
+ *
+ *   public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
+ *     return new MapReducer<E>(null, array, mapper, reducer,
+ *                              0, array.length, null).invoke();
+ *   }
+ * }}</pre>
+ *
+ * <p><b>Triggers.</b> Some CountedCompleters are themselves never
+ * forked, but instead serve as bits of plumbing in other designs;
+ * including those in which the completion of one or more async tasks
+ * triggers another async task. For example:
+ *
+ * <pre> {@code
+ * class HeaderBuilder extends CountedCompleter<...> { ... }
+ * class BodyBuilder extends CountedCompleter<...> { ... }
+ * class PacketSender extends CountedCompleter<...> {
+ *   PacketSender(...) { super(null, 1); ... } // trigger on second completion
+ *   public void compute() { } // never called
+ *   public void onCompletion(CountedCompleter<?> caller) { sendPacket(); }
+ * }
+ * // sample use:
+ * PacketSender p = new PacketSender();
+ * new HeaderBuilder(p, ...).fork();
+ * new BodyBuilder(p, ...).fork();
+ * }</pre>
+ *
+ * @since 1.8
+ * @author Doug Lea
+ */
+public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
+    private static final long serialVersionUID = 5232453752276485070L;
+
+    /** This task's completer, or null if none */
+    final CountedCompleter<?> completer;
+    /** The number of pending tasks until completion */
+    volatile int pending;
+
+    /**
+     * Creates a new CountedCompleter with the given completer
+     * and initial pending count.
+     *
+     * @param completer this task's completer, or {@code null} if none
+     * @param initialPendingCount the initial pending count
+     */
+    protected CountedCompleter(CountedCompleter<?> completer,
+                               int initialPendingCount) {
+        this.completer = completer;
+        this.pending = initialPendingCount;
+    }
+
+    /**
+     * Creates a new CountedCompleter with the given completer
+     * and an initial pending count of zero.
+     *
+     * @param completer this task's completer, or {@code null} if none
+     */
+    protected CountedCompleter(CountedCompleter<?> completer) {
+        this.completer = completer;
+    }
+
+    /**
+     * Creates a new CountedCompleter with no completer
+     * and an initial pending count of zero.
+     */
+    protected CountedCompleter() {
+        this.completer = null;
+    }
+
+    /**
+     * The main computation performed by this task.
+     */
+    public abstract void compute();
+
+    /**
+     * Performs an action when method {@link #tryComplete} is invoked
+     * and the pending count is zero, or when the unconditional
+     * method {@link #complete} is invoked.  By default, this method
+     * does nothing. You can distinguish cases by checking the
+     * identity of the given caller argument. If not equal to {@code
+     * this}, then it is typically a subtask that may contain results
+     * (and/or links to other results) to combine.
+     *
+     * @param caller the task invoking this method (which may
+     * be this task itself)
+     */
+    public void onCompletion(CountedCompleter<?> caller) {
+    }
+
+    /**
+     * Performs an action when method {@link
+     * #completeExceptionally(Throwable)} is invoked or method {@link
+     * #compute} throws an exception, and this task has not already
+     * otherwise completed normally. On entry to this method, this task
+     * {@link ForkJoinTask#isCompletedAbnormally}.  The return value
+     * of this method controls further propagation: If {@code true}
+     * and this task has a completer that has not completed, then that
+     * completer is also completed exceptionally, with the same
+     * exception as this completer.  The default implementation of
+     * this method does nothing except return {@code true}.
+     *
+     * @param ex the exception
+     * @param caller the task invoking this method (which may
+     * be this task itself)
+     * @return {@code true} if this exception should be propagated to this
+     * task's completer, if one exists
+     */
+    public boolean onExceptionalCompletion(Throwable ex, CountedCompleter<?> caller) {
+        return true;
+    }
+
+    /**
+     * Returns the completer established in this task's constructor,
+     * or {@code null} if none.
+     *
+     * @return the completer
+     */
+    public final CountedCompleter<?> getCompleter() {
+        return completer;
+    }
+
+    /**
+     * Returns the current pending count.
+     *
+     * @return the current pending count
+     */
+    public final int getPendingCount() {
+        return pending;
+    }
+
+    /**
+     * Sets the pending count to the given value.
+     *
+     * @param count the count
+     */
+    public final void setPendingCount(int count) {
+        pending = count;
+    }
+
+    /**
+     * Adds (atomically) the given value to the pending count.
+     *
+     * @param delta the value to add
+     */
+    public final void addToPendingCount(int delta) {
+        int c;
+        do {} while (!U.compareAndSwapInt(this, PENDING, c = pending, c+delta));
+    }
+
+    /**
+     * Sets (atomically) the pending count to the given count only if
+     * it currently holds the given expected value.
+     *
+     * @param expected the expected value
+     * @param count the new value
+     * @return {@code true} if successful
+     */
+    public final boolean compareAndSetPendingCount(int expected, int count) {
+        return U.compareAndSwapInt(this, PENDING, expected, count);
+    }
+
+    /**
+     * If the pending count is nonzero, (atomically) decrements it.
+     *
+     * @return the initial (undecremented) pending count holding on entry
+     * to this method
+     */
+    public final int decrementPendingCountUnlessZero() {
+        int c;
+        do {} while ((c = pending) != 0 &&
+                     !U.compareAndSwapInt(this, PENDING, c, c - 1));
+        return c;
+    }
+
+    /**
+     * Returns the root of the current computation; i.e., this
+     * task if it has no completer, else its completer's root.
+     *
+     * @return the root of the current computation
+     */
+    public final CountedCompleter<?> getRoot() {
+        CountedCompleter<?> a = this, p;
+        while ((p = a.completer) != null)
+            a = p;
+        return a;
+    }
+
+    /**
+     * If the pending count is nonzero, decrements the count;
+     * otherwise invokes {@link #onCompletion(CountedCompleter)}
+     * and then similarly tries to complete this task's completer,
+     * if one exists, else marks this task as complete.
+     */
+    public final void tryComplete() {
+        CountedCompleter<?> a = this, s = a;
+        for (int c;;) {
+            if ((c = a.pending) == 0) {
+                a.onCompletion(s);
+                if ((a = (s = a).completer) == null) {
+                    s.quietlyComplete();
+                    return;
+                }
+            }
+            else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
+                return;
+        }
+    }
+
+    /**
+     * Equivalent to {@link #tryComplete} but does not invoke {@link
+     * #onCompletion(CountedCompleter)} along the completion path:
+     * If the pending count is nonzero, decrements the count;
+     * otherwise, similarly tries to complete this task's completer, if
+     * one exists, else marks this task as complete. This method may be
+     * useful in cases where {@code onCompletion} should not, or need
+     * not, be invoked for each completer in a computation.
+     */
+    public final void propagateCompletion() {
+        CountedCompleter<?> a = this, s = a;
+        for (int c;;) {
+            if ((c = a.pending) == 0) {
+                if ((a = (s = a).completer) == null) {
+                    s.quietlyComplete();
+                    return;
+                }
+            }
+            else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
+                return;
+        }
+    }
+
+    /**
+     * Regardless of pending count, invokes
+     * {@link #onCompletion(CountedCompleter)}, marks this task as
+     * complete and further triggers {@link #tryComplete} on this
+     * task's completer, if one exists.  The given rawResult is
+     * used as an argument to {@link #setRawResult} before invoking
+     * {@link #onCompletion(CountedCompleter)} or marking this task
+     * as complete; its value is meaningful only for classes
+     * overriding {@code setRawResult}.  This method does not modify
+     * the pending count.
+     *
+     * <p>This method may be useful when forcing completion as soon as
+     * any one (versus all) of several subtask results are obtained.
+     * However, in the common (and recommended) case in which {@code
+     * setRawResult} is not overridden, this effect can be obtained
+     * more simply using {@code quietlyCompleteRoot();}.
+     *
+     * @param rawResult the raw result
+     */
+    public void complete(T rawResult) {
+        CountedCompleter<?> p;
+        setRawResult(rawResult);
+        onCompletion(this);
+        quietlyComplete();
+        if ((p = completer) != null)
+            p.tryComplete();
+    }
+
+
+    /**
+     * If this task's pending count is zero, returns this task;
+     * otherwise decrements its pending count and returns {@code
+     * null}. This method is designed to be used with {@link
+     * #nextComplete} in completion traversal loops.
+     *
+     * @return this task, if pending count was zero, else {@code null}
+     */
+    public final CountedCompleter<?> firstComplete() {
+        for (int c;;) {
+            if ((c = pending) == 0)
+                return this;
+            else if (U.compareAndSwapInt(this, PENDING, c, c - 1))
+                return null;
+        }
+    }
+
+    /**
+     * If this task does not have a completer, invokes {@link
+     * ForkJoinTask#quietlyComplete} and returns {@code null}.  Or, if
+     * the completer's pending count is non-zero, decrements that
+     * pending count and returns {@code null}.  Otherwise, returns the
+     * completer.  This method can be used as part of a completion
+     * traversal loop for homogeneous task hierarchies:
+     *
+     * <pre> {@code
+     * for (CountedCompleter<?> c = firstComplete();
+     *      c != null;
+     *      c = c.nextComplete()) {
+     *   // ... process c ...
+     * }}</pre>
+     *
+     * @return the completer, or {@code null} if none
+     */
+    public final CountedCompleter<?> nextComplete() {
+        CountedCompleter<?> p;
+        if ((p = completer) != null)
+            return p.firstComplete();
+        else {
+            quietlyComplete();
+            return null;
+        }
+    }
+
+    /**
+     * Equivalent to {@code getRoot().quietlyComplete()}.
+     */
+    public final void quietlyCompleteRoot() {
+        for (CountedCompleter<?> a = this, p;;) {
+            if ((p = a.completer) == null) {
+                a.quietlyComplete();
+                return;
+            }
+            a = p;
+        }
+    }
+
+    /**
+     * Supports ForkJoinTask exception propagation.
+     */
+    void internalPropagateException(Throwable ex) {
+        CountedCompleter<?> a = this, s = a;
+        while (a.onExceptionalCompletion(ex, s) &&
+               (a = (s = a).completer) != null && a.status >= 0 &&
+               a.recordExceptionalCompletion(ex) == EXCEPTIONAL)
+            ;
+    }
+
+    /**
+     * Implements execution conventions for CountedCompleters.
+     */
+    protected final boolean exec() {
+        compute();
+        return false;
+    }
+
+    /**
+     * Returns the result of the computation.  By default,
+     * returns {@code null}, which is appropriate for {@code Void}
+     * actions, but in other cases should be overridden, almost
+     * always to return a field or function of a field that
+     * holds the result upon completion.
+     *
+     * @return the result of the computation
+     */
+    public T getRawResult() { return null; }
+
+    /**
+     * A method that result-bearing CountedCompleters may optionally
+     * use to help maintain result data.  By default, does nothing.
+     * Overrides are not recommended. However, if this method is
+     * overridden to update existing objects or fields, then it must
+     * in general be defined to be thread-safe.
+     */
+    protected void setRawResult(T t) { }
+
+    // Unsafe mechanics
+    private static final sun.misc.Unsafe U;
+    private static final long PENDING;
+    static {
+        try {
+            U = getUnsafe();
+            PENDING = U.objectFieldOffset
+                (CountedCompleter.class.getDeclaredField("pending"));
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
+
+    /**
+     * Returns a sun.misc.Unsafe.  Suitable for use in a 3rd party package.
+     * Replace with a simple call to Unsafe.getUnsafe when integrating
+     * into a jdk.
+     *
+     * @return a sun.misc.Unsafe
+     */
+    private static sun.misc.Unsafe getUnsafe() {
+        try {
+            return sun.misc.Unsafe.getUnsafe();
+        } catch (SecurityException tryReflectionInstead) {}
+        try {
+            return java.security.AccessController.doPrivileged
+            (new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() {
+                public sun.misc.Unsafe run() throws Exception {
+                    Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
+                    for (java.lang.reflect.Field f : k.getDeclaredFields()) {
+                        f.setAccessible(true);
+                        Object x = f.get(null);
+                        if (k.isInstance(x))
+                            return k.cast(x);
+                    }
+                    throw new NoSuchFieldError("the Unsafe");
+                }});
+        } catch (java.security.PrivilegedActionException e) {
+            throw new RuntimeException("Could not initialize intrinsics",
+                                       e.getCause());
+        }
+    }
+}

+ 3349 - 0
src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/ForkJoinPool.java

@@ -0,0 +1,3349 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package com.jeeplus.common.utils.concurrent.jsr166e;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ForkJoinPool.java 1.66
+ * 
+ * An {@link ExecutorService} for running {@link ForkJoinTask}s.
+ * A {@code ForkJoinPool} provides the entry point for submissions
+ * from non-{@code ForkJoinTask} clients, as well as management and
+ * monitoring operations.
+ *
+ * <p>A {@code ForkJoinPool} differs from other kinds of {@link
+ * ExecutorService} mainly by virtue of employing
+ * <em>work-stealing</em>: all threads in the pool attempt to find and
+ * execute tasks submitted to the pool and/or created by other active
+ * tasks (eventually blocking waiting for work if none exist). This
+ * enables efficient processing when most tasks spawn other subtasks
+ * (as do most {@code ForkJoinTask}s), as well as when many small
+ * tasks are submitted to the pool from external clients.  Especially
+ * when setting <em>asyncMode</em> to true in constructors, {@code
+ * ForkJoinPool}s may also be appropriate for use with event-style
+ * tasks that are never joined.
+ *
+ * <p>A static {@link #commonPool()} is available and appropriate for
+ * most applications. The common pool is used by any ForkJoinTask that
+ * is not explicitly submitted to a specified pool. Using the common
+ * pool normally reduces resource usage (its threads are slowly
+ * reclaimed during periods of non-use, and reinstated upon subsequent
+ * use).
+ *
+ * <p>For applications that require separate or custom pools, a {@code
+ * ForkJoinPool} may be constructed with a given target parallelism
+ * level; by default, equal to the number of available processors. The
+ * pool attempts to maintain enough active (or available) threads by
+ * dynamically adding, suspending, or resuming internal worker
+ * threads, even if some tasks are stalled waiting to join others.
+ * However, no such adjustments are guaranteed in the face of blocked
+ * I/O or other unmanaged synchronization. The nested {@link
+ * ManagedBlocker} interface enables extension of the kinds of
+ * synchronization accommodated.
+ *
+ * <p>In addition to execution and lifecycle control methods, this
+ * class provides status check methods (for example
+ * {@link #getStealCount}) that are intended to aid in developing,
+ * tuning, and monitoring fork/join applications. Also, method
+ * {@link #toString} returns indications of pool state in a
+ * convenient form for informal monitoring.
+ *
+ * <p>As is the case with other ExecutorServices, there are three
+ * main task execution methods summarized in the following table.
+ * These are designed to be used primarily by clients not already
+ * engaged in fork/join computations in the current pool.  The main
+ * forms of these methods accept instances of {@code ForkJoinTask},
+ * but overloaded forms also allow mixed execution of plain {@code
+ * Runnable}- or {@code Callable}- based activities as well.  However,
+ * tasks that are already executing in a pool should normally instead
+ * use the within-computation forms listed in the table unless using
+ * async event-style tasks that are not usually joined, in which case
+ * there is little difference among choice of methods.
+ *
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ * <caption>Summary of task execution methods</caption>
+ *  <tr>
+ *    <td></td>
+ *    <td ALIGN=CENTER> <b>Call from non-fork/join clients</b></td>
+ *    <td ALIGN=CENTER> <b>Call from within fork/join computations</b></td>
+ *  </tr>
+ *  <tr>
+ *    <td> <b>Arrange async execution</b></td>
+ *    <td> {@link #execute(ForkJoinTask)}</td>
+ *    <td> {@link ForkJoinTask#fork}</td>
+ *  </tr>
+ *  <tr>
+ *    <td> <b>Await and obtain result</b></td>
+ *    <td> {@link #invoke(ForkJoinTask)}</td>
+ *    <td> {@link ForkJoinTask#invoke}</td>
+ *  </tr>
+ *  <tr>
+ *    <td> <b>Arrange exec and obtain Future</b></td>
+ *    <td> {@link #submit(ForkJoinTask)}</td>
+ *    <td> {@link ForkJoinTask#fork} (ForkJoinTasks <em>are</em> Futures)</td>
+ *  </tr>
+ * </table>
+ *
+ * <p>The common pool is by default constructed with default
+ * parameters, but these may be controlled by setting three
+ * {@linkplain System#getProperty system properties}:
+ * <ul>
+ * <li>{@code java.util.concurrent.ForkJoinPool.common.parallelism}
+ * - the parallelism level, a non-negative integer
+ * <li>{@code java.util.concurrent.ForkJoinPool.common.threadFactory}
+ * - the class name of a {@link ForkJoinWorkerThreadFactory}
+ * <li>{@code java.util.concurrent.ForkJoinPool.common.exceptionHandler}
+ * - the class name of a {@link UncaughtExceptionHandler}
+ * </ul>
+ * The system class loader is used to load these classes.
+ * Upon any error in establishing these settings, default parameters
+ * are used. It is possible to disable or limit the use of threads in
+ * the common pool by setting the parallelism property to zero, and/or
+ * using a factory that may return {@code null}.
+ *
+ * <p><b>Implementation notes</b>: This implementation restricts the
+ * maximum number of running threads to 32767. Attempts to create
+ * pools with greater than the maximum number result in
+ * {@code IllegalArgumentException}.
+ *
+ * <p>This implementation rejects submitted tasks (that is, by throwing
+ * {@link RejectedExecutionException}) only when the pool is shut down
+ * or internal resources have been exhausted.
+ *
+ * @since 1.7
+ * @author Doug Lea
+ */
+public class ForkJoinPool extends AbstractExecutorService {
+
+    /*
+     * Implementation Overview
+     *
+     * This class and its nested classes provide the main
+     * functionality and control for a set of worker threads:
+     * Submissions from non-FJ threads enter into submission queues.
+     * Workers take these tasks and typically split them into subtasks
+     * that may be stolen by other workers.  Preference rules give
+     * first priority to processing tasks from their own queues (LIFO
+     * or FIFO, depending on mode), then to randomized FIFO steals of
+     * tasks in other queues.
+     *
+     * WorkQueues
+     * ==========
+     *
+     * Most operations occur within work-stealing queues (in nested
+     * class WorkQueue).  These are special forms of Deques that
+     * support only three of the four possible end-operations -- push,
+     * pop, and poll (aka steal), under the further constraints that
+     * push and pop are called only from the owning thread (or, as
+     * extended here, under a lock), while poll may be called from
+     * other threads.  (If you are unfamiliar with them, you probably
+     * want to read Herlihy and Shavit's book "The Art of
+     * Multiprocessor programming", chapter 16 describing these in
+     * more detail before proceeding.)  The main work-stealing queue
+     * design is roughly similar to those in the papers "Dynamic
+     * Circular Work-Stealing Deque" by Chase and Lev, SPAA 2005
+     * (http://research.sun.com/scalable/pubs/index.html) and
+     * "Idempotent work stealing" by Michael, Saraswat, and Vechev,
+     * PPoPP 2009 (http://portal.acm.org/citation.cfm?id=1504186).
+     * See also "Correct and Efficient Work-Stealing for Weak Memory
+     * Models" by Le, Pop, Cohen, and Nardelli, PPoPP 2013
+     * (http://www.di.ens.fr/~zappa/readings/ppopp13.pdf) for an
+     * analysis of memory ordering (atomic, volatile etc) issues.  The
+     * main differences ultimately stem from GC requirements that we
+     * null out taken slots as soon as we can, to maintain as small a
+     * footprint as possible even in programs generating huge numbers
+     * of tasks. To accomplish this, we shift the CAS arbitrating pop
+     * vs poll (steal) from being on the indices ("base" and "top") to
+     * the slots themselves.  So, both a successful pop and poll
+     * mainly entail a CAS of a slot from non-null to null.  Because
+     * we rely on CASes of references, we do not need tag bits on base
+     * or top.  They are simple ints as used in any circular
+     * array-based queue (see for example ArrayDeque).  Updates to the
+     * indices must still be ordered in a way that guarantees that top
+     * == base means the queue is empty, but otherwise may err on the
+     * side of possibly making the queue appear nonempty when a push,
+     * pop, or poll have not fully committed. Note that this means
+     * that the poll operation, considered individually, is not
+     * wait-free. One thief cannot successfully continue until another
+     * in-progress one (or, if previously empty, a push) completes.
+     * However, in the aggregate, we ensure at least probabilistic
+     * non-blockingness.  If an attempted steal fails, a thief always
+     * chooses a different random victim target to try next. So, in
+     * order for one thief to progress, it suffices for any
+     * in-progress poll or new push on any empty queue to
+     * complete. (This is why we normally use method pollAt and its
+     * variants that try once at the apparent base index, else
+     * consider alternative actions, rather than method poll.)
+     *
+     * This approach also enables support of a user mode in which local
+     * task processing is in FIFO, not LIFO order, simply by using
+     * poll rather than pop.  This can be useful in message-passing
+     * frameworks in which tasks are never joined.  However neither
+     * mode considers affinities, loads, cache localities, etc, so
+     * rarely provide the best possible performance on a given
+     * machine, but portably provide good throughput by averaging over
+     * these factors.  (Further, even if we did try to use such
+     * information, we do not usually have a basis for exploiting it.
+     * For example, some sets of tasks profit from cache affinities,
+     * but others are harmed by cache pollution effects.)
+     *
+     * WorkQueues are also used in a similar way for tasks submitted
+     * to the pool. We cannot mix these tasks in the same queues used
+     * for work-stealing (this would contaminate lifo/fifo
+     * processing). Instead, we randomly associate submission queues
+     * with submitting threads, using a form of hashing.  The
+     * Submitter probe value serves as a hash code for
+     * choosing existing queues, and may be randomly repositioned upon
+     * contention with other submitters.  In essence, submitters act
+     * like workers except that they are restricted to executing local
+     * tasks that they submitted (or in the case of CountedCompleters,
+     * others with the same root task).  However, because most
+     * shared/external queue operations are more expensive than
+     * internal, and because, at steady state, external submitters
+     * will compete for CPU with workers, ForkJoinTask.join and
+     * related methods disable them from repeatedly helping to process
+     * tasks if all workers are active.  Insertion of tasks in shared
+     * mode requires a lock (mainly to protect in the case of
+     * resizing) but we use only a simple spinlock (using bits in
+     * field qlock), because submitters encountering a busy queue move
+     * on to try or create other queues -- they block only when
+     * creating and registering new queues.
+     *
+     * Management
+     * ==========
+     *
+     * The main throughput advantages of work-stealing stem from
+     * decentralized control -- workers mostly take tasks from
+     * themselves or each other. We cannot negate this in the
+     * implementation of other management responsibilities. The main
+     * tactic for avoiding bottlenecks is packing nearly all
+     * essentially atomic control state into two volatile variables
+     * that are by far most often read (not written) as status and
+     * consistency checks.
+     *
+     * Field "ctl" contains 64 bits holding all the information needed
+     * to atomically decide to add, inactivate, enqueue (on an event
+     * queue), dequeue, and/or re-activate workers.  To enable this
+     * packing, we restrict maximum parallelism to (1<<15)-1 (which is
+     * far in excess of normal operating range) to allow ids, counts,
+     * and their negations (used for thresholding) to fit into 16bit
+     * fields.
+     *
+     * Field "plock" is a form of sequence lock with a saturating
+     * shutdown bit (similarly for per-queue "qlocks"), mainly
+     * protecting updates to the workQueues array, as well as to
+     * enable shutdown.  When used as a lock, it is normally only very
+     * briefly held, so is nearly always available after at most a
+     * brief spin, but we use a monitor-based backup strategy to
+     * block when needed.
+     *
+     * Recording WorkQueues.  WorkQueues are recorded in the
+     * "workQueues" array that is created upon first use and expanded
+     * if necessary.  Updates to the array while recording new workers
+     * and unrecording terminated ones are protected from each other
+     * by a lock but the array is otherwise concurrently readable, and
+     * accessed directly.  To simplify index-based operations, the
+     * array size is always a power of two, and all readers must
+     * tolerate null slots. Worker queues are at odd indices. Shared
+     * (submission) queues are at even indices, up to a maximum of 64
+     * slots, to limit growth even if array needs to expand to add
+     * more workers. Grouping them together in this way simplifies and
+     * speeds up task scanning.
+     *
+     * All worker thread creation is on-demand, triggered by task
+     * submissions, replacement of terminated workers, and/or
+     * compensation for blocked workers. However, all other support
+     * code is set up to work with other policies.  To ensure that we
+     * do not hold on to worker references that would prevent GC, ALL
+     * accesses to workQueues are via indices into the workQueues
+     * array (which is one source of some of the messy code
+     * constructions here). In essence, the workQueues array serves as
+     * a weak reference mechanism. Thus for example the wait queue
+     * field of ctl stores indices, not references.  Access to the
+     * workQueues in associated methods (for example signalWork) must
+     * both index-check and null-check the IDs. All such accesses
+     * ignore bad IDs by returning out early from what they are doing,
+     * since this can only be associated with termination, in which
+     * case it is OK to give up.  All uses of the workQueues array
+     * also check that it is non-null (even if previously
+     * non-null). This allows nulling during termination, which is
+     * currently not necessary, but remains an option for
+     * resource-revocation-based shutdown schemes. It also helps
+     * reduce JIT issuance of uncommon-trap code, which tends to
+     * unnecessarily complicate control flow in some methods.
+     *
+     * Event Queuing. Unlike HPC work-stealing frameworks, we cannot
+     * let workers spin indefinitely scanning for tasks when none can
+     * be found immediately, and we cannot start/resume workers unless
+     * there appear to be tasks available.  On the other hand, we must
+     * quickly prod them into action when new tasks are submitted or
+     * generated. In many usages, ramp-up time to activate workers is
+     * the main limiting factor in overall performance (this is
+     * compounded at program start-up by JIT compilation and
+     * allocation). So we try to streamline this as much as possible.
+     * We park/unpark workers after placing in an event wait queue
+     * when they cannot find work. This "queue" is actually a simple
+     * Treiber stack, headed by the "id" field of ctl, plus a 15bit
+     * counter value (that reflects the number of times a worker has
+     * been inactivated) to avoid ABA effects (we need only as many
+     * version numbers as worker threads). Successors are held in
+     * field WorkQueue.nextWait.  Queuing deals with several intrinsic
+     * races, mainly that a task-producing thread can miss seeing (and
+     * signalling) another thread that gave up looking for work but
+     * has not yet entered the wait queue. We solve this by requiring
+     * a full sweep of all workers (via repeated calls to method
+     * scan()) both before and after a newly waiting worker is added
+     * to the wait queue.  Because enqueued workers may actually be
+     * rescanning rather than waiting, we set and clear the "parker"
+     * field of WorkQueues to reduce unnecessary calls to unpark.
+     * (This requires a secondary recheck to avoid missed signals.)
+     * Note the unusual conventions about Thread.interrupts
+     * surrounding parking and other blocking: Because interrupts are
+     * used solely to alert threads to check termination, which is
+     * checked anyway upon blocking, we clear status (using
+     * Thread.interrupted) before any call to park, so that park does
+     * not immediately return due to status being set via some other
+     * unrelated call to interrupt in user code.
+     *
+     * Signalling.  We create or wake up workers only when there
+     * appears to be at least one task they might be able to find and
+     * execute.  When a submission is added or another worker adds a
+     * task to a queue that has fewer than two tasks, they signal
+     * waiting workers (or trigger creation of new ones if fewer than
+     * the given parallelism level -- signalWork).  These primary
+     * signals are buttressed by others whenever other threads remove
+     * a task from a queue and notice that there are other tasks there
+     * as well.  So in general, pools will be over-signalled. On most
+     * platforms, signalling (unpark) overhead time is noticeably
+     * long, and the time between signalling a thread and it actually
+     * making progress can be very noticeably long, so it is worth
+     * offloading these delays from critical paths as much as
+     * possible. Additionally, workers spin-down gradually, by staying
+     * alive so long as they see the ctl state changing.  Similar
+     * stability-sensing techniques are also used before blocking in
+     * awaitJoin and helpComplete.
+     *
+     * Trimming workers. To release resources after periods of lack of
+     * use, a worker starting to wait when the pool is quiescent will
+     * time out and terminate if the pool has remained quiescent for a
+     * given period -- a short period if there are more threads than
+     * parallelism, longer as the number of threads decreases. This
+     * will slowly propagate, eventually terminating all workers after
+     * periods of non-use.
+     *
+     * Shutdown and Termination. A call to shutdownNow atomically sets
+     * a plock bit and then (non-atomically) sets each worker's
+     * qlock status, cancels all unprocessed tasks, and wakes up
+     * all waiting workers.  Detecting whether termination should
+     * commence after a non-abrupt shutdown() call requires more work
+     * and bookkeeping. We need consensus about quiescence (i.e., that
+     * there is no more work). The active count provides a primary
+     * indication but non-abrupt shutdown still requires a rechecking
+     * scan for any workers that are inactive but not queued.
+     *
+     * Joining Tasks
+     * =============
+     *
+     * Any of several actions may be taken when one worker is waiting
+     * to join a task stolen (or always held) by another.  Because we
+     * are multiplexing many tasks on to a pool of workers, we can't
+     * just let them block (as in Thread.join).  We also cannot just
+     * reassign the joiner's run-time stack with another and replace
+     * it later, which would be a form of "continuation", that even if
+     * possible is not necessarily a good idea since we sometimes need
+     * both an unblocked task and its continuation to progress.
+     * Instead we combine two tactics:
+     *
+     *   Helping: Arranging for the joiner to execute some task that it
+     *      would be running if the steal had not occurred.
+     *
+     *   Compensating: Unless there are already enough live threads,
+     *      method tryCompensate() may create or re-activate a spare
+     *      thread to compensate for blocked joiners until they unblock.
+     *
+     * A third form (implemented in tryRemoveAndExec) amounts to
+     * helping a hypothetical compensator: If we can readily tell that
+     * a possible action of a compensator is to steal and execute the
+     * task being joined, the joining thread can do so directly,
+     * without the need for a compensation thread (although at the
+     * expense of larger run-time stacks, but the tradeoff is
+     * typically worthwhile).
+     *
+     * The ManagedBlocker extension API can't use helping so relies
+     * only on compensation in method awaitBlocker.
+     *
+     * The algorithm in tryHelpStealer entails a form of "linear"
+     * helping: Each worker records (in field currentSteal) the most
+     * recent task it stole from some other worker. Plus, it records
+     * (in field currentJoin) the task it is currently actively
+     * joining. Method tryHelpStealer uses these markers to try to
+     * find a worker to help (i.e., steal back a task from and execute
+     * it) that could hasten completion of the actively joined task.
+     * In essence, the joiner executes a task that would be on its own
+     * local deque had the to-be-joined task not been stolen. This may
+     * be seen as a conservative variant of the approach in Wagner &
+     * Calder "Leapfrogging: a portable technique for implementing
+     * efficient futures" SIGPLAN Notices, 1993
+     * (http://portal.acm.org/citation.cfm?id=155354). It differs in
+     * that: (1) We only maintain dependency links across workers upon
+     * steals, rather than use per-task bookkeeping.  This sometimes
+     * requires a linear scan of workQueues array to locate stealers,
+     * but often doesn't because stealers leave hints (that may become
+     * stale/wrong) of where to locate them.  It is only a hint
+     * because a worker might have had multiple steals and the hint
+     * records only one of them (usually the most current).  Hinting
+     * isolates cost to when it is needed, rather than adding to
+     * per-task overhead.  (2) It is "shallow", ignoring nesting and
+     * potentially cyclic mutual steals.  (3) It is intentionally
+     * racy: field currentJoin is updated only while actively joining,
+     * which means that we miss links in the chain during long-lived
+     * tasks, GC stalls etc (which is OK since blocking in such cases
+     * is usually a good idea).  (4) We bound the number of attempts
+     * to find work (see MAX_HELP) and fall back to suspending the
+     * worker and if necessary replacing it with another.
+     *
+     * Helping actions for CountedCompleters are much simpler: Method
+     * helpComplete can take and execute any task with the same root
+     * as the task being waited on. However, this still entails some
+     * traversal of completer chains, so is less efficient than using
+     * CountedCompleters without explicit joins.
+     *
+     * It is impossible to keep exactly the target parallelism number
+     * of threads running at any given time.  Determining the
+     * existence of conservatively safe helping targets, the
+     * availability of already-created spares, and the apparent need
+     * to create new spares are all racy, so we rely on multiple
+     * retries of each.  Compensation in the apparent absence of
+     * helping opportunities is challenging to control on JVMs, where
+     * GC and other activities can stall progress of tasks that in
+     * turn stall out many other dependent tasks, without us being
+     * able to determine whether they will ever require compensation.
+     * Even though work-stealing otherwise encounters little
+     * degradation in the presence of more threads than cores,
+     * aggressively adding new threads in such cases entails risk of
+     * unwanted positive feedback control loops in which more threads
+     * cause more dependent stalls (as well as delayed progress of
+     * unblocked threads to the point that we know they are available)
+     * leading to more situations requiring more threads, and so
+     * on. This aspect of control can be seen as an (analytically
+     * intractable) game with an opponent that may choose the worst
+     * (for us) active thread to stall at any time.  We take several
+     * precautions to bound losses (and thus bound gains), mainly in
+     * methods tryCompensate and awaitJoin.
+     *
+     * Common Pool
+     * ===========
+     *
+     * The static common pool always exists after static
+     * initialization.  Since it (or any other created pool) need
+     * never be used, we minimize initial construction overhead and
+     * footprint to the setup of about a dozen fields, with no nested
+     * allocation. Most bootstrapping occurs within method
+     * fullExternalPush during the first submission to the pool.
+     *
+     * When external threads submit to the common pool, they can
+     * perform subtask processing (see externalHelpJoin and related
+     * methods).  This caller-helps policy makes it sensible to set
+     * common pool parallelism level to one (or more) less than the
+     * total number of available cores, or even zero for pure
+     * caller-runs.  We do not need to record whether external
+     * submissions are to the common pool -- if not, externalHelpJoin
+     * returns quickly (at the most helping to signal some common pool
+     * workers). These submitters would otherwise be blocked waiting
+     * for completion, so the extra effort (with liberally sprinkled
+     * task status checks) in inapplicable cases amounts to an odd
+     * form of limited spin-wait before blocking in ForkJoinTask.join.
+     *
+     * Style notes
+     * ===========
+     *
+     * There is a lot of representation-level coupling among classes
+     * ForkJoinPool, ForkJoinWorkerThread, and ForkJoinTask.  The
+     * fields of WorkQueue maintain data structures managed by
+     * ForkJoinPool, so are directly accessed.  There is little point
+     * trying to reduce this, since any associated future changes in
+     * representations will need to be accompanied by algorithmic
+     * changes anyway. Several methods intrinsically sprawl because
+     * they must accumulate sets of consistent reads of volatiles held
+     * in local variables.  Methods signalWork() and scan() are the
+     * main bottlenecks, so are especially heavily
+     * micro-optimized/mangled.  There are lots of inline assignments
+     * (of form "while ((local = field) != 0)") which are usually the
+     * simplest way to ensure the required read orderings (which are
+     * sometimes critical). This leads to a "C"-like style of listing
+     * declarations of these locals at the heads of methods or blocks.
+     * There are several occurrences of the unusual "do {} while
+     * (!cas...)"  which is the simplest way to force an update of a
+     * CAS'ed variable. There are also other coding oddities (including
+     * several unnecessary-looking hoisted null checks) that help
+     * some methods perform reasonably even when interpreted (not
+     * compiled).
+     *
+     * The order of declarations in this file is:
+     * (1) Static utility functions
+     * (2) Nested (static) classes
+     * (3) Static fields
+     * (4) Fields, along with constants used when unpacking some of them
+     * (5) Internal control methods
+     * (6) Callbacks and other support for ForkJoinTask methods
+     * (7) Exported methods
+     * (8) Static block initializing statics in minimally dependent order
+     */
+
+    // Static utilities
+
+    /**
+     * If there is a security manager, makes sure caller has
+     * permission to modify threads.
+     */
+    private static void checkPermission() {
+        SecurityManager security = System.getSecurityManager();
+        if (security != null)
+            security.checkPermission(modifyThreadPermission);
+    }
+
+    // Nested classes
+
+    /**
+     * Factory for creating new {@link ForkJoinWorkerThread}s.
+     * A {@code ForkJoinWorkerThreadFactory} must be defined and used
+     * for {@code ForkJoinWorkerThread} subclasses that extend base
+     * functionality or initialize threads with different contexts.
+     */
+    public static interface ForkJoinWorkerThreadFactory {
+        /**
+         * Returns a new worker thread operating in the given pool.
+         *
+         * @param pool the pool this thread works in
+         * @return the new worker thread
+         * @throws NullPointerException if the pool is null
+         */
+        public ForkJoinWorkerThread newThread(ForkJoinPool pool);
+    }
+
+    /**
+     * Default ForkJoinWorkerThreadFactory implementation; creates a
+     * new ForkJoinWorkerThread.
+     */
+    static final class DefaultForkJoinWorkerThreadFactory
+        implements ForkJoinWorkerThreadFactory {
+        public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
+            return new ForkJoinWorkerThread(pool);
+        }
+    }
+
+    /**
+     * Class for artificial tasks that are used to replace the target
+     * of local joins if they are removed from an interior queue slot
+     * in WorkQueue.tryRemoveAndExec. We don't need the proxy to
+     * actually do anything beyond having a unique identity.
+     */
+    static final class EmptyTask extends ForkJoinTask<Void> {
+        private static final long serialVersionUID = -7721805057305804111L;
+        EmptyTask() { status = ForkJoinTask.NORMAL; } // force done
+        public final Void getRawResult() { return null; }
+        public final void setRawResult(Void x) {}
+        public final boolean exec() { return true; }
+    }
+
+    /**
+     * Queues supporting work-stealing as well as external task
+     * submission. See above for main rationale and algorithms.
+     * Implementation relies heavily on "Unsafe" intrinsics
+     * and selective use of "volatile":
+     *
+     * Field "base" is the index (mod array.length) of the least valid
+     * queue slot, which is always the next position to steal (poll)
+     * from if nonempty. Reads and writes require volatile orderings
+     * but not CAS, because updates are only performed after slot
+     * CASes.
+     *
+     * Field "top" is the index (mod array.length) of the next queue
+     * slot to push to or pop from. It is written only by owner thread
+     * for push, or under lock for external/shared push, and accessed
+     * by other threads only after reading (volatile) base.  Both top
+     * and base are allowed to wrap around on overflow, but (top -
+     * base) (or more commonly -(base - top) to force volatile read of
+     * base before top) still estimates size. The lock ("qlock") is
+     * forced to -1 on termination, causing all further lock attempts
+     * to fail. (Note: we don't need CAS for termination state because
+     * upon pool shutdown, all shared-queues will stop being used
+     * anyway.)  Nearly all lock bodies are set up so that exceptions
+     * within lock bodies are "impossible" (modulo JVM errors that
+     * would cause failure anyway.)
+     *
+     * The array slots are read and written using the emulation of
+     * volatiles/atomics provided by Unsafe. Insertions must in
+     * general use putOrderedObject as a form of releasing store to
+     * ensure that all writes to the task object are ordered before
+     * its publication in the queue.  All removals entail a CAS to
+     * null.  The array is always a power of two. To ensure safety of
+     * Unsafe array operations, all accesses perform explicit null
+     * checks and implicit bounds checks via power-of-two masking.
+     *
+     * In addition to basic queuing support, this class contains
+     * fields described elsewhere to control execution. It turns out
+     * to work better memory-layout-wise to include them in this class
+     * rather than a separate class.
+     *
+     * Performance on most platforms is very sensitive to placement of
+     * instances of both WorkQueues and their arrays -- we absolutely
+     * do not want multiple WorkQueue instances or multiple queue
+     * arrays sharing cache lines. (It would be best for queue objects
+     * and their arrays to share, but there is nothing available to
+     * help arrange that). The @Contended annotation alerts JVMs to
+     * try to keep instances apart.
+     */
+    static final class WorkQueue {
+        /**
+         * Capacity of work-stealing queue array upon initialization.
+         * Must be a power of two; at least 4, but should be larger to
+         * reduce or eliminate cacheline sharing among queues.
+         * Currently, it is much larger, as a partial workaround for
+         * the fact that JVMs often place arrays in locations that
+         * share GC bookkeeping (especially cardmarks) such that
+         * per-write accesses encounter serious memory contention.
+         */
+        static final int INITIAL_QUEUE_CAPACITY = 1 << 13;
+
+        /**
+         * Maximum size for queue arrays. Must be a power of two less
+         * than or equal to 1 << (31 - width of array entry) to ensure
+         * lack of wraparound of index calculations, but defined to a
+         * value a bit less than this to help users trap runaway
+         * programs before saturating systems.
+         */
+        static final int MAXIMUM_QUEUE_CAPACITY = 1 << 26; // 64M
+
+        // Heuristic padding to ameliorate unfortunate memory placements
+        volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06;
+
+        volatile int eventCount;   // encoded inactivation count; < 0 if inactive
+        int nextWait;              // encoded record of next event waiter
+        int nsteals;               // number of steals
+        int hint;                  // steal index hint
+        short poolIndex;           // index of this queue in pool
+        final short mode;          // 0: lifo, > 0: fifo, < 0: shared
+        volatile int qlock;        // 1: locked, -1: terminate; else 0
+        volatile int base;         // index of next slot for poll
+        int top;                   // index of next slot for push
+        ForkJoinTask<?>[] array;   // the elements (initially unallocated)
+        final ForkJoinPool pool;   // the containing pool (may be null)
+        final ForkJoinWorkerThread owner; // owning thread or null if shared
+        volatile Thread parker;    // == owner during call to park; else null
+        volatile ForkJoinTask<?> currentJoin;  // task being joined in awaitJoin
+        ForkJoinTask<?> currentSteal; // current non-local task being executed
+
+        volatile Object pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17;
+        volatile Object pad18, pad19, pad1a, pad1b, pad1c, pad1d;
+
+        WorkQueue(ForkJoinPool pool, ForkJoinWorkerThread owner, int mode,
+                  int seed) {
+            this.pool = pool;
+            this.owner = owner;
+            this.mode = (short)mode;
+            this.hint = seed; // store initial seed for runWorker
+            // Place indices in the center of array (that is not yet allocated)
+            base = top = INITIAL_QUEUE_CAPACITY >>> 1;
+        }
+
+        /**
+         * Returns the approximate number of tasks in the queue.
+         */
+        final int queueSize() {
+            int n = base - top;       // non-owner callers must read base first
+            return (n >= 0) ? 0 : -n; // ignore transient negative
+        }
+
+        /**
+         * Provides a more accurate estimate of whether this queue has
+         * any tasks than does queueSize, by checking whether a
+         * near-empty queue has at least one unclaimed task.
+         */
+        final boolean isEmpty() {
+            ForkJoinTask<?>[] a; int m, s;
+            int n = base - (s = top);
+            return (n >= 0 ||
+                    (n == -1 &&
+                     ((a = array) == null ||
+                      (m = a.length - 1) < 0 ||
+                      U.getObject
+                      (a, (long)((m & (s - 1)) << ASHIFT) + ABASE) == null)));
+        }
+
+        /**
+         * Pushes a task. Call only by owner in unshared queues.  (The
+         * shared-queue version is embedded in method externalPush.)
+         *
+         * @param task the task. Caller must ensure non-null.
+         * @throws RejectedExecutionException if array cannot be resized
+         */
+        final void push(ForkJoinTask<?> task) {
+            ForkJoinTask<?>[] a; ForkJoinPool p;
+            int s = top, n;
+            if ((a = array) != null) {    // ignore if queue removed
+                int m = a.length - 1;
+                U.putOrderedObject(a, ((m & s) << ASHIFT) + ABASE, task);
+                if ((n = (top = s + 1) - base) <= 2)
+                    (p = pool).signalWork(p.workQueues, this);
+                else if (n >= m)
+                    growArray();
+            }
+        }
+
+        /**
+         * Initializes or doubles the capacity of array. Call either
+         * by owner or with lock held -- it is OK for base, but not
+         * top, to move while resizings are in progress.
+         */
+        final ForkJoinTask<?>[] growArray() {
+            ForkJoinTask<?>[] oldA = array;
+            int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY;
+            if (size > MAXIMUM_QUEUE_CAPACITY)
+                throw new RejectedExecutionException("Queue capacity exceeded");
+            int oldMask, t, b;
+            ForkJoinTask<?>[] a = array = new ForkJoinTask<?>[size];
+            if (oldA != null && (oldMask = oldA.length - 1) >= 0 &&
+                (t = top) - (b = base) > 0) {
+                int mask = size - 1;
+                do {
+                    ForkJoinTask<?> x;
+                    int oldj = ((b & oldMask) << ASHIFT) + ABASE;
+                    int j    = ((b &    mask) << ASHIFT) + ABASE;
+                    x = (ForkJoinTask<?>)U.getObjectVolatile(oldA, oldj);
+                    if (x != null &&
+                        U.compareAndSwapObject(oldA, oldj, x, null))
+                        U.putObjectVolatile(a, j, x);
+                } while (++b != t);
+            }
+            return a;
+        }
+
+        /**
+         * Takes next task, if one exists, in LIFO order.  Call only
+         * by owner in unshared queues.
+         */
+        final ForkJoinTask<?> pop() {
+            ForkJoinTask<?>[] a; ForkJoinTask<?> t; int m;
+            if ((a = array) != null && (m = a.length - 1) >= 0) {
+                for (int s; (s = top - 1) - base >= 0;) {
+                    long j = ((m & s) << ASHIFT) + ABASE;
+                    if ((t = (ForkJoinTask<?>)U.getObject(a, j)) == null)
+                        break;
+                    if (U.compareAndSwapObject(a, j, t, null)) {
+                        top = s;
+                        return t;
+                    }
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Takes a task in FIFO order if b is base of queue and a task
+         * can be claimed without contention. Specialized versions
+         * appear in ForkJoinPool methods scan and tryHelpStealer.
+         */
+        final ForkJoinTask<?> pollAt(int b) {
+            ForkJoinTask<?> t; ForkJoinTask<?>[] a;
+            if ((a = array) != null) {
+                int j = (((a.length - 1) & b) << ASHIFT) + ABASE;
+                if ((t = (ForkJoinTask<?>)U.getObjectVolatile(a, j)) != null &&
+                    base == b && U.compareAndSwapObject(a, j, t, null)) {
+                    U.putOrderedInt(this, QBASE, b + 1);
+                    return t;
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Takes next task, if one exists, in FIFO order.
+         */
+        final ForkJoinTask<?> poll() {
+            ForkJoinTask<?>[] a; int b; ForkJoinTask<?> t;
+            while ((b = base) - top < 0 && (a = array) != null) {
+                int j = (((a.length - 1) & b) << ASHIFT) + ABASE;
+                t = (ForkJoinTask<?>)U.getObjectVolatile(a, j);
+                if (t != null) {
+                    if (U.compareAndSwapObject(a, j, t, null)) {
+                        U.putOrderedInt(this, QBASE, b + 1);
+                        return t;
+                    }
+                }
+                else if (base == b) {
+                    if (b + 1 == top)
+                        break;
+                    Thread.yield(); // wait for lagging update (very rare)
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Takes next task, if one exists, in order specified by mode.
+         */
+        final ForkJoinTask<?> nextLocalTask() {
+            return mode == 0 ? pop() : poll();
+        }
+
+        /**
+         * Returns next task, if one exists, in order specified by mode.
+         */
+        final ForkJoinTask<?> peek() {
+            ForkJoinTask<?>[] a = array; int m;
+            if (a == null || (m = a.length - 1) < 0)
+                return null;
+            int i = mode == 0 ? top - 1 : base;
+            int j = ((i & m) << ASHIFT) + ABASE;
+            return (ForkJoinTask<?>)U.getObjectVolatile(a, j);
+        }
+
+        /**
+         * Pops the given task only if it is at the current top.
+         * (A shared version is available only via FJP.tryExternalUnpush)
+         */
+        final boolean tryUnpush(ForkJoinTask<?> t) {
+            ForkJoinTask<?>[] a; int s;
+            if ((a = array) != null && (s = top) != base &&
+                U.compareAndSwapObject
+                (a, (((a.length - 1) & --s) << ASHIFT) + ABASE, t, null)) {
+                top = s;
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Removes and cancels all known tasks, ignoring any exceptions.
+         */
+        final void cancelAll() {
+            ForkJoinTask.cancelIgnoringExceptions(currentJoin);
+            ForkJoinTask.cancelIgnoringExceptions(currentSteal);
+            for (ForkJoinTask<?> t; (t = poll()) != null; )
+                ForkJoinTask.cancelIgnoringExceptions(t);
+        }
+
+        // Specialized execution methods
+
+        /**
+         * Polls and runs tasks until empty.
+         */
+        final void pollAndExecAll() {
+            for (ForkJoinTask<?> t; (t = poll()) != null;)
+                t.doExec();
+        }
+
+        /**
+         * Executes a top-level task and any local tasks remaining
+         * after execution.
+         */
+        final void runTask(ForkJoinTask<?> task) {
+            if ((currentSteal = task) != null) {
+                task.doExec();
+                ForkJoinTask<?>[] a = array;
+                int md = mode;
+                ++nsteals;
+                currentSteal = null;
+                if (md != 0)
+                    pollAndExecAll();
+                else if (a != null) {
+                    int s, m = a.length - 1;
+                    while ((s = top - 1) - base >= 0) {
+                        long i = ((m & s) << ASHIFT) + ABASE;
+                        ForkJoinTask<?> t = (ForkJoinTask<?>)U.getObject(a, i);
+                        if (t == null)
+                            break;
+                        if (U.compareAndSwapObject(a, i, t, null)) {
+                            top = s;
+                            t.doExec();
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * If present, removes from queue and executes the given task,
+         * or any other cancelled task. Returns (true) on any CAS
+         * or consistency check failure so caller can retry.
+         *
+         * @return false if no progress can be made, else true
+         */
+        final boolean tryRemoveAndExec(ForkJoinTask<?> task) {
+            boolean stat;
+            ForkJoinTask<?>[] a; int m, s, b, n;
+            if (task != null && (a = array) != null && (m = a.length - 1) >= 0 &&
+                (n = (s = top) - (b = base)) > 0) {
+                boolean removed = false, empty = true;
+                stat = true;
+                for (ForkJoinTask<?> t;;) {           // traverse from s to b
+                    long j = ((--s & m) << ASHIFT) + ABASE;
+                    t = (ForkJoinTask<?>)U.getObject(a, j);
+                    if (t == null)                    // inconsistent length
+                        break;
+                    else if (t == task) {
+                        if (s + 1 == top) {           // pop
+                            if (!U.compareAndSwapObject(a, j, task, null))
+                                break;
+                            top = s;
+                            removed = true;
+                        }
+                        else if (base == b)           // replace with proxy
+                            removed = U.compareAndSwapObject(a, j, task,
+                                                             new EmptyTask());
+                        break;
+                    }
+                    else if (t.status >= 0)
+                        empty = false;
+                    else if (s + 1 == top) {          // pop and throw away
+                        if (U.compareAndSwapObject(a, j, t, null))
+                            top = s;
+                        break;
+                    }
+                    if (--n == 0) {
+                        if (!empty && base == b)
+                            stat = false;
+                        break;
+                    }
+                }
+                if (removed)
+                    task.doExec();
+            }
+            else
+                stat = false;
+            return stat;
+        }
+
+        /**
+         * Tries to poll for and execute the given task or any other
+         * task in its CountedCompleter computation.
+         */
+        final boolean pollAndExecCC(CountedCompleter<?> root) {
+            ForkJoinTask<?>[] a; int b; Object o; CountedCompleter<?> t, r;
+            if ((b = base) - top < 0 && (a = array) != null) {
+                long j = (((a.length - 1) & b) << ASHIFT) + ABASE;
+                if ((o = U.getObjectVolatile(a, j)) == null)
+                    return true; // retry
+                if (o instanceof CountedCompleter) {
+                    for (t = (CountedCompleter<?>)o, r = t;;) {
+                        if (r == root) {
+                            if (base == b &&
+                                U.compareAndSwapObject(a, j, t, null)) {
+                                U.putOrderedInt(this, QBASE, b + 1);
+                                t.doExec();
+                            }
+                            return true;
+                        }
+                        else if ((r = r.completer) == null)
+                            break; // not part of root computation
+                    }
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Tries to pop and execute the given task or any other task
+         * in its CountedCompleter computation.
+         */
+        final boolean externalPopAndExecCC(CountedCompleter<?> root) {
+            ForkJoinTask<?>[] a; int s; Object o; CountedCompleter<?> t, r;
+            if (base - (s = top) < 0 && (a = array) != null) {
+                long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
+                if ((o = U.getObject(a, j)) instanceof CountedCompleter) {
+                    for (t = (CountedCompleter<?>)o, r = t;;) {
+                        if (r == root) {
+                            if (U.compareAndSwapInt(this, QLOCK, 0, 1)) {
+                                if (top == s && array == a &&
+                                    U.compareAndSwapObject(a, j, t, null)) {
+                                    top = s - 1;
+                                    qlock = 0;
+                                    t.doExec();
+                                }
+                                else
+                                    qlock = 0;
+                            }
+                            return true;
+                        }
+                        else if ((r = r.completer) == null)
+                            break;
+                    }
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Internal version
+         */
+        final boolean internalPopAndExecCC(CountedCompleter<?> root) {
+            ForkJoinTask<?>[] a; int s; Object o; CountedCompleter<?> t, r;
+            if (base - (s = top) < 0 && (a = array) != null) {
+                long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
+                if ((o = U.getObject(a, j)) instanceof CountedCompleter) {
+                    for (t = (CountedCompleter<?>)o, r = t;;) {
+                        if (r == root) {
+                            if (U.compareAndSwapObject(a, j, t, null)) {
+                                top = s - 1;
+                                t.doExec();
+                            }
+                            return true;
+                        }
+                        else if ((r = r.completer) == null)
+                            break;
+                    }
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Returns true if owned and not known to be blocked.
+         */
+        final boolean isApparentlyUnblocked() {
+            Thread wt; Thread.State s;
+            return (eventCount >= 0 &&
+                    (wt = owner) != null &&
+                    (s = wt.getState()) != Thread.State.BLOCKED &&
+                    s != Thread.State.WAITING &&
+                    s != Thread.State.TIMED_WAITING);
+        }
+
+        // Unsafe mechanics
+        private static final sun.misc.Unsafe U;
+        private static final long QBASE;
+        private static final long QLOCK;
+        private static final int ABASE;
+        private static final int ASHIFT;
+        static {
+            try {
+                U = getUnsafe();
+                Class<?> k = WorkQueue.class;
+                Class<?> ak = ForkJoinTask[].class;
+                QBASE = U.objectFieldOffset
+                    (k.getDeclaredField("base"));
+                QLOCK = U.objectFieldOffset
+                    (k.getDeclaredField("qlock"));
+                ABASE = U.arrayBaseOffset(ak);
+                int scale = U.arrayIndexScale(ak);
+                if ((scale & (scale - 1)) != 0)
+                    throw new Error("data type scale not a power of two");
+                ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
+            } catch (Exception e) {
+                throw new Error(e);
+            }
+        }
+    }
+
+    // static fields (initialized in static initializer below)
+
+    /**
+     * Per-thread submission bookkeeping. Shared across all pools
+     * to reduce ThreadLocal pollution and because random motion
+     * to avoid contention in one pool is likely to hold for others.
+     * Lazily initialized on first submission (but null-checked
+     * in other contexts to avoid unnecessary initialization).
+     */
+    static final ThreadLocal<Submitter> submitters;
+
+    /**
+     * Creates a new ForkJoinWorkerThread. This factory is used unless
+     * overridden in ForkJoinPool constructors.
+     */
+    public static final ForkJoinWorkerThreadFactory
+        defaultForkJoinWorkerThreadFactory;
+
+    /**
+     * Permission required for callers of methods that may start or
+     * kill threads.
+     */
+    private static final RuntimePermission modifyThreadPermission;
+
+    /**
+     * Common (static) pool. Non-null for public use unless a static
+     * construction exception, but internal usages null-check on use
+     * to paranoically avoid potential initialization circularities
+     * as well as to simplify generated code.
+     */
+    static final ForkJoinPool common;
+
+    /**
+     * Common pool parallelism. To allow simpler use and management
+     * when common pool threads are disabled, we allow the underlying
+     * common.parallelism field to be zero, but in that case still report
+     * parallelism as 1 to reflect resulting caller-runs mechanics.
+     */
+    static final int commonParallelism;
+
+    /**
+     * Sequence number for creating workerNamePrefix.
+     */
+    private static int poolNumberSequence;
+
+    /**
+     * Returns the next sequence number. We don't expect this to
+     * ever contend, so use simple builtin sync.
+     */
+    private static final synchronized int nextPoolId() {
+        return ++poolNumberSequence;
+    }
+
+    // static constants
+
+    /**
+     * Initial timeout value (in nanoseconds) for the thread
+     * triggering quiescence to park waiting for new work. On timeout,
+     * the thread will instead try to shrink the number of
+     * workers. The value should be large enough to avoid overly
+     * aggressive shrinkage during most transient stalls (long GCs
+     * etc).
+     */
+    private static final long IDLE_TIMEOUT      = 2000L * 1000L * 1000L; // 2sec
+
+    /**
+     * Timeout value when there are more threads than parallelism level
+     */
+    private static final long FAST_IDLE_TIMEOUT =  200L * 1000L * 1000L;
+
+    /**
+     * Tolerance for idle timeouts, to cope with timer undershoots
+     */
+    private static final long TIMEOUT_SLOP = 2000000L;
+
+    /**
+     * The maximum stolen->joining link depth allowed in method
+     * tryHelpStealer.  Must be a power of two.  Depths for legitimate
+     * chains are unbounded, but we use a fixed constant to avoid
+     * (otherwise unchecked) cycles and to bound staleness of
+     * traversal parameters at the expense of sometimes blocking when
+     * we could be helping.
+     */
+    private static final int MAX_HELP = 64;
+
+    /**
+     * Increment for seed generators. See class ThreadLocal for
+     * explanation.
+     */
+    private static final int SEED_INCREMENT = 0x61c88647;
+
+    /*
+     * Bits and masks for control variables
+     *
+     * Field ctl is a long packed with:
+     * AC: Number of active running workers minus target parallelism (16 bits)
+     * TC: Number of total workers minus target parallelism (16 bits)
+     * ST: true if pool is terminating (1 bit)
+     * EC: the wait count of top waiting thread (15 bits)
+     * ID: poolIndex of top of Treiber stack of waiters (16 bits)
+     *
+     * When convenient, we can extract the upper 32 bits of counts and
+     * the lower 32 bits of queue state, u = (int)(ctl >>> 32) and e =
+     * (int)ctl.  The ec field is never accessed alone, but always
+     * together with id and st. The offsets of counts by the target
+     * parallelism and the positionings of fields makes it possible to
+     * perform the most common checks via sign tests of fields: When
+     * ac is negative, there are not enough active workers, when tc is
+     * negative, there are not enough total workers, and when e is
+     * negative, the pool is terminating.  To deal with these possibly
+     * negative fields, we use casts in and out of "short" and/or
+     * signed shifts to maintain signedness.
+     *
+     * When a thread is queued (inactivated), its eventCount field is
+     * set negative, which is the only way to tell if a worker is
+     * prevented from executing tasks, even though it must continue to
+     * scan for them to avoid queuing races. Note however that
+     * eventCount updates lag releases so usage requires care.
+     *
+     * Field plock is an int packed with:
+     * SHUTDOWN: true if shutdown is enabled (1 bit)
+     * SEQ:  a sequence lock, with PL_LOCK bit set if locked (30 bits)
+     * SIGNAL: set when threads may be waiting on the lock (1 bit)
+     *
+     * The sequence number enables simple consistency checks:
+     * Staleness of read-only operations on the workQueues array can
+     * be checked by comparing plock before vs after the reads.
+     */
+
+    // bit positions/shifts for fields
+    private static final int  AC_SHIFT   = 48;
+    private static final int  TC_SHIFT   = 32;
+    private static final int  ST_SHIFT   = 31;
+    private static final int  EC_SHIFT   = 16;
+
+    // bounds
+    private static final int  SMASK      = 0xffff;  // short bits
+    private static final int  MAX_CAP    = 0x7fff;  // max #workers - 1
+    private static final int  EVENMASK   = 0xfffe;  // even short bits
+    private static final int  SQMASK     = 0x007e;  // max 64 (even) slots
+    private static final int  SHORT_SIGN = 1 << 15;
+    private static final int  INT_SIGN   = 1 << 31;
+
+    // masks
+    private static final long STOP_BIT   = 0x0001L << ST_SHIFT;
+    private static final long AC_MASK    = ((long)SMASK) << AC_SHIFT;
+    private static final long TC_MASK    = ((long)SMASK) << TC_SHIFT;
+
+    // units for incrementing and decrementing
+    private static final long TC_UNIT    = 1L << TC_SHIFT;
+    private static final long AC_UNIT    = 1L << AC_SHIFT;
+
+    // masks and units for dealing with u = (int)(ctl >>> 32)
+    private static final int  UAC_SHIFT  = AC_SHIFT - 32;
+    private static final int  UTC_SHIFT  = TC_SHIFT - 32;
+    private static final int  UAC_MASK   = SMASK << UAC_SHIFT;
+    private static final int  UTC_MASK   = SMASK << UTC_SHIFT;
+    private static final int  UAC_UNIT   = 1 << UAC_SHIFT;
+    private static final int  UTC_UNIT   = 1 << UTC_SHIFT;
+
+    // masks and units for dealing with e = (int)ctl
+    private static final int E_MASK      = 0x7fffffff; // no STOP_BIT
+    private static final int E_SEQ       = 1 << EC_SHIFT;
+
+    // plock bits
+    private static final int SHUTDOWN    = 1 << 31;
+    private static final int PL_LOCK     = 2;
+    private static final int PL_SIGNAL   = 1;
+    private static final int PL_SPINS    = 1 << 8;
+
+    // access mode for WorkQueue
+    static final int LIFO_QUEUE          =  0;
+    static final int FIFO_QUEUE          =  1;
+    static final int SHARED_QUEUE        = -1;
+
+    // Heuristic padding to ameliorate unfortunate memory placements
+    volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06;
+
+    // Instance fields
+    volatile long stealCount;                  // collects worker counts
+    volatile long ctl;                         // main pool control
+    volatile int plock;                        // shutdown status and seqLock
+    volatile int indexSeed;                    // worker/submitter index seed
+    final short parallelism;                   // parallelism level
+    final short mode;                          // LIFO/FIFO
+    WorkQueue[] workQueues;                    // main registry
+    final ForkJoinWorkerThreadFactory factory;
+    final UncaughtExceptionHandler ueh;        // per-worker UEH
+    final String workerNamePrefix;             // to create worker name string
+
+    volatile Object pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17;
+    volatile Object pad18, pad19, pad1a, pad1b;
+
+    /**
+     * Acquires the plock lock to protect worker array and related
+     * updates. This method is called only if an initial CAS on plock
+     * fails. This acts as a spinlock for normal cases, but falls back
+     * to builtin monitor to block when (rarely) needed. This would be
+     * a terrible idea for a highly contended lock, but works fine as
+     * a more conservative alternative to a pure spinlock.
+     */
+    private int acquirePlock() {
+        int spins = PL_SPINS, ps, nps;
+        for (;;) {
+            if (((ps = plock) & PL_LOCK) == 0 &&
+                U.compareAndSwapInt(this, PLOCK, ps, nps = ps + PL_LOCK))
+                return nps;
+            else if (spins >= 0) {
+                if (ThreadLocalRandom.current().nextInt() >= 0)
+                    --spins;
+            }
+            else if (U.compareAndSwapInt(this, PLOCK, ps, ps | PL_SIGNAL)) {
+                synchronized (this) {
+                    if ((plock & PL_SIGNAL) != 0) {
+                        try {
+                            wait();
+                        } catch (InterruptedException ie) {
+                            try {
+                                Thread.currentThread().interrupt();
+                            } catch (SecurityException ignore) {
+                            }
+                        }
+                    }
+                    else
+                        notifyAll();
+                }
+            }
+        }
+    }
+
+    /**
+     * Unlocks and signals any thread waiting for plock. Called only
+     * when CAS of seq value for unlock fails.
+     */
+    private void releasePlock(int ps) {
+        plock = ps;
+        synchronized (this) { notifyAll(); }
+    }
+
+    /**
+     * Tries to create and start one worker if fewer than target
+     * parallelism level exist. Adjusts counts etc on failure.
+     */
+    private void tryAddWorker() {
+        long c; int u, e;
+        while ((u = (int)((c = ctl) >>> 32)) < 0 &&
+               (u & SHORT_SIGN) != 0 && (e = (int)c) >= 0) {
+            long nc = ((long)(((u + UTC_UNIT) & UTC_MASK) |
+                              ((u + UAC_UNIT) & UAC_MASK)) << 32) | (long)e;
+            if (U.compareAndSwapLong(this, CTL, c, nc)) {
+                ForkJoinWorkerThreadFactory fac;
+                Throwable ex = null;
+                ForkJoinWorkerThread wt = null;
+                try {
+                    if ((fac = factory) != null &&
+                        (wt = fac.newThread(this)) != null) {
+                        wt.start();
+                        break;
+                    }
+                } catch (Throwable rex) {
+                    ex = rex;
+                }
+                deregisterWorker(wt, ex);
+                break;
+            }
+        }
+    }
+
+    //  Registering and deregistering workers
+
+    /**
+     * Callback from ForkJoinWorkerThread to establish and record its
+     * WorkQueue. To avoid scanning bias due to packing entries in
+     * front of the workQueues array, we treat the array as a simple
+     * power-of-two hash table using per-thread seed as hash,
+     * expanding as needed.
+     *
+     * @param wt the worker thread
+     * @return the worker's queue
+     */
+    final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
+        UncaughtExceptionHandler handler; WorkQueue[] ws; int s, ps;
+        wt.setDaemon(true);
+        if ((handler = ueh) != null)
+            wt.setUncaughtExceptionHandler(handler);
+        do {} while (!U.compareAndSwapInt(this, INDEXSEED, s = indexSeed,
+                                          s += SEED_INCREMENT) ||
+                     s == 0); // skip 0
+        WorkQueue w = new WorkQueue(this, wt, mode, s);
+        if (((ps = plock) & PL_LOCK) != 0 ||
+            !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+            ps = acquirePlock();
+        int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+        try {
+            if ((ws = workQueues) != null) {    // skip if shutting down
+                int n = ws.length, m = n - 1;
+                int r = (s << 1) | 1;           // use odd-numbered indices
+                if (ws[r &= m] != null) {       // collision
+                    int probes = 0;             // step by approx half size
+                    int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
+                    while (ws[r = (r + step) & m] != null) {
+                        if (++probes >= n) {
+                            workQueues = ws = Arrays.copyOf(ws, n <<= 1);
+                            m = n - 1;
+                            probes = 0;
+                        }
+                    }
+                }
+                w.poolIndex = (short)r;
+                w.eventCount = r; // volatile write orders
+                ws[r] = w;
+            }
+        } finally {
+            if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+                releasePlock(nps);
+        }
+        wt.setName(workerNamePrefix.concat(Integer.toString(w.poolIndex >>> 1)));
+        return w;
+    }
+
+    /**
+     * Final callback from terminating worker, as well as upon failure
+     * to construct or start a worker.  Removes record of worker from
+     * array, and adjusts counts. If pool is shutting down, tries to
+     * complete termination.
+     *
+     * @param wt the worker thread, or null if construction failed
+     * @param ex the exception causing failure, or null if none
+     */
+    final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) {
+        WorkQueue w = null;
+        if (wt != null && (w = wt.workQueue) != null) {
+            int ps; long sc;
+            w.qlock = -1;                // ensure set
+            do {} while (!U.compareAndSwapLong(this, STEALCOUNT,
+                                               sc = stealCount,
+                                               sc + w.nsteals));
+            if (((ps = plock) & PL_LOCK) != 0 ||
+                !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+                ps = acquirePlock();
+            int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+            try {
+                int idx = w.poolIndex;
+                WorkQueue[] ws = workQueues;
+                if (ws != null && idx >= 0 && idx < ws.length && ws[idx] == w)
+                    ws[idx] = null;
+            } finally {
+                if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+                    releasePlock(nps);
+            }
+        }
+
+        long c;                          // adjust ctl counts
+        do {} while (!U.compareAndSwapLong
+                     (this, CTL, c = ctl, (((c - AC_UNIT) & AC_MASK) |
+                                           ((c - TC_UNIT) & TC_MASK) |
+                                           (c & ~(AC_MASK|TC_MASK)))));
+
+        if (!tryTerminate(false, false) && w != null && w.array != null) {
+            w.cancelAll();               // cancel remaining tasks
+            WorkQueue[] ws; WorkQueue v; Thread p; int u, i, e;
+            while ((u = (int)((c = ctl) >>> 32)) < 0 && (e = (int)c) >= 0) {
+                if (e > 0) {             // activate or create replacement
+                    if ((ws = workQueues) == null ||
+                        (i = e & SMASK) >= ws.length ||
+                        (v = ws[i]) == null)
+                        break;
+                    long nc = (((long)(v.nextWait & E_MASK)) |
+                               ((long)(u + UAC_UNIT) << 32));
+                    if (v.eventCount != (e | INT_SIGN))
+                        break;
+                    if (U.compareAndSwapLong(this, CTL, c, nc)) {
+                        v.eventCount = (e + E_SEQ) & E_MASK;
+                        if ((p = v.parker) != null)
+                            U.unpark(p);
+                        break;
+                    }
+                }
+                else {
+                    if ((short)u < 0)
+                        tryAddWorker();
+                    break;
+                }
+            }
+        }
+        if (ex == null)                     // help clean refs on way out
+            ForkJoinTask.helpExpungeStaleExceptions();
+        else                                // rethrow
+            ForkJoinTask.rethrow(ex);
+    }
+
+    // Submissions
+
+    /**
+     * Per-thread records for threads that submit to pools. Currently
+     * holds only pseudo-random seed / index that is used to choose
+     * submission queues in method externalPush. In the future, this may
+     * also incorporate a means to implement different task rejection
+     * and resubmission policies.
+     *
+     * Seeds for submitters and workers/workQueues work in basically
+     * the same way but are initialized and updated using slightly
+     * different mechanics. Both are initialized using the same
+     * approach as in class ThreadLocal, where successive values are
+     * unlikely to collide with previous values. Seeds are then
+     * randomly modified upon collisions using xorshifts, which
+     * requires a non-zero seed.
+     */
+    static final class Submitter {
+        int seed;
+        Submitter(int s) { seed = s; }
+    }
+
+    /**
+     * Unless shutting down, adds the given task to a submission queue
+     * at submitter's current queue index (modulo submission
+     * range). Only the most common path is directly handled in this
+     * method. All others are relayed to fullExternalPush.
+     *
+     * @param task the task. Caller must ensure non-null.
+     */
+    final void externalPush(ForkJoinTask<?> task) {
+        Submitter z = submitters.get();
+        WorkQueue q; int r, m, s, n, am; ForkJoinTask<?>[] a;
+        int ps = plock;
+        WorkQueue[] ws = workQueues;
+        if (z != null && ps > 0 && ws != null && (m = (ws.length - 1)) >= 0 &&
+            (q = ws[m & (r = z.seed) & SQMASK]) != null && r != 0 &&
+            U.compareAndSwapInt(q, QLOCK, 0, 1)) { // lock
+            if ((a = q.array) != null &&
+                (am = a.length - 1) > (n = (s = q.top) - q.base)) {
+                int j = ((am & s) << ASHIFT) + ABASE;
+                U.putOrderedObject(a, j, task);
+                q.top = s + 1;                     // push on to deque
+                q.qlock = 0;
+                if (n <= 1)
+                    signalWork(ws, q);
+                return;
+            }
+            q.qlock = 0;
+        }
+        fullExternalPush(task);
+    }
+
+    /**
+     * Full version of externalPush. This method is called, among
+     * other times, upon the first submission of the first task to the
+     * pool, so must perform secondary initialization.  It also
+     * detects first submission by an external thread by looking up
+     * its ThreadLocal, and creates a new shared queue if the one at
+     * index if empty or contended. The plock lock body must be
+     * exception-free (so no try/finally) so we optimistically
+     * allocate new queues outside the lock and throw them away if
+     * (very rarely) not needed.
+     *
+     * Secondary initialization occurs when plock is zero, to create
+     * workQueue array and set plock to a valid value.  This lock body
+     * must also be exception-free. Because the plock seq value can
+     * eventually wrap around zero, this method harmlessly fails to
+     * reinitialize if workQueues exists, while still advancing plock.
+     */
+    private void fullExternalPush(ForkJoinTask<?> task) {
+        int r = 0; // random index seed
+        for (Submitter z = submitters.get();;) {
+            WorkQueue[] ws; WorkQueue q; int ps, m, k;
+            if (z == null) {
+                if (U.compareAndSwapInt(this, INDEXSEED, r = indexSeed,
+                                        r += SEED_INCREMENT) && r != 0)
+                    submitters.set(z = new Submitter(r));
+            }
+            else if (r == 0) {                  // move to a different index
+                r = z.seed;
+                r ^= r << 13;                   // same xorshift as WorkQueues
+                r ^= r >>> 17;
+                z.seed = r ^= (r << 5);
+            }
+            if ((ps = plock) < 0)
+                throw new RejectedExecutionException();
+            else if (ps == 0 || (ws = workQueues) == null ||
+                     (m = ws.length - 1) < 0) { // initialize workQueues
+                int p = parallelism;            // find power of two table size
+                int n = (p > 1) ? p - 1 : 1;    // ensure at least 2 slots
+                n |= n >>> 1;
+                n |= n >>> 2;
+                n |= n >>> 4;
+                n |= n >>> 8;
+                n |= n >>> 16;
+                n = (n + 1) << 1;
+                WorkQueue[] nws = ((ws = workQueues) == null || ws.length == 0 ?
+                                   new WorkQueue[n] : null);
+                if (((ps = plock) & PL_LOCK) != 0 ||
+                    !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+                    ps = acquirePlock();
+                if (((ws = workQueues) == null || ws.length == 0) && nws != null)
+                    workQueues = nws;
+                int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+                if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+                    releasePlock(nps);
+            }
+            else if ((q = ws[k = r & m & SQMASK]) != null) {
+                if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) {
+                    ForkJoinTask<?>[] a = q.array;
+                    int s = q.top;
+                    boolean submitted = false;
+                    try {                      // locked version of push
+                        if ((a != null && a.length > s + 1 - q.base) ||
+                            (a = q.growArray()) != null) {   // must presize
+                            int j = (((a.length - 1) & s) << ASHIFT) + ABASE;
+                            U.putOrderedObject(a, j, task);
+                            q.top = s + 1;
+                            submitted = true;
+                        }
+                    } finally {
+                        q.qlock = 0;  // unlock
+                    }
+                    if (submitted) {
+                        signalWork(ws, q);
+                        return;
+                    }
+                }
+                r = 0; // move on failure
+            }
+            else if (((ps = plock) & PL_LOCK) == 0) { // create new queue
+                q = new WorkQueue(this, null, SHARED_QUEUE, r);
+                q.poolIndex = (short)k;
+                if (((ps = plock) & PL_LOCK) != 0 ||
+                    !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+                    ps = acquirePlock();
+                if ((ws = workQueues) != null && k < ws.length && ws[k] == null)
+                    ws[k] = q;
+                int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+                if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+                    releasePlock(nps);
+            }
+            else
+                r = 0;
+        }
+    }
+
+    // Maintaining ctl counts
+
+    /**
+     * Increments active count; mainly called upon return from blocking.
+     */
+    final void incrementActiveCount() {
+        long c;
+        do {} while (!U.compareAndSwapLong
+                     (this, CTL, c = ctl, ((c & ~AC_MASK) |
+                                           ((c & AC_MASK) + AC_UNIT))));
+    }
+
+    /**
+     * Tries to create or activate a worker if too few are active.
+     *
+     * @param ws the worker array to use to find signallees
+     * @param q if non-null, the queue holding tasks to be processed
+     */
+    final void signalWork(WorkQueue[] ws, WorkQueue q) {
+        for (;;) {
+            long c; int e, u, i; WorkQueue w; Thread p;
+            if ((u = (int)((c = ctl) >>> 32)) >= 0)
+                break;
+            if ((e = (int)c) <= 0) {
+                if ((short)u < 0)
+                    tryAddWorker();
+                break;
+            }
+            if (ws == null || ws.length <= (i = e & SMASK) ||
+                (w = ws[i]) == null)
+                break;
+            long nc = (((long)(w.nextWait & E_MASK)) |
+                       ((long)(u + UAC_UNIT)) << 32);
+            int ne = (e + E_SEQ) & E_MASK;
+            if (w.eventCount == (e | INT_SIGN) &&
+                U.compareAndSwapLong(this, CTL, c, nc)) {
+                w.eventCount = ne;
+                if ((p = w.parker) != null)
+                    U.unpark(p);
+                break;
+            }
+            if (q != null && q.base >= q.top)
+                break;
+        }
+    }
+
+    // Scanning for tasks
+
+    /**
+     * Top-level runloop for workers, called by ForkJoinWorkerThread.run.
+     */
+    final void runWorker(WorkQueue w) {
+        w.growArray(); // allocate queue
+        for (int r = w.hint; scan(w, r) == 0; ) {
+            r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
+        }
+    }
+
+    /**
+     * Scans for and, if found, runs one task, else possibly
+     * inactivates the worker. This method operates on single reads of
+     * volatile state and is designed to be re-invoked continuously,
+     * in part because it returns upon detecting inconsistencies,
+     * contention, or state changes that indicate possible success on
+     * re-invocation.
+     *
+     * The scan searches for tasks across queues starting at a random
+     * index, checking each at least twice.  The scan terminates upon
+     * either finding a non-empty queue, or completing the sweep. If
+     * the worker is not inactivated, it takes and runs a task from
+     * this queue. Otherwise, if not activated, it tries to activate
+     * itself or some other worker by signalling. On failure to find a
+     * task, returns (for retry) if pool state may have changed during
+     * an empty scan, or tries to inactivate if active, else possibly
+     * blocks or terminates via method awaitWork.
+     *
+     * @param w the worker (via its WorkQueue)
+     * @param r a random seed
+     * @return worker qlock status if would have waited, else 0
+     */
+    private final int scan(WorkQueue w, int r) {
+        WorkQueue[] ws; int m;
+        long c = ctl;                            // for consistency check
+        if ((ws = workQueues) != null && (m = ws.length - 1) >= 0 && w != null) {
+            for (int j = m + m + 1, ec = w.eventCount;;) {
+                WorkQueue q; int b, e; ForkJoinTask<?>[] a; ForkJoinTask<?> t;
+                if ((q = ws[(r - j) & m]) != null &&
+                    (b = q.base) - q.top < 0 && (a = q.array) != null) {
+                    long i = (((a.length - 1) & b) << ASHIFT) + ABASE;
+                    if ((t = ((ForkJoinTask<?>)
+                              U.getObjectVolatile(a, i))) != null) {
+                        if (ec < 0)
+                            helpRelease(c, ws, w, q, b);
+                        else if (q.base == b &&
+                                 U.compareAndSwapObject(a, i, t, null)) {
+                            U.putOrderedInt(q, QBASE, b + 1);
+                            if ((b + 1) - q.top < 0)
+                                signalWork(ws, q);
+                            w.runTask(t);
+                        }
+                    }
+                    break;
+                }
+                else if (--j < 0) {
+                    if ((ec | (e = (int)c)) < 0) // inactive or terminating
+                        return awaitWork(w, c, ec);
+                    else if (ctl == c) {         // try to inactivate and enqueue
+                        long nc = (long)ec | ((c - AC_UNIT) & (AC_MASK|TC_MASK));
+                        w.nextWait = e;
+                        w.eventCount = ec | INT_SIGN;
+                        if (!U.compareAndSwapLong(this, CTL, c, nc))
+                            w.eventCount = ec;   // back out
+                    }
+                    break;
+                }
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * A continuation of scan(), possibly blocking or terminating
+     * worker w. Returns without blocking if pool state has apparently
+     * changed since last invocation.  Also, if inactivating w has
+     * caused the pool to become quiescent, checks for pool
+     * termination, and, so long as this is not the only worker, waits
+     * for event for up to a given duration.  On timeout, if ctl has
+     * not changed, terminates the worker, which will in turn wake up
+     * another worker to possibly repeat this process.
+     *
+     * @param w the calling worker
+     * @param c the ctl value on entry to scan
+     * @param ec the worker's eventCount on entry to scan
+     */
+    private final int awaitWork(WorkQueue w, long c, int ec) {
+        int stat, ns; long parkTime, deadline;
+        if ((stat = w.qlock) >= 0 && w.eventCount == ec && ctl == c &&
+            !Thread.interrupted()) {
+            int e = (int)c;
+            int u = (int)(c >>> 32);
+            int d = (u >> UAC_SHIFT) + parallelism; // active count
+
+            if (e < 0 || (d <= 0 && tryTerminate(false, false)))
+                stat = w.qlock = -1;          // pool is terminating
+            else if ((ns = w.nsteals) != 0) { // collect steals and retry
+                long sc;
+                w.nsteals = 0;
+                do {} while (!U.compareAndSwapLong(this, STEALCOUNT,
+                                                   sc = stealCount, sc + ns));
+            }
+            else {
+                long pc = ((d > 0 || ec != (e | INT_SIGN)) ? 0L :
+                           ((long)(w.nextWait & E_MASK)) | // ctl to restore
+                           ((long)(u + UAC_UNIT)) << 32);
+                if (pc != 0L) {               // timed wait if last waiter
+                    int dc = -(short)(c >>> TC_SHIFT);
+                    parkTime = (dc < 0 ? FAST_IDLE_TIMEOUT:
+                                (dc + 1) * IDLE_TIMEOUT);
+                    deadline = System.nanoTime() + parkTime - TIMEOUT_SLOP;
+                }
+                else
+                    parkTime = deadline = 0L;
+                if (w.eventCount == ec && ctl == c) {
+                    Thread wt = Thread.currentThread();
+                    U.putObject(wt, PARKBLOCKER, this);
+                    w.parker = wt;            // emulate LockSupport.park
+                    if (w.eventCount == ec && ctl == c)
+                        U.park(false, parkTime);  // must recheck before park
+                    w.parker = null;
+                    U.putObject(wt, PARKBLOCKER, null);
+                    if (parkTime != 0L && ctl == c &&
+                        deadline - System.nanoTime() <= 0L &&
+                        U.compareAndSwapLong(this, CTL, c, pc))
+                        stat = w.qlock = -1;  // shrink pool
+                }
+            }
+        }
+        return stat;
+    }
+
+    /**
+     * Possibly releases (signals) a worker. Called only from scan()
+     * when a worker with apparently inactive status finds a non-empty
+     * queue. This requires revalidating all of the associated state
+     * from caller.
+     */
+    private final void helpRelease(long c, WorkQueue[] ws, WorkQueue w,
+                                   WorkQueue q, int b) {
+        WorkQueue v; int e, i; Thread p;
+        if (w != null && w.eventCount < 0 && (e = (int)c) > 0 &&
+            ws != null && ws.length > (i = e & SMASK) &&
+            (v = ws[i]) != null && ctl == c) {
+            long nc = (((long)(v.nextWait & E_MASK)) |
+                       ((long)((int)(c >>> 32) + UAC_UNIT)) << 32);
+            int ne = (e + E_SEQ) & E_MASK;
+            if (q != null && q.base == b && w.eventCount < 0 &&
+                v.eventCount == (e | INT_SIGN) &&
+                U.compareAndSwapLong(this, CTL, c, nc)) {
+                v.eventCount = ne;
+                if ((p = v.parker) != null)
+                    U.unpark(p);
+            }
+        }
+    }
+
+    /**
+     * Tries to locate and execute tasks for a stealer of the given
+     * task, or in turn one of its stealers, Traces currentSteal ->
+     * currentJoin links looking for a thread working on a descendant
+     * of the given task and with a non-empty queue to steal back and
+     * execute tasks from. The first call to this method upon a
+     * waiting join will often entail scanning/search, (which is OK
+     * because the joiner has nothing better to do), but this method
+     * leaves hints in workers to speed up subsequent calls. The
+     * implementation is very branchy to cope with potential
+     * inconsistencies or loops encountering chains that are stale,
+     * unknown, or so long that they are likely cyclic.
+     *
+     * @param joiner the joining worker
+     * @param task the task to join
+     * @return 0 if no progress can be made, negative if task
+     * known complete, else positive
+     */
+    private int tryHelpStealer(WorkQueue joiner, ForkJoinTask<?> task) {
+        int stat = 0, steps = 0;                    // bound to avoid cycles
+        if (task != null && joiner != null &&
+            joiner.base - joiner.top >= 0) {        // hoist checks
+            restart: for (;;) {
+                ForkJoinTask<?> subtask = task;     // current target
+                for (WorkQueue j = joiner, v;;) {   // v is stealer of subtask
+                    WorkQueue[] ws; int m, s, h;
+                    if ((s = task.status) < 0) {
+                        stat = s;
+                        break restart;
+                    }
+                    if ((ws = workQueues) == null || (m = ws.length - 1) <= 0)
+                        break restart;              // shutting down
+                    if ((v = ws[h = (j.hint | 1) & m]) == null ||
+                        v.currentSteal != subtask) {
+                        for (int origin = h;;) {    // find stealer
+                            if (((h = (h + 2) & m) & 15) == 1 &&
+                                (subtask.status < 0 || j.currentJoin != subtask))
+                                continue restart;   // occasional staleness check
+                            if ((v = ws[h]) != null &&
+                                v.currentSteal == subtask) {
+                                j.hint = h;        // save hint
+                                break;
+                            }
+                            if (h == origin)
+                                break restart;      // cannot find stealer
+                        }
+                    }
+                    for (;;) { // help stealer or descend to its stealer
+                        ForkJoinTask<?>[] a; int b;
+                        if (subtask.status < 0)     // surround probes with
+                            continue restart;       //   consistency checks
+                        if ((b = v.base) - v.top < 0 && (a = v.array) != null) {
+                            int i = (((a.length - 1) & b) << ASHIFT) + ABASE;
+                            ForkJoinTask<?> t =
+                                (ForkJoinTask<?>)U.getObjectVolatile(a, i);
+                            if (subtask.status < 0 || j.currentJoin != subtask ||
+                                v.currentSteal != subtask)
+                                continue restart;   // stale
+                            stat = 1;               // apparent progress
+                            if (v.base == b) {
+                                if (t == null)
+                                    break restart;
+                                if (U.compareAndSwapObject(a, i, t, null)) {
+                                    U.putOrderedInt(v, QBASE, b + 1);
+                                    ForkJoinTask<?> ps = joiner.currentSteal;
+                                    int jt = joiner.top;
+                                    do {
+                                        joiner.currentSteal = t;
+                                        t.doExec(); // clear local tasks too
+                                    } while (task.status >= 0 &&
+                                             joiner.top != jt &&
+                                             (t = joiner.pop()) != null);
+                                    joiner.currentSteal = ps;
+                                    break restart;
+                                }
+                            }
+                        }
+                        else {                      // empty -- try to descend
+                            ForkJoinTask<?> next = v.currentJoin;
+                            if (subtask.status < 0 || j.currentJoin != subtask ||
+                                v.currentSteal != subtask)
+                                continue restart;   // stale
+                            else if (next == null || ++steps == MAX_HELP)
+                                break restart;      // dead-end or maybe cyclic
+                            else {
+                                subtask = next;
+                                j = v;
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return stat;
+    }
+
+    /**
+     * Analog of tryHelpStealer for CountedCompleters. Tries to steal
+     * and run tasks within the target's computation.
+     *
+     * @param task the task to join
+     */
+    private int helpComplete(WorkQueue joiner, CountedCompleter<?> task) {
+        WorkQueue[] ws; int m;
+        int s = 0;
+        if ((ws = workQueues) != null && (m = ws.length - 1) >= 0 &&
+            joiner != null && task != null) {
+            int j = joiner.poolIndex;
+            int scans = m + m + 1;
+            long c = 0L;              // for stability check
+            for (int k = scans; ; j += 2) {
+                WorkQueue q;
+                if ((s = task.status) < 0)
+                    break;
+                else if (joiner.internalPopAndExecCC(task))
+                    k = scans;
+                else if ((s = task.status) < 0)
+                    break;
+                else if ((q = ws[j & m]) != null && q.pollAndExecCC(task))
+                    k = scans;
+                else if (--k < 0) {
+                    if (c == (c = ctl))
+                        break;
+                    k = scans;
+                }
+            }
+        }
+        return s;
+    }
+
+    /**
+     * Tries to decrement active count (sometimes implicitly) and
+     * possibly release or create a compensating worker in preparation
+     * for blocking. Fails on contention or termination. Otherwise,
+     * adds a new thread if no idle workers are available and pool
+     * may become starved.
+     *
+     * @param c the assumed ctl value
+     */
+    final boolean tryCompensate(long c) {
+        WorkQueue[] ws = workQueues;
+        int pc = parallelism, e = (int)c, m, tc;
+        if (ws != null && (m = ws.length - 1) >= 0 && e >= 0 && ctl == c) {
+            WorkQueue w = ws[e & m];
+            if (e != 0 && w != null) {
+                Thread p;
+                long nc = ((long)(w.nextWait & E_MASK) |
+                           (c & (AC_MASK|TC_MASK)));
+                int ne = (e + E_SEQ) & E_MASK;
+                if (w.eventCount == (e | INT_SIGN) &&
+                    U.compareAndSwapLong(this, CTL, c, nc)) {
+                    w.eventCount = ne;
+                    if ((p = w.parker) != null)
+                        U.unpark(p);
+                    return true;   // replace with idle worker
+                }
+            }
+            else if ((tc = (short)(c >>> TC_SHIFT)) >= 0 &&
+                     (int)(c >> AC_SHIFT) + pc > 1) {
+                long nc = ((c - AC_UNIT) & AC_MASK) | (c & ~AC_MASK);
+                if (U.compareAndSwapLong(this, CTL, c, nc))
+                    return true;   // no compensation
+            }
+            else if (tc + pc < MAX_CAP) {
+                long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK);
+                if (U.compareAndSwapLong(this, CTL, c, nc)) {
+                    ForkJoinWorkerThreadFactory fac;
+                    Throwable ex = null;
+                    ForkJoinWorkerThread wt = null;
+                    try {
+                        if ((fac = factory) != null &&
+                            (wt = fac.newThread(this)) != null) {
+                            wt.start();
+                            return true;
+                        }
+                    } catch (Throwable rex) {
+                        ex = rex;
+                    }
+                    deregisterWorker(wt, ex); // clean up and return false
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Helps and/or blocks until the given task is done.
+     *
+     * @param joiner the joining worker
+     * @param task the task
+     * @return task status on exit
+     */
+    final int awaitJoin(WorkQueue joiner, ForkJoinTask<?> task) {
+        int s = 0;
+        if (task != null && (s = task.status) >= 0 && joiner != null) {
+            ForkJoinTask<?> prevJoin = joiner.currentJoin;
+            joiner.currentJoin = task;
+            do {} while (joiner.tryRemoveAndExec(task) && // process local tasks
+                         (s = task.status) >= 0);
+            if (s >= 0 && (task instanceof CountedCompleter))
+                s = helpComplete(joiner, (CountedCompleter<?>)task);
+            long cc = 0;        // for stability checks
+            while (s >= 0 && (s = task.status) >= 0) {
+                if ((s = tryHelpStealer(joiner, task)) == 0 &&
+                    (s = task.status) >= 0) {
+                    if (!tryCompensate(cc))
+                        cc = ctl;
+                    else {
+                        if (task.trySetSignal() && (s = task.status) >= 0) {
+                            synchronized (task) {
+                                if (task.status >= 0) {
+                                    try {                // see ForkJoinTask
+                                        task.wait();     //  for explanation
+                                    } catch (InterruptedException ie) {
+                                    }
+                                }
+                                else
+                                    task.notifyAll();
+                            }
+                        }
+                        long c; // reactivate
+                        do {} while (!U.compareAndSwapLong
+                                     (this, CTL, c = ctl,
+                                      ((c & ~AC_MASK) |
+                                       ((c & AC_MASK) + AC_UNIT))));
+                    }
+                }
+            }
+            joiner.currentJoin = prevJoin;
+        }
+        return s;
+    }
+
+    /**
+     * Stripped-down variant of awaitJoin used by timed joins. Tries
+     * to help join only while there is continuous progress. (Caller
+     * will then enter a timed wait.)
+     *
+     * @param joiner the joining worker
+     * @param task the task
+     */
+    final void helpJoinOnce(WorkQueue joiner, ForkJoinTask<?> task) {
+        int s;
+        if (joiner != null && task != null && (s = task.status) >= 0) {
+            ForkJoinTask<?> prevJoin = joiner.currentJoin;
+            joiner.currentJoin = task;
+            do {} while (joiner.tryRemoveAndExec(task) && // process local tasks
+                         (s = task.status) >= 0);
+            if (s >= 0) {
+                if (task instanceof CountedCompleter)
+                    helpComplete(joiner, (CountedCompleter<?>)task);
+                do {} while (task.status >= 0 &&
+                             tryHelpStealer(joiner, task) > 0);
+            }
+            joiner.currentJoin = prevJoin;
+        }
+    }
+
+    /**
+     * Returns a (probably) non-empty steal queue, if one is found
+     * during a scan, else null.  This method must be retried by
+     * caller if, by the time it tries to use the queue, it is empty.
+     */
+    private WorkQueue findNonEmptyStealQueue() {
+        int r = ThreadLocalRandom.current().nextInt();
+        for (;;) {
+            int ps = plock, m; WorkQueue[] ws; WorkQueue q;
+            if ((ws = workQueues) != null && (m = ws.length - 1) >= 0) {
+                for (int j = (m + 1) << 2; j >= 0; --j) {
+                    if ((q = ws[(((r - j) << 1) | 1) & m]) != null &&
+                        q.base - q.top < 0)
+                        return q;
+                }
+            }
+            if (plock == ps)
+                return null;
+        }
+    }
+
+    /**
+     * Runs tasks until {@code isQuiescent()}. We piggyback on
+     * active count ctl maintenance, but rather than blocking
+     * when tasks cannot be found, we rescan until all others cannot
+     * find tasks either.
+     */
+    final void helpQuiescePool(WorkQueue w) {
+        ForkJoinTask<?> ps = w.currentSteal;
+        for (boolean active = true;;) {
+            long c; WorkQueue q; ForkJoinTask<?> t; int b;
+            while ((t = w.nextLocalTask()) != null)
+                t.doExec();
+            if ((q = findNonEmptyStealQueue()) != null) {
+                if (!active) {      // re-establish active count
+                    active = true;
+                    do {} while (!U.compareAndSwapLong
+                                 (this, CTL, c = ctl,
+                                  ((c & ~AC_MASK) |
+                                   ((c & AC_MASK) + AC_UNIT))));
+                }
+                if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null) {
+                    (w.currentSteal = t).doExec();
+                    w.currentSteal = ps;
+                }
+            }
+            else if (active) {      // decrement active count without queuing
+                long nc = ((c = ctl) & ~AC_MASK) | ((c & AC_MASK) - AC_UNIT);
+                if ((int)(nc >> AC_SHIFT) + parallelism == 0)
+                    break;          // bypass decrement-then-increment
+                if (U.compareAndSwapLong(this, CTL, c, nc))
+                    active = false;
+            }
+            else if ((int)((c = ctl) >> AC_SHIFT) + parallelism <= 0 &&
+                     U.compareAndSwapLong
+                     (this, CTL, c, ((c & ~AC_MASK) |
+                                     ((c & AC_MASK) + AC_UNIT))))
+                break;
+        }
+    }
+
+    /**
+     * Gets and removes a local or stolen task for the given worker.
+     *
+     * @return a task, if available
+     */
+    final ForkJoinTask<?> nextTaskFor(WorkQueue w) {
+        for (ForkJoinTask<?> t;;) {
+            WorkQueue q; int b;
+            if ((t = w.nextLocalTask()) != null)
+                return t;
+            if ((q = findNonEmptyStealQueue()) == null)
+                return null;
+            if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null)
+                return t;
+        }
+    }
+
+    /**
+     * Returns a cheap heuristic guide for task partitioning when
+     * programmers, frameworks, tools, or languages have little or no
+     * idea about task granularity.  In essence by offering this
+     * method, we ask users only about tradeoffs in overhead vs
+     * expected throughput and its variance, rather than how finely to
+     * partition tasks.
+     *
+     * In a steady state strict (tree-structured) computation, each
+     * thread makes available for stealing enough tasks for other
+     * threads to remain active. Inductively, if all threads play by
+     * the same rules, each thread should make available only a
+     * constant number of tasks.
+     *
+     * The minimum useful constant is just 1. But using a value of 1
+     * would require immediate replenishment upon each steal to
+     * maintain enough tasks, which is infeasible.  Further,
+     * partitionings/granularities of offered tasks should minimize
+     * steal rates, which in general means that threads nearer the top
+     * of computation tree should generate more than those nearer the
+     * bottom. In perfect steady state, each thread is at
+     * approximately the same level of computation tree. However,
+     * producing extra tasks amortizes the uncertainty of progress and
+     * diffusion assumptions.
+     *
+     * So, users will want to use values larger (but not much larger)
+     * than 1 to both smooth over transient shortages and hedge
+     * against uneven progress; as traded off against the cost of
+     * extra task overhead. We leave the user to pick a threshold
+     * value to compare with the results of this call to guide
+     * decisions, but recommend values such as 3.
+     *
+     * When all threads are active, it is on average OK to estimate
+     * surplus strictly locally. In steady-state, if one thread is
+     * maintaining say 2 surplus tasks, then so are others. So we can
+     * just use estimated queue length.  However, this strategy alone
+     * leads to serious mis-estimates in some non-steady-state
+     * conditions (ramp-up, ramp-down, other stalls). We can detect
+     * many of these by further considering the number of "idle"
+     * threads, that are known to have zero queued tasks, so
+     * compensate by a factor of (#idle/#active) threads.
+     *
+     * Note: The approximation of #busy workers as #active workers is
+     * not very good under current signalling scheme, and should be
+     * improved.
+     */
+    static int getSurplusQueuedTaskCount() {
+        Thread t; ForkJoinWorkerThread wt; ForkJoinPool pool; WorkQueue q;
+        if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)) {
+            int p = (pool = (wt = (ForkJoinWorkerThread)t).pool).parallelism;
+            int n = (q = wt.workQueue).top - q.base;
+            int a = (int)(pool.ctl >> AC_SHIFT) + p;
+            return n - (a > (p >>>= 1) ? 0 :
+                        a > (p >>>= 1) ? 1 :
+                        a > (p >>>= 1) ? 2 :
+                        a > (p >>>= 1) ? 4 :
+                        8);
+        }
+        return 0;
+    }
+
+    //  Termination
+
+    /**
+     * Possibly initiates and/or completes termination.  The caller
+     * triggering termination runs three passes through workQueues:
+     * (0) Setting termination status, followed by wakeups of queued
+     * workers; (1) cancelling all tasks; (2) interrupting lagging
+     * threads (likely in external tasks, but possibly also blocked in
+     * joins).  Each pass repeats previous steps because of potential
+     * lagging thread creation.
+     *
+     * @param now if true, unconditionally terminate, else only
+     * if no work and no active workers
+     * @param enable if true, enable shutdown when next possible
+     * @return true if now terminating or terminated
+     */
+    private boolean tryTerminate(boolean now, boolean enable) {
+        int ps;
+        if (this == common)                        // cannot shut down
+            return false;
+        if ((ps = plock) >= 0) {                   // enable by setting plock
+            if (!enable)
+                return false;
+            if ((ps & PL_LOCK) != 0 ||
+                !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+                ps = acquirePlock();
+            int nps = ((ps + PL_LOCK) & ~SHUTDOWN) | SHUTDOWN;
+            if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+                releasePlock(nps);
+        }
+        for (long c;;) {
+            if (((c = ctl) & STOP_BIT) != 0) {     // already terminating
+                if ((short)(c >>> TC_SHIFT) + parallelism <= 0) {
+                    synchronized (this) {
+                        notifyAll();               // signal when 0 workers
+                    }
+                }
+                return true;
+            }
+            if (!now) {                            // check if idle & no tasks
+                WorkQueue[] ws; WorkQueue w;
+                if ((int)(c >> AC_SHIFT) + parallelism > 0)
+                    return false;
+                if ((ws = workQueues) != null) {
+                    for (int i = 0; i < ws.length; ++i) {
+                        if ((w = ws[i]) != null &&
+                            (!w.isEmpty() ||
+                             ((i & 1) != 0 && w.eventCount >= 0))) {
+                            signalWork(ws, w);
+                            return false;
+                        }
+                    }
+                }
+            }
+            if (U.compareAndSwapLong(this, CTL, c, c | STOP_BIT)) {
+                for (int pass = 0; pass < 3; ++pass) {
+                    WorkQueue[] ws; WorkQueue w; Thread wt;
+                    if ((ws = workQueues) != null) {
+                        int n = ws.length;
+                        for (int i = 0; i < n; ++i) {
+                            if ((w = ws[i]) != null) {
+                                w.qlock = -1;
+                                if (pass > 0) {
+                                    w.cancelAll();
+                                    if (pass > 1 && (wt = w.owner) != null) {
+                                        if (!wt.isInterrupted()) {
+                                            try {
+                                                wt.interrupt();
+                                            } catch (Throwable ignore) {
+                                            }
+                                        }
+                                        U.unpark(wt);
+                                    }
+                                }
+                            }
+                        }
+                        // Wake up workers parked on event queue
+                        int i, e; long cc; Thread p;
+                        while ((e = (int)(cc = ctl) & E_MASK) != 0 &&
+                               (i = e & SMASK) < n && i >= 0 &&
+                               (w = ws[i]) != null) {
+                            long nc = ((long)(w.nextWait & E_MASK) |
+                                       ((cc + AC_UNIT) & AC_MASK) |
+                                       (cc & (TC_MASK|STOP_BIT)));
+                            if (w.eventCount == (e | INT_SIGN) &&
+                                U.compareAndSwapLong(this, CTL, cc, nc)) {
+                                w.eventCount = (e + E_SEQ) & E_MASK;
+                                w.qlock = -1;
+                                if ((p = w.parker) != null)
+                                    U.unpark(p);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // external operations on common pool
+
+    /**
+     * Returns common pool queue for a thread that has submitted at
+     * least one task.
+     */
+    static WorkQueue commonSubmitterQueue() {
+        Submitter z; ForkJoinPool p; WorkQueue[] ws; int m, r;
+        return ((z = submitters.get()) != null &&
+                (p = common) != null &&
+                (ws = p.workQueues) != null &&
+                (m = ws.length - 1) >= 0) ?
+            ws[m & z.seed & SQMASK] : null;
+    }
+
+    /**
+     * Tries to pop the given task from submitter's queue in common pool.
+     */
+    final boolean tryExternalUnpush(ForkJoinTask<?> task) {
+        WorkQueue joiner; ForkJoinTask<?>[] a; int m, s;
+        Submitter z = submitters.get();
+        WorkQueue[] ws = workQueues;
+        boolean popped = false;
+        if (z != null && ws != null && (m = ws.length - 1) >= 0 &&
+            (joiner = ws[z.seed & m & SQMASK]) != null &&
+            joiner.base != (s = joiner.top) &&
+            (a = joiner.array) != null) {
+            long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
+            if (U.getObject(a, j) == task &&
+                U.compareAndSwapInt(joiner, QLOCK, 0, 1)) {
+                if (joiner.top == s && joiner.array == a &&
+                    U.compareAndSwapObject(a, j, task, null)) {
+                    joiner.top = s - 1;
+                    popped = true;
+                }
+                joiner.qlock = 0;
+            }
+        }
+        return popped;
+    }
+
+    final int externalHelpComplete(CountedCompleter<?> task) {
+        WorkQueue joiner; int m, j;
+        Submitter z = submitters.get();
+        WorkQueue[] ws = workQueues;
+        int s = 0;
+        if (z != null && ws != null && (m = ws.length - 1) >= 0 &&
+            (joiner = ws[(j = z.seed) & m & SQMASK]) != null && task != null) {
+            int scans = m + m + 1;
+            long c = 0L;             // for stability check
+            j |= 1;                  // poll odd queues
+            for (int k = scans; ; j += 2) {
+                WorkQueue q;
+                if ((s = task.status) < 0)
+                    break;
+                else if (joiner.externalPopAndExecCC(task))
+                    k = scans;
+                else if ((s = task.status) < 0)
+                    break;
+                else if ((q = ws[j & m]) != null && q.pollAndExecCC(task))
+                    k = scans;
+                else if (--k < 0) {
+                    if (c == (c = ctl))
+                        break;
+                    k = scans;
+                }
+            }
+        }
+        return s;
+    }
+
+    // Exported methods
+
+    // Constructors
+
+    /**
+     * Creates a {@code ForkJoinPool} with parallelism equal to {@link
+     * Runtime#availableProcessors}, using the {@linkplain
+     * #defaultForkJoinWorkerThreadFactory default thread factory},
+     * no UncaughtExceptionHandler, and non-async LIFO processing mode.
+     *
+     * @throws SecurityException if a security manager exists and
+     *         the caller is not permitted to modify threads
+     *         because it does not hold {@link
+     *         RuntimePermission}{@code ("modifyThread")}
+     */
+    public ForkJoinPool() {
+        this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
+             defaultForkJoinWorkerThreadFactory, null, false);
+    }
+
+    /**
+     * Creates a {@code ForkJoinPool} with the indicated parallelism
+     * level, the {@linkplain
+     * #defaultForkJoinWorkerThreadFactory default thread factory},
+     * no UncaughtExceptionHandler, and non-async LIFO processing mode.
+     *
+     * @param parallelism the parallelism level
+     * @throws IllegalArgumentException if parallelism less than or
+     *         equal to zero, or greater than implementation limit
+     * @throws SecurityException if a security manager exists and
+     *         the caller is not permitted to modify threads
+     *         because it does not hold {@link
+     *         RuntimePermission}{@code ("modifyThread")}
+     */
+    public ForkJoinPool(int parallelism) {
+        this(parallelism, defaultForkJoinWorkerThreadFactory, null, false);
+    }
+
+    /**
+     * Creates a {@code ForkJoinPool} with the given parameters.
+     *
+     * @param parallelism the parallelism level. For default value,
+     * use {@link Runtime#availableProcessors}.
+     * @param factory the factory for creating new threads. For default value,
+     * use {@link #defaultForkJoinWorkerThreadFactory}.
+     * @param handler the handler for internal worker threads that
+     * terminate due to unrecoverable errors encountered while executing
+     * tasks. For default value, use {@code null}.
+     * @param asyncMode if true,
+     * establishes local first-in-first-out scheduling mode for forked
+     * tasks that are never joined. This mode may be more appropriate
+     * than default locally stack-based mode in applications in which
+     * worker threads only process event-style asynchronous tasks.
+     * For default value, use {@code false}.
+     * @throws IllegalArgumentException if parallelism less than or
+     *         equal to zero, or greater than implementation limit
+     * @throws NullPointerException if the factory is null
+     * @throws SecurityException if a security manager exists and
+     *         the caller is not permitted to modify threads
+     *         because it does not hold {@link
+     *         RuntimePermission}{@code ("modifyThread")}
+     */
+    public ForkJoinPool(int parallelism,
+                        ForkJoinWorkerThreadFactory factory,
+                        UncaughtExceptionHandler handler,
+                        boolean asyncMode) {
+        this(checkParallelism(parallelism),
+             checkFactory(factory),
+             handler,
+             (asyncMode ? FIFO_QUEUE : LIFO_QUEUE),
+             "ForkJoinPool-" + nextPoolId() + "-worker-");
+        checkPermission();
+    }
+
+    private static int checkParallelism(int parallelism) {
+        if (parallelism <= 0 || parallelism > MAX_CAP)
+            throw new IllegalArgumentException();
+        return parallelism;
+    }
+
+    private static ForkJoinWorkerThreadFactory checkFactory
+        (ForkJoinWorkerThreadFactory factory) {
+        if (factory == null)
+            throw new NullPointerException();
+        return factory;
+    }
+
+    /**
+     * Creates a {@code ForkJoinPool} with the given parameters, without
+     * any security checks or parameter validation.  Invoked directly by
+     * makeCommonPool.
+     */
+    private ForkJoinPool(int parallelism,
+                         ForkJoinWorkerThreadFactory factory,
+                         UncaughtExceptionHandler handler,
+                         int mode,
+                         String workerNamePrefix) {
+        this.workerNamePrefix = workerNamePrefix;
+        this.factory = factory;
+        this.ueh = handler;
+        this.mode = (short)mode;
+        this.parallelism = (short)parallelism;
+        long np = (long)(-parallelism); // offset ctl counts
+        this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
+    }
+
+    /**
+     * Returns the common pool instance. This pool is statically
+     * constructed; its run state is unaffected by attempts to {@link
+     * #shutdown} or {@link #shutdownNow}. However this pool and any
+     * ongoing processing are automatically terminated upon program
+     * {@link System#exit}.  Any program that relies on asynchronous
+     * task processing to complete before program termination should
+     * invoke {@code commonPool().}{@link #awaitQuiescence awaitQuiescence},
+     * before exit.
+     *
+     * @return the common pool instance
+     * @since 1.8
+     */
+    public static ForkJoinPool commonPool() {
+        // assert common != null : "static init error";
+        return common;
+    }
+
+    // Execution methods
+
+    /**
+     * Performs the given task, returning its result upon completion.
+     * If the computation encounters an unchecked Exception or Error,
+     * it is rethrown as the outcome of this invocation.  Rethrown
+     * exceptions behave in the same way as regular exceptions, but,
+     * when possible, contain stack traces (as displayed for example
+     * using {@code ex.printStackTrace()}) of both the current thread
+     * as well as the thread actually encountering the exception;
+     * minimally only the latter.
+     *
+     * @param task the task
+     * @param <T> the type of the task's result
+     * @return the task's result
+     * @throws NullPointerException if the task is null
+     * @throws RejectedExecutionException if the task cannot be
+     *         scheduled for execution
+     */
+    public <T> T invoke(ForkJoinTask<T> task) {
+        if (task == null)
+            throw new NullPointerException();
+        externalPush(task);
+        return task.join();
+    }
+
+    /**
+     * Arranges for (asynchronous) execution of the given task.
+     *
+     * @param task the task
+     * @throws NullPointerException if the task is null
+     * @throws RejectedExecutionException if the task cannot be
+     *         scheduled for execution
+     */
+    public void execute(ForkJoinTask<?> task) {
+        if (task == null)
+            throw new NullPointerException();
+        externalPush(task);
+    }
+
+    // AbstractExecutorService methods
+
+    /**
+     * @throws NullPointerException if the task is null
+     * @throws RejectedExecutionException if the task cannot be
+     *         scheduled for execution
+     */
+    public void execute(Runnable task) {
+        if (task == null)
+            throw new NullPointerException();
+        ForkJoinTask<?> job;
+        if (task instanceof ForkJoinTask<?>) // avoid re-wrap
+            job = (ForkJoinTask<?>) task;
+        else
+            job = new ForkJoinTask.RunnableExecuteAction(task);
+        externalPush(job);
+    }
+
+    /**
+     * Submits a ForkJoinTask for execution.
+     *
+     * @param task the task to submit
+     * @param <T> the type of the task's result
+     * @return the task
+     * @throws NullPointerException if the task is null
+     * @throws RejectedExecutionException if the task cannot be
+     *         scheduled for execution
+     */
+    public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
+        if (task == null)
+            throw new NullPointerException();
+        externalPush(task);
+        return task;
+    }
+
+    /**
+     * @throws NullPointerException if the task is null
+     * @throws RejectedExecutionException if the task cannot be
+     *         scheduled for execution
+     */
+    public <T> ForkJoinTask<T> submit(Callable<T> task) {
+        ForkJoinTask<T> job = new ForkJoinTask.AdaptedCallable<T>(task);
+        externalPush(job);
+        return job;
+    }
+
+    /**
+     * @throws NullPointerException if the task is null
+     * @throws RejectedExecutionException if the task cannot be
+     *         scheduled for execution
+     */
+    public <T> ForkJoinTask<T> submit(Runnable task, T result) {
+        ForkJoinTask<T> job = new ForkJoinTask.AdaptedRunnable<T>(task, result);
+        externalPush(job);
+        return job;
+    }
+
+    /**
+     * @throws NullPointerException if the task is null
+     * @throws RejectedExecutionException if the task cannot be
+     *         scheduled for execution
+     */
+    public ForkJoinTask<?> submit(Runnable task) {
+        if (task == null)
+            throw new NullPointerException();
+        ForkJoinTask<?> job;
+        if (task instanceof ForkJoinTask<?>) // avoid re-wrap
+            job = (ForkJoinTask<?>) task;
+        else
+            job = new ForkJoinTask.AdaptedRunnableAction(task);
+        externalPush(job);
+        return job;
+    }
+
+    /**
+     * @throws NullPointerException       {@inheritDoc}
+     * @throws RejectedExecutionException {@inheritDoc}
+     */
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
+        // In previous versions of this class, this method constructed
+        // a task to run ForkJoinTask.invokeAll, but now external
+        // invocation of multiple tasks is at least as efficient.
+        ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
+
+        boolean done = false;
+        try {
+            for (Callable<T> t : tasks) {
+                ForkJoinTask<T> f = new ForkJoinTask.AdaptedCallable<T>(t);
+                futures.add(f);
+                externalPush(f);
+            }
+            for (int i = 0, size = futures.size(); i < size; i++)
+                ((ForkJoinTask<?>)futures.get(i)).quietlyJoin();
+            done = true;
+            return futures;
+        } finally {
+            if (!done)
+                for (int i = 0, size = futures.size(); i < size; i++)
+                    futures.get(i).cancel(false);
+        }
+    }
+
+    /**
+     * Returns the factory used for constructing new workers.
+     *
+     * @return the factory used for constructing new workers
+     */
+    public ForkJoinWorkerThreadFactory getFactory() {
+        return factory;
+    }
+
+    /**
+     * Returns the handler for internal worker threads that terminate
+     * due to unrecoverable errors encountered while executing tasks.
+     *
+     * @return the handler, or {@code null} if none
+     */
+    public UncaughtExceptionHandler getUncaughtExceptionHandler() {
+        return ueh;
+    }
+
+    /**
+     * Returns the targeted parallelism level of this pool.
+     *
+     * @return the targeted parallelism level of this pool
+     */
+    public int getParallelism() {
+        int par;
+        return ((par = parallelism) > 0) ? par : 1;
+    }
+
+    /**
+     * Returns the targeted parallelism level of the common pool.
+     *
+     * @return the targeted parallelism level of the common pool
+     * @since 1.8
+     */
+    public static int getCommonPoolParallelism() {
+        return commonParallelism;
+    }
+
+    /**
+     * Returns the number of worker threads that have started but not
+     * yet terminated.  The result returned by this method may differ
+     * from {@link #getParallelism} when threads are created to
+     * maintain parallelism when others are cooperatively blocked.
+     *
+     * @return the number of worker threads
+     */
+    public int getPoolSize() {
+        return parallelism + (short)(ctl >>> TC_SHIFT);
+    }
+
+    /**
+     * Returns {@code true} if this pool uses local first-in-first-out
+     * scheduling mode for forked tasks that are never joined.
+     *
+     * @return {@code true} if this pool uses async mode
+     */
+    public boolean getAsyncMode() {
+        return mode == FIFO_QUEUE;
+    }
+
+    /**
+     * Returns an estimate of the number of worker threads that are
+     * not blocked waiting to join tasks or for other managed
+     * synchronization. This method may overestimate the
+     * number of running threads.
+     *
+     * @return the number of worker threads
+     */
+    public int getRunningThreadCount() {
+        int rc = 0;
+        WorkQueue[] ws; WorkQueue w;
+        if ((ws = workQueues) != null) {
+            for (int i = 1; i < ws.length; i += 2) {
+                if ((w = ws[i]) != null && w.isApparentlyUnblocked())
+                    ++rc;
+            }
+        }
+        return rc;
+    }
+
+    /**
+     * Returns an estimate of the number of threads that are currently
+     * stealing or executing tasks. This method may overestimate the
+     * number of active threads.
+     *
+     * @return the number of active threads
+     */
+    public int getActiveThreadCount() {
+        int r = parallelism + (int)(ctl >> AC_SHIFT);
+        return (r <= 0) ? 0 : r; // suppress momentarily negative values
+    }
+
+    /**
+     * Returns {@code true} if all worker threads are currently idle.
+     * An idle worker is one that cannot obtain a task to execute
+     * because none are available to steal from other threads, and
+     * there are no pending submissions to the pool. This method is
+     * conservative; it might not return {@code true} immediately upon
+     * idleness of all threads, but will eventually become true if
+     * threads remain inactive.
+     *
+     * @return {@code true} if all threads are currently idle
+     */
+    public boolean isQuiescent() {
+        return parallelism + (int)(ctl >> AC_SHIFT) <= 0;
+    }
+
+    /**
+     * Returns an estimate of the total number of tasks stolen from
+     * one thread's work queue by another. The reported value
+     * underestimates the actual total number of steals when the pool
+     * is not quiescent. This value may be useful for monitoring and
+     * tuning fork/join programs: in general, steal counts should be
+     * high enough to keep threads busy, but low enough to avoid
+     * overhead and contention across threads.
+     *
+     * @return the number of steals
+     */
+    public long getStealCount() {
+        long count = stealCount;
+        WorkQueue[] ws; WorkQueue w;
+        if ((ws = workQueues) != null) {
+            for (int i = 1; i < ws.length; i += 2) {
+                if ((w = ws[i]) != null)
+                    count += w.nsteals;
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Returns an estimate of the total number of tasks currently held
+     * in queues by worker threads (but not including tasks submitted
+     * to the pool that have not begun executing). This value is only
+     * an approximation, obtained by iterating across all threads in
+     * the pool. This method may be useful for tuning task
+     * granularities.
+     *
+     * @return the number of queued tasks
+     */
+    public long getQueuedTaskCount() {
+        long count = 0;
+        WorkQueue[] ws; WorkQueue w;
+        if ((ws = workQueues) != null) {
+            for (int i = 1; i < ws.length; i += 2) {
+                if ((w = ws[i]) != null)
+                    count += w.queueSize();
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Returns an estimate of the number of tasks submitted to this
+     * pool that have not yet begun executing.  This method may take
+     * time proportional to the number of submissions.
+     *
+     * @return the number of queued submissions
+     */
+    public int getQueuedSubmissionCount() {
+        int count = 0;
+        WorkQueue[] ws; WorkQueue w;
+        if ((ws = workQueues) != null) {
+            for (int i = 0; i < ws.length; i += 2) {
+                if ((w = ws[i]) != null)
+                    count += w.queueSize();
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Returns {@code true} if there are any tasks submitted to this
+     * pool that have not yet begun executing.
+     *
+     * @return {@code true} if there are any queued submissions
+     */
+    public boolean hasQueuedSubmissions() {
+        WorkQueue[] ws; WorkQueue w;
+        if ((ws = workQueues) != null) {
+            for (int i = 0; i < ws.length; i += 2) {
+                if ((w = ws[i]) != null && !w.isEmpty())
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Removes and returns the next unexecuted submission if one is
+     * available.  This method may be useful in extensions to this
+     * class that re-assign work in systems with multiple pools.
+     *
+     * @return the next submission, or {@code null} if none
+     */
+    protected ForkJoinTask<?> pollSubmission() {
+        WorkQueue[] ws; WorkQueue w; ForkJoinTask<?> t;
+        if ((ws = workQueues) != null) {
+            for (int i = 0; i < ws.length; i += 2) {
+                if ((w = ws[i]) != null && (t = w.poll()) != null)
+                    return t;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Removes all available unexecuted submitted and forked tasks
+     * from scheduling queues and adds them to the given collection,
+     * without altering their execution status. These may include
+     * artificially generated or wrapped tasks. This method is
+     * designed to be invoked only when the pool is known to be
+     * quiescent. Invocations at other times may not remove all
+     * tasks. A failure encountered while attempting to add elements
+     * to collection {@code c} may result in elements being in
+     * neither, either or both collections when the associated
+     * exception is thrown.  The behavior of this operation is
+     * undefined if the specified collection is modified while the
+     * operation is in progress.
+     *
+     * @param c the collection to transfer elements into
+     * @return the number of elements transferred
+     */
+    protected int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
+        int count = 0;
+        WorkQueue[] ws; WorkQueue w; ForkJoinTask<?> t;
+        if ((ws = workQueues) != null) {
+            for (int i = 0; i < ws.length; ++i) {
+                if ((w = ws[i]) != null) {
+                    while ((t = w.poll()) != null) {
+                        c.add(t);
+                        ++count;
+                    }
+                }
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Returns a string identifying this pool, as well as its state,
+     * including indications of run state, parallelism level, and
+     * worker and task counts.
+     *
+     * @return a string identifying this pool, as well as its state
+     */
+    public String toString() {
+        // Use a single pass through workQueues to collect counts
+        long qt = 0L, qs = 0L; int rc = 0;
+        long st = stealCount;
+        long c = ctl;
+        WorkQueue[] ws; WorkQueue w;
+        if ((ws = workQueues) != null) {
+            for (int i = 0; i < ws.length; ++i) {
+                if ((w = ws[i]) != null) {
+                    int size = w.queueSize();
+                    if ((i & 1) == 0)
+                        qs += size;
+                    else {
+                        qt += size;
+                        st += w.nsteals;
+                        if (w.isApparentlyUnblocked())
+                            ++rc;
+                    }
+                }
+            }
+        }
+        int pc = parallelism;
+        int tc = pc + (short)(c >>> TC_SHIFT);
+        int ac = pc + (int)(c >> AC_SHIFT);
+        if (ac < 0) // ignore transient negative
+            ac = 0;
+        String level;
+        if ((c & STOP_BIT) != 0)
+            level = (tc == 0) ? "Terminated" : "Terminating";
+        else
+            level = plock < 0 ? "Shutting down" : "Running";
+        return super.toString() +
+            "[" + level +
+            ", parallelism = " + pc +
+            ", size = " + tc +
+            ", active = " + ac +
+            ", running = " + rc +
+            ", steals = " + st +
+            ", tasks = " + qt +
+            ", submissions = " + qs +
+            "]";
+    }
+
+    /**
+     * Possibly initiates an orderly shutdown in which previously
+     * submitted tasks are executed, but no new tasks will be
+     * accepted. Invocation has no effect on execution state if this
+     * is the {@link #commonPool()}, and no additional effect if
+     * already shut down.  Tasks that are in the process of being
+     * submitted concurrently during the course of this method may or
+     * may not be rejected.
+     *
+     * @throws SecurityException if a security manager exists and
+     *         the caller is not permitted to modify threads
+     *         because it does not hold {@link
+     *         RuntimePermission}{@code ("modifyThread")}
+     */
+    public void shutdown() {
+        checkPermission();
+        tryTerminate(false, true);
+    }
+
+    /**
+     * Possibly attempts to cancel and/or stop all tasks, and reject
+     * all subsequently submitted tasks.  Invocation has no effect on
+     * execution state if this is the {@link #commonPool()}, and no
+     * additional effect if already shut down. Otherwise, tasks that
+     * are in the process of being submitted or executed concurrently
+     * during the course of this method may or may not be
+     * rejected. This method cancels both existing and unexecuted
+     * tasks, in order to permit termination in the presence of task
+     * dependencies. So the method always returns an empty list
+     * (unlike the case for some other Executors).
+     *
+     * @return an empty list
+     * @throws SecurityException if a security manager exists and
+     *         the caller is not permitted to modify threads
+     *         because it does not hold {@link
+     *         RuntimePermission}{@code ("modifyThread")}
+     */
+    public List<Runnable> shutdownNow() {
+        checkPermission();
+        tryTerminate(true, true);
+        return Collections.emptyList();
+    }
+
+    /**
+     * Returns {@code true} if all tasks have completed following shut down.
+     *
+     * @return {@code true} if all tasks have completed following shut down
+     */
+    public boolean isTerminated() {
+        long c = ctl;
+        return ((c & STOP_BIT) != 0L &&
+                (short)(c >>> TC_SHIFT) + parallelism <= 0);
+    }
+
+    /**
+     * Returns {@code true} if the process of termination has
+     * commenced but not yet completed.  This method may be useful for
+     * debugging. A return of {@code true} reported a sufficient
+     * period after shutdown may indicate that submitted tasks have
+     * ignored or suppressed interruption, or are waiting for I/O,
+     * causing this executor not to properly terminate. (See the
+     * advisory notes for class {@link ForkJoinTask} stating that
+     * tasks should not normally entail blocking operations.  But if
+     * they do, they must abort them on interrupt.)
+     *
+     * @return {@code true} if terminating but not yet terminated
+     */
+    public boolean isTerminating() {
+        long c = ctl;
+        return ((c & STOP_BIT) != 0L &&
+                (short)(c >>> TC_SHIFT) + parallelism > 0);
+    }
+
+    /**
+     * Returns {@code true} if this pool has been shut down.
+     *
+     * @return {@code true} if this pool has been shut down
+     */
+    public boolean isShutdown() {
+        return plock < 0;
+    }
+
+    /**
+     * Blocks until all tasks have completed execution after a
+     * shutdown request, or the timeout occurs, or the current thread
+     * is interrupted, whichever happens first. Because the {@link
+     * #commonPool()} never terminates until program shutdown, when
+     * applied to the common pool, this method is equivalent to {@link
+     * #awaitQuiescence(long, TimeUnit)} but always returns {@code false}.
+     *
+     * @param timeout the maximum time to wait
+     * @param unit the time unit of the timeout argument
+     * @return {@code true} if this executor terminated and
+     *         {@code false} if the timeout elapsed before termination
+     * @throws InterruptedException if interrupted while waiting
+     */
+    public boolean awaitTermination(long timeout, TimeUnit unit)
+        throws InterruptedException {
+        if (Thread.interrupted())
+            throw new InterruptedException();
+        if (this == common) {
+            awaitQuiescence(timeout, unit);
+            return false;
+        }
+        long nanos = unit.toNanos(timeout);
+        if (isTerminated())
+            return true;
+        if (nanos <= 0L)
+            return false;
+        long deadline = System.nanoTime() + nanos;
+        synchronized (this) {
+            for (;;) {
+                if (isTerminated())
+                    return true;
+                if (nanos <= 0L)
+                    return false;
+                long millis = TimeUnit.NANOSECONDS.toMillis(nanos);
+                wait(millis > 0L ? millis : 1L);
+                nanos = deadline - System.nanoTime();
+            }
+        }
+    }
+
+    /**
+     * If called by a ForkJoinTask operating in this pool, equivalent
+     * in effect to {@link ForkJoinTask#helpQuiesce}. Otherwise,
+     * waits and/or attempts to assist performing tasks until this
+     * pool {@link #isQuiescent} or the indicated timeout elapses.
+     *
+     * @param timeout the maximum time to wait
+     * @param unit the time unit of the timeout argument
+     * @return {@code true} if quiescent; {@code false} if the
+     * timeout elapsed.
+     */
+    public boolean awaitQuiescence(long timeout, TimeUnit unit) {
+        long nanos = unit.toNanos(timeout);
+        ForkJoinWorkerThread wt;
+        Thread thread = Thread.currentThread();
+        if ((thread instanceof ForkJoinWorkerThread) &&
+            (wt = (ForkJoinWorkerThread)thread).pool == this) {
+            helpQuiescePool(wt.workQueue);
+            return true;
+        }
+        long startTime = System.nanoTime();
+        WorkQueue[] ws;
+        int r = 0, m;
+        boolean found = true;
+        while (!isQuiescent() && (ws = workQueues) != null &&
+               (m = ws.length - 1) >= 0) {
+            if (!found) {
+                if ((System.nanoTime() - startTime) > nanos)
+                    return false;
+                Thread.yield(); // cannot block
+            }
+            found = false;
+            for (int j = (m + 1) << 2; j >= 0; --j) {
+                ForkJoinTask<?> t; WorkQueue q; int b;
+                if ((q = ws[r++ & m]) != null && (b = q.base) - q.top < 0) {
+                    found = true;
+                    if ((t = q.pollAt(b)) != null)
+                        t.doExec();
+                    break;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Waits and/or attempts to assist performing tasks indefinitely
+     * until the {@link #commonPool()} {@link #isQuiescent}.
+     */
+    static void quiesceCommonPool() {
+        common.awaitQuiescence(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
+    }
+
+    /**
+     * Interface for extending managed parallelism for tasks running
+     * in {@link ForkJoinPool}s.
+     *
+     * <p>A {@code ManagedBlocker} provides two methods.  Method
+     * {@code isReleasable} must return {@code true} if blocking is
+     * not necessary. Method {@code block} blocks the current thread
+     * if necessary (perhaps internally invoking {@code isReleasable}
+     * before actually blocking). These actions are performed by any
+     * thread invoking {@link ForkJoinPool#managedBlock(ManagedBlocker)}.
+     * The unusual methods in this API accommodate synchronizers that
+     * may, but don't usually, block for long periods. Similarly, they
+     * allow more efficient internal handling of cases in which
+     * additional workers may be, but usually are not, needed to
+     * ensure sufficient parallelism.  Toward this end,
+     * implementations of method {@code isReleasable} must be amenable
+     * to repeated invocation.
+     *
+     * <p>For example, here is a ManagedBlocker based on a
+     * ReentrantLock:
+     *  <pre> {@code
+     * class ManagedLocker implements ManagedBlocker {
+     *   final ReentrantLock lock;
+     *   boolean hasLock = false;
+     *   ManagedLocker(ReentrantLock lock) { this.lock = lock; }
+     *   public boolean block() {
+     *     if (!hasLock)
+     *       lock.lock();
+     *     return true;
+     *   }
+     *   public boolean isReleasable() {
+     *     return hasLock || (hasLock = lock.tryLock());
+     *   }
+     * }}</pre>
+     *
+     * <p>Here is a class that possibly blocks waiting for an
+     * item on a given queue:
+     *  <pre> {@code
+     * class QueueTaker<E> implements ManagedBlocker {
+     *   final BlockingQueue<E> queue;
+     *   volatile E item = null;
+     *   QueueTaker(BlockingQueue<E> q) { this.queue = q; }
+     *   public boolean block() throws InterruptedException {
+     *     if (item == null)
+     *       item = queue.take();
+     *     return true;
+     *   }
+     *   public boolean isReleasable() {
+     *     return item != null || (item = queue.poll()) != null;
+     *   }
+     *   public E getItem() { // call after pool.managedBlock completes
+     *     return item;
+     *   }
+     * }}</pre>
+     */
+    public static interface ManagedBlocker {
+        /**
+         * Possibly blocks the current thread, for example waiting for
+         * a lock or condition.
+         *
+         * @return {@code true} if no additional blocking is necessary
+         * (i.e., if isReleasable would return true)
+         * @throws InterruptedException if interrupted while waiting
+         * (the method is not required to do so, but is allowed to)
+         */
+        boolean block() throws InterruptedException;
+
+        /**
+         * Returns {@code true} if blocking is unnecessary.
+         * @return {@code true} if blocking is unnecessary
+         */
+        boolean isReleasable();
+    }
+
+    /**
+     * Blocks in accord with the given blocker.  If the current thread
+     * is a {@link ForkJoinWorkerThread}, this method possibly
+     * arranges for a spare thread to be activated if necessary to
+     * ensure sufficient parallelism while the current thread is blocked.
+     *
+     * <p>If the caller is not a {@link ForkJoinTask}, this method is
+     * behaviorally equivalent to
+     *  <pre> {@code
+     * while (!blocker.isReleasable())
+     *   if (blocker.block())
+     *     return;
+     * }</pre>
+     *
+     * If the caller is a {@code ForkJoinTask}, then the pool may
+     * first be expanded to ensure parallelism, and later adjusted.
+     *
+     * @param blocker the blocker
+     * @throws InterruptedException if blocker.block did so
+     */
+    public static void managedBlock(ManagedBlocker blocker)
+        throws InterruptedException {
+        Thread t = Thread.currentThread();
+        if (t instanceof ForkJoinWorkerThread) {
+            ForkJoinPool p = ((ForkJoinWorkerThread)t).pool;
+            while (!blocker.isReleasable()) {
+                if (p.tryCompensate(p.ctl)) {
+                    try {
+                        do {} while (!blocker.isReleasable() &&
+                                     !blocker.block());
+                    } finally {
+                        p.incrementActiveCount();
+                    }
+                    break;
+                }
+            }
+        }
+        else {
+            do {} while (!blocker.isReleasable() &&
+                         !blocker.block());
+        }
+    }
+
+    // AbstractExecutorService overrides.  These rely on undocumented
+    // fact that ForkJoinTask.adapt returns ForkJoinTasks that also
+    // implement RunnableFuture.
+
+    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
+        return new ForkJoinTask.AdaptedRunnable<T>(runnable, value);
+    }
+
+    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
+        return new ForkJoinTask.AdaptedCallable<T>(callable);
+    }
+
+    // Unsafe mechanics
+    private static final sun.misc.Unsafe U;
+    private static final long CTL;
+    private static final long PARKBLOCKER;
+    private static final int ABASE;
+    private static final int ASHIFT;
+    private static final long STEALCOUNT;
+    private static final long PLOCK;
+    private static final long INDEXSEED;
+    private static final long QBASE;
+    private static final long QLOCK;
+
+    static {
+        // initialize field offsets for CAS etc
+        try {
+            U = getUnsafe();
+            Class<?> k = ForkJoinPool.class;
+            CTL = U.objectFieldOffset
+                (k.getDeclaredField("ctl"));
+            STEALCOUNT = U.objectFieldOffset
+                (k.getDeclaredField("stealCount"));
+            PLOCK = U.objectFieldOffset
+                (k.getDeclaredField("plock"));
+            INDEXSEED = U.objectFieldOffset
+                (k.getDeclaredField("indexSeed"));
+            Class<?> tk = Thread.class;
+            PARKBLOCKER = U.objectFieldOffset
+                (tk.getDeclaredField("parkBlocker"));
+            Class<?> wk = WorkQueue.class;
+            QBASE = U.objectFieldOffset
+                (wk.getDeclaredField("base"));
+            QLOCK = U.objectFieldOffset
+                (wk.getDeclaredField("qlock"));
+            Class<?> ak = ForkJoinTask[].class;
+            ABASE = U.arrayBaseOffset(ak);
+            int scale = U.arrayIndexScale(ak);
+            if ((scale & (scale - 1)) != 0)
+                throw new Error("data type scale not a power of two");
+            ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+
+        submitters = new ThreadLocal<Submitter>();
+        defaultForkJoinWorkerThreadFactory =
+            new DefaultForkJoinWorkerThreadFactory();
+        modifyThreadPermission = new RuntimePermission("modifyThread");
+
+        common = java.security.AccessController.doPrivileged
+            (new java.security.PrivilegedAction<ForkJoinPool>() {
+                public ForkJoinPool run() { return makeCommonPool(); }});
+        int par = common.parallelism; // report 1 even if threads disabled
+        commonParallelism = par > 0 ? par : 1;
+    }
+
+    /**
+     * Creates and returns the common pool, respecting user settings
+     * specified via system properties.
+     */
+    private static ForkJoinPool makeCommonPool() {
+        int parallelism = -1;
+        ForkJoinWorkerThreadFactory factory
+            = defaultForkJoinWorkerThreadFactory;
+        UncaughtExceptionHandler handler = null;
+        try {  // ignore exceptions in accessing/parsing properties
+            String pp = System.getProperty
+                ("java.util.concurrent.ForkJoinPool.common.parallelism");
+            String fp = System.getProperty
+                ("java.util.concurrent.ForkJoinPool.common.threadFactory");
+            String hp = System.getProperty
+                ("java.util.concurrent.ForkJoinPool.common.exceptionHandler");
+            if (pp != null)
+                parallelism = Integer.parseInt(pp);
+            if (fp != null)
+                factory = ((ForkJoinWorkerThreadFactory)ClassLoader.
+                           getSystemClassLoader().loadClass(fp).newInstance());
+            if (hp != null)
+                handler = ((UncaughtExceptionHandler)ClassLoader.
+                           getSystemClassLoader().loadClass(hp).newInstance());
+        } catch (Exception ignore) {
+        }
+
+        if (parallelism < 0 && // default 1 less than #cores
+            (parallelism = Runtime.getRuntime().availableProcessors() - 1) < 0)
+            parallelism = 0;
+        if (parallelism > MAX_CAP)
+            parallelism = MAX_CAP;
+        return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE,
+                                "ForkJoinPool.commonPool-worker-");
+    }
+
+    /**
+     * Returns a sun.misc.Unsafe.  Suitable for use in a 3rd party package.
+     * Replace with a simple call to Unsafe.getUnsafe when integrating
+     * into a jdk.
+     *
+     * @return a sun.misc.Unsafe
+     */
+    private static sun.misc.Unsafe getUnsafe() {
+        try {
+            return sun.misc.Unsafe.getUnsafe();
+        } catch (SecurityException tryReflectionInstead) {}
+        try {
+            return java.security.AccessController.doPrivileged
+            (new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() {
+                public sun.misc.Unsafe run() throws Exception {
+                    Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
+                    for (java.lang.reflect.Field f : k.getDeclaredFields()) {
+                        f.setAccessible(true);
+                        Object x = f.get(null);
+                        if (k.isInstance(x))
+                            return k.cast(x);
+                    }
+                    throw new NoSuchFieldError("the Unsafe");
+                }});
+        } catch (java.security.PrivilegedActionException e) {
+            throw new RuntimeException("Could not initialize intrinsics",
+                                       e.getCause());
+        }
+    }
+}

+ 1557 - 0
src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/ForkJoinTask.java

@@ -0,0 +1,1557 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package com.jeeplus.common.utils.concurrent.jsr166e;
+
+import java.io.Serializable;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.util.Collection;
+import java.util.List;
+import java.util.RandomAccess;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.locks.ReentrantLock;
+
+
+/**
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ForkJoinTask.java 1.19
+ * 
+ * Abstract base class for tasks that run within a {@link ForkJoinPool}.
+ * A {@code ForkJoinTask} is a thread-like entity that is much
+ * lighter weight than a normal thread.  Huge numbers of tasks and
+ * subtasks may be hosted by a small number of actual threads in a
+ * ForkJoinPool, at the price of some usage limitations.
+ *
+ * <p>A "main" {@code ForkJoinTask} begins execution when it is
+ * explicitly submitted to a {@link ForkJoinPool}, or, if not already
+ * engaged in a ForkJoin computation, commenced in the {@link
+ * ForkJoinPool#commonPool()} via {@link #fork}, {@link #invoke}, or
+ * related methods.  Once started, it will usually in turn start other
+ * subtasks.  As indicated by the name of this class, many programs
+ * using {@code ForkJoinTask} employ only methods {@link #fork} and
+ * {@link #join}, or derivatives such as {@link
+ * #invokeAll(ForkJoinTask...) invokeAll}.  However, this class also
+ * provides a number of other methods that can come into play in
+ * advanced usages, as well as extension mechanics that allow support
+ * of new forms of fork/join processing.
+ *
+ * <p>A {@code ForkJoinTask} is a lightweight form of {@link Future}.
+ * The efficiency of {@code ForkJoinTask}s stems from a set of
+ * restrictions (that are only partially statically enforceable)
+ * reflecting their main use as computational tasks calculating pure
+ * functions or operating on purely isolated objects.  The primary
+ * coordination mechanisms are {@link #fork}, that arranges
+ * asynchronous execution, and {@link #join}, that doesn't proceed
+ * until the task's result has been computed.  Computations should
+ * ideally avoid {@code synchronized} methods or blocks, and should
+ * minimize other blocking synchronization apart from joining other
+ * tasks or using synchronizers such as Phasers that are advertised to
+ * cooperate with fork/join scheduling. Subdividable tasks should also
+ * not perform blocking I/O, and should ideally access variables that
+ * are completely independent of those accessed by other running
+ * tasks. These guidelines are loosely enforced by not permitting
+ * checked exceptions such as {@code IOExceptions} to be
+ * thrown. However, computations may still encounter unchecked
+ * exceptions, that are rethrown to callers attempting to join
+ * them. These exceptions may additionally include {@link
+ * RejectedExecutionException} stemming from internal resource
+ * exhaustion, such as failure to allocate internal task
+ * queues. Rethrown exceptions behave in the same way as regular
+ * exceptions, but, when possible, contain stack traces (as displayed
+ * for example using {@code ex.printStackTrace()}) of both the thread
+ * that initiated the computation as well as the thread actually
+ * encountering the exception; minimally only the latter.
+ *
+ * <p>It is possible to define and use ForkJoinTasks that may block,
+ * but doing so requires three further considerations: (1) Completion
+ * of few if any <em>other</em> tasks should be dependent on a task
+ * that blocks on external synchronization or I/O. Event-style async
+ * tasks that are never joined (for example, those subclassing {@link
+ * CountedCompleter}) often fall into this category.  (2) To minimize
+ * resource impact, tasks should be small; ideally performing only the
+ * (possibly) blocking action. (3) Unless the {@link
+ * ForkJoinPool.ManagedBlocker} API is used, or the number of possibly
+ * blocked tasks is known to be less than the pool's {@link
+ * ForkJoinPool#getParallelism} level, the pool cannot guarantee that
+ * enough threads will be available to ensure progress or good
+ * performance.
+ *
+ * <p>The primary method for awaiting completion and extracting
+ * results of a task is {@link #join}, but there are several variants:
+ * The {@link Future#get} methods support interruptible and/or timed
+ * waits for completion and report results using {@code Future}
+ * conventions. Method {@link #invoke} is semantically
+ * equivalent to {@code fork(); join()} but always attempts to begin
+ * execution in the current thread. The "<em>quiet</em>" forms of
+ * these methods do not extract results or report exceptions. These
+ * may be useful when a set of tasks are being executed, and you need
+ * to delay processing of results or exceptions until all complete.
+ * Method {@code invokeAll} (available in multiple versions)
+ * performs the most common form of parallel invocation: forking a set
+ * of tasks and joining them all.
+ *
+ * <p>In the most typical usages, a fork-join pair act like a call
+ * (fork) and return (join) from a parallel recursive function. As is
+ * the case with other forms of recursive calls, returns (joins)
+ * should be performed innermost-first. For example, {@code a.fork();
+ * b.fork(); b.join(); a.join();} is likely to be substantially more
+ * efficient than joining {@code a} before {@code b}.
+ *
+ * <p>The execution status of tasks may be queried at several levels
+ * of detail: {@link #isDone} is true if a task completed in any way
+ * (including the case where a task was cancelled without executing);
+ * {@link #isCompletedNormally} is true if a task completed without
+ * cancellation or encountering an exception; {@link #isCancelled} is
+ * true if the task was cancelled (in which case {@link #getException}
+ * returns a {@link CancellationException}); and
+ * {@link #isCompletedAbnormally} is true if a task was either
+ * cancelled or encountered an exception, in which case {@link
+ * #getException} will return either the encountered exception or
+ * {@link CancellationException}.
+ *
+ * <p>The ForkJoinTask class is not usually directly subclassed.
+ * Instead, you subclass one of the abstract classes that support a
+ * particular style of fork/join processing, typically {@link
+ * RecursiveAction} for most computations that do not return results,
+ * {@link RecursiveTask} for those that do, and {@link
+ * CountedCompleter} for those in which completed actions trigger
+ * other actions.  Normally, a concrete ForkJoinTask subclass declares
+ * fields comprising its parameters, established in a constructor, and
+ * then defines a {@code compute} method that somehow uses the control
+ * methods supplied by this base class.
+ *
+ * <p>Method {@link #join} and its variants are appropriate for use
+ * only when completion dependencies are acyclic; that is, the
+ * parallel computation can be described as a directed acyclic graph
+ * (DAG). Otherwise, executions may encounter a form of deadlock as
+ * tasks cyclically wait for each other.  However, this framework
+ * supports other methods and techniques (for example the use of
+ * {@link java.util.concurrent.Phaser Phaser}, {@link #helpQuiesce}, and {@link #complete}) that
+ * may be of use in constructing custom subclasses for problems that
+ * are not statically structured as DAGs. To support such usages, a
+ * ForkJoinTask may be atomically <em>tagged</em> with a {@code short}
+ * value using {@link #setForkJoinTaskTag} or {@link
+ * #compareAndSetForkJoinTaskTag} and checked using {@link
+ * #getForkJoinTaskTag}. The ForkJoinTask implementation does not use
+ * these {@code protected} methods or tags for any purpose, but they
+ * may be of use in the construction of specialized subclasses.  For
+ * example, parallel graph traversals can use the supplied methods to
+ * avoid revisiting nodes/tasks that have already been processed.
+ * (Method names for tagging are bulky in part to encourage definition
+ * of methods that reflect their usage patterns.)
+ *
+ * <p>Most base support methods are {@code final}, to prevent
+ * overriding of implementations that are intrinsically tied to the
+ * underlying lightweight task scheduling framework.  Developers
+ * creating new basic styles of fork/join processing should minimally
+ * implement {@code protected} methods {@link #exec}, {@link
+ * #setRawResult}, and {@link #getRawResult}, while also introducing
+ * an abstract computational method that can be implemented in its
+ * subclasses, possibly relying on other {@code protected} methods
+ * provided by this class.
+ *
+ * <p>ForkJoinTasks should perform relatively small amounts of
+ * computation. Large tasks should be split into smaller subtasks,
+ * usually via recursive decomposition. As a very rough rule of thumb,
+ * a task should perform more than 100 and less than 10000 basic
+ * computational steps, and should avoid indefinite looping. If tasks
+ * are too big, then parallelism cannot improve throughput. If too
+ * small, then memory and internal task maintenance overhead may
+ * overwhelm processing.
+ *
+ * <p>This class provides {@code adapt} methods for {@link Runnable}
+ * and {@link Callable}, that may be of use when mixing execution of
+ * {@code ForkJoinTasks} with other kinds of tasks. When all tasks are
+ * of this form, consider using a pool constructed in <em>asyncMode</em>.
+ *
+ * <p>ForkJoinTasks are {@code Serializable}, which enables them to be
+ * used in extensions such as remote execution frameworks. It is
+ * sensible to serialize tasks only before or after, but not during,
+ * execution. Serialization is not relied on during execution itself.
+ *
+ * @since 1.7
+ * @author Doug Lea
+ */
+public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
+
+    /*
+     * See the internal documentation of class ForkJoinPool for a
+     * general implementation overview.  ForkJoinTasks are mainly
+     * responsible for maintaining their "status" field amidst relays
+     * to methods in ForkJoinWorkerThread and ForkJoinPool.
+     *
+     * The methods of this class are more-or-less layered into
+     * (1) basic status maintenance
+     * (2) execution and awaiting completion
+     * (3) user-level methods that additionally report results.
+     * This is sometimes hard to see because this file orders exported
+     * methods in a way that flows well in javadocs.
+     */
+
+    /*
+     * The status field holds run control status bits packed into a
+     * single int to minimize footprint and to ensure atomicity (via
+     * CAS).  Status is initially zero, and takes on nonnegative
+     * values until completed, upon which status (anded with
+     * DONE_MASK) holds value NORMAL, CANCELLED, or EXCEPTIONAL. Tasks
+     * undergoing blocking waits by other threads have the SIGNAL bit
+     * set.  Completion of a stolen task with SIGNAL set awakens any
+     * waiters via notifyAll. Even though suboptimal for some
+     * purposes, we use basic builtin wait/notify to take advantage of
+     * "monitor inflation" in JVMs that we would otherwise need to
+     * emulate to avoid adding further per-task bookkeeping overhead.
+     * We want these monitors to be "fat", i.e., not use biasing or
+     * thin-lock techniques, so use some odd coding idioms that tend
+     * to avoid them, mainly by arranging that every synchronized
+     * block performs a wait, notifyAll or both.
+     *
+     * These control bits occupy only (some of) the upper half (16
+     * bits) of status field. The lower bits are used for user-defined
+     * tags.
+     */
+
+    /** The run status of this task */
+    volatile int status; // accessed directly by pool and workers
+    static final int DONE_MASK   = 0xf0000000;  // mask out non-completion bits
+    static final int NORMAL      = 0xf0000000;  // must be negative
+    static final int CANCELLED   = 0xc0000000;  // must be < NORMAL
+    static final int EXCEPTIONAL = 0x80000000;  // must be < CANCELLED
+    static final int SIGNAL      = 0x00010000;  // must be >= 1 << 16
+    static final int SMASK       = 0x0000ffff;  // short bits for tags
+
+    /**
+     * Marks completion and wakes up threads waiting to join this
+     * task.
+     *
+     * @param completion one of NORMAL, CANCELLED, EXCEPTIONAL
+     * @return completion status on exit
+     */
+    private int setCompletion(int completion) {
+        for (int s;;) {
+            if ((s = status) < 0)
+                return s;
+            if (U.compareAndSwapInt(this, STATUS, s, s | completion)) {
+                if ((s >>> 16) != 0)
+                    synchronized (this) { notifyAll(); }
+                return completion;
+            }
+        }
+    }
+
+    /**
+     * Primary execution method for stolen tasks. Unless done, calls
+     * exec and records status if completed, but doesn't wait for
+     * completion otherwise.
+     *
+     * @return status on exit from this method
+     */
+    final int doExec() {
+        int s; boolean completed;
+        if ((s = status) >= 0) {
+            try {
+                completed = exec();
+            } catch (Throwable rex) {
+                return setExceptionalCompletion(rex);
+            }
+            if (completed)
+                s = setCompletion(NORMAL);
+        }
+        return s;
+    }
+
+    /**
+     * Tries to set SIGNAL status unless already completed. Used by
+     * ForkJoinPool. Other variants are directly incorporated into
+     * externalAwaitDone etc.
+     *
+     * @return true if successful
+     */
+    final boolean trySetSignal() {
+        int s = status;
+        return s >= 0 && U.compareAndSwapInt(this, STATUS, s, s | SIGNAL);
+    }
+
+    /**
+     * Blocks a non-worker-thread until completion.
+     * @return status upon completion
+     */
+    private int externalAwaitDone() {
+        int s;
+        ForkJoinPool cp = ForkJoinPool.common;
+        if ((s = status) >= 0) {
+            if (cp != null) {
+                if (this instanceof CountedCompleter)
+                    s = cp.externalHelpComplete((CountedCompleter<?>)this);
+                else if (cp.tryExternalUnpush(this))
+                    s = doExec();
+            }
+            if (s >= 0 && (s = status) >= 0) {
+                boolean interrupted = false;
+                do {
+                    if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+                        synchronized (this) {
+                            if (status >= 0) {
+                                try {
+                                    wait();
+                                } catch (InterruptedException ie) {
+                                    interrupted = true;
+                                }
+                            }
+                            else
+                                notifyAll();
+                        }
+                    }
+                } while ((s = status) >= 0);
+                if (interrupted)
+                    Thread.currentThread().interrupt();
+            }
+        }
+        return s;
+    }
+
+    /**
+     * Blocks a non-worker-thread until completion or interruption.
+     */
+    private int externalInterruptibleAwaitDone() throws InterruptedException {
+        int s;
+        ForkJoinPool cp = ForkJoinPool.common;
+        if (Thread.interrupted())
+            throw new InterruptedException();
+        if ((s = status) >= 0 && cp != null) {
+            if (this instanceof CountedCompleter)
+                cp.externalHelpComplete((CountedCompleter<?>)this);
+            else if (cp.tryExternalUnpush(this))
+                doExec();
+        }
+        while ((s = status) >= 0) {
+            if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+                synchronized (this) {
+                    if (status >= 0)
+                        wait();
+                    else
+                        notifyAll();
+                }
+            }
+        }
+        return s;
+    }
+
+
+    /**
+     * Implementation for join, get, quietlyJoin. Directly handles
+     * only cases of already-completed, external wait, and
+     * unfork+exec.  Others are relayed to ForkJoinPool.awaitJoin.
+     *
+     * @return status upon completion
+     */
+    private int doJoin() {
+        int s; Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue w;
+        return (s = status) < 0 ? s :
+            ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+            (w = (wt = (ForkJoinWorkerThread)t).workQueue).
+            tryUnpush(this) && (s = doExec()) < 0 ? s :
+            wt.pool.awaitJoin(w, this) :
+            externalAwaitDone();
+    }
+
+    /**
+     * Implementation for invoke, quietlyInvoke.
+     *
+     * @return status upon completion
+     */
+    private int doInvoke() {
+        int s; Thread t; ForkJoinWorkerThread wt;
+        return (s = doExec()) < 0 ? s :
+            ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+            (wt = (ForkJoinWorkerThread)t).pool.awaitJoin(wt.workQueue, this) :
+            externalAwaitDone();
+    }
+
+    // Exception table support
+
+    /**
+     * Table of exceptions thrown by tasks, to enable reporting by
+     * callers. Because exceptions are rare, we don't directly keep
+     * them with task objects, but instead use a weak ref table.  Note
+     * that cancellation exceptions don't appear in the table, but are
+     * instead recorded as status values.
+     *
+     * Note: These statics are initialized below in static block.
+     */
+    private static final ExceptionNode[] exceptionTable;
+    private static final ReentrantLock exceptionTableLock;
+    private static final ReferenceQueue<Object> exceptionTableRefQueue;
+
+    /**
+     * Fixed capacity for exceptionTable.
+     */
+    private static final int EXCEPTION_MAP_CAPACITY = 32;
+
+    /**
+     * Key-value nodes for exception table.  The chained hash table
+     * uses identity comparisons, full locking, and weak references
+     * for keys. The table has a fixed capacity because it only
+     * maintains task exceptions long enough for joiners to access
+     * them, so should never become very large for sustained
+     * periods. However, since we do not know when the last joiner
+     * completes, we must use weak references and expunge them. We do
+     * so on each operation (hence full locking). Also, some thread in
+     * any ForkJoinPool will call helpExpungeStaleExceptions when its
+     * pool becomes isQuiescent.
+     */
+    static final class ExceptionNode extends WeakReference<ForkJoinTask<?>> {
+        final Throwable ex;
+        ExceptionNode next;
+        final long thrower;  // use id not ref to avoid weak cycles
+        final int hashCode;  // store task hashCode before weak ref disappears
+        ExceptionNode(ForkJoinTask<?> task, Throwable ex, ExceptionNode next) {
+            super(task, exceptionTableRefQueue);
+            this.ex = ex;
+            this.next = next;
+            this.thrower = Thread.currentThread().getId();
+            this.hashCode = System.identityHashCode(task);
+        }
+    }
+
+    /**
+     * Records exception and sets status.
+     *
+     * @return status on exit
+     */
+    final int recordExceptionalCompletion(Throwable ex) {
+        int s;
+        if ((s = status) >= 0) {
+            int h = System.identityHashCode(this);
+            final ReentrantLock lock = exceptionTableLock;
+            lock.lock();
+            try {
+                expungeStaleExceptions();
+                ExceptionNode[] t = exceptionTable;
+                int i = h & (t.length - 1);
+                for (ExceptionNode e = t[i]; ; e = e.next) {
+                    if (e == null) {
+                        t[i] = new ExceptionNode(this, ex, t[i]);
+                        break;
+                    }
+                    if (e.get() == this) // already present
+                        break;
+                }
+            } finally {
+                lock.unlock();
+            }
+            s = setCompletion(EXCEPTIONAL);
+        }
+        return s;
+    }
+
+    /**
+     * Records exception and possibly propagates.
+     *
+     * @return status on exit
+     */
+    private int setExceptionalCompletion(Throwable ex) {
+        int s = recordExceptionalCompletion(ex);
+        if ((s & DONE_MASK) == EXCEPTIONAL)
+            internalPropagateException(ex);
+        return s;
+    }
+
+    /**
+     * Hook for exception propagation support for tasks with completers.
+     */
+    void internalPropagateException(Throwable ex) {
+    }
+
+    /**
+     * Cancels, ignoring any exceptions thrown by cancel. Used during
+     * worker and pool shutdown. Cancel is spec'ed not to throw any
+     * exceptions, but if it does anyway, we have no recourse during
+     * shutdown, so guard against this case.
+     */
+    static final void cancelIgnoringExceptions(ForkJoinTask<?> t) {
+        if (t != null && t.status >= 0) {
+            try {
+                t.cancel(false);
+            } catch (Throwable ignore) {
+            }
+        }
+    }
+
+    /**
+     * Removes exception node and clears status.
+     */
+    private void clearExceptionalCompletion() {
+        int h = System.identityHashCode(this);
+        final ReentrantLock lock = exceptionTableLock;
+        lock.lock();
+        try {
+            ExceptionNode[] t = exceptionTable;
+            int i = h & (t.length - 1);
+            ExceptionNode e = t[i];
+            ExceptionNode pred = null;
+            while (e != null) {
+                ExceptionNode next = e.next;
+                if (e.get() == this) {
+                    if (pred == null)
+                        t[i] = next;
+                    else
+                        pred.next = next;
+                    break;
+                }
+                pred = e;
+                e = next;
+            }
+            expungeStaleExceptions();
+            status = 0;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * Returns a rethrowable exception for the given task, if
+     * available. To provide accurate stack traces, if the exception
+     * was not thrown by the current thread, we try to create a new
+     * exception of the same type as the one thrown, but with the
+     * recorded exception as its cause. If there is no such
+     * constructor, we instead try to use a no-arg constructor,
+     * followed by initCause, to the same effect. If none of these
+     * apply, or any fail due to other exceptions, we return the
+     * recorded exception, which is still correct, although it may
+     * contain a misleading stack trace.
+     *
+     * @return the exception, or null if none
+     */
+    private Throwable getThrowableException() {
+        if ((status & DONE_MASK) != EXCEPTIONAL)
+            return null;
+        int h = System.identityHashCode(this);
+        ExceptionNode e;
+        final ReentrantLock lock = exceptionTableLock;
+        lock.lock();
+        try {
+            expungeStaleExceptions();
+            ExceptionNode[] t = exceptionTable;
+            e = t[h & (t.length - 1)];
+            while (e != null && e.get() != this)
+                e = e.next;
+        } finally {
+            lock.unlock();
+        }
+        Throwable ex;
+        if (e == null || (ex = e.ex) == null)
+            return null;
+        if (false && e.thrower != Thread.currentThread().getId()) {
+            Class<? extends Throwable> ec = ex.getClass();
+            try {
+                Constructor<?> noArgCtor = null;
+                Constructor<?>[] cs = ec.getConstructors();// public ctors only
+                for (int i = 0; i < cs.length; ++i) {
+                    Constructor<?> c = cs[i];
+                    Class<?>[] ps = c.getParameterTypes();
+                    if (ps.length == 0)
+                        noArgCtor = c;
+                    else if (ps.length == 1 && ps[0] == Throwable.class)
+                        return (Throwable)(c.newInstance(ex));
+                }
+                if (noArgCtor != null) {
+                    Throwable wx = (Throwable)(noArgCtor.newInstance());
+                    wx.initCause(ex);
+                    return wx;
+                }
+            } catch (Exception ignore) {
+            }
+        }
+        return ex;
+    }
+
+    /**
+     * Poll stale refs and remove them. Call only while holding lock.
+     */
+    /**
+     * Poll stale refs and remove them. Call only while holding lock.
+     */
+    private static void expungeStaleExceptions() {
+        for (Object x; (x = exceptionTableRefQueue.poll()) != null;) {
+            if (x instanceof ExceptionNode) {
+                int hashCode = ((ExceptionNode)x).hashCode;
+                ExceptionNode[] t = exceptionTable;
+                int i = hashCode & (t.length - 1);
+                ExceptionNode e = t[i];
+                ExceptionNode pred = null;
+                while (e != null) {
+                    ExceptionNode next = e.next;
+                    if (e == x) {
+                        if (pred == null)
+                            t[i] = next;
+                        else
+                            pred.next = next;
+                        break;
+                    }
+                    pred = e;
+                    e = next;
+                }
+            }
+        }
+    }
+
+    /**
+     * If lock is available, poll stale refs and remove them.
+     * Called from ForkJoinPool when pools become quiescent.
+     */
+    static final void helpExpungeStaleExceptions() {
+        final ReentrantLock lock = exceptionTableLock;
+        if (lock.tryLock()) {
+            try {
+                expungeStaleExceptions();
+            } finally {
+                lock.unlock();
+            }
+        }
+    }
+
+    /**
+     * A version of "sneaky throw" to relay exceptions
+     */
+    static void rethrow(Throwable ex) {
+        if (ex != null)
+            ForkJoinTask.<RuntimeException>uncheckedThrow(ex);
+    }
+
+    /**
+     * The sneaky part of sneaky throw, relying on generics
+     * limitations to evade compiler complaints about rethrowing
+     * unchecked exceptions
+     */
+    @SuppressWarnings("unchecked") static <T extends Throwable>
+        void uncheckedThrow(Throwable t) throws T {
+        throw (T)t; // rely on vacuous cast
+    }
+
+    /**
+     * Throws exception, if any, associated with the given status.
+     */
+    private void reportException(int s) {
+        if (s == CANCELLED)
+            throw new CancellationException();
+        if (s == EXCEPTIONAL)
+            rethrow(getThrowableException());
+    }
+
+    // public methods
+
+    /**
+     * Arranges to asynchronously execute this task in the pool the
+     * current task is running in, if applicable, or using the {@link
+     * ForkJoinPool#commonPool()} if not {@link #inForkJoinPool}.  While
+     * it is not necessarily enforced, it is a usage error to fork a
+     * task more than once unless it has completed and been
+     * reinitialized.  Subsequent modifications to the state of this
+     * task or any data it operates on are not necessarily
+     * consistently observable by any thread other than the one
+     * executing it unless preceded by a call to {@link #join} or
+     * related methods, or a call to {@link #isDone} returning {@code
+     * true}.
+     *
+     * @return {@code this}, to simplify usage
+     */
+    public final ForkJoinTask<V> fork() {
+        Thread t;
+        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
+            ((ForkJoinWorkerThread)t).workQueue.push(this);
+        else
+            ForkJoinPool.common.externalPush(this);
+        return this;
+    }
+
+    /**
+     * Returns the result of the computation when it {@link #isDone is
+     * done}.  This method differs from {@link #get()} in that
+     * abnormal completion results in {@code RuntimeException} or
+     * {@code Error}, not {@code ExecutionException}, and that
+     * interrupts of the calling thread do <em>not</em> cause the
+     * method to abruptly return by throwing {@code
+     * InterruptedException}.
+     *
+     * @return the computed result
+     */
+    public final V join() {
+        int s;
+        if ((s = doJoin() & DONE_MASK) != NORMAL)
+            reportException(s);
+        return getRawResult();
+    }
+
+    /**
+     * Commences performing this task, awaits its completion if
+     * necessary, and returns its result, or throws an (unchecked)
+     * {@code RuntimeException} or {@code Error} if the underlying
+     * computation did so.
+     *
+     * @return the computed result
+     */
+    public final V invoke() {
+        int s;
+        if ((s = doInvoke() & DONE_MASK) != NORMAL)
+            reportException(s);
+        return getRawResult();
+    }
+
+    /**
+     * Forks the given tasks, returning when {@code isDone} holds for
+     * each task or an (unchecked) exception is encountered, in which
+     * case the exception is rethrown. If more than one task
+     * encounters an exception, then this method throws any one of
+     * these exceptions. If any task encounters an exception, the
+     * other may be cancelled. However, the execution status of
+     * individual tasks is not guaranteed upon exceptional return. The
+     * status of each task may be obtained using {@link
+     * #getException()} and related methods to check if they have been
+     * cancelled, completed normally or exceptionally, or left
+     * unprocessed.
+     *
+     * @param t1 the first task
+     * @param t2 the second task
+     * @throws NullPointerException if any task is null
+     */
+    public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) {
+        int s1, s2;
+        t2.fork();
+        if ((s1 = t1.doInvoke() & DONE_MASK) != NORMAL)
+            t1.reportException(s1);
+        if ((s2 = t2.doJoin() & DONE_MASK) != NORMAL)
+            t2.reportException(s2);
+    }
+
+    /**
+     * Forks the given tasks, returning when {@code isDone} holds for
+     * each task or an (unchecked) exception is encountered, in which
+     * case the exception is rethrown. If more than one task
+     * encounters an exception, then this method throws any one of
+     * these exceptions. If any task encounters an exception, others
+     * may be cancelled. However, the execution status of individual
+     * tasks is not guaranteed upon exceptional return. The status of
+     * each task may be obtained using {@link #getException()} and
+     * related methods to check if they have been cancelled, completed
+     * normally or exceptionally, or left unprocessed.
+     *
+     * @param tasks the tasks
+     * @throws NullPointerException if any task is null
+     */
+    public static void invokeAll(ForkJoinTask<?>... tasks) {
+        Throwable ex = null;
+        int last = tasks.length - 1;
+        for (int i = last; i >= 0; --i) {
+            ForkJoinTask<?> t = tasks[i];
+            if (t == null) {
+                if (ex == null)
+                    ex = new NullPointerException();
+            }
+            else if (i != 0)
+                t.fork();
+            else if (t.doInvoke() < NORMAL && ex == null)
+                ex = t.getException();
+        }
+        for (int i = 1; i <= last; ++i) {
+            ForkJoinTask<?> t = tasks[i];
+            if (t != null) {
+                if (ex != null)
+                    t.cancel(false);
+                else if (t.doJoin() < NORMAL)
+                    ex = t.getException();
+            }
+        }
+        if (ex != null)
+            rethrow(ex);
+    }
+
+    /**
+     * Forks all tasks in the specified collection, returning when
+     * {@code isDone} holds for each task or an (unchecked) exception
+     * is encountered, in which case the exception is rethrown. If
+     * more than one task encounters an exception, then this method
+     * throws any one of these exceptions. If any task encounters an
+     * exception, others may be cancelled. However, the execution
+     * status of individual tasks is not guaranteed upon exceptional
+     * return. The status of each task may be obtained using {@link
+     * #getException()} and related methods to check if they have been
+     * cancelled, completed normally or exceptionally, or left
+     * unprocessed.
+     *
+     * @param tasks the collection of tasks
+     * @param <T> the type of the values returned from the tasks
+     * @return the tasks argument, to simplify usage
+     * @throws NullPointerException if tasks or any element are null
+     */
+    public static <T extends ForkJoinTask<?>> Collection<T> invokeAll(Collection<T> tasks) {
+        if (!(tasks instanceof RandomAccess) || !(tasks instanceof List<?>)) {
+            invokeAll(tasks.toArray(new ForkJoinTask<?>[tasks.size()]));
+            return tasks;
+        }
+        @SuppressWarnings("unchecked")
+        List<? extends ForkJoinTask<?>> ts =
+            (List<? extends ForkJoinTask<?>>) tasks;
+        Throwable ex = null;
+        int last = ts.size() - 1;
+        for (int i = last; i >= 0; --i) {
+            ForkJoinTask<?> t = ts.get(i);
+            if (t == null) {
+                if (ex == null)
+                    ex = new NullPointerException();
+            }
+            else if (i != 0)
+                t.fork();
+            else if (t.doInvoke() < NORMAL && ex == null)
+                ex = t.getException();
+        }
+        for (int i = 1; i <= last; ++i) {
+            ForkJoinTask<?> t = ts.get(i);
+            if (t != null) {
+                if (ex != null)
+                    t.cancel(false);
+                else if (t.doJoin() < NORMAL)
+                    ex = t.getException();
+            }
+        }
+        if (ex != null)
+            rethrow(ex);
+        return tasks;
+    }
+
+    /**
+     * Attempts to cancel execution of this task. This attempt will
+     * fail if the task has already completed or could not be
+     * cancelled for some other reason. If successful, and this task
+     * has not started when {@code cancel} is called, execution of
+     * this task is suppressed. After this method returns
+     * successfully, unless there is an intervening call to {@link
+     * #reinitialize}, subsequent calls to {@link #isCancelled},
+     * {@link #isDone}, and {@code cancel} will return {@code true}
+     * and calls to {@link #join} and related methods will result in
+     * {@code CancellationException}.
+     *
+     * <p>This method may be overridden in subclasses, but if so, must
+     * still ensure that these properties hold. In particular, the
+     * {@code cancel} method itself must not throw exceptions.
+     *
+     * <p>This method is designed to be invoked by <em>other</em>
+     * tasks. To terminate the current task, you can just return or
+     * throw an unchecked exception from its computation method, or
+     * invoke {@link #completeExceptionally(Throwable)}.
+     *
+     * @param mayInterruptIfRunning this value has no effect in the
+     * default implementation because interrupts are not used to
+     * control cancellation.
+     *
+     * @return {@code true} if this task is now cancelled
+     */
+    public boolean cancel(boolean mayInterruptIfRunning) {
+        return (setCompletion(CANCELLED) & DONE_MASK) == CANCELLED;
+    }
+
+    public final boolean isDone() {
+        return status < 0;
+    }
+
+    public final boolean isCancelled() {
+        return (status & DONE_MASK) == CANCELLED;
+    }
+
+    /**
+     * Returns {@code true} if this task threw an exception or was cancelled.
+     *
+     * @return {@code true} if this task threw an exception or was cancelled
+     */
+    public final boolean isCompletedAbnormally() {
+        return status < NORMAL;
+    }
+
+    /**
+     * Returns {@code true} if this task completed without throwing an
+     * exception and was not cancelled.
+     *
+     * @return {@code true} if this task completed without throwing an
+     * exception and was not cancelled
+     */
+    public final boolean isCompletedNormally() {
+        return (status & DONE_MASK) == NORMAL;
+    }
+
+    /**
+     * Returns the exception thrown by the base computation, or a
+     * {@code CancellationException} if cancelled, or {@code null} if
+     * none or if the method has not yet completed.
+     *
+     * @return the exception, or {@code null} if none
+     */
+    public final Throwable getException() {
+        int s = status & DONE_MASK;
+        return ((s >= NORMAL)    ? null :
+                (s == CANCELLED) ? new CancellationException() :
+                getThrowableException());
+    }
+
+    /**
+     * Completes this task abnormally, and if not already aborted or
+     * cancelled, causes it to throw the given exception upon
+     * {@code join} and related operations. This method may be used
+     * to induce exceptions in asynchronous tasks, or to force
+     * completion of tasks that would not otherwise complete.  Its use
+     * in other situations is discouraged.  This method is
+     * overridable, but overridden versions must invoke {@code super}
+     * implementation to maintain guarantees.
+     *
+     * @param ex the exception to throw. If this exception is not a
+     * {@code RuntimeException} or {@code Error}, the actual exception
+     * thrown will be a {@code RuntimeException} with cause {@code ex}.
+     */
+    public void completeExceptionally(Throwable ex) {
+        setExceptionalCompletion((ex instanceof RuntimeException) ||
+                                 (ex instanceof Error) ? ex :
+                                 new RuntimeException(ex));
+    }
+
+    /**
+     * Completes this task, and if not already aborted or cancelled,
+     * returning the given value as the result of subsequent
+     * invocations of {@code join} and related operations. This method
+     * may be used to provide results for asynchronous tasks, or to
+     * provide alternative handling for tasks that would not otherwise
+     * complete normally. Its use in other situations is
+     * discouraged. This method is overridable, but overridden
+     * versions must invoke {@code super} implementation to maintain
+     * guarantees.
+     *
+     * @param value the result value for this task
+     */
+    public void complete(V value) {
+        try {
+            setRawResult(value);
+        } catch (Throwable rex) {
+            setExceptionalCompletion(rex);
+            return;
+        }
+        setCompletion(NORMAL);
+    }
+
+    /**
+     * Completes this task normally without setting a value. The most
+     * recent value established by {@link #setRawResult} (or {@code
+     * null} by default) will be returned as the result of subsequent
+     * invocations of {@code join} and related operations.
+     *
+     * @since 1.8
+     */
+    public final void quietlyComplete() {
+        setCompletion(NORMAL);
+    }
+
+    /**
+     * Waits if necessary for the computation to complete, and then
+     * retrieves its result.
+     *
+     * @return the computed result
+     * @throws CancellationException if the computation was cancelled
+     * @throws ExecutionException if the computation threw an
+     * exception
+     * @throws InterruptedException if the current thread is not a
+     * member of a ForkJoinPool and was interrupted while waiting
+     */
+    public final V get() throws InterruptedException, ExecutionException {
+        int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ?
+            doJoin() : externalInterruptibleAwaitDone();
+        Throwable ex;
+        if ((s &= DONE_MASK) == CANCELLED)
+            throw new CancellationException();
+        if (s == EXCEPTIONAL && (ex = getThrowableException()) != null)
+            throw new ExecutionException(ex);
+        return getRawResult();
+    }
+
+    /**
+     * Waits if necessary for at most the given time for the computation
+     * to complete, and then retrieves its result, if available.
+     *
+     * @param timeout the maximum time to wait
+     * @param unit the time unit of the timeout argument
+     * @return the computed result
+     * @throws CancellationException if the computation was cancelled
+     * @throws ExecutionException if the computation threw an
+     * exception
+     * @throws InterruptedException if the current thread is not a
+     * member of a ForkJoinPool and was interrupted while waiting
+     * @throws TimeoutException if the wait timed out
+     */
+    public final V get(long timeout, TimeUnit unit)
+        throws InterruptedException, ExecutionException, TimeoutException {
+        if (Thread.interrupted())
+            throw new InterruptedException();
+        // Messy in part because we measure in nanosecs, but wait in millisecs
+        int s; long ms;
+        long ns = unit.toNanos(timeout);
+        ForkJoinPool cp;
+        if ((s = status) >= 0 && ns > 0L) {
+            long deadline = System.nanoTime() + ns;
+            ForkJoinPool p = null;
+            ForkJoinPool.WorkQueue w = null;
+            Thread t = Thread.currentThread();
+            if (t instanceof ForkJoinWorkerThread) {
+                ForkJoinWorkerThread wt = (ForkJoinWorkerThread)t;
+                p = wt.pool;
+                w = wt.workQueue;
+                p.helpJoinOnce(w, this); // no retries on failure
+            }
+            else if ((cp = ForkJoinPool.common) != null) {
+                if (this instanceof CountedCompleter)
+                    cp.externalHelpComplete((CountedCompleter<?>)this);
+                else if (cp.tryExternalUnpush(this))
+                    doExec();
+            }
+            boolean canBlock = false;
+            boolean interrupted = false;
+            try {
+                while ((s = status) >= 0) {
+                    if (w != null && w.qlock < 0)
+                        cancelIgnoringExceptions(this);
+                    else if (!canBlock) {
+                        if (p == null || p.tryCompensate(p.ctl))
+                            canBlock = true;
+                    }
+                    else {
+                        if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
+                            U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+                            synchronized (this) {
+                                if (status >= 0) {
+                                    try {
+                                        wait(ms);
+                                    } catch (InterruptedException ie) {
+                                        if (p == null)
+                                            interrupted = true;
+                                    }
+                                }
+                                else
+                                    notifyAll();
+                            }
+                        }
+                        if ((s = status) < 0 || interrupted ||
+                            (ns = deadline - System.nanoTime()) <= 0L)
+                            break;
+                    }
+                }
+            } finally {
+                if (p != null && canBlock)
+                    p.incrementActiveCount();
+            }
+            if (interrupted)
+                throw new InterruptedException();
+        }
+        if ((s &= DONE_MASK) != NORMAL) {
+            Throwable ex;
+            if (s == CANCELLED)
+                throw new CancellationException();
+            if (s != EXCEPTIONAL)
+                throw new TimeoutException();
+            if ((ex = getThrowableException()) != null)
+                throw new ExecutionException(ex);
+        }
+        return getRawResult();
+    }
+
+    /**
+     * Joins this task, without returning its result or throwing its
+     * exception. This method may be useful when processing
+     * collections of tasks when some have been cancelled or otherwise
+     * known to have aborted.
+     */
+    public final void quietlyJoin() {
+        doJoin();
+    }
+
+    /**
+     * Commences performing this task and awaits its completion if
+     * necessary, without returning its result or throwing its
+     * exception.
+     */
+    public final void quietlyInvoke() {
+        doInvoke();
+    }
+
+    /**
+     * Possibly executes tasks until the pool hosting the current task
+     * {@linkplain ForkJoinPool#isQuiescent is quiescent}.  This
+     * method may be of use in designs in which many tasks are forked,
+     * but none are explicitly joined, instead executing them until
+     * all are processed.
+     */
+    public static void helpQuiesce() {
+        Thread t;
+        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
+            ForkJoinWorkerThread wt = (ForkJoinWorkerThread)t;
+            wt.pool.helpQuiescePool(wt.workQueue);
+        }
+        else
+            ForkJoinPool.quiesceCommonPool();
+    }
+
+    /**
+     * Resets the internal bookkeeping state of this task, allowing a
+     * subsequent {@code fork}. This method allows repeated reuse of
+     * this task, but only if reuse occurs when this task has either
+     * never been forked, or has been forked, then completed and all
+     * outstanding joins of this task have also completed. Effects
+     * under any other usage conditions are not guaranteed.
+     * This method may be useful when executing
+     * pre-constructed trees of subtasks in loops.
+     *
+     * <p>Upon completion of this method, {@code isDone()} reports
+     * {@code false}, and {@code getException()} reports {@code
+     * null}. However, the value returned by {@code getRawResult} is
+     * unaffected. To clear this value, you can invoke {@code
+     * setRawResult(null)}.
+     */
+    public void reinitialize() {
+        if ((status & DONE_MASK) == EXCEPTIONAL)
+            clearExceptionalCompletion();
+        else
+            status = 0;
+    }
+
+    /**
+     * Returns the pool hosting the current task execution, or null
+     * if this task is executing outside of any ForkJoinPool.
+     *
+     * @see #inForkJoinPool
+     * @return the pool, or {@code null} if none
+     */
+    public static ForkJoinPool getPool() {
+        Thread t = Thread.currentThread();
+        return (t instanceof ForkJoinWorkerThread) ?
+            ((ForkJoinWorkerThread) t).pool : null;
+    }
+
+    /**
+     * Returns {@code true} if the current thread is a {@link
+     * ForkJoinWorkerThread} executing as a ForkJoinPool computation.
+     *
+     * @return {@code true} if the current thread is a {@link
+     * ForkJoinWorkerThread} executing as a ForkJoinPool computation,
+     * or {@code false} otherwise
+     */
+    public static boolean inForkJoinPool() {
+        return Thread.currentThread() instanceof ForkJoinWorkerThread;
+    }
+
+    /**
+     * Tries to unschedule this task for execution. This method will
+     * typically (but is not guaranteed to) succeed if this task is
+     * the most recently forked task by the current thread, and has
+     * not commenced executing in another thread.  This method may be
+     * useful when arranging alternative local processing of tasks
+     * that could have been, but were not, stolen.
+     *
+     * @return {@code true} if unforked
+     */
+    public boolean tryUnfork() {
+        Thread t;
+        return (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+                ((ForkJoinWorkerThread)t).workQueue.tryUnpush(this) :
+                ForkJoinPool.common.tryExternalUnpush(this));
+    }
+
+    /**
+     * Returns an estimate of the number of tasks that have been
+     * forked by the current worker thread but not yet executed. This
+     * value may be useful for heuristic decisions about whether to
+     * fork other tasks.
+     *
+     * @return the number of tasks
+     */
+    public static int getQueuedTaskCount() {
+        Thread t; ForkJoinPool.WorkQueue q;
+        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
+            q = ((ForkJoinWorkerThread)t).workQueue;
+        else
+            q = ForkJoinPool.commonSubmitterQueue();
+        return (q == null) ? 0 : q.queueSize();
+    }
+
+    /**
+     * Returns an estimate of how many more locally queued tasks are
+     * held by the current worker thread than there are other worker
+     * threads that might steal them, or zero if this thread is not
+     * operating in a ForkJoinPool. This value may be useful for
+     * heuristic decisions about whether to fork other tasks. In many
+     * usages of ForkJoinTasks, at steady state, each worker should
+     * aim to maintain a small constant surplus (for example, 3) of
+     * tasks, and to process computations locally if this threshold is
+     * exceeded.
+     *
+     * @return the surplus number of tasks, which may be negative
+     */
+    public static int getSurplusQueuedTaskCount() {
+        return ForkJoinPool.getSurplusQueuedTaskCount();
+    }
+
+    // Extension methods
+
+    /**
+     * Returns the result that would be returned by {@link #join}, even
+     * if this task completed abnormally, or {@code null} if this task
+     * is not known to have been completed.  This method is designed
+     * to aid debugging, as well as to support extensions. Its use in
+     * any other context is discouraged.
+     *
+     * @return the result, or {@code null} if not completed
+     */
+    public abstract V getRawResult();
+
+    /**
+     * Forces the given value to be returned as a result.  This method
+     * is designed to support extensions, and should not in general be
+     * called otherwise.
+     *
+     * @param value the value
+     */
+    protected abstract void setRawResult(V value);
+
+    /**
+     * Immediately performs the base action of this task and returns
+     * true if, upon return from this method, this task is guaranteed
+     * to have completed normally. This method may return false
+     * otherwise, to indicate that this task is not necessarily
+     * complete (or is not known to be complete), for example in
+     * asynchronous actions that require explicit invocations of
+     * completion methods. This method may also throw an (unchecked)
+     * exception to indicate abnormal exit. This method is designed to
+     * support extensions, and should not in general be called
+     * otherwise.
+     *
+     * @return {@code true} if this task is known to have completed normally
+     */
+    protected abstract boolean exec();
+
+    /**
+     * Returns, but does not unschedule or execute, a task queued by
+     * the current thread but not yet executed, if one is immediately
+     * available. There is no guarantee that this task will actually
+     * be polled or executed next. Conversely, this method may return
+     * null even if a task exists but cannot be accessed without
+     * contention with other threads.  This method is designed
+     * primarily to support extensions, and is unlikely to be useful
+     * otherwise.
+     *
+     * @return the next task, or {@code null} if none are available
+     */
+    protected static ForkJoinTask<?> peekNextLocalTask() {
+        Thread t; ForkJoinPool.WorkQueue q;
+        if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
+            q = ((ForkJoinWorkerThread)t).workQueue;
+        else
+            q = ForkJoinPool.commonSubmitterQueue();
+        return (q == null) ? null : q.peek();
+    }
+
+    /**
+     * Unschedules and returns, without executing, the next task
+     * queued by the current thread but not yet executed, if the
+     * current thread is operating in a ForkJoinPool.  This method is
+     * designed primarily to support extensions, and is unlikely to be
+     * useful otherwise.
+     *
+     * @return the next task, or {@code null} if none are available
+     */
+    protected static ForkJoinTask<?> pollNextLocalTask() {
+        Thread t;
+        return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+            ((ForkJoinWorkerThread)t).workQueue.nextLocalTask() :
+            null;
+    }
+
+    /**
+     * If the current thread is operating in a ForkJoinPool,
+     * unschedules and returns, without executing, the next task
+     * queued by the current thread but not yet executed, if one is
+     * available, or if not available, a task that was forked by some
+     * other thread, if available. Availability may be transient, so a
+     * {@code null} result does not necessarily imply quiescence of
+     * the pool this task is operating in.  This method is designed
+     * primarily to support extensions, and is unlikely to be useful
+     * otherwise.
+     *
+     * @return a task, or {@code null} if none are available
+     */
+    protected static ForkJoinTask<?> pollTask() {
+        Thread t; ForkJoinWorkerThread wt;
+        return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+            (wt = (ForkJoinWorkerThread)t).pool.nextTaskFor(wt.workQueue) :
+            null;
+    }
+
+    // tag operations
+
+    /**
+     * Returns the tag for this task.
+     *
+     * @return the tag for this task
+     * @since 1.8
+     */
+    public final short getForkJoinTaskTag() {
+        return (short)status;
+    }
+
+    /**
+     * Atomically sets the tag value for this task.
+     *
+     * @param tag the tag value
+     * @return the previous value of the tag
+     * @since 1.8
+     */
+    public final short setForkJoinTaskTag(short tag) {
+        for (int s;;) {
+            if (U.compareAndSwapInt(this, STATUS, s = status,
+                                    (s & ~SMASK) | (tag & SMASK)))
+                return (short)s;
+        }
+    }
+
+    /**
+     * Atomically conditionally sets the tag value for this task.
+     * Among other applications, tags can be used as visit markers
+     * in tasks operating on graphs, as in methods that check: {@code
+     * if (task.compareAndSetForkJoinTaskTag((short)0, (short)1))}
+     * before processing, otherwise exiting because the node has
+     * already been visited.
+     *
+     * @param e the expected tag value
+     * @param tag the new tag value
+     * @return {@code true} if successful; i.e., the current value was
+     * equal to e and is now tag.
+     * @since 1.8
+     */
+    public final boolean compareAndSetForkJoinTaskTag(short e, short tag) {
+        for (int s;;) {
+            if ((short)(s = status) != e)
+                return false;
+            if (U.compareAndSwapInt(this, STATUS, s,
+                                    (s & ~SMASK) | (tag & SMASK)))
+                return true;
+        }
+    }
+
+    /**
+     * Adapter for Runnables. This implements RunnableFuture
+     * to be compliant with AbstractExecutorService constraints
+     * when used in ForkJoinPool.
+     */
+    static final class AdaptedRunnable<T> extends ForkJoinTask<T>
+        implements RunnableFuture<T> {
+        final Runnable runnable;
+        T result;
+        AdaptedRunnable(Runnable runnable, T result) {
+            if (runnable == null) throw new NullPointerException();
+            this.runnable = runnable;
+            this.result = result; // OK to set this even before completion
+        }
+        public final T getRawResult() { return result; }
+        public final void setRawResult(T v) { result = v; }
+        public final boolean exec() { runnable.run(); return true; }
+        public final void run() { invoke(); }
+        private static final long serialVersionUID = 5232453952276885070L;
+    }
+
+    /**
+     * Adapter for Runnables without results
+     */
+    static final class AdaptedRunnableAction extends ForkJoinTask<Void>
+        implements RunnableFuture<Void> {
+        final Runnable runnable;
+        AdaptedRunnableAction(Runnable runnable) {
+            if (runnable == null) throw new NullPointerException();
+            this.runnable = runnable;
+        }
+        public final Void getRawResult() { return null; }
+        public final void setRawResult(Void v) { }
+        public final boolean exec() { runnable.run(); return true; }
+        public final void run() { invoke(); }
+        private static final long serialVersionUID = 5232453952276885070L;
+    }
+
+    /**
+     * Adapter for Runnables in which failure forces worker exception
+     */
+    static final class RunnableExecuteAction extends ForkJoinTask<Void> {
+        final Runnable runnable;
+        RunnableExecuteAction(Runnable runnable) {
+            if (runnable == null) throw new NullPointerException();
+            this.runnable = runnable;
+        }
+        public final Void getRawResult() { return null; }
+        public final void setRawResult(Void v) { }
+        public final boolean exec() { runnable.run(); return true; }
+        void internalPropagateException(Throwable ex) {
+            rethrow(ex); // rethrow outside exec() catches.
+        }
+        private static final long serialVersionUID = 5232453952276885070L;
+    }
+
+    /**
+     * Adapter for Callables
+     */
+    static final class AdaptedCallable<T> extends ForkJoinTask<T>
+        implements RunnableFuture<T> {
+        final Callable<? extends T> callable;
+        T result;
+        AdaptedCallable(Callable<? extends T> callable) {
+            if (callable == null) throw new NullPointerException();
+            this.callable = callable;
+        }
+        public final T getRawResult() { return result; }
+        public final void setRawResult(T v) { result = v; }
+        public final boolean exec() {
+            try {
+                result = callable.call();
+                return true;
+            } catch (Error err) {
+                throw err;
+            } catch (RuntimeException rex) {
+                throw rex;
+            } catch (Exception ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+        public final void run() { invoke(); }
+        private static final long serialVersionUID = 2838392045355241008L;
+    }
+
+    /**
+     * Returns a new {@code ForkJoinTask} that performs the {@code run}
+     * method of the given {@code Runnable} as its action, and returns
+     * a null result upon {@link #join}.
+     *
+     * @param runnable the runnable action
+     * @return the task
+     */
+    public static ForkJoinTask<?> adapt(Runnable runnable) {
+        return new AdaptedRunnableAction(runnable);
+    }
+
+    /**
+     * Returns a new {@code ForkJoinTask} that performs the {@code run}
+     * method of the given {@code Runnable} as its action, and returns
+     * the given result upon {@link #join}.
+     *
+     * @param runnable the runnable action
+     * @param result the result upon completion
+     * @param <T> the type of the result
+     * @return the task
+     */
+    public static <T> ForkJoinTask<T> adapt(Runnable runnable, T result) {
+        return new AdaptedRunnable<T>(runnable, result);
+    }
+
+    /**
+     * Returns a new {@code ForkJoinTask} that performs the {@code call}
+     * method of the given {@code Callable} as its action, and returns
+     * its result upon {@link #join}, translating any checked exceptions
+     * encountered into {@code RuntimeException}.
+     *
+     * @param callable the callable action
+     * @param <T> the type of the callable's result
+     * @return the task
+     */
+    public static <T> ForkJoinTask<T> adapt(Callable<? extends T> callable) {
+        return new AdaptedCallable<T>(callable);
+    }
+
+    // Serialization support
+
+    private static final long serialVersionUID = -7721805057305804111L;
+
+    /**
+     * Saves this task to a stream (that is, serializes it).
+     *
+     * @param s the stream
+     * @throws java.io.IOException if an I/O error occurs
+     * @serialData the current run status and the exception thrown
+     * during execution, or {@code null} if none
+     */
+    private void writeObject(java.io.ObjectOutputStream s)
+        throws java.io.IOException {
+        s.defaultWriteObject();
+        s.writeObject(getException());
+    }
+
+    /**
+     * Reconstitutes this task from a stream (that is, deserializes it).
+     * @param s the stream
+     * @throws ClassNotFoundException if the class of a serialized object
+     *         could not be found
+     * @throws java.io.IOException if an I/O error occurs
+     */
+    private void readObject(java.io.ObjectInputStream s)
+        throws java.io.IOException, ClassNotFoundException {
+        s.defaultReadObject();
+        Object ex = s.readObject();
+        if (ex != null)
+            setExceptionalCompletion((Throwable)ex);
+    }
+
+    // Unsafe mechanics
+    private static final sun.misc.Unsafe U;
+    private static final long STATUS;
+
+    static {
+        exceptionTableLock = new ReentrantLock();
+        exceptionTableRefQueue = new ReferenceQueue<Object>();
+        exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY];
+        try {
+            U = getUnsafe();
+            Class<?> k = ForkJoinTask.class;
+            STATUS = U.objectFieldOffset
+                (k.getDeclaredField("status"));
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
+
+    /**
+     * Returns a sun.misc.Unsafe.  Suitable for use in a 3rd party package.
+     * Replace with a simple call to Unsafe.getUnsafe when integrating
+     * into a jdk.
+     *
+     * @return a sun.misc.Unsafe
+     */
+    private static sun.misc.Unsafe getUnsafe() {
+        try {
+            return sun.misc.Unsafe.getUnsafe();
+        } catch (SecurityException tryReflectionInstead) {}
+        try {
+            return java.security.AccessController.doPrivileged
+            (new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() {
+                public sun.misc.Unsafe run() throws Exception {
+                    Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
+                    for (java.lang.reflect.Field f : k.getDeclaredFields()) {
+                        f.setAccessible(true);
+                        Object x = f.get(null);
+                        if (k.isInstance(x))
+                            return k.cast(x);
+                    }
+                    throw new NoSuchFieldError("the Unsafe");
+                }});
+        } catch (java.security.PrivilegedActionException e) {
+            throw new RuntimeException("Could not initialize intrinsics",
+                                       e.getCause());
+        }
+    }
+}

+ 125 - 0
src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/ForkJoinWorkerThread.java

@@ -0,0 +1,125 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package com.jeeplus.common.utils.concurrent.jsr166e;
+
+/**
+ * 
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ForkJoinWorkerThread.java 1.6
+ * 
+ * A thread managed by a {@link ForkJoinPool}, which executes
+ * {@link ForkJoinTask}s.
+ * This class is subclassable solely for the sake of adding
+ * functionality -- there are no overridable methods dealing with
+ * scheduling or execution.  However, you can override initialization
+ * and termination methods surrounding the main task processing loop.
+ * If you do create such a subclass, you will also need to supply a
+ * custom {@link ForkJoinPool.ForkJoinWorkerThreadFactory} to
+ * {@linkplain ForkJoinPool#ForkJoinPool use it} in a {@code ForkJoinPool}.
+ *
+ * @since 1.7
+ * @author Doug Lea
+ */
+public class ForkJoinWorkerThread extends Thread {
+    /*
+     * ForkJoinWorkerThreads are managed by ForkJoinPools and perform
+     * ForkJoinTasks. For explanation, see the internal documentation
+     * of class ForkJoinPool.
+     *
+     * This class just maintains links to its pool and WorkQueue.  The
+     * pool field is set immediately upon construction, but the
+     * workQueue field is not set until a call to registerWorker
+     * completes. This leads to a visibility race, that is tolerated
+     * by requiring that the workQueue field is only accessed by the
+     * owning thread.
+     */
+
+    final ForkJoinPool pool;                // the pool this thread works in
+    final ForkJoinPool.WorkQueue workQueue; // work-stealing mechanics
+
+    /**
+     * Creates a ForkJoinWorkerThread operating in the given pool.
+     *
+     * @param pool the pool this thread works in
+     * @throws NullPointerException if pool is null
+     */
+    protected ForkJoinWorkerThread(ForkJoinPool pool) {
+        // Use a placeholder until a useful name can be set in registerWorker
+        super("aForkJoinWorkerThread");
+        this.pool = pool;
+        this.workQueue = pool.registerWorker(this);
+    }
+
+    /**
+     * Returns the pool hosting this thread.
+     *
+     * @return the pool
+     */
+    public ForkJoinPool getPool() {
+        return pool;
+    }
+
+    /**
+     * Returns the unique index number of this thread in its pool.
+     * The returned value ranges from zero to the maximum number of
+     * threads (minus one) that may exist in the pool, and does not
+     * change during the lifetime of the thread.  This method may be
+     * useful for applications that track status or collect results
+     * per-worker-thread rather than per-task.
+     *
+     * @return the index number
+     */
+    public int getPoolIndex() {
+        return workQueue.poolIndex >>> 1; // ignore odd/even tag bit
+    }
+
+    /**
+     * Initializes internal state after construction but before
+     * processing any tasks. If you override this method, you must
+     * invoke {@code super.onStart()} at the beginning of the method.
+     * Initialization requires care: Most fields must have legal
+     * default values, to ensure that attempted accesses from other
+     * threads work correctly even before this thread starts
+     * processing tasks.
+     */
+    protected void onStart() {
+    }
+
+    /**
+     * Performs cleanup associated with termination of this worker
+     * thread.  If you override this method, you must invoke
+     * {@code super.onTermination} at the end of the overridden method.
+     *
+     * @param exception the exception causing this thread to abort due
+     * to an unrecoverable error, or {@code null} if completed normally
+     */
+    protected void onTermination(Throwable exception) {
+    }
+
+    /**
+     * This method is required to be public, but should never be
+     * called explicitly. It performs the main run loop to execute
+     * {@link ForkJoinTask}s.
+     */
+    public void run() {
+        Throwable exception = null;
+        try {
+            onStart();
+            pool.runWorker(workQueue);
+        } catch (Throwable ex) {
+            exception = ex;
+        } finally {
+            try {
+                onTermination(exception);
+            } catch (Throwable ex) {
+                if (exception == null)
+                    exception = ex;
+            } finally {
+                pool.deregisterWorker(this, exception);
+            }
+        }
+    }
+}

+ 204 - 0
src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/LongAdder.java

@@ -0,0 +1,204 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package com.jeeplus.common.utils.concurrent.jsr166e;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java 1.17
+ * 
+ * One or more variables that together maintain an initially zero
+ * {@code long} sum.  When updates (method {@link #add}) are contended
+ * across threads, the set of variables may grow dynamically to reduce
+ * contention. Method {@link #sum} (or, equivalently, {@link
+ * #longValue}) returns the current total combined across the
+ * variables maintaining the sum.
+ *
+ * <p>This class is usually preferable to {@link AtomicLong} when
+ * multiple threads update a common sum that is used for purposes such
+ * as collecting statistics, not for fine-grained synchronization
+ * control.  Under low update contention, the two classes have similar
+ * characteristics. But under high contention, expected throughput of
+ * this class is significantly higher, at the expense of higher space
+ * consumption.
+ *
+ * <p>This class extends {@link Number}, but does <em>not</em> define
+ * methods such as {@code equals}, {@code hashCode} and {@code
+ * compareTo} because instances are expected to be mutated, and so are
+ * not useful as collection keys.
+ *
+ * <p><em>jsr166e note: This class is targeted to be placed in
+ * java.util.concurrent.atomic.</em>
+ *
+ * @since 1.8
+ * @author Doug Lea
+ */
+public class LongAdder extends Striped64 implements Serializable {
+    private static final long serialVersionUID = 7249069246863182397L;
+
+    /**
+     * Version of plus for use in retryUpdate
+     */
+    final long fn(long v, long x) { return v + x; }
+
+    /**
+     * Creates a new adder with initial sum of zero.
+     */
+    public LongAdder() {
+    }
+
+    /**
+     * Adds the given value.
+     *
+     * @param x the value to add
+     */
+    public void add(long x) {
+        Cell[] as; long b, v; int[] hc; Cell a; int n;
+        if ((as = cells) != null || !casBase(b = base, b + x)) {
+            boolean uncontended = true;
+            if ((hc = threadHashCode.get()) == null ||
+                as == null || (n = as.length) < 1 ||
+                (a = as[(n - 1) & hc[0]]) == null ||
+                !(uncontended = a.cas(v = a.value, v + x)))
+                retryUpdate(x, hc, uncontended);
+        }
+    }
+
+    /**
+     * Equivalent to {@code add(1)}.
+     */
+    public void increment() {
+        add(1L);
+    }
+
+    /**
+     * Equivalent to {@code add(-1)}.
+     */
+    public void decrement() {
+        add(-1L);
+    }
+
+    /**
+     * Returns the current sum.  The returned value is <em>NOT</em> an
+     * atomic snapshot; invocation in the absence of concurrent
+     * updates returns an accurate result, but concurrent updates that
+     * occur while the sum is being calculated might not be
+     * incorporated.
+     *
+     * @return the sum
+     */
+    public long sum() {
+        long sum = base;
+        Cell[] as = cells;
+        if (as != null) {
+            int n = as.length;
+            for (int i = 0; i < n; ++i) {
+                Cell a = as[i];
+                if (a != null)
+                    sum += a.value;
+            }
+        }
+        return sum;
+    }
+
+    /**
+     * Resets variables maintaining the sum to zero.  This method may
+     * be a useful alternative to creating a new adder, but is only
+     * effective if there are no concurrent updates.  Because this
+     * method is intrinsically racy, it should only be used when it is
+     * known that no threads are concurrently updating.
+     */
+    public void reset() {
+        internalReset(0L);
+    }
+
+    /**
+     * Equivalent in effect to {@link #sum} followed by {@link
+     * #reset}. This method may apply for example during quiescent
+     * points between multithreaded computations.  If there are
+     * updates concurrent with this method, the returned value is
+     * <em>not</em> guaranteed to be the final value occurring before
+     * the reset.
+     *
+     * @return the sum
+     */
+    public long sumThenReset() {
+        long sum = base;
+        Cell[] as = cells;
+        base = 0L;
+        if (as != null) {
+            int n = as.length;
+            for (int i = 0; i < n; ++i) {
+                Cell a = as[i];
+                if (a != null) {
+                    sum += a.value;
+                    a.value = 0L;
+                }
+            }
+        }
+        return sum;
+    }
+
+    /**
+     * Returns the String representation of the {@link #sum}.
+     * @return the String representation of the {@link #sum}
+     */
+    public String toString() {
+        return Long.toString(sum());
+    }
+
+    /**
+     * Equivalent to {@link #sum}.
+     *
+     * @return the sum
+     */
+    public long longValue() {
+        return sum();
+    }
+
+    /**
+     * Returns the {@link #sum} as an {@code int} after a narrowing
+     * primitive conversion.
+     */
+    public int intValue() {
+        return (int)sum();
+    }
+
+    /**
+     * Returns the {@link #sum} as a {@code float}
+     * after a widening primitive conversion.
+     */
+    public float floatValue() {
+        return (float)sum();
+    }
+
+    /**
+     * Returns the {@link #sum} as a {@code double} after a widening
+     * primitive conversion.
+     */
+    public double doubleValue() {
+        return (double)sum();
+    }
+
+    private void writeObject(ObjectOutputStream s) throws IOException {
+        s.defaultWriteObject();
+        s.writeLong(sum());
+    }
+
+    private void readObject(ObjectInputStream s)
+            throws IOException, ClassNotFoundException {
+        s.defaultReadObject();
+        busy = 0;
+        cells = null;
+        base = s.readLong();
+    }
+
+}

+ 166 - 0
src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/RecursiveAction.java

@@ -0,0 +1,166 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package com.jeeplus.common.utils.concurrent.jsr166e;
+
+/**
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/RecursiveAction.java 1.3
+ * 
+ * A recursive resultless {@link ForkJoinTask}.  This class
+ * establishes conventions to parameterize resultless actions as
+ * {@code Void} {@code ForkJoinTask}s. Because {@code null} is the
+ * only valid value of type {@code Void}, methods such as {@code join}
+ * always return {@code null} upon completion.
+ *
+ * <p><b>Sample Usages.</b> Here is a simple but complete ForkJoin
+ * sort that sorts a given {@code long[]} array:
+ *
+ *  <pre> {@code
+ * static class SortTask extends RecursiveAction {
+ *   final long[] array; final int lo, hi;
+ *   SortTask(long[] array, int lo, int hi) {
+ *     this.array = array; this.lo = lo; this.hi = hi;
+ *   }
+ *   SortTask(long[] array) { this(array, 0, array.length); }
+ *   protected void compute() {
+ *     if (hi - lo < THRESHOLD)
+ *       sortSequentially(lo, hi);
+ *     else {
+ *       int mid = (lo + hi) >>> 1;
+ *       invokeAll(new SortTask(array, lo, mid),
+ *                 new SortTask(array, mid, hi));
+ *       merge(lo, mid, hi);
+ *     }
+ *   }
+ *   // implementation details follow:
+ *   static final int THRESHOLD = 1000;
+ *   void sortSequentially(int lo, int hi) {
+ *     Arrays.sort(array, lo, hi);
+ *   }
+ *   void merge(int lo, int mid, int hi) {
+ *     long[] buf = Arrays.copyOfRange(array, lo, mid);
+ *     for (int i = 0, j = lo, k = mid; i < buf.length; j++)
+ *       array[j] = (k == hi || buf[i] < array[k]) ?
+ *         buf[i++] : array[k++];
+ *   }
+ * }}</pre>
+ *
+ * You could then sort {@code anArray} by creating {@code new
+ * SortTask(anArray)} and invoking it in a ForkJoinPool.  As a more
+ * concrete simple example, the following task increments each element
+ * of an array:
+ *  <pre> {@code
+ * class IncrementTask extends RecursiveAction {
+ *   final long[] array; final int lo, hi;
+ *   IncrementTask(long[] array, int lo, int hi) {
+ *     this.array = array; this.lo = lo; this.hi = hi;
+ *   }
+ *   protected void compute() {
+ *     if (hi - lo < THRESHOLD) {
+ *       for (int i = lo; i < hi; ++i)
+ *         array[i]++;
+ *     }
+ *     else {
+ *       int mid = (lo + hi) >>> 1;
+ *       invokeAll(new IncrementTask(array, lo, mid),
+ *                 new IncrementTask(array, mid, hi));
+ *     }
+ *   }
+ * }}</pre>
+ *
+ * <p>The following example illustrates some refinements and idioms
+ * that may lead to better performance: RecursiveActions need not be
+ * fully recursive, so long as they maintain the basic
+ * divide-and-conquer approach. Here is a class that sums the squares
+ * of each element of a double array, by subdividing out only the
+ * right-hand-sides of repeated divisions by two, and keeping track of
+ * them with a chain of {@code next} references. It uses a dynamic
+ * threshold based on method {@code getSurplusQueuedTaskCount}, but
+ * counterbalances potential excess partitioning by directly
+ * performing leaf actions on unstolen tasks rather than further
+ * subdividing.
+ *
+ *  <pre> {@code
+ * double sumOfSquares(ForkJoinPool pool, double[] array) {
+ *   int n = array.length;
+ *   Applyer a = new Applyer(array, 0, n, null);
+ *   pool.invoke(a);
+ *   return a.result;
+ * }
+ *
+ * class Applyer extends RecursiveAction {
+ *   final double[] array;
+ *   final int lo, hi;
+ *   double result;
+ *   Applyer next; // keeps track of right-hand-side tasks
+ *   Applyer(double[] array, int lo, int hi, Applyer next) {
+ *     this.array = array; this.lo = lo; this.hi = hi;
+ *     this.next = next;
+ *   }
+ *
+ *   double atLeaf(int l, int h) {
+ *     double sum = 0;
+ *     for (int i = l; i < h; ++i) // perform leftmost base step
+ *       sum += array[i] * array[i];
+ *     return sum;
+ *   }
+ *
+ *   protected void compute() {
+ *     int l = lo;
+ *     int h = hi;
+ *     Applyer right = null;
+ *     while (h - l > 1 && getSurplusQueuedTaskCount() <= 3) {
+ *       int mid = (l + h) >>> 1;
+ *       right = new Applyer(array, mid, h, right);
+ *       right.fork();
+ *       h = mid;
+ *     }
+ *     double sum = atLeaf(l, h);
+ *     while (right != null) {
+ *       if (right.tryUnfork()) // directly calculate if not stolen
+ *         sum += right.atLeaf(right.lo, right.hi);
+ *       else {
+ *         right.join();
+ *         sum += right.result;
+ *       }
+ *       right = right.next;
+ *     }
+ *     result = sum;
+ *   }
+ * }}</pre>
+ *
+ * @since 1.7
+ * @author Doug Lea
+ */
+public abstract class RecursiveAction extends ForkJoinTask<Void> {
+    private static final long serialVersionUID = 5232453952276485070L;
+
+    /**
+     * The main computation performed by this task.
+     */
+    protected abstract void compute();
+
+    /**
+     * Always returns {@code null}.
+     *
+     * @return {@code null} always
+     */
+    public final Void getRawResult() { return null; }
+
+    /**
+     * Requires null completion value.
+     */
+    protected final void setRawResult(Void mustBeNull) { }
+
+    /**
+     * Implements execution conventions for RecursiveActions.
+     */
+    protected final boolean exec() {
+        compute();
+        return true;
+    }
+
+}

+ 71 - 0
src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/RecursiveTask.java

@@ -0,0 +1,71 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package com.jeeplus.common.utils.concurrent.jsr166e;
+
+/**
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/RecursiveTask.java 1.4
+ * 
+ * A recursive result-bearing {@link ForkJoinTask}.
+ *
+ * <p>For a classic example, here is a task computing Fibonacci numbers:
+ *
+ *  <pre> {@code
+ * class Fibonacci extends RecursiveTask<Integer> {
+ *   final int n;
+ *   Fibonacci(int n) { this.n = n; }
+ *   protected Integer compute() {
+ *     if (n <= 1)
+ *       return n;
+ *     Fibonacci f1 = new Fibonacci(n - 1);
+ *     f1.fork();
+ *     Fibonacci f2 = new Fibonacci(n - 2);
+ *     return f2.compute() + f1.join();
+ *   }
+ * }}</pre>
+ *
+ * However, besides being a dumb way to compute Fibonacci functions
+ * (there is a simple fast linear algorithm that you'd use in
+ * practice), this is likely to perform poorly because the smallest
+ * subtasks are too small to be worthwhile splitting up. Instead, as
+ * is the case for nearly all fork/join applications, you'd pick some
+ * minimum granularity size (for example 10 here) for which you always
+ * sequentially solve rather than subdividing.
+ *
+ * @since 1.7
+ * @author Doug Lea
+ */
+public abstract class RecursiveTask<V> extends ForkJoinTask<V> {
+    private static final long serialVersionUID = 5232453952276485270L;
+
+    /**
+     * The result of the computation.
+     */
+    V result;
+
+    /**
+     * The main computation performed by this task.
+     * @return the result of the computation
+     */
+    protected abstract V compute();
+
+    public final V getRawResult() {
+        return result;
+    }
+
+    protected final void setRawResult(V value) {
+        result = value;
+    }
+
+    /**
+     * Implements execution conventions for RecursiveTask.
+     */
+    protected final boolean exec() {
+        result = compute();
+        return true;
+    }
+
+}

+ 336 - 0
src/main/java/com/jeeplus/common/utils/concurrent/jsr166e/Striped64.java

@@ -0,0 +1,336 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package com.jeeplus.common.utils.concurrent.jsr166e;
+
+import java.util.Random;
+
+/**
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java 1.10
+ * 
+ * A package-local class holding common representation and mechanics
+ * for classes supporting dynamic striping on 64bit values. The class
+ * extends Number so that concrete subclasses must publicly do so.
+ */
+public abstract class Striped64 extends Number {
+    /*
+     * This class maintains a lazily-initialized table of atomically
+     * updated variables, plus an extra "base" field. The table size
+     * is a power of two. Indexing uses masked per-thread hash codes.
+     * Nearly all declarations in this class are package-private,
+     * accessed directly by subclasses.
+     *
+     * Table entries are of class Cell; a variant of AtomicLong padded
+     * to reduce cache contention on most processors. Padding is
+     * overkill for most Atomics because they are usually irregularly
+     * scattered in memory and thus don't interfere much with each
+     * other. But Atomic objects residing in arrays will tend to be
+     * placed adjacent to each other, and so will most often share
+     * cache lines (with a huge negative performance impact) without
+     * this precaution.
+     *
+     * In part because Cells are relatively large, we avoid creating
+     * them until they are needed.  When there is no contention, all
+     * updates are made to the base field.  Upon first contention (a
+     * failed CAS on base update), the table is initialized to size 2.
+     * The table size is doubled upon further contention until
+     * reaching the nearest power of two greater than or equal to the
+     * number of CPUS. Table slots remain empty (null) until they are
+     * needed.
+     *
+     * A single spinlock ("busy") is used for initializing and
+     * resizing the table, as well as populating slots with new Cells.
+     * There is no need for a blocking lock; when the lock is not
+     * available, threads try other slots (or the base).  During these
+     * retries, there is increased contention and reduced locality,
+     * which is still better than alternatives.
+     *
+     * Per-thread hash codes are initialized to random values.
+     * Contention and/or table collisions are indicated by failed
+     * CASes when performing an update operation (see method
+     * retryUpdate). Upon a collision, if the table size is less than
+     * the capacity, it is doubled in size unless some other thread
+     * holds the lock. If a hashed slot is empty, and lock is
+     * available, a new Cell is created. Otherwise, if the slot
+     * exists, a CAS is tried.  Retries proceed by "double hashing",
+     * using a secondary hash (Marsaglia XorShift) to try to find a
+     * free slot.
+     *
+     * The table size is capped because, when there are more threads
+     * than CPUs, supposing that each thread were bound to a CPU,
+     * there would exist a perfect hash function mapping threads to
+     * slots that eliminates collisions. When we reach capacity, we
+     * search for this mapping by randomly varying the hash codes of
+     * colliding threads.  Because search is random, and collisions
+     * only become known via CAS failures, convergence can be slow,
+     * and because threads are typically not bound to CPUS forever,
+     * may not occur at all. However, despite these limitations,
+     * observed contention rates are typically low in these cases.
+     *
+     * It is possible for a Cell to become unused when threads that
+     * once hashed to it terminate, as well as in the case where
+     * doubling the table causes no thread to hash to it under
+     * expanded mask.  We do not try to detect or remove such cells,
+     * under the assumption that for long-running instances, observed
+     * contention levels will recur, so the cells will eventually be
+     * needed again; and for short-lived ones, it does not matter.
+     */
+
+    /**
+     * Padded variant of AtomicLong supporting only raw accesses plus CAS.
+     * The value field is placed between pads, hoping that the JVM doesn't
+     * reorder them.
+     *
+     * JVM intrinsics note: It would be possible to use a release-only
+     * form of CAS here, if it were provided.
+     */
+    static final class Cell {
+        volatile long p0, p1, p2, p3, p4, p5, p6;
+        volatile long value;
+        volatile long q0, q1, q2, q3, q4, q5, q6;
+        Cell(long x) { value = x; }
+
+        final boolean cas(long cmp, long val) {
+            return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
+        }
+
+        // Unsafe mechanics
+        private static final sun.misc.Unsafe UNSAFE;
+        private static final long valueOffset;
+        static {
+            try {
+                UNSAFE = getUnsafe();
+                Class<?> ak = Cell.class;
+                valueOffset = UNSAFE.objectFieldOffset
+                    (ak.getDeclaredField("value"));
+            } catch (Exception e) {
+                throw new Error(e);
+            }
+        }
+
+    }
+
+    /**
+     * ThreadLocal holding a single-slot int array holding hash code.
+     * Unlike the JDK8 version of this class, we use a suboptimal
+     * int[] representation to avoid introducing a new type that can
+     * impede class-unloading when ThreadLocals are not removed.
+     */
+    static final ThreadLocal<int[]> threadHashCode = new ThreadLocal<int[]>();
+
+    /**
+     * Generator of new random hash codes
+     */
+    static final Random rng = new Random();
+
+    /** Number of CPUS, to place bound on table size */
+    static final int NCPU = Runtime.getRuntime().availableProcessors();
+
+    /**
+     * Table of cells. When non-null, size is a power of 2.
+     */
+    transient volatile Cell[] cells;
+
+    /**
+     * Base value, used mainly when there is no contention, but also as
+     * a fallback during table initialization races. Updated via CAS.
+     */
+    transient volatile long base;
+
+    /**
+     * Spinlock (locked via CAS) used when resizing and/or creating Cells.
+     */
+    transient volatile int busy;
+
+    /**
+     * Package-private default constructor
+     */
+    Striped64() {
+    }
+
+    /**
+     * CASes the base field.
+     */
+    final boolean casBase(long cmp, long val) {
+        return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val);
+    }
+
+    /**
+     * CASes the busy field from 0 to 1 to acquire lock.
+     */
+    final boolean casBusy() {
+        return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1);
+    }
+
+    /**
+     * Computes the function of current and new value. Subclasses
+     * should open-code this update function for most uses, but the
+     * virtualized form is needed within retryUpdate.
+     *
+     * @param currentValue the current value (of either base or a cell)
+     * @param newValue the argument from a user update call
+     * @return result of the update function
+     */
+    abstract long fn(long currentValue, long newValue);
+
+    /**
+     * Handles cases of updates involving initialization, resizing,
+     * creating new Cells, and/or contention. See above for
+     * explanation. This method suffers the usual non-modularity
+     * problems of optimistic retry code, relying on rechecked sets of
+     * reads.
+     *
+     * @param x the value
+     * @param hc the hash code holder
+     * @param wasUncontended false if CAS failed before call
+     */
+    final void retryUpdate(long x, int[] hc, boolean wasUncontended) {
+        int h;
+        if (hc == null) {
+            threadHashCode.set(hc = new int[1]); // Initialize randomly
+            int r = rng.nextInt(); // Avoid zero to allow xorShift rehash
+            h = hc[0] = (r == 0) ? 1 : r;
+        }
+        else
+            h = hc[0];
+        boolean collide = false;                // True if last slot nonempty
+        for (;;) {
+            Cell[] as; Cell a; int n; long v;
+            if ((as = cells) != null && (n = as.length) > 0) {
+                if ((a = as[(n - 1) & h]) == null) {
+                    if (busy == 0) {            // Try to attach new Cell
+                        Cell r = new Cell(x);   // Optimistically create
+                        if (busy == 0 && casBusy()) {
+                            boolean created = false;
+                            try {               // Recheck under lock
+                                Cell[] rs; int m, j;
+                                if ((rs = cells) != null &&
+                                    (m = rs.length) > 0 &&
+                                    rs[j = (m - 1) & h] == null) {
+                                    rs[j] = r;
+                                    created = true;
+                                }
+                            } finally {
+                                busy = 0;
+                            }
+                            if (created)
+                                break;
+                            continue;           // Slot is now non-empty
+                        }
+                    }
+                    collide = false;
+                }
+                else if (!wasUncontended)       // CAS already known to fail
+                    wasUncontended = true;      // Continue after rehash
+                else if (a.cas(v = a.value, fn(v, x)))
+                    break;
+                else if (n >= NCPU || cells != as)
+                    collide = false;            // At max size or stale
+                else if (!collide)
+                    collide = true;
+                else if (busy == 0 && casBusy()) {
+                    try {
+                        if (cells == as) {      // Expand table unless stale
+                            Cell[] rs = new Cell[n << 1];
+                            for (int i = 0; i < n; ++i)
+                                rs[i] = as[i];
+                            cells = rs;
+                        }
+                    } finally {
+                        busy = 0;
+                    }
+                    collide = false;
+                    continue;                   // Retry with expanded table
+                }
+                h ^= h << 13;                   // Rehash
+                h ^= h >>> 17;
+                h ^= h << 5;
+                hc[0] = h;                      // Record index for next time
+            }
+            else if (busy == 0 && cells == as && casBusy()) {
+                boolean init = false;
+                try {                           // Initialize table
+                    if (cells == as) {
+                        Cell[] rs = new Cell[2];
+                        rs[h & 1] = new Cell(x);
+                        cells = rs;
+                        init = true;
+                    }
+                } finally {
+                    busy = 0;
+                }
+                if (init)
+                    break;
+            }
+            else if (casBase(v = base, fn(v, x)))
+                break;                          // Fall back on using base
+        }
+    }
+
+
+    /**
+     * Sets base and all cells to the given value.
+     */
+    final void internalReset(long initialValue) {
+        Cell[] as = cells;
+        base = initialValue;
+        if (as != null) {
+            int n = as.length;
+            for (int i = 0; i < n; ++i) {
+                Cell a = as[i];
+                if (a != null)
+                    a.value = initialValue;
+            }
+        }
+    }
+
+    // Unsafe mechanics
+    private static final sun.misc.Unsafe UNSAFE;
+    private static final long baseOffset;
+    private static final long busyOffset;
+    static {
+        try {
+            UNSAFE = getUnsafe();
+            Class<?> sk = Striped64.class;
+            baseOffset = UNSAFE.objectFieldOffset
+                (sk.getDeclaredField("base"));
+            busyOffset = UNSAFE.objectFieldOffset
+                (sk.getDeclaredField("busy"));
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
+
+    /**
+     * Returns a sun.misc.Unsafe.  Suitable for use in a 3rd party package.
+     * Replace with a simple call to Unsafe.getUnsafe when integrating
+     * into a jdk.
+     *
+     * @return a sun.misc.Unsafe
+     */
+    private static sun.misc.Unsafe getUnsafe() {
+        try {
+            return sun.misc.Unsafe.getUnsafe();
+        } catch (SecurityException tryReflectionInstead) {}
+        try {
+            return java.security.AccessController.doPrivileged
+            (new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() {
+                public sun.misc.Unsafe run() throws Exception {
+                    Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
+                    for (java.lang.reflect.Field f : k.getDeclaredFields()) {
+                        f.setAccessible(true);
+                        Object x = f.get(null);
+                        if (k.isInstance(x))
+                            return k.cast(x);
+                    }
+                    throw new NoSuchFieldError("the Unsafe");
+                }});
+        } catch (java.security.PrivilegedActionException e) {
+            throw new RuntimeException("Could not initialize intrinsics",
+                                       e.getCause());
+        }
+    }
+}

+ 135 - 0
src/main/java/com/jeeplus/common/utils/concurrent/threadpool/QueuableCachedThreadPool.java

@@ -0,0 +1,135 @@
+package com.jeeplus.common.utils.concurrent.threadpool;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * From Tomcat 8.5.6, 传统的FixedThreadPool有Queue但线程数量不变,而CachedThreadPool线程数可变但没有Queue
+ * 
+ * Tomcat的线程池,通过控制TaskQueue,线程数,但线程数到达最大时会进入Queue中.
+ * 
+ * 代码从Tomcat复制,主要修改包括:
+ * 
+ * 1. 删除定期重启线程避免内存泄漏的功能,
+ * 
+ * 2. TaskQueue中可能3次有锁的读取线程数量,改为只读取1次,这把锁也是这个实现里的唯一遗憾了。
+ * 
+ * @author calvin
+ *
+ */
+public class QueuableCachedThreadPool extends java.util.concurrent.ThreadPoolExecutor {
+
+	/**
+	 * The number of tasks submitted but not yet finished. This includes tasks in the queue and tasks that have been
+	 * handed to a worker thread but the latter did not start executing the task yet. This number is always greater or
+	 * equal to {@link #getActiveCount()}.
+	 */
+	private final AtomicInteger submittedCount = new AtomicInteger(0);
+
+	public QueuableCachedThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
+			ControllableQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
+		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
+		workQueue.setParent(this);
+	}
+
+	@Override
+	protected void afterExecute(Runnable r, Throwable t) {
+		submittedCount.decrementAndGet();
+	}
+
+	public int getSubmittedCount() {
+		return submittedCount.get();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void execute(Runnable command) {
+		execute(command, 0, TimeUnit.MILLISECONDS);
+	}
+
+	/**
+	 * Executes the given command at some time in the future. The command may execute in a new thread, in a pooled
+	 * thread, or in the calling thread, at the discretion of the <tt>Executor</tt> implementation. If no threads are
+	 * available, it will be added to the work queue. If the work queue is full, the system will wait for the specified
+	 * time and it throw a RejectedExecutionException if the queue is still full after that.
+	 *
+	 * @param command the runnable task
+	 * @param timeout A timeout for the completion of the task
+	 * @param unit The timeout time unit
+	 * @throws RejectedExecutionException if this task cannot be accepted for execution - the queue is full
+	 * @throws NullPointerException if command or unit is null
+	 */
+	public void execute(Runnable command, long timeout, TimeUnit unit) {
+		submittedCount.incrementAndGet();
+		try {
+			super.execute(command);
+		} catch (RejectedExecutionException rx) {
+			final ControllableQueue queue = (ControllableQueue) super.getQueue();
+			try {
+				if (!queue.force(command, timeout, unit)) {
+					submittedCount.decrementAndGet();
+					throw new RejectedExecutionException("Queue capacity is full.");
+				}
+			} catch (InterruptedException x) {
+				submittedCount.decrementAndGet();
+				throw new RejectedExecutionException(x);
+			}
+		}
+	}
+
+	public static class ControllableQueue extends LinkedBlockingQueue<Runnable> {
+
+		private static final long serialVersionUID = 5044057462066661171L;
+		private volatile QueuableCachedThreadPool parent = null;
+
+		public ControllableQueue(int capacity) {
+			super(capacity);
+		}
+
+		public void setParent(QueuableCachedThreadPool tp) {
+			parent = tp;
+		}
+
+		public boolean force(Runnable o) {
+			if (parent.isShutdown()) {
+				throw new RejectedExecutionException("Executor not running, can't force a command into the queue");
+			}
+			return super.offer(o); // forces the item onto the queue, to be used if the task is rejected
+		}
+
+		public boolean force(Runnable o, long timeout, TimeUnit unit) throws InterruptedException {
+			if (parent.isShutdown()) {
+				throw new RejectedExecutionException("Executor not running, can't force a command into the queue");
+			}
+			return super.offer(o, timeout, unit); // forces the item onto the queue, to be used if the task is rejected
+		}
+
+		@Override
+		public boolean offer(Runnable o) {
+			// springside: threadPool.getPoolSize() 是个有锁的操作,所以尽量减少
+
+			int currentPoolSize = parent.getPoolSize();
+
+			// we are maxed out on threads, simply queue the object
+			if (currentPoolSize >= parent.getMaximumPoolSize()) {
+				return super.offer(o);
+			}
+			// we have idle threads, just add it to the queue
+			if (parent.getSubmittedCount() < currentPoolSize) {
+				return super.offer(o);
+			}
+			// if we have less threads than maximum force creation of a new thread
+			if (currentPoolSize < parent.getMaximumPoolSize()) {
+				return false;
+			}
+			// if we reached here, we need to add it to the queue
+			return super.offer(o);
+		}
+	}
+}

+ 390 - 0
src/main/java/com/jeeplus/common/utils/concurrent/threadpool/ThreadPoolBuilder.java

@@ -0,0 +1,390 @@
+package com.jeeplus.common.utils.concurrent.threadpool;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang3.Validate;
+import com.jeeplus.common.utils.concurrent.threadpool.QueuableCachedThreadPool.ControllableQueue;
+
+/**
+ * ThreadPool创建的工具类.
+ * 
+ * 对比JDK Executors中的newFixedThreadPool(), newCachedThreadPool(),newScheduledThreadPool, 提供更多有用的配置项.
+ * 
+ * 另包含了移植自Tomcat的QueuableCachedPool.
+ * 
+ * 使用示例如下:
+ * 
+ * <pre>
+ * ExecutorService ExecutorService = new FixedThreadPoolBuilder().setPoolSize(10).build();
+ * </pre>
+ * 
+ * 参考文章 《Java ThreadPool的正确打开方式》http://calvin1978.blogcn.com/articles/java-threadpool.html
+ */
+public class ThreadPoolBuilder {
+
+	private static RejectedExecutionHandler defaultRejectHandler = new AbortPolicy();
+
+	/**
+	 * @see FixedThreadPoolBuilder
+	 */
+	public static FixedThreadPoolBuilder fixedPool() {
+		return new FixedThreadPoolBuilder();
+	}
+
+	/**
+	 * @see CacheedThreadPoolBuilder
+	 */
+	public static CachedThreadPoolBuilder cachedPool() {
+		return new CachedThreadPoolBuilder();
+	}
+
+	/**
+	 * @see ScheduledThreadPoolBuilder
+	 */
+	public static ScheduledThreadPoolBuilder scheduledPool() {
+		return new ScheduledThreadPoolBuilder();
+	}
+
+	/**
+	 * @see QueuableCachedThreadPoolBuilder
+	 */
+	public static QueuableCachedThreadPoolBuilder queuableCachedPool() {
+		return new QueuableCachedThreadPoolBuilder();
+	}
+
+	/**
+	 * 创建FixedThreadPool.
+	 * 
+	 * 1. 任务提交时, 如果线程数还没达到poolSize即创建新线程并绑定任务(即poolSize次提交后线程总数必达到poolSize,不会重用之前的线程)
+	 * 
+	 * poolSize默认为1,即singleThreadPool.
+	 * 
+	 * 2. 第poolSize次任务提交后, 新增任务放入Queue中, Pool中的所有线程从Queue中take任务执行.
+	 * 
+	 * Queue默认为无限长的LinkedBlockingQueue, 也可以设置queueSize换成有界的队列.
+	 * 
+	 * 如果使用有界队列, 当队列满了之后,会调用RejectHandler进行处理, 默认为AbortPolicy,抛出RejectedExecutionException异常.
+	 * 其他可选的Policy包括静默放弃当前任务(Discard),放弃Queue里最老的任务(DisacardOldest),或由主线程来直接执行(CallerRuns).
+	 * 
+	 * 3. 因为线程全部为core线程,所以不会在空闲回收.
+	 */
+	public static class FixedThreadPoolBuilder {
+
+		private int poolSize = 1;
+		private int queueSize = -1;
+
+		private ThreadFactory threadFactory = null;
+		private String threadNamePrefix = null;
+		private Boolean daemon = null;
+
+		private RejectedExecutionHandler rejectHandler;
+
+		/**
+		 * Pool大小,默认为1,即singleThreadPool
+		 */
+		public FixedThreadPoolBuilder setPoolSize(int poolSize) {
+			Validate.isTrue(poolSize >= 1);
+			this.poolSize = poolSize;
+			return this;
+		}
+
+		/**
+		 * 默认为-1, 使用无限长的LinkedBlockingQueue,为正数时使用ArrayBlockingQueue
+		 */
+		public FixedThreadPoolBuilder setQueueSize(int queueSize) {
+			this.queueSize = queueSize;
+			return this;
+		}
+
+		/**
+		 * 与threadNamePrefix互斥, 优先使用ThreadFactory
+		 */
+		public FixedThreadPoolBuilder setThreadFactory(ThreadFactory threadFactory) {
+			this.threadFactory = threadFactory;
+			return this;
+		}
+
+		/**
+		 * 与ThreadFactory互斥, 优先使用ThreadFactory
+		 */
+		public FixedThreadPoolBuilder setThreadNamePrefix(String threadNamePrefix) {
+			this.threadNamePrefix = threadNamePrefix;
+			return this;
+		}
+
+		/**
+		 * 与threadFactory互斥, 优先使用ThreadFactory
+		 * 
+		 * 默认为NULL,不进行设置,使用JDK的默认值.
+		 */
+		public FixedThreadPoolBuilder setDaemon(Boolean daemon) {
+			this.daemon = daemon;
+			return this;
+		}
+
+		public FixedThreadPoolBuilder setRejectHanlder(RejectedExecutionHandler rejectHandler) {
+			this.rejectHandler = rejectHandler;
+			return this;
+		}
+
+		public ThreadPoolExecutor build() {
+			BlockingQueue<Runnable> queue = null;
+			if (queueSize < 1) {
+				queue = new LinkedBlockingQueue<Runnable>();
+			} else {
+				queue = new ArrayBlockingQueue<Runnable>(queueSize);
+			}
+
+			threadFactory = createThreadFactory(threadFactory, threadNamePrefix, daemon);
+
+			if (rejectHandler == null) {
+				rejectHandler = defaultRejectHandler;
+			}
+
+			return new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS, queue, threadFactory,
+					rejectHandler);
+		}
+	}
+
+	/**
+	 * 创建CachedThreadPool.
+	 * 
+	 * 1. 任务提交时, 如果线程数还没达到minSize即创建新线程并绑定任务(即minSize次提交后线程总数必达到minSize, 不会重用之前的线程)
+	 * 
+	 * minSize默认为0, 可设置保证有基本的线程处理请求不被回收.
+	 * 
+	 * 2. 第minSize次任务提交后, 新增任务提交进SynchronousQueue后,如果没有空闲线程立刻处理,则会创建新的线程, 直到总线程数达到上限.
+	 * 
+	 * maxSize默认为Integer.Max, 可进行设置.
+	 * 
+	 * 如果设置了maxSize, 当总线程数达到上限, 会调用RejectHandler进行处理, 默认为AbortPolicy, 抛出RejectedExecutionException异常.
+	 * 其他可选的Policy包括静默放弃当前任务(Discard),或由主线程来直接执行(CallerRuns).
+	 * 
+	 * 3. minSize以上, maxSize以下的线程, 如果在keepAliveTime中都poll不到任务执行将会被结束掉, keeAliveTimeJDK默认为10秒.
+	 * JDK默认值60秒太高,如高达1000线程时,要低于16QPS时才会开始回收线程, 因此改为默认10秒.
+	 */
+	public static class CachedThreadPoolBuilder {
+
+		private int minSize = 0;
+		private int maxSize = Integer.MAX_VALUE;
+		private int keepAliveSecs = 10;
+
+		private ThreadFactory threadFactory = null;
+		private String threadNamePrefix = null;
+		private Boolean daemon = null;
+
+		private RejectedExecutionHandler rejectHandler;
+
+		public CachedThreadPoolBuilder setMinSize(int minSize) {
+			this.minSize = minSize;
+			return this;
+		}
+
+		public CachedThreadPoolBuilder setMaxSize(int maxSize) {
+			this.maxSize = maxSize;
+			return this;
+		}
+
+		/**
+		 * JDK默认值60秒太高,如高达1000线程时,要低于16QPS时才会开始回收线程, 因此改为默认10秒.
+		 */
+		public CachedThreadPoolBuilder setKeepAliveSecs(int keepAliveSecs) {
+			this.keepAliveSecs = keepAliveSecs;
+			return this;
+		}
+
+		/**
+		 * 与threadNamePrefix互斥, 优先使用ThreadFactory
+		 */
+		public CachedThreadPoolBuilder setThreadFactory(ThreadFactory threadFactory) {
+			this.threadFactory = threadFactory;
+			return this;
+		}
+
+		/**
+		 * 与threadFactory互斥, 优先使用ThreadFactory
+		 */
+		public CachedThreadPoolBuilder setThreadNamePrefix(String threadNamePrefix) {
+			this.threadNamePrefix = threadNamePrefix;
+			return this;
+		}
+
+		/**
+		 * 与threadFactory互斥, 优先使用ThreadFactory
+		 * 
+		 * 默认为NULL,不进行设置,使用JDK的默认值.
+		 */
+		public CachedThreadPoolBuilder setDaemon(Boolean daemon) {
+			this.daemon = daemon;
+			return this;
+		}
+
+		public CachedThreadPoolBuilder setRejectHanlder(RejectedExecutionHandler rejectHandler) {
+			this.rejectHandler = rejectHandler;
+			return this;
+		}
+
+		public ThreadPoolExecutor build() {
+
+			threadFactory = createThreadFactory(threadFactory, threadNamePrefix, daemon);
+
+			if (rejectHandler == null) {
+				rejectHandler = defaultRejectHandler;
+			}
+
+			return new ThreadPoolExecutor(minSize, maxSize, keepAliveSecs, TimeUnit.SECONDS,
+					new SynchronousQueue<Runnable>(), threadFactory, rejectHandler);
+		}
+	}
+
+	/*
+	 * 创建ScheduledPool.
+	 */
+	public static class ScheduledThreadPoolBuilder {
+
+		private int poolSize = 1;
+		private ThreadFactory threadFactory = null;
+		private String threadNamePrefix = null;
+
+		/**
+		 * 默认为1
+		 */
+		public ScheduledThreadPoolBuilder setPoolSize(int poolSize) {
+			this.poolSize = poolSize;
+			return this;
+		}
+
+		/**
+		 * 与threadNamePrefix互斥, 优先使用ThreadFactory
+		 */
+		public ScheduledThreadPoolBuilder setThreadFactory(ThreadFactory threadFactory) {
+			this.threadFactory = threadFactory;
+			return this;
+		}
+
+		public ScheduledThreadPoolBuilder setThreadNamePrefix(String threadNamePrefix) {
+			this.threadNamePrefix = threadNamePrefix;
+			return this;
+		}
+
+		public ScheduledThreadPoolExecutor build() {
+			threadFactory = createThreadFactory(threadFactory, threadNamePrefix, Boolean.TRUE);
+			return new ScheduledThreadPoolExecutor(poolSize, threadFactory);
+		}
+	}
+
+	/**
+	 * 从Tomcat移植过来的可扩展可用Queue缓存任务的ThreadPool
+	 * 
+	 * @see QueuableCachedThreadPool
+	 */
+	public static class QueuableCachedThreadPoolBuilder {
+
+		private int minSize = 0;
+		private int maxSize = Integer.MAX_VALUE;
+		private int keepAliveSecs = 10;
+		private int queueSize = 100;
+
+		private ThreadFactory threadFactory = null;
+		private String threadNamePrefix = null;
+		private Boolean daemon = null;
+
+		private RejectedExecutionHandler rejectHandler;
+
+		public QueuableCachedThreadPoolBuilder setMinSize(int minSize) {
+			this.minSize = minSize;
+			return this;
+		}
+
+		public QueuableCachedThreadPoolBuilder setMaxSize(int maxSize) {
+			this.maxSize = maxSize;
+			return this;
+		}
+
+		/**
+		 * LinkedQueue长度, 默认100
+		 */
+		public QueuableCachedThreadPoolBuilder setQueueSize(int queueSize) {
+			this.queueSize = queueSize;
+			return this;
+		}
+
+		public QueuableCachedThreadPoolBuilder setKeepAliveSecs(int keepAliveSecs) {
+			this.keepAliveSecs = keepAliveSecs;
+			return this;
+		}
+
+		/**
+		 * 与threadNamePrefix互斥, 优先使用ThreadFactory
+		 */
+		public QueuableCachedThreadPoolBuilder setThreadFactory(ThreadFactory threadFactory) {
+			this.threadFactory = threadFactory;
+			return this;
+		}
+
+		/**
+		 * 与threadFactory互斥, 优先使用ThreadFactory
+		 */
+		public QueuableCachedThreadPoolBuilder setThreadNamePrefix(String threadNamePrefix) {
+			this.threadNamePrefix = threadNamePrefix;
+			return this;
+		}
+
+		/**
+		 * 与threadFactory互斥, 优先使用ThreadFactory
+		 * 
+		 * 默认为NULL,不进行设置,使用JDK的默认值.
+		 */
+		public QueuableCachedThreadPoolBuilder setDaemon(Boolean daemon) {
+			this.daemon = daemon;
+			return this;
+		}
+
+		public QueuableCachedThreadPoolBuilder setRejectHanlder(RejectedExecutionHandler rejectHandler) {
+			this.rejectHandler = rejectHandler;
+			return this;
+		}
+
+		public QueuableCachedThreadPool build() {
+
+			threadFactory = createThreadFactory(threadFactory, threadNamePrefix, daemon);
+
+			if (rejectHandler == null) {
+				rejectHandler = defaultRejectHandler;
+			}
+
+			return new QueuableCachedThreadPool(minSize, maxSize, keepAliveSecs, TimeUnit.SECONDS,
+					new ControllableQueue(queueSize), threadFactory, rejectHandler);
+		}
+	}
+
+	/**
+	 * 优先使用threadFactory,否则如果threadNamePrefix不为空则使用自建ThreadFactory,否则使用defaultThreadFactory
+	 */
+	private static ThreadFactory createThreadFactory(ThreadFactory threadFactory, String threadNamePrefix,
+			Boolean daemon) {
+		if (threadFactory != null) {
+			return threadFactory;
+		}
+
+		if (threadNamePrefix != null) {
+			if (daemon != null) {
+				return ThreadPoolUtil.buildThreadFactory(threadNamePrefix, daemon);
+			} else {
+				return ThreadPoolUtil.buildThreadFactory(threadNamePrefix);
+			}
+		}
+
+		return Executors.defaultThreadFactory();
+	}
+}

+ 114 - 0
src/main/java/com/jeeplus/common/utils/concurrent/threadpool/ThreadPoolUtil.java

@@ -0,0 +1,114 @@
+package com.jeeplus.common.utils.concurrent.threadpool;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang3.Validate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.jeeplus.common.utils.base.annotation.NotNull;
+import com.jeeplus.common.utils.base.annotation.Nullable;
+
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+/**
+ * 线程池工具集
+ * 
+ * 1. 优雅关闭线程池的(via guava)
+ * 
+ * 2. 创建可自定义线程名的ThreadFactory(via guava)
+ * 
+ * 3. 防止第三方Runnable未捕捉异常导致线程跑飞
+ * 
+ * @author calvin
+ *
+ */
+public class ThreadPoolUtil {
+
+	/**
+	 * 按照ExecutorService JavaDoc示例代码编写的Graceful Shutdown方法.
+	 * 
+	 * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
+	 * 
+	 * 如果1/2超时时间后, 则调用shutdownNow,取消在workQueue中Pending的任务,并中断所有阻塞函数.
+	 * 
+	 * 如果1/2超时仍然超時,則強制退出.
+	 * 
+	 * 另对在shutdown时线程本身被调用中断做了处理.
+	 * 
+	 * 返回线程最后是否被中断.
+	 * 
+	 * 使用了Guava的工具类
+	 * @see MoreExecutors#shutdownAndAwaitTermination(ExecutorService, long, TimeUnit)
+	 */
+	public static boolean gracefulShutdown(@Nullable ExecutorService threadPool, int shutdownTimeoutMills) {
+		return threadPool != null
+				? MoreExecutors.shutdownAndAwaitTermination(threadPool, shutdownTimeoutMills, TimeUnit.MILLISECONDS)
+				: true;
+	}
+
+	/**
+	 * @see gracefulShutdown
+	 */
+	public static boolean gracefulShutdown(@Nullable ExecutorService threadPool, int shutdownTimeout,
+			TimeUnit timeUnit) {
+		return threadPool != null ? MoreExecutors.shutdownAndAwaitTermination(threadPool, shutdownTimeout, timeUnit)
+				: true;
+	}
+
+	/**
+	 * 创建ThreadFactory,使得创建的线程有自己的名字而不是默认的"pool-x-thread-y"
+	 * 
+	 * 使用了Guava的工具类
+	 * 
+	 * @see ThreadFactoryBuilder#build()
+	 */
+	public static ThreadFactory buildThreadFactory(@NotNull String threadNamePrefix) {
+		return new ThreadFactoryBuilder().setNameFormat(threadNamePrefix + "-%d").build();
+	}
+
+	/**
+	 * 可设定是否daemon, daemon线程在主线程已执行完毕时, 不会阻塞应用不退出, 而非daemon线程则会阻塞.
+	 * 
+	 * @see buildThreadFactory
+	 */
+	public static ThreadFactory buildThreadFactory(@NotNull String threadNamePrefix, @NotNull boolean daemon) {
+		return new ThreadFactoryBuilder().setNameFormat(threadNamePrefix + "-%d").setDaemon(daemon).build();
+	}
+
+	/**
+	 * 防止用户没有捕捉异常导致中断了线程池中的线程, 使得SchedulerService无法继续执行.
+	 * 
+	 * 在无法控制第三方包的Runnable实现时,调用本函数进行包裹.
+	 */
+	public static Runnable safeRunnable(@NotNull Runnable runnable) {
+		return new SafeRunnable(runnable);
+	}
+
+	/**
+	 * 保证不会有Exception抛出到线程池的Runnable包裹类,防止用户没有捕捉异常导致中断了线程池中的线程, 使得SchedulerService无法执行. 在无法控制第三方包的Runnalbe实现时,使用本类进行包裹.
+	 */
+	public static class SafeRunnable implements Runnable {
+
+		private static Logger logger = LoggerFactory.getLogger(SafeRunnable.class);
+
+		private Runnable runnable;
+
+		public SafeRunnable(Runnable runnable) {
+			Validate.notNull(runnable);
+			this.runnable = runnable;
+		}
+
+		@Override
+		public void run() {
+			try {
+				runnable.run();
+			} catch (Throwable e) {
+				// catch any exception, because the scheduled thread will break if the exception thrown to outside.
+				logger.error("Unexpected error occurred in task", e);
+			}
+		}
+	}
+}

+ 72 - 0
src/main/java/com/jeeplus/common/utils/concurrent/throttle/Sampler.java

@@ -0,0 +1,72 @@
+package com.jeeplus.common.utils.concurrent.throttle;
+
+import org.apache.commons.lang3.Validate;
+import com.jeeplus.common.utils.number.RandomUtil;
+
+/**
+ * 采样器
+ * 
+ * from Twitter Common, 优化使用ThreadLocalRandom
+ * 
+ * @author calvin
+ */
+public class Sampler {
+
+	private static final Double ALWAYS = Double.valueOf(100);
+	private static final Double NEVER = Double.valueOf(0);
+
+	private double threshold;
+
+	protected Sampler() {
+	}
+
+	/**
+	 * @param selectPercent 采样率,在0-100 之间,可以有小数位
+	 */
+	protected Sampler(double selectPercent) {
+		Validate.isTrue((selectPercent >= 0) && (selectPercent <= 100),
+				"Invalid selectPercent value: " + selectPercent);
+
+		this.threshold = selectPercent / 100;
+	}
+
+	/**
+	 * 优化的创建函数,如果为0或100时,返回更直接的采样器
+	 */
+	public static Sampler create(Double selectPercent) {
+		if (selectPercent.equals(ALWAYS)) {
+			return new AlwaysSampler();
+		} else if (selectPercent.equals(NEVER)) {
+			return new NeverSampler();
+		} else {
+			return new Sampler(selectPercent);
+		}
+	}
+
+	/**
+	 * 判断当前请求是否命中采样
+	 */
+	public boolean select() {
+		return RandomUtil.threadLocalRandom().nextDouble() < threshold;
+	}
+
+	/**
+	 * 采样率为100时,总是返回true
+	 */
+	public static class AlwaysSampler extends Sampler {
+		@Override
+		public boolean select() {
+			return true;
+		}
+	}
+
+	/**
+	 * 采样率为0时,总是返回false
+	 */
+	public static class NeverSampler extends Sampler {
+		@Override
+		public boolean select() {
+			return false;
+		}
+	}
+}

+ 477 - 0
src/main/java/com/jeeplus/common/utils/excel/ExportExcel.java

@@ -0,0 +1,477 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils.excel;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Comment;
+import org.apache.poi.ss.usermodel.DataFormat;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
+import org.apache.poi.xssf.usermodel.XSSFRichTextString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Lists;
+import com.jeeplus.common.utils.Encodes;
+import com.jeeplus.common.utils.Reflections;
+import com.jeeplus.common.utils.excel.annotation.ExcelField;
+import com.jeeplus.modules.sys.utils.DictUtils;
+
+/**
+ * 导出Excel文件(导出“XLSX”格式,支持大数据量导出   @see org.apache.poi.ss.SpreadsheetVersion)
+ * @author jeeplus
+ * @version 2016-04-21
+ */
+public class ExportExcel {
+	
+	private static Logger log = LoggerFactory.getLogger(ExportExcel.class);
+			
+	/**
+	 * 工作薄对象
+	 */
+	private SXSSFWorkbook wb;
+	
+	/**
+	 * 工作表对象
+	 */
+	private Sheet sheet;
+	
+	/**
+	 * 样式列表
+	 */
+	private Map<String, CellStyle> styles;
+	
+	/**
+	 * 当前行号
+	 */
+	private int rownum;
+	
+	/**
+	 * 注解列表(Object[]{ ExcelField, Field/Method })
+	 */
+	List<Object[]> annotationList = Lists.newArrayList();
+	
+	/**
+	 * 构造函数
+	 * @param title 表格标题,传“空值”,表示无标题
+	 * @param cls 实体对象,通过annotation.ExportField获取标题
+	 */
+	public ExportExcel(String title, Class<?> cls){
+		this(title, cls, 1);
+	}
+	
+	/**
+	 * 构造函数
+	 * @param title 表格标题,传“空值”,表示无标题
+	 * @param cls 实体对象,通过annotation.ExportField获取标题
+	 * @param type 导出类型(1:导出数据;2:导出模板)
+	 * @param groups 导入分组
+	 */
+	public ExportExcel(String title, Class<?> cls, int type, int... groups){
+		// Get annotation field 
+		Field[] fs = cls.getDeclaredFields();
+		for (Field f : fs){
+			ExcelField ef = f.getAnnotation(ExcelField.class);
+			if (ef != null && (ef.type()==0 || ef.type()==type)){
+				if (groups!=null && groups.length>0){
+					boolean inGroup = false;
+					for (int g : groups){
+						if (inGroup){
+							break;
+						}
+						for (int efg : ef.groups()){
+							if (g == efg){
+								inGroup = true;
+								annotationList.add(new Object[]{ef, f});
+								break;
+							}
+						}
+					}
+				}else{
+					annotationList.add(new Object[]{ef, f});
+				}
+			}
+		}
+		// Get annotation method
+		Method[] ms = cls.getDeclaredMethods();
+		for (Method m : ms){
+			ExcelField ef = m.getAnnotation(ExcelField.class);
+			if (ef != null && (ef.type()==0 || ef.type()==type)){
+				if (groups!=null && groups.length>0){
+					boolean inGroup = false;
+					for (int g : groups){
+						if (inGroup){
+							break;
+						}
+						for (int efg : ef.groups()){
+							if (g == efg){
+								inGroup = true;
+								annotationList.add(new Object[]{ef, m});
+								break;
+							}
+						}
+					}
+				}else{
+					annotationList.add(new Object[]{ef, m});
+				}
+			}
+		}
+		// Field sorting
+		Collections.sort(annotationList, new Comparator<Object[]>() {
+			public int compare(Object[] o1, Object[] o2) {
+				return new Integer(((ExcelField)o1[0]).sort()).compareTo(
+						new Integer(((ExcelField)o2[0]).sort()));
+			};
+		});
+		// Initialize
+		List<String> headerList = Lists.newArrayList();
+		for (Object[] os : annotationList){
+			String t = ((ExcelField)os[0]).title();
+			// 如果是导出,则去掉注释
+			if (type==1){
+				String[] ss = StringUtils.split(t, "**", 2);
+				if (ss.length==2){
+					t = ss[0];
+				}
+			}
+			headerList.add(t);
+		}
+		initialize(title, headerList);
+	}
+	
+	/**
+	 * 构造函数
+	 * @param title 表格标题,传“空值”,表示无标题
+	 * @param headers 表头数组
+	 */
+	public ExportExcel(String title, String[] headers) {
+		initialize(title, Lists.newArrayList(headers));
+	}
+	
+	/**
+	 * 构造函数
+	 * @param title 表格标题,传“空值”,表示无标题
+	 * @param headerList 表头列表
+	 */
+	public ExportExcel(String title, List<String> headerList) {
+		initialize(title, headerList);
+	}
+	
+	/**
+	 * 初始化函数
+	 * @param title 表格标题,传“空值”,表示无标题
+	 * @param headerList 表头列表
+	 */
+	private void initialize(String title, List<String> headerList) {
+		this.wb = new SXSSFWorkbook(500);
+		this.sheet = wb.createSheet("Export");
+		this.styles = createStyles(wb);
+		// Create title
+		if (StringUtils.isNotBlank(title)){
+			Row titleRow = sheet.createRow(rownum++);
+			titleRow.setHeightInPoints(30);
+			Cell titleCell = titleRow.createCell(0);
+			titleCell.setCellStyle(styles.get("title"));
+			titleCell.setCellValue(title);
+			sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(),
+					titleRow.getRowNum(), titleRow.getRowNum(), headerList.size()-1));
+		}
+		// Create header
+		if (headerList == null){
+			throw new RuntimeException("headerList not null!");
+		}
+		Row headerRow = sheet.createRow(rownum++);
+		headerRow.setHeightInPoints(16);
+		for (int i = 0; i < headerList.size(); i++) {
+			Cell cell = headerRow.createCell(i);
+			cell.setCellStyle(styles.get("header"));
+			String[] ss = StringUtils.split(headerList.get(i), "**", 2);
+			if (ss.length==2){
+				cell.setCellValue(ss[0]);
+				Comment comment = this.sheet.createDrawingPatriarch().createCellComment(
+						new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6));
+				comment.setString(new XSSFRichTextString(ss[1]));
+				cell.setCellComment(comment);
+			}else{
+				cell.setCellValue(headerList.get(i));
+			}
+			sheet.autoSizeColumn(i);
+		}
+		for (int i = 0; i < headerList.size(); i++) {  
+			int colWidth = sheet.getColumnWidth(i)*2;
+	        sheet.setColumnWidth(i, colWidth < 3000 ? 3000 : colWidth);  
+		}
+		log.debug("Initialize success.");
+	}
+	
+	/**
+	 * 创建表格样式
+	 * @param wb 工作薄对象
+	 * @return 样式列表
+	 */
+	private Map<String, CellStyle> createStyles(Workbook wb) {
+		Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
+		
+		CellStyle style = wb.createCellStyle();
+		style.setAlignment(CellStyle.ALIGN_CENTER);
+		style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
+		Font titleFont = wb.createFont();
+		titleFont.setFontName("Arial");
+		titleFont.setFontHeightInPoints((short) 16);
+		titleFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
+		style.setFont(titleFont);
+		styles.put("title", style);
+
+		style = wb.createCellStyle();
+		style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
+		style.setBorderRight(CellStyle.BORDER_THIN);
+		style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+		style.setBorderLeft(CellStyle.BORDER_THIN);
+		style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+		style.setBorderTop(CellStyle.BORDER_THIN);
+		style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+		style.setBorderBottom(CellStyle.BORDER_THIN);
+		style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+		Font dataFont = wb.createFont();
+		dataFont.setFontName("Arial");
+		dataFont.setFontHeightInPoints((short) 10);
+		style.setFont(dataFont);
+		styles.put("data", style);
+		
+		style = wb.createCellStyle();
+		style.cloneStyleFrom(styles.get("data"));
+		style.setAlignment(CellStyle.ALIGN_LEFT);
+		styles.put("data1", style);
+
+		style = wb.createCellStyle();
+		style.cloneStyleFrom(styles.get("data"));
+		style.setAlignment(CellStyle.ALIGN_CENTER);
+		styles.put("data2", style);
+
+		style = wb.createCellStyle();
+		style.cloneStyleFrom(styles.get("data"));
+		style.setAlignment(CellStyle.ALIGN_RIGHT);
+		styles.put("data3", style);
+		
+		style = wb.createCellStyle();
+		style.cloneStyleFrom(styles.get("data"));
+//		style.setWrapText(true);
+		style.setAlignment(CellStyle.ALIGN_CENTER);
+		style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
+		style.setFillPattern(CellStyle.SOLID_FOREGROUND);
+		Font headerFont = wb.createFont();
+		headerFont.setFontName("Arial");
+		headerFont.setFontHeightInPoints((short) 10);
+		headerFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
+		headerFont.setColor(IndexedColors.WHITE.getIndex());
+		style.setFont(headerFont);
+		styles.put("header", style);
+		
+		return styles;
+	}
+
+	/**
+	 * 添加一行
+	 * @return 行对象
+	 */
+	public Row addRow(){
+		return sheet.createRow(rownum++);
+	}
+	
+
+	/**
+	 * 添加一个单元格
+	 * @param row 添加的行
+	 * @param column 添加列号
+	 * @param val 添加值
+	 * @return 单元格对象
+	 */
+	public Cell addCell(Row row, int column, Object val){
+		return this.addCell(row, column, val, 0, Class.class);
+	}
+	
+	/**
+	 * 添加一个单元格
+	 * @param row 添加的行
+	 * @param column 添加列号
+	 * @param val 添加值
+	 * @param align 对齐方式(1:靠左;2:居中;3:靠右)
+	 * @return 单元格对象
+	 */
+	public Cell addCell(Row row, int column, Object val, int align, Class<?> fieldType){
+		Cell cell = row.createCell(column);
+		CellStyle style = styles.get("data"+(align>=1&&align<=3?align:""));
+		try {
+			if (val == null){
+				cell.setCellValue("");
+			} else if (val instanceof String) {
+				cell.setCellValue((String) val);
+			} else if (val instanceof Integer) {
+				cell.setCellValue((Integer) val);
+			} else if (val instanceof Long) {
+				cell.setCellValue((Long) val);
+			} else if (val instanceof Double) {
+				cell.setCellValue((Double) val);
+			} else if (val instanceof Float) {
+				cell.setCellValue((Float) val);
+			} else if (val instanceof Date) {
+				DataFormat format = wb.createDataFormat();
+	            style.setDataFormat(format.getFormat("yyyy-MM-dd"));
+				cell.setCellValue((Date) val);
+			} else {
+				if (fieldType != Class.class){
+					cell.setCellValue((String)fieldType.getMethod("setValue", Object.class).invoke(null, val));
+				}else{
+					cell.setCellValue((String)Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(), 
+						"fieldtype."+val.getClass().getSimpleName()+"Type")).getMethod("setValue", Object.class).invoke(null, val));
+				}
+			}
+		} catch (Exception ex) {
+			log.info("Set cell value ["+row.getRowNum()+","+column+"] error: " + ex.toString());
+			cell.setCellValue(val.toString());
+		}
+		cell.setCellStyle(style);
+		return cell;
+	}
+
+	/**
+	 * 添加数据(通过annotation.ExportField添加数据)
+	 * @return list 数据列表
+	 */
+	public <E> ExportExcel setDataList(List<E> list){
+		for (E e : list){
+			int colunm = 0;
+			Row row = this.addRow();
+			StringBuilder sb = new StringBuilder();
+			for (Object[] os : annotationList){
+				ExcelField ef = (ExcelField)os[0];
+				Object val = null;
+				// Get entity value
+				try{
+					if (StringUtils.isNotBlank(ef.value())){
+						val = Reflections.invokeGetter(e, ef.value());
+					}else{
+						if (os[1] instanceof Field){
+							val = Reflections.invokeGetter(e, ((Field)os[1]).getName());
+						}else if (os[1] instanceof Method){
+							val = Reflections.invokeMethod(e, ((Method)os[1]).getName(), new Class[] {}, new Object[] {});
+						}
+					}
+					// If is dict, get dict label
+					if (StringUtils.isNotBlank(ef.dictType())){
+						val = DictUtils.getDictLabel(val==null?"":val.toString(), ef.dictType(), "");
+					}
+				}catch(Exception ex) {
+					// Failure to ignore
+					log.info(ex.toString());
+					val = "";
+				}
+				this.addCell(row, colunm++, val, ef.align(), ef.fieldType());
+				sb.append(val + ", ");
+			}
+			log.debug("Write success: ["+row.getRowNum()+"] "+sb.toString());
+		}
+		return this;
+	}
+	
+	/**
+	 * 输出数据流
+	 * @param os 输出数据流
+	 */
+	public ExportExcel write(OutputStream os) throws IOException{
+		wb.write(os);
+		return this;
+	}
+	
+	/**
+	 * 输出到客户端
+	 * @param fileName 输出文件名
+	 */
+	public ExportExcel write(HttpServletResponse response, String fileName) throws IOException{
+		response.reset();
+        response.setContentType("application/octet-stream; charset=utf-8");
+        response.setHeader("Content-Disposition", "attachment; filename="+Encodes.urlEncode(fileName));
+		write(response.getOutputStream());
+		return this;
+	}
+	
+	/**
+	 * 输出到文件
+	 * @param name 输出文件名
+	 */
+	public ExportExcel writeFile(String name) throws FileNotFoundException, IOException{
+		FileOutputStream os = new FileOutputStream(name);
+		this.write(os);
+		return this;
+	}
+	
+	/**
+	 * 清理临时文件
+	 */
+	public ExportExcel dispose(){
+		wb.dispose();
+		return this;
+	}
+	
+//	/**
+//	 * 导出测试
+//	 */
+//	public static void main(String[] args) throws Throwable {
+//		
+//		List<String> headerList = Lists.newArrayList();
+//		for (int i = 1; i <= 10; i++) {
+//			headerList.add("表头"+i);
+//		}
+//		
+//		List<String> dataRowList = Lists.newArrayList();
+//		for (int i = 1; i <= headerList.size(); i++) {
+//			dataRowList.add("数据"+i);
+//		}
+//		
+//		List<List<String>> dataList = Lists.newArrayList();
+//		for (int i = 1; i <=1000000; i++) {
+//			dataList.add(dataRowList);
+//		}
+//
+//		ExportExcel ee = new ExportExcel("表格标题", headerList);
+//		
+//		for (int i = 0; i < dataList.size(); i++) {
+//			Row row = ee.addRow();
+//			for (int j = 0; j < dataList.get(i).size(); j++) {
+//				ee.addCell(row, j, dataList.get(i).get(j));
+//			}
+//		}
+//		
+//		ee.writeFile("target/export.xlsx");
+//
+//		ee.dispose();
+//		
+//		log.debug("Export success.");
+//		
+//	}
+
+}

+ 397 - 0
src/main/java/com/jeeplus/common/utils/excel/ImportExcel.java

@@ -0,0 +1,397 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils.excel;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.text.NumberFormat;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.hssf.usermodel.HSSFDateUtil;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.google.common.collect.Lists;
+import com.jeeplus.common.utils.Reflections;
+import com.jeeplus.common.utils.excel.annotation.ExcelField;
+import com.jeeplus.modules.sys.entity.Area;
+import com.jeeplus.modules.sys.entity.Office;
+import com.jeeplus.modules.sys.entity.User;
+import com.jeeplus.modules.sys.utils.DictUtils;
+import com.jeeplus.modules.sys.utils.UserUtils;
+
+/**
+ * 导入Excel文件(支持“XLS”和“XLSX”格式)
+ * @author jeeplus
+ * @version 2016-03-10
+ */
+public class ImportExcel {
+	
+	private static Logger log = LoggerFactory.getLogger(ImportExcel.class);
+			
+	/**
+	 * 工作薄对象
+	 */
+	private Workbook wb;
+	
+	/**
+	 * 工作表对象
+	 */
+	private Sheet sheet;
+	
+	/**
+	 * 标题行号
+	 */
+	private int headerNum;
+	
+	/**
+	 * 构造函数
+	 * @param path 导入文件,读取第一个工作表
+	 * @param headerNum 标题行号,数据行号=标题行号+1
+	 * @throws InvalidFormatException 
+	 * @throws IOException 
+	 */
+	public ImportExcel(String fileName, int headerNum) 
+			throws InvalidFormatException, IOException {
+		this(new File(fileName), headerNum);
+	}
+	
+	/**
+	 * 构造函数
+	 * @param path 导入文件对象,读取第一个工作表
+	 * @param headerNum 标题行号,数据行号=标题行号+1
+	 * @throws InvalidFormatException 
+	 * @throws IOException 
+	 */
+	public ImportExcel(File file, int headerNum) 
+			throws InvalidFormatException, IOException {
+		this(file, headerNum, 0);
+	}
+
+	/**
+	 * 构造函数
+	 * @param path 导入文件
+	 * @param headerNum 标题行号,数据行号=标题行号+1
+	 * @param sheetIndex 工作表编号
+	 * @throws InvalidFormatException 
+	 * @throws IOException 
+	 */
+	public ImportExcel(String fileName, int headerNum, int sheetIndex) 
+			throws InvalidFormatException, IOException {
+		this(new File(fileName), headerNum, sheetIndex);
+	}
+	
+	/**
+	 * 构造函数
+	 * @param path 导入文件对象
+	 * @param headerNum 标题行号,数据行号=标题行号+1
+	 * @param sheetIndex 工作表编号
+	 * @throws InvalidFormatException 
+	 * @throws IOException 
+	 */
+	public ImportExcel(File file, int headerNum, int sheetIndex) 
+			throws InvalidFormatException, IOException {
+		this(file.getName(), new FileInputStream(file), headerNum, sheetIndex);
+	}
+	
+	/**
+	 * 构造函数
+	 * @param file 导入文件对象
+	 * @param headerNum 标题行号,数据行号=标题行号+1
+	 * @param sheetIndex 工作表编号
+	 * @throws InvalidFormatException 
+	 * @throws IOException 
+	 */
+	public ImportExcel(MultipartFile multipartFile, int headerNum, int sheetIndex) 
+			throws InvalidFormatException, IOException {
+		this(multipartFile.getOriginalFilename(), multipartFile.getInputStream(), headerNum, sheetIndex);
+	}
+
+	/**
+	 * 构造函数
+	 * @param path 导入文件对象
+	 * @param headerNum 标题行号,数据行号=标题行号+1
+	 * @param sheetIndex 工作表编号
+	 * @throws InvalidFormatException 
+	 * @throws IOException 
+	 */
+	public ImportExcel(String fileName, InputStream is, int headerNum, int sheetIndex) 
+			throws InvalidFormatException, IOException {
+		if (StringUtils.isBlank(fileName)){
+			throw new RuntimeException("导入文档为空!");
+		}else if(fileName.toLowerCase().endsWith("xls")){    
+			this.wb = new HSSFWorkbook(is);    
+        }else if(fileName.toLowerCase().endsWith("xlsx")){  
+        	this.wb = new XSSFWorkbook(is);
+        }else{  
+        	throw new RuntimeException("文档格式不正确!");
+        }  
+		if (this.wb.getNumberOfSheets()<sheetIndex){
+			throw new RuntimeException("文档中没有工作表!");
+		}
+		this.sheet = this.wb.getSheetAt(sheetIndex);
+		this.headerNum = headerNum;
+		log.debug("Initialize success.");
+	}
+	
+	/**
+	 * 获取行对象
+	 * @param rownum
+	 * @return
+	 */
+	public Row getRow(int rownum){
+		return this.sheet.getRow(rownum);
+	}
+
+	/**
+	 * 获取数据行号
+	 * @return
+	 */
+	public int getDataRowNum(){
+		return headerNum+1;
+	}
+	
+	/**
+	 * 获取最后一个数据行号
+	 * @return
+	 */
+	public int getLastDataRowNum(){
+		return this.sheet.getLastRowNum()+headerNum;
+	}
+	
+	/**
+	 * 获取最后一个列号
+	 * @return
+	 */
+	public int getLastCellNum(){
+		return this.getRow(headerNum).getLastCellNum();
+	}
+	
+	/**
+	 * 获取单元格值
+	 * @param row 获取的行
+	 * @param column 获取单元格列号
+	 * @return 单元格值
+	 */
+	public Object getCellValue(Row row, int column) {
+		Object val = "";
+		try {
+			Cell cell = row.getCell(column);
+			if (cell != null) {
+				if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
+					// val = cell.getNumericCellValue();
+					// 当excel 中的数据为数值或日期是需要特殊处理
+					if (HSSFDateUtil.isCellDateFormatted(cell)) {
+						double d = cell.getNumericCellValue();
+						Date date = HSSFDateUtil.getJavaDate(d);
+						SimpleDateFormat dformat = new SimpleDateFormat(
+								"yyyy-MM-dd");
+						val = dformat.format(date);
+					} else {
+						NumberFormat nf = NumberFormat.getInstance();
+						nf.setGroupingUsed(false);// true时的格式:1,234,567,890
+						val = nf.format(cell.getNumericCellValue());// 数值类型的数据为double,所以需要转换一下
+					}
+				} else if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
+					val = cell.getStringCellValue();
+				} else if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
+					val = cell.getCellFormula();
+				} else if (cell.getCellType() == Cell.CELL_TYPE_BOOLEAN) {
+					val = cell.getBooleanCellValue();
+				} else if (cell.getCellType() == Cell.CELL_TYPE_ERROR) {
+					val = cell.getErrorCellValue();
+				}
+			}
+		} catch (Exception e) {
+			return val;
+		}
+		return val;
+	}
+	
+	/**
+	 * 获取导入数据列表
+	 * @param cls 导入对象类型
+	 * @param groups 导入分组
+	 */
+	public <E> List<E> getDataList(Class<E> cls, int... groups) throws InstantiationException, IllegalAccessException{
+		List<Object[]> annotationList = Lists.newArrayList();
+		// Get annotation field 
+		Field[] fs = cls.getDeclaredFields();
+		for (Field f : fs){
+			ExcelField ef = f.getAnnotation(ExcelField.class);
+			if (ef != null && (ef.type()==0 || ef.type()==2)){
+				if (groups!=null && groups.length>0){
+					boolean inGroup = false;
+					for (int g : groups){
+						if (inGroup){
+							break;
+						}
+						for (int efg : ef.groups()){
+							if (g == efg){
+								inGroup = true;
+								annotationList.add(new Object[]{ef, f});
+								break;
+							}
+						}
+					}
+				}else{
+					annotationList.add(new Object[]{ef, f});
+				}
+			}
+		}
+		// Get annotation method
+		Method[] ms = cls.getDeclaredMethods();
+		for (Method m : ms){
+			ExcelField ef = m.getAnnotation(ExcelField.class);
+			if (ef != null && (ef.type()==0 || ef.type()==2)){
+				if (groups!=null && groups.length>0){
+					boolean inGroup = false;
+					for (int g : groups){
+						if (inGroup){
+							break;
+						}
+						for (int efg : ef.groups()){
+							if (g == efg){
+								inGroup = true;
+								annotationList.add(new Object[]{ef, m});
+								break;
+							}
+						}
+					}
+				}else{
+					annotationList.add(new Object[]{ef, m});
+				}
+			}
+		}
+		// Field sorting
+		Collections.sort(annotationList, new Comparator<Object[]>() {
+			public int compare(Object[] o1, Object[] o2) {
+				return new Integer(((ExcelField)o1[0]).sort()).compareTo(
+						new Integer(((ExcelField)o2[0]).sort()));
+			};
+		});
+		//log.debug("Import column count:"+annotationList.size());
+		// Get excel data
+		List<E> dataList = Lists.newArrayList();
+		for (int i = this.getDataRowNum(); i < this.getLastDataRowNum(); i++) {
+			E e = (E)cls.newInstance();
+			int column = 0;
+			Row row = this.getRow(i);
+			StringBuilder sb = new StringBuilder();
+			for (Object[] os : annotationList){
+				Object val = this.getCellValue(row, column++);
+				if (val != null){
+					ExcelField ef = (ExcelField)os[0];
+					// If is dict type, get dict value
+					if (StringUtils.isNotBlank(ef.dictType())){
+						val = DictUtils.getDictValue(val.toString(), ef.dictType(), "");
+						//log.debug("Dictionary type value: ["+i+","+colunm+"] " + val);
+					}
+					// Get param type and type cast
+					Class<?> valType = Class.class;
+					if (os[1] instanceof Field){
+						valType = ((Field)os[1]).getType();
+					}else if (os[1] instanceof Method){
+						Method method = ((Method)os[1]);
+						if ("get".equals(method.getName().substring(0, 3))){
+							valType = method.getReturnType();
+						}else if("set".equals(method.getName().substring(0, 3))){
+							valType = ((Method)os[1]).getParameterTypes()[0];
+						}
+					}
+					//log.debug("Import value type: ["+i+","+column+"] " + valType);
+					try {
+						//如果导入的java对象,需要在这里自己进行变换。
+						if (valType == String.class){
+							String s = String.valueOf(val.toString());
+							if(StringUtils.endsWith(s, ".0")){
+								val = StringUtils.substringBefore(s, ".0");
+							}else{
+								val = String.valueOf(val.toString());
+							}
+						}else if (valType == Integer.class){
+							val = Double.valueOf(val.toString()).intValue();
+						}else if (valType == Long.class){
+							val = Double.valueOf(val.toString()).longValue();
+						}else if (valType == Double.class){
+							val = Double.valueOf(val.toString());
+						}else if (valType == Float.class){
+							val = Float.valueOf(val.toString());
+						}else if (valType == Date.class){
+							SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
+							val=sdf.parse(val.toString()); 
+						}else if (valType == User.class){
+							val = UserUtils.getByUserName(val.toString());
+						}else if (valType == Office.class){
+							val = UserUtils.getByOfficeName(val.toString());
+						}else if (valType == Area.class){
+							val = UserUtils.getByAreaName(val.toString());
+						}else{
+							if (ef.fieldType() != Class.class){
+								val = ef.fieldType().getMethod("getValue", String.class).invoke(null, val.toString());
+							}else{
+								val = Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(), 
+										"fieldtype."+valType.getSimpleName()+"Type")).getMethod("getValue", String.class).invoke(null, val.toString());
+							}
+						}
+					} catch (Exception ex) {
+						log.info("Get cell value ["+i+","+column+"] error: " + ex.toString());
+						val = null;
+					}
+					// set entity value
+					if (os[1] instanceof Field){
+						Reflections.invokeSetter(e, ((Field)os[1]).getName(), val);
+					}else if (os[1] instanceof Method){
+						String mthodName = ((Method)os[1]).getName();
+						if ("get".equals(mthodName.substring(0, 3))){
+							mthodName = "set"+StringUtils.substringAfter(mthodName, "get");
+						}
+						Reflections.invokeMethod(e, mthodName, new Class[] {valType}, new Object[] {val});
+					}
+				}
+				sb.append(val+", ");
+			}
+			dataList.add(e);
+			log.debug("Read success: ["+i+"] "+sb.toString());
+		}
+		return dataList;
+	}
+
+//	/**
+//	 * 导入测试
+//	 */
+//	public static void main(String[] args) throws Throwable {
+//		
+//		ImportExcel ei = new ImportExcel("target/export.xlsx", 1);
+//		
+//		for (int i = ei.getDataRowNum(); i < ei.getLastDataRowNum(); i++) {
+//			Row row = ei.getRow(i);
+//			for (int j = 0; j < ei.getLastCellNum(); j++) {
+//				Object val = ei.getCellValue(row, j);
+//				System.out.print(val+", ");
+//			}
+//			System.out.print("\n");
+//		}
+//		
+//	}
+
+}

+ 59 - 0
src/main/java/com/jeeplus/common/utils/excel/annotation/ExcelField.java

@@ -0,0 +1,59 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils.excel.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Excel注解定义
+ * @author jeeplus
+ * @version 2016-03-10
+ */
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ExcelField {
+
+	/**
+	 * 导出字段名(默认调用当前字段的“get”方法,如指定导出字段为对象,请填写“对象名.对象属性”,例:“area.name”、“office.name”)
+	 */
+	String value() default "";
+	
+	/**
+	 * 导出字段标题(需要添加批注请用“**”分隔,标题**批注,仅对导出模板有效)
+	 */
+	String title();
+	
+	/**
+	 * 字段类型(0:导出导入;1:仅导出;2:仅导入)
+	 */
+	int type() default 0;
+
+	/**
+	 * 导出字段对齐方式(0:自动;1:靠左;2:居中;3:靠右)
+	 */
+	int align() default 0;
+	
+	/**
+	 * 导出字段字段排序(升序)
+	 */
+	int sort() default 0;
+
+	/**
+	 * 如果是字典类型,请设置字典的type值
+	 */
+	String dictType() default "";
+	
+	/**
+	 * 反射类型
+	 */
+	Class<?> fieldType() default Class.class;
+	
+	/**
+	 * 字段归属组(根据分组导出导入)
+	 */
+	int[] groups() default {};
+}

+ 38 - 0
src/main/java/com/jeeplus/common/utils/excel/fieldtype/AreaType.java

@@ -0,0 +1,38 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils.excel.fieldtype;
+
+import com.jeeplus.common.utils.StringUtils;
+import com.jeeplus.modules.sys.entity.Area;
+import com.jeeplus.modules.sys.utils.UserUtils;
+
+/**
+ * 字段类型转换
+ * @author jeeplus
+ * @version 2016-03-10
+ */
+public class AreaType {
+
+	/**
+	 * 获取对象值(导入)
+	 */
+	public static Object getValue(String val) {
+		for (Area e : UserUtils.getAreaList()){
+			if (StringUtils.trimToEmpty(val).equals(e.getName())){
+				return e;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * 获取对象值(导出)
+	 */
+	public static String setValue(Object val) {
+		if (val != null && ((Area)val).getName() != null){
+			return ((Area)val).getName();
+		}
+		return "";
+	}
+}

+ 38 - 0
src/main/java/com/jeeplus/common/utils/excel/fieldtype/OfficeType.java

@@ -0,0 +1,38 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils.excel.fieldtype;
+
+import com.jeeplus.common.utils.StringUtils;
+import com.jeeplus.modules.sys.entity.Office;
+import com.jeeplus.modules.sys.utils.UserUtils;
+
+/**
+ * 字段类型转换
+ * @author jeeplus
+ * @version 2016-03-10
+ */
+public class OfficeType {
+
+	/**
+	 * 获取对象值(导入)
+	 */
+	public static Object getValue(String val) {
+		for (Office e : UserUtils.getOfficeList()){
+			if (StringUtils.trimToEmpty(val).equals(e.getName())){
+				return e;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * 设置对象值(导出)
+	 */
+	public static String setValue(Object val) {
+		if (val != null && ((Office)val).getName() != null){
+			return ((Office)val).getName();
+		}
+		return "";
+	}
+}

+ 52 - 0
src/main/java/com/jeeplus/common/utils/excel/fieldtype/RoleListType.java

@@ -0,0 +1,52 @@
+/**
+ * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
+ */
+package com.jeeplus.common.utils.excel.fieldtype;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+import com.jeeplus.common.utils.Collections3;
+import com.jeeplus.common.utils.SpringContextHolder;
+import com.jeeplus.common.utils.StringUtils;
+import com.jeeplus.modules.sys.entity.Role;
+import com.jeeplus.modules.sys.service.SystemService;
+
+/**
+ * 字段类型转换
+ * @author jeeplus
+ * @version 2016-5-29
+ */
+public class RoleListType {
+
+	private static SystemService systemService = SpringContextHolder.getBean(SystemService.class);
+	
+	/**
+	 * 获取对象值(导入)
+	 */
+	public static Object getValue(String val) {
+		List<Role> roleList = Lists.newArrayList();
+		List<Role> allRoleList = systemService.findAllRole();
+		for (String s : StringUtils.split(val, ",")){
+			for (Role e : allRoleList){
+				if (StringUtils.trimToEmpty(s).equals(e.getName())){
+					roleList.add(e);
+				}
+			}
+		}
+		return roleList.size()>0?roleList:null;
+	}
+
+	/**
+	 * 设置对象值(导出)
+	 */
+	public static String setValue(Object val) {
+		if (val != null){
+			@SuppressWarnings("unchecked")
+			List<Role> roleList = (List<Role>)val;
+			return Collections3.extractToString(roleList, "name", ", ");
+		}
+		return "";
+	}
+	
+}

+ 88 - 0
src/main/java/com/jeeplus/common/utils/io/FilePathUtil.java

@@ -0,0 +1,88 @@
+package com.jeeplus.common.utils.io;
+
+import org.apache.commons.lang3.StringUtils;
+import com.jeeplus.common.utils.base.Platforms;
+import com.jeeplus.common.utils.text.MoreStringUtil;
+
+import com.google.common.io.Files;
+
+/**
+ * 关于文件路径的工具集
+ * 
+ * @author calvin
+ */
+public class FilePathUtil {
+
+	/**
+	 * 在Windows环境里,兼容Windows上的路径分割符,将 '/' 转回 '\'
+	 */
+	public static String normalizePath(String path) {
+		if (Platforms.FILE_PATH_SEPARATOR_CHAR == Platforms.WINDOWS_FILE_PATH_SEPARATOR_CHAR
+				&& StringUtils.indexOf(path, Platforms.LINUX_FILE_PATH_SEPARATOR_CHAR) != -1) {
+			return StringUtils.replaceChars(path, Platforms.LINUX_FILE_PATH_SEPARATOR_CHAR,
+					Platforms.WINDOWS_FILE_PATH_SEPARATOR_CHAR);
+		}
+		return path;
+
+	}
+
+	/**
+	 * 将路径整理,如 "a/../b",整理成 "b"
+	 */
+	public static String simplifyPath(String path) {
+		return Files.simplifyPath(path);
+	}
+
+	/**
+	 * 以拼接路径名
+	 */
+	public static String contact(String baseName, String... appendName) {
+		if (appendName.length == 0) {
+			return baseName;
+		}
+
+		String contactName;
+		if (MoreStringUtil.endWith(baseName, Platforms.FILE_PATH_SEPARATOR_CHAR)) {
+			contactName = baseName + appendName[0];
+		} else {
+			contactName = baseName + Platforms.FILE_PATH_SEPARATOR_CHAR + appendName[0];
+		}
+
+		if (appendName.length > 1) {
+			for (int i = 1; i < appendName.length; i++) {
+				contactName += Platforms.FILE_PATH_SEPARATOR_CHAR + appendName[i];
+			}
+		}
+
+		return contactName;
+	}
+
+	/**
+	 * 获得上层目录的路径
+	 */
+	public static String getParentPath(String path) {
+		String parentPath = path;
+
+		if (Platforms.FILE_PATH_SEPARATOR.equals(parentPath)) {
+			return parentPath;
+		}
+
+		parentPath = MoreStringUtil.removeEnd(parentPath, Platforms.FILE_PATH_SEPARATOR_CHAR);
+
+		int idx = parentPath.lastIndexOf(Platforms.FILE_PATH_SEPARATOR_CHAR);
+		if (idx >= 0) {
+			parentPath = parentPath.substring(0, idx + 1);
+		} else {
+			parentPath = Platforms.FILE_PATH_SEPARATOR;
+		}
+
+		return parentPath;
+	}
+
+	/**
+	 * 获得参数clazz所在的Jar文件的绝对路径
+	 */
+	public static String getJarPath(Class<?> clazz) {
+		return clazz.getProtectionDomain().getCodeSource().getLocation().getFile();
+	}
+}

+ 146 - 0
src/main/java/com/jeeplus/common/utils/io/FileTreeWalker.java

@@ -0,0 +1,146 @@
+package com.jeeplus.common.utils.io;
+
+import java.io.File;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import com.jeeplus.common.utils.text.WildcardMatcher;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.TreeTraverser;
+import com.google.common.io.Files;
+
+public class FileTreeWalker {
+
+	/**
+	 * 前序递归列出所有文件, 包含文件与目录,及根目录本身.
+	 * 
+	 * 前序即先列出父目录,在列出子目录. 如要后序遍历, 直接使用Files.fileTreeTraverser()
+	 */
+	public static List<File> listAll(File rootDir) {
+		return Files.fileTreeTraverser().preOrderTraversal(rootDir).toList();
+	}
+
+	/**
+	 * 前序递归列出所有文件, 只包含文件.
+	 */
+	public static List<File> listFile(File rootDir) {
+		return Files.fileTreeTraverser().preOrderTraversal(rootDir).filter(Files.isFile()).toList();
+	}
+
+	/**
+	 * 前序递归列出所有文件, 列出后缀名匹配的文件. (后缀名不包含.)
+	 */
+	public static List<File> listFileWithExtension(final File rootDir, final String extension) {
+		return Files.fileTreeTraverser().preOrderTraversal(rootDir).filter(new FileExtensionFilter(extension)).toList();
+	}
+
+	/**
+	 * 前序递归列出所有文件, 列出文件名匹配通配符的文件
+	 * 
+	 * 如 ("/a/b/hello.txt", "he*") 将被返回
+	 */
+	public static List<File> listFileWithWildcardFileName(final File rootDir, final String fileNamePattern) {
+		return Files.fileTreeTraverser().preOrderTraversal(rootDir).filter(new WildcardFileNameFilter(fileNamePattern))
+				.toList();
+	}
+
+	/**
+	 * 前序递归列出所有文件, 列出文件名匹配正则表达式的文件
+	 * 
+	 * 如 ("/a/b/hello.txt", "he.*\.text") 将被返回
+	 */
+	public static List<File> listFileWithRegexFileName(final File rootDir, final String regexFileNamePattern) {
+		return Files.fileTreeTraverser().preOrderTraversal(rootDir)
+				.filter(new RegexFileNameFilter(regexFileNamePattern)).toList();
+	}
+
+	/**
+	 * 前序递归列出所有文件, 列出符合ant path风格表达式的文件
+	 * 
+	 * 如 ("/a/b/hello.txt", "he.*\.text") 将被返回
+	 */
+	public static List<File> listFileWithAntPath(final File rootDir, final String antPathPattern) {
+		return Files.fileTreeTraverser().preOrderTraversal(rootDir)
+				.filter(new AntPathFilter(FilePathUtil.contact(rootDir.getAbsolutePath(), antPathPattern))).toList();
+	}
+
+	/**
+	 * 直接使用Guava的TreeTraverser,获得更大的灵活度, 比如加入各类filter,前序/后序的选择,一边遍历一边操作
+	 * 
+	 * <pre>
+	 * FileUtil.fileTreeTraverser().preOrderTraversal(root).iterator();
+	 * </pre>
+	 */
+	public static TreeTraverser<File> fileTreeTraverser() {
+		return Files.fileTreeTraverser();
+	}
+
+	/**
+	 * 以文件名正则表达式为filter,配合fileTreeTraverser使用
+	 */
+	public static final class RegexFileNameFilter implements Predicate<File> {
+		private final Pattern pattern;
+
+		private RegexFileNameFilter(String pattern) {
+			this.pattern = Pattern.compile(pattern);
+		}
+
+		@Override
+		public boolean apply(File input) {
+			return input.isFile() && pattern.matcher(input.getName()).matches();
+		}
+	}
+
+	/**
+	 * 以文件名通配符为filter,配合fileTreeTraverser使用.
+	 * 
+	 * @param pattern 支持*与?的通配符,如hello*.txt 匹配 helloworld.txt
+	 */
+	public static final class WildcardFileNameFilter implements Predicate<File> {
+		private final String pattern;
+
+		private WildcardFileNameFilter(String pattern) {
+			this.pattern = pattern;
+		}
+
+		@Override
+		public boolean apply(File input) {
+			return input.isFile() && WildcardMatcher.match(input.getName(), pattern);
+		}
+	}
+
+	/**
+	 * 以文件名后缀做filter,配合fileTreeTraverser使用
+	 */
+	public static final class FileExtensionFilter implements Predicate<File> {
+		private final String extension;
+
+		private FileExtensionFilter(String extension) {
+			this.extension = extension;
+		}
+
+		@Override
+		public boolean apply(File input) {
+			return input.isFile() && extension.equals(FileUtil.getFileExtension(input));
+		}
+	}
+
+	/**
+	 * 以ant风格的path为filter,配合fileTreeTraverser使用.
+	 * 
+	 * @param pattern 支持ant风格的通配符,如/var/?/a?.txt 匹配 /var/b/ab.txt, 其他通配符包括**,*
+	 */
+	public static final class AntPathFilter implements Predicate<File> {
+		private final String pattern;
+
+		private AntPathFilter(String pattern) {
+			this.pattern = pattern;
+		}
+
+		@Override
+		public boolean apply(File input) {
+			return input.isFile() && WildcardMatcher.matchPath(input.getAbsolutePath(), pattern);
+		}
+	}
+}

+ 357 - 0
src/main/java/com/jeeplus/common/utils/io/FileUtil.java

@@ -0,0 +1,357 @@
+package com.jeeplus.common.utils.io;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import com.jeeplus.common.utils.base.Platforms;
+import com.jeeplus.common.utils.base.annotation.NotNull;
+import com.jeeplus.common.utils.base.annotation.Nullable;
+import com.jeeplus.common.utils.text.Charsets;
+
+import com.google.common.io.Files;
+
+/**
+ * 关于文件的工具集
+ * 
+ * 代码基本从调用Guava Files, 固定encoding为UTF8.
+ * 
+ * 1.文件读写
+ * 
+ * 2.文件及目录操作
+ * 
+ * @author calvin
+ */
+public class FileUtil {
+	
+	
+
+	//////// 文件读写//////
+
+	/**
+	 * 读取文件到byte[].
+	 */
+	public static byte[] toByteArray(final File file) throws IOException {
+		return Files.toByteArray(file);
+	}
+
+	/**
+	 * 读取文件到String.
+	 */
+	public static String toString(final File file) throws IOException {
+		return Files.toString(file, Charsets.UTF_8);
+	}
+
+	/**
+	 * 读取文件的每行内容到List<String>
+	 */
+	public static List<String> toLines(final File file) throws IOException {
+		return Files.readLines(file, Charsets.UTF_8);
+	}
+
+	/**
+	 * 简单写入String到File.
+	 */
+	public static void write(final CharSequence data, final File file) throws IOException {
+		Files.write(data, file, Charsets.UTF_8);
+	}
+
+	/**
+	 * 追加String到File.
+	 */
+	public static void append(final CharSequence from, final File to) throws IOException {
+		Files.append(from, to, Charsets.UTF_8);
+	}
+
+	/**
+	 * 打开文件为InputStream
+	 */
+	public static InputStream asInputStream(String fileName) throws IOException {
+		return new FileInputStream(getFileByPath(fileName));
+	}
+	
+	/**
+	 * 打开文件为InputStream
+	 */
+	public static InputStream asInputStream(File file) throws IOException {
+		return new FileInputStream(file);
+	}
+
+	/**
+	 * 打开文件为OutputStream
+	 */
+	public static OutputStream asOututStream(String fileName) throws IOException {
+		return new FileOutputStream(getFileByPath(fileName));
+	}
+	
+	/**
+	 * 打开文件为OutputStream
+	 */
+	public static OutputStream asOututStream(File file) throws IOException {
+		return new FileOutputStream(file);
+	}
+
+	/**
+	 * 获取File的BufferedReader
+	 */
+	public static BufferedReader asBufferedReader(String fileName) throws FileNotFoundException {
+		return Files.newReader(getFileByPath(fileName), Charsets.UTF_8);
+	}
+
+	/**
+	 * 获取File的BufferedWriter
+	 */
+	public static BufferedWriter asBufferedWriter(String fileName) throws FileNotFoundException {
+		return Files.newWriter(getFileByPath(fileName), Charsets.UTF_8);
+	}
+
+	///// 文件操作 /////
+
+	/**
+	 * 复制文件或目录
+	 * 
+	 * @param from 如果为null,或者是不存在的文件或目录,抛出异常.
+	 * @param to 如果为null,或者from是目录而to是已存在文件,或相反
+	 */
+	public static void copy(@NotNull File from, @NotNull File to) throws IOException {
+		Validate.notNull(from);
+		Validate.notNull(to);
+
+		if (from.isDirectory()) {
+			copyDir(from, to);
+		} else {
+			copyFile(from, to);
+		}
+	}
+
+	/**
+	 * 文件复制.
+	 * 
+	 * @param from 如果为nll,或文件不存在或者是目录,,抛出异常
+	 * @param to 如果to为null,或文件存在但是一个目录,抛出异常
+	 */
+	public static void copyFile(@NotNull File from, @NotNull File to) throws IOException {
+		Validate.isTrue(isFileExists(from), from + " is not exist or not a file");
+		Validate.notNull(to);
+		Validate.isTrue(!FileUtil.isDirExists(to), to + " is exist but it is a dir");
+		Files.copy(from, to);
+	}
+
+	/**
+	 * 复制目录
+	 */
+	public static void copyDir(@NotNull File from, @NotNull File to) throws IOException {
+		Validate.isTrue(isDirExists(from), from + " is not exist or not a dir");
+		Validate.notNull(to);
+
+		if (to.exists()) {
+			Validate.isTrue(!to.isFile(), to + " is exist but it is a file");
+		} else {
+			to.mkdirs();
+		}
+
+		File[] files = from.listFiles();
+		if (files != null) {
+			for (int i = 0; i < files.length; i++) {
+				String name = files[i].getName();
+				if (".".equals(name) || "..".equals(name)) {
+					continue;
+				}
+				copy(files[i], new File(to, name));
+			}
+		}
+	}
+
+	/**
+	 * 文件移动/重命名.
+	 */
+	public static void moveFile(@NotNull File from, @NotNull File to) throws IOException {
+		Validate.isTrue(isFileExists(from), from + " is not exist or not a file");
+		Validate.notNull(to);
+		Validate.isTrue(!isDirExists(to), to + " is  exist but it is a dir");
+
+		Files.move(from, to);
+	}
+
+	/**
+	 * 目录移动/重命名
+	 */
+	public static void moveDir(@NotNull File from, @NotNull File to) throws IOException {
+		Validate.isTrue(isDirExists(from), from + " is not exist or not a dir");
+		Validate.notNull(to);
+		Validate.isTrue(!isFileExists(to), to + " is exist but it is a file");
+
+		final boolean rename = from.renameTo(to);
+		if (!rename) {
+			if (to.getCanonicalPath().startsWith(from.getCanonicalPath() + File.separator)) {
+				throw new IOException("Cannot move directory: " + from + " to a subdirectory of itself: " + to);
+			}
+			copyDir(from, to);
+			deleteDir(from);
+			if (from.exists()) {
+				throw new IOException("Failed to delete original directory '" + from + "' after copy to '" + to + '\'');
+			}
+		}
+	}
+
+	/**
+	 * 创建文件或更新时间戳.
+	 */
+	public static void touch(String filePath) throws IOException {
+		Files.touch(getFileByPath(filePath));
+	}
+
+	/**
+	 * 创建文件或更新时间戳.
+	 */
+	public static void touch(File file) throws IOException {
+		Files.touch(file);
+	}
+
+	/**
+	 * 删除文件.
+	 * 
+	 * 如果文件不存在或者是目录,则不做修改
+	 */
+	public static void deleteFile(@Nullable File file) throws IOException {
+		Validate.isTrue(isFileExists(file), file + " is not exist or not a file");
+		file.delete();
+	}
+
+	/**
+	 * 删除目录及所有子目录/文件
+	 */
+	public static void deleteDir(File dir) {
+		Validate.isTrue(isDirExists(dir), dir + " is not exist or not a dir");
+
+		// 后序遍历,先删掉子目录中的文件/目录
+		Iterator<File> iterator = Files.fileTreeTraverser().postOrderTraversal(dir).iterator();
+		while (iterator.hasNext()) {
+			iterator.next().delete();
+		}
+	}
+
+	/**
+	 * 判断目录是否存在, from Jodd
+	 */
+	public static boolean isDirExists(String dirPath) {
+		return isDirExists(getFileByPath(dirPath));
+	}
+
+	/**
+	 * 判断目录是否存在, from Jodd
+	 */
+	public static boolean isDirExists(File dir) {
+		if (dir == null) {
+			return false;
+		}
+		return dir.exists() && dir.isDirectory();
+	}
+
+	/**
+	 * 确保目录存在, 如不存在则创建
+	 */
+	public static void makesureDirExists(String dirPath) throws IOException {
+		makesureDirExists(getFileByPath(dirPath));
+	}
+
+	/**
+	 * 确保目录存在, 如不存在则创建
+	 */
+	public static void makesureDirExists(File file) throws IOException {
+		Validate.notNull(file);
+		if (file.exists()) {
+			if (!file.isDirectory()) {
+				throw new IOException("There is a file exists " + file);
+			}
+		} else {
+			file.mkdirs();
+		}
+	}
+
+	/**
+	 * 确保父目录及其父目录直到根目录都已经创建.
+	 * 
+	 * @see Files#createParentDirs(File)
+	 */
+	public static void makesureParentDirExists(File file) throws IOException {
+		Files.createParentDirs(file);
+	}
+
+	/**
+	 * 判断文件是否存在, from Jodd
+	 */
+	public static boolean isFileExists(String fileName) {
+		return isFileExists(getFileByPath(fileName));
+	}
+
+	/**
+	 * 判断文件是否存在, from Jodd
+	 */
+	public static boolean isFileExists(File file) {
+		if (file == null) {
+			return false;
+		}
+		return file.exists() && file.isFile();
+	}
+
+	/**
+	 * 在临时目录创建临时目录,命名为${毫秒级时间戳}-${同一毫秒内的计数器}, from guava
+	 * 
+	 * @see Files#createTempDir()
+	 */
+	public static File createTempDir() {
+		return Files.createTempDir();
+	}
+
+	/**
+	 * 在临时目录创建临时文件,命名为tmp-${random.nextLong()}.tmp
+	 */
+	public static File createTempFile() throws IOException {
+		return File.createTempFile("tmp-", ".tmp");
+	}
+
+	/**
+	 * 在临时目录创建临时文件,命名为${prefix}${random.nextLong()}${suffix}
+	 */
+	public static File createTempFile(String prefix, String suffix) throws IOException {
+		return File.createTempFile(prefix, suffix);
+	}
+
+	private static File getFileByPath(String filePath) {
+		return StringUtils.isBlank(filePath) ? null : new File(filePath);
+	}
+
+	/**
+	 * 获取文件名(不包含路径)
+	 */
+	public static String getFileName(@NotNull String fullName) {
+		Validate.notEmpty(fullName);
+		int last = fullName.lastIndexOf(Platforms.FILE_PATH_SEPARATOR_CHAR);
+		return fullName.substring(last + 1);
+	}
+
+	/**
+	 * 获取文件名的扩展名部分(不包含.)
+	 */
+	public static String getFileExtension(File file) {
+		return Files.getFileExtension(file.getName());
+	}
+
+	/**
+	 * 获取文件名的扩展名部分(不包含.)
+	 */
+	public static String getFileExtension(String fullName) {
+		return Files.getFileExtension(fullName);
+	}
+}

Some files were not shown because too many files changed in this diff