├── .travis.yml ├── src ├── main │ └── java │ │ └── com │ │ └── github │ │ └── phantomthief │ │ └── pool │ │ ├── StatsKey.java │ │ ├── impl │ │ ├── ConcurrencyInfo.java │ │ ├── SharedResource.java │ │ ├── ConcurrencyAdjustStrategy.java │ │ ├── LazyPool.java │ │ ├── SimpleConcurrencyAdjustStrategy.java │ │ ├── ConcurrencyAwarePoolBuilder.java │ │ └── ConcurrencyAwarePool.java │ │ ├── Pooled.java │ │ └── Pool.java └── test │ ├── resources │ └── logback.xml │ └── java │ └── com │ └── github │ └── phantomthief │ └── pool │ ├── TestStats.java │ └── impl │ ├── SimpleConcurrencyAdjustStrategyTest.java │ └── ConcurrencyAwarePoolTest.java ├── .gitignore ├── LICENSE ├── README.md └── pom.xml /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | after_success: 5 | - mvn clean test jacoco:report coveralls:report -------------------------------------------------------------------------------- /src/main/java/com/github/phantomthief/pool/StatsKey.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool; 2 | 3 | /** 4 | * @author w.vela 5 | * Created on 2017-11-15. 6 | */ 7 | public interface StatsKey { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/github/phantomthief/pool/impl/ConcurrencyInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool.impl; 2 | 3 | /** 4 | * @author w.vela 5 | * Created on 2017-10-20. 6 | */ 7 | public interface ConcurrencyInfo { 8 | 9 | int currentConcurrency(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/github/phantomthief/pool/Pooled.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import javax.annotation.Nonnull; 6 | 7 | /** 8 | * @author w.vela 9 | * Created on 2017-10-19. 10 | */ 11 | @Deprecated 12 | public interface Pooled extends Supplier { 13 | 14 | @Nonnull 15 | @Override 16 | T get(); 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | .idea 3 | *.iml 4 | 5 | # Mobile Tools for Java (J2ME) 6 | .mtj.tmp/ 7 | 8 | # Package Files # 9 | *.jar 10 | *.war 11 | *.ear 12 | 13 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 14 | hs_err_pid* 15 | /target/ 16 | 17 | .project 18 | .settings 19 | .classpath 20 | 21 | .DS_Store 22 | 23 | .README.md.html 24 | 25 | pom.xml.releaseBackup 26 | release.properties 27 | -------------------------------------------------------------------------------- /src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level \(%F:%L\) - %msg %n 7 | 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/java/com/github/phantomthief/pool/impl/SharedResource.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool.impl; 2 | 3 | import static java.lang.Thread.MIN_PRIORITY; 4 | import static java.util.concurrent.Executors.newCachedThreadPool; 5 | 6 | import java.util.concurrent.Executor; 7 | 8 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 9 | 10 | /** 11 | * @author w.vela 12 | * Created on 2017-10-21. 13 | */ 14 | class SharedResource { 15 | 16 | static Executor cleanupExecutor() { 17 | return LazyHolder.EXECUTOR; 18 | } 19 | 20 | private static final class LazyHolder { 21 | 22 | private static final Executor EXECUTOR = newCachedThreadPool(new ThreadFactoryBuilder() 23 | .setNameFormat("simple-pool-cleanup-%d") 24 | .setPriority(MIN_PRIORITY) 25 | .build()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/com/github/phantomthief/pool/TestStats.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool; 2 | 3 | import static com.github.phantomthief.pool.impl.ConcurrencyAwarePool.CURRENT_CONCURRENCY; 4 | import static com.github.phantomthief.pool.impl.ConcurrencyAwarePool.CURRENT_COUNT; 5 | import static java.lang.Integer.valueOf; 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | import static org.junit.jupiter.api.Assertions.assertNull; 8 | 9 | import org.junit.jupiter.api.Test; 10 | 11 | import com.github.phantomthief.pool.impl.ConcurrencyAwarePool; 12 | 13 | /** 14 | * @author w.vela 15 | * Created on 2017-11-15. 16 | */ 17 | class TestStats { 18 | 19 | @Test 20 | void testStats() { 21 | Pool pool = ConcurrencyAwarePool.builder() 22 | .simpleThresholdStrategy(1, 0.7) 23 | .build(String::new); 24 | assertNull(pool.getStats(CURRENT_COUNT)); 25 | pool.run(System.out::println); 26 | assertEquals(valueOf(1), pool.getStats(CURRENT_COUNT)); 27 | assertEquals(valueOf(0), pool.getStats(CURRENT_CONCURRENCY)); 28 | pool.run(it -> assertEquals(valueOf(1), pool.getStats(CURRENT_CONCURRENCY))); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/phantomthief/pool/impl/ConcurrencyAdjustStrategy.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool.impl; 2 | 3 | import java.util.Collection; 4 | 5 | import javax.annotation.Nonnull; 6 | import javax.annotation.Nullable; 7 | 8 | /** 9 | * @author w.vela 10 | * Created on 2017-10-18. 11 | */ 12 | public interface ConcurrencyAdjustStrategy { 13 | 14 | AdjustResult NO_CHANGE = null; 15 | 16 | /** 17 | * @return {@link #NO_CHANGE} if no changed 18 | */ 19 | @Nullable 20 | AdjustResult adjust(@Nonnull Collection current) throws Throwable; 21 | 22 | class AdjustResult { 23 | 24 | private final Collection evict; 25 | private final int create; 26 | 27 | /** 28 | * @param evict {@code null} if no item to evict 29 | */ 30 | public AdjustResult(@Nullable Collection evict, int create) { 31 | this.evict = evict; 32 | this.create = create; 33 | } 34 | 35 | @Nullable 36 | Collection getEvict() { 37 | return evict; 38 | } 39 | 40 | int getCreate() { 41 | return create; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/phantomthief/pool/impl/LazyPool.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool.impl; 2 | 3 | import static com.github.phantomthief.util.MoreSuppliers.lazy; 4 | 5 | import java.util.function.Supplier; 6 | 7 | import javax.annotation.Nonnull; 8 | import javax.annotation.Nullable; 9 | 10 | import com.github.phantomthief.pool.Pool; 11 | import com.github.phantomthief.pool.Pooled; 12 | import com.github.phantomthief.pool.StatsKey; 13 | import com.github.phantomthief.util.MoreSuppliers.CloseableSupplier; 14 | 15 | /** 16 | * @author w.vela 17 | * Created on 09/09/2016. 18 | */ 19 | class LazyPool implements Pool { 20 | 21 | private final CloseableSupplier> factory; 22 | 23 | LazyPool(Supplier> factory) { 24 | this.factory = lazy(factory, false); 25 | } 26 | 27 | @Nonnull 28 | @Override 29 | public Pooled borrow() { 30 | return factory.get().borrow(); 31 | } 32 | 33 | @Nullable 34 | @Override 35 | public V getStats(@Nonnull StatsKey key) { 36 | return factory.map(pool -> pool.getStats(key)).orElse(null); 37 | } 38 | 39 | @Override 40 | public void returnObject(@Nonnull Pooled pooled) { 41 | factory.get().returnObject(pooled); 42 | } 43 | 44 | @Override 45 | public void close() { 46 | factory.tryClose(Pool::close); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | simple-pool 2 | ======================= 3 | [![Build Status](https://travis-ci.org/PhantomThief/simple-pool.svg)](https://travis-ci.org/PhantomThief/simple-pool) 4 | [![Coverage Status](https://coveralls.io/repos/PhantomThief/simple-pool/badge.svg?branch=master&service=github)](https://coveralls.io/github/PhantomThief/simple-pool?branch=master) 5 | [![Total alerts](https://img.shields.io/lgtm/alerts/g/PhantomThief/simple-pool.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/PhantomThief/simple-pool/alerts/) 6 | [![Language grade: Java](https://img.shields.io/lgtm/grade/java/g/PhantomThief/simple-pool.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/PhantomThief/simple-pool/context:java) 7 | [![Maven Central](https://img.shields.io/maven-central/v/com.github.phantomthief/simple-pool)](https://search.maven.org/artifact/com.github.phantomthief/simple-pool/) 8 | 9 | A simple pool library for Java 10 | 11 | * support concurrency use for objects. 12 | * jdk1.8 only 13 | 14 | ## Get Started 15 | 16 | ```Java 17 | Pool pool = ConcurrencyAwarePool. builder() 18 | .destroy(MyObject::close) 19 | .maxSize(30) 20 | .minIdle(1) 21 | .evaluatePeriod(ofSeconds(2)) 22 | .simpleThresholdStrategy(10, 0.8) 23 | .build(MyObject::new); 24 | 25 | MyResult myResult = pool.supply(myObject-> myObject.doSomething()); 26 | ``` -------------------------------------------------------------------------------- /src/main/java/com/github/phantomthief/pool/Pool.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool; 2 | 3 | import javax.annotation.Nonnull; 4 | import javax.annotation.Nullable; 5 | 6 | import com.github.phantomthief.util.ThrowableConsumer; 7 | import com.github.phantomthief.util.ThrowableFunction; 8 | 9 | /** 10 | * @author w.vela 11 | * Created on 06/09/2016. 12 | */ 13 | @Deprecated 14 | public interface Pool extends AutoCloseable { 15 | 16 | default V supply(ThrowableFunction function) throws X { 17 | Pooled pooled = borrow(); 18 | try { 19 | return function.apply(pooled.get()); 20 | } finally { 21 | returnObject(pooled); 22 | } 23 | } 24 | 25 | default void run(ThrowableConsumer consumer) throws X { 26 | supply(obj -> { 27 | consumer.accept(obj); 28 | return null; 29 | }); 30 | } 31 | 32 | /** 33 | * better use {@link #supply} or {@link #run} 34 | * 35 | * @throws IllegalStateException if pool was already closed. 36 | */ 37 | @Nonnull 38 | Pooled borrow(); 39 | 40 | @Nullable 41 | V getStats(@Nonnull StatsKey key); 42 | 43 | /** 44 | * must call exactly once after {@link #borrow} in pair 45 | */ 46 | void returnObject(@Nonnull Pooled pooled); 47 | 48 | @Override 49 | void close(); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/phantomthief/pool/impl/SimpleConcurrencyAdjustStrategy.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool.impl; 2 | 3 | import static com.google.common.base.Preconditions.checkArgument; 4 | import static java.util.Collections.singleton; 5 | import static java.util.Comparator.comparingInt; 6 | import static java.util.stream.Collectors.toList; 7 | 8 | import java.util.Collection; 9 | import java.util.List; 10 | 11 | import javax.annotation.Nonnegative; 12 | import javax.annotation.Nonnull; 13 | import javax.annotation.Nullable; 14 | import javax.annotation.concurrent.NotThreadSafe; 15 | 16 | /** 17 | * extend or shrink at most one item on each cycle. 18 | * 19 | * @author w.vela 20 | * Created on 2017-10-18. 21 | */ 22 | @NotThreadSafe 23 | class SimpleConcurrencyAdjustStrategy implements ConcurrencyAdjustStrategy { 24 | 25 | private final int extendThreshold; 26 | private final double shrinkThreshold; 27 | private final int continuousExtendThreshold; 28 | private final int continuousShrinkThreshold; 29 | 30 | private int continuousExtendCount; 31 | private int continuousShrinkCount; 32 | 33 | SimpleConcurrencyAdjustStrategy(@Nonnegative int extendThreshold, 34 | @Nonnegative double shrinkThreshold, @Nonnegative int continuousExtendThreshold, 35 | @Nonnegative int continuousShrinkThreshold) { 36 | checkArgument(continuousExtendThreshold > 0); 37 | checkArgument(continuousShrinkThreshold > 0); 38 | checkArgument(extendThreshold > 0); 39 | checkArgument(shrinkThreshold > 0 && shrinkThreshold < 1); 40 | this.continuousExtendThreshold = continuousExtendThreshold; 41 | this.continuousShrinkThreshold = continuousShrinkThreshold; 42 | this.extendThreshold = extendThreshold; 43 | this.shrinkThreshold = shrinkThreshold; 44 | } 45 | 46 | @Nullable 47 | @Override 48 | public AdjustResult adjust(@Nonnull Collection current) { 49 | List minList = current.stream() 50 | .sorted(comparingInt(ConcurrencyInfo::currentConcurrency)) 51 | .limit(2) 52 | .collect(toList()); 53 | ConcurrencyInfo first = minList.get(0); 54 | if (first.currentConcurrency() >= extendThreshold) { 55 | continuousExtendCount++; 56 | if (continuousExtendCount == continuousExtendThreshold) { 57 | resetContinuousCounter(); 58 | return new AdjustResult(null, 1); 59 | } else { 60 | continuousShrinkCount = 0; 61 | return NO_CHANGE; 62 | } 63 | } 64 | if (minList.size() == 1) { 65 | resetContinuousCounter(); 66 | return NO_CHANGE; 67 | } 68 | ConcurrencyInfo second = minList.get(1); 69 | if (second.currentConcurrency() < extendThreshold * shrinkThreshold) { 70 | continuousShrinkCount++; 71 | if (continuousShrinkCount == continuousShrinkThreshold) { 72 | resetContinuousCounter(); 73 | return new AdjustResult(singleton(first), 0); 74 | } else { 75 | continuousExtendCount = 0; 76 | return NO_CHANGE; 77 | } 78 | } else { 79 | resetContinuousCounter(); 80 | return NO_CHANGE; 81 | } 82 | } 83 | 84 | private void resetContinuousCounter() { 85 | continuousExtendCount = 0; 86 | continuousShrinkCount = 0; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/test/java/com/github/phantomthief/pool/impl/SimpleConcurrencyAdjustStrategyTest.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool.impl; 2 | 3 | import static com.google.common.collect.ImmutableSet.of; 4 | import static org.junit.jupiter.api.Assertions.assertEquals; 5 | import static org.junit.jupiter.api.Assertions.assertNotNull; 6 | import static org.junit.jupiter.api.Assertions.assertNull; 7 | import static org.junit.jupiter.api.Assertions.assertSame; 8 | import static org.junit.jupiter.api.Assertions.assertTrue; 9 | 10 | import org.junit.jupiter.api.Test; 11 | 12 | import com.github.phantomthief.pool.impl.ConcurrencyAdjustStrategy.AdjustResult; 13 | 14 | /** 15 | * @author w.vela 16 | * Created on 2017-10-18. 17 | */ 18 | class SimpleConcurrencyAdjustStrategyTest { 19 | 20 | @Test 21 | void testAdjust() { 22 | SimpleConcurrencyAdjustStrategy strategy = new SimpleConcurrencyAdjustStrategy(10, 0.5, 1, 23 | 1); 24 | // heavy to expend 25 | AdjustResult adjust = strategy.adjust(of(new MyConcurrencyInfo(20))); 26 | assertNotNull(adjust); 27 | assertEquals(1, adjust.getCreate()); 28 | 29 | // idle to shrink 30 | ConcurrencyInfo toEvict = new MyConcurrencyInfo(3); 31 | adjust = strategy.adjust(of( 32 | new MyConcurrencyInfo(4), 33 | toEvict)); 34 | assertNotNull(adjust); 35 | assertEquals(0, adjust.getCreate()); 36 | assertNotNull(adjust.getEvict()); 37 | assertSame(toEvict, adjust.getEvict().iterator().next()); 38 | 39 | strategy = new SimpleConcurrencyAdjustStrategy(10, 0.9, 1, 1); 40 | adjust = strategy.adjust(of( 41 | new MyConcurrencyInfo(9), 42 | new MyConcurrencyInfo(9), 43 | new MyConcurrencyInfo(8) 44 | )); 45 | assertNull(adjust); 46 | 47 | toEvict = new MyConcurrencyInfo(1); 48 | adjust = strategy.adjust(of( 49 | new MyConcurrencyInfo(9), 50 | new MyConcurrencyInfo(8), 51 | toEvict 52 | )); 53 | assertNotNull(adjust); 54 | assertEquals(0, adjust.getCreate()); 55 | assertNotNull(adjust.getEvict()); 56 | assertSame(toEvict, adjust.getEvict().iterator().next()); 57 | 58 | adjust = strategy.adjust(of(new MyConcurrencyInfo(1))); 59 | assertNull(adjust); 60 | 61 | adjust = strategy.adjust(of( 62 | new MyConcurrencyInfo(9), new MyConcurrencyInfo(10))); 63 | assertNull(adjust); 64 | } 65 | 66 | @Test 67 | void testContinuous() { 68 | SimpleConcurrencyAdjustStrategy strategy = new SimpleConcurrencyAdjustStrategy(10, 0.5, 3, 69 | 5); 70 | // heavy to expend 71 | MyConcurrencyInfo heavyOne = new MyConcurrencyInfo(20); 72 | AdjustResult adjust = strategy.adjust(of(heavyOne)); 73 | assertNull(adjust); 74 | adjust = strategy.adjust(of(heavyOne)); 75 | assertNull(adjust); 76 | adjust = strategy.adjust(of(heavyOne)); 77 | assertNotNull(adjust); 78 | assertEquals(1, adjust.getCreate()); 79 | adjust = strategy.adjust(of(heavyOne)); 80 | assertNull(adjust); 81 | 82 | // idle to shrink 83 | MyConcurrencyInfo notReached = new MyConcurrencyInfo(4); 84 | ConcurrencyInfo toEvict = new MyConcurrencyInfo(3); 85 | adjust = strategy.adjust(of(notReached, toEvict)); 86 | assertNull(adjust); 87 | adjust = strategy.adjust(of(notReached, toEvict)); 88 | assertNull(adjust); 89 | adjust = strategy.adjust(of(notReached, toEvict)); 90 | assertNull(adjust); 91 | adjust = strategy.adjust(of(notReached, toEvict)); 92 | assertNull(adjust); 93 | 94 | adjust = strategy.adjust(of(notReached, toEvict)); 95 | assertNotNull(adjust); 96 | assertEquals(0, adjust.getCreate()); 97 | assertNotNull(adjust.getEvict()); 98 | assertSame(toEvict, adjust.getEvict().iterator().next()); 99 | 100 | adjust = strategy.adjust(of(notReached, toEvict)); 101 | assertNull(adjust); 102 | } 103 | 104 | private static class MyConcurrencyInfo implements ConcurrencyInfo { 105 | 106 | private final int currentConcurrency; 107 | 108 | private MyConcurrencyInfo(int currentConcurrency) { 109 | this.currentConcurrency = currentConcurrency; 110 | } 111 | 112 | @Override 113 | public int currentConcurrency() { 114 | return currentConcurrency; 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /src/main/java/com/github/phantomthief/pool/impl/ConcurrencyAwarePoolBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool.impl; 2 | 3 | import static com.google.common.base.Preconditions.checkArgument; 4 | import static com.google.common.base.Preconditions.checkNotNull; 5 | import static java.time.Duration.ofSeconds; 6 | 7 | import java.time.Duration; 8 | 9 | import javax.annotation.CheckReturnValue; 10 | import javax.annotation.Nonnegative; 11 | import javax.annotation.Nonnull; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import com.github.phantomthief.pool.Pool; 17 | import com.github.phantomthief.util.ThrowableConsumer; 18 | import com.github.phantomthief.util.ThrowableSupplier; 19 | 20 | /** 21 | * @author w.vela 22 | * Created on 2017-10-18. 23 | */ 24 | public class ConcurrencyAwarePoolBuilder { 25 | 26 | private static final Logger logger = LoggerFactory.getLogger(ConcurrencyAwarePoolBuilder.class); 27 | 28 | private static final Duration DEFAULT_EVALUATE_PERIOD = ofSeconds(1); 29 | private static final int DEFAULT_MIN_IDLE = 1; 30 | private static final int DEFAULT_MAX_SIZE = 10000; 31 | private static final int DEFAULT_CONTINUOUS_EXTEND_THRESHOLD = 1; 32 | private static final int DEFAULT_CONTINUOUS_SHRINK_THRESHOLD = 1; 33 | 34 | ThrowableSupplier factory; 35 | ThrowableConsumer destroy; 36 | int minIdle = DEFAULT_MIN_IDLE; 37 | int maxSize = DEFAULT_MAX_SIZE; 38 | ConcurrencyAdjustStrategy strategy; 39 | Duration evaluatePeriod = DEFAULT_EVALUATE_PERIOD; 40 | 41 | ConcurrencyAwarePoolBuilder() { 42 | } 43 | 44 | @CheckReturnValue 45 | @Nonnull 46 | public ConcurrencyAwarePoolBuilder destroy(@Nonnull ThrowableConsumer value) { 47 | this.destroy = checkNotNull(value); 48 | return this; 49 | } 50 | 51 | @CheckReturnValue 52 | @Nonnull 53 | public ConcurrencyAwarePoolBuilder minIdle(@Nonnegative int value) { 54 | checkArgument(value > 0); 55 | this.minIdle = value; 56 | return this; 57 | } 58 | 59 | @CheckReturnValue 60 | @Nonnull 61 | public ConcurrencyAwarePoolBuilder maxSize(@Nonnegative int value) { 62 | checkArgument(value > 0); 63 | this.maxSize = value; 64 | return this; 65 | } 66 | 67 | @CheckReturnValue 68 | @Nonnull 69 | public ConcurrencyAwarePoolBuilder strategy(@Nonnull ConcurrencyAdjustStrategy strategy) { 70 | this.strategy = checkNotNull(strategy); 71 | return this; 72 | } 73 | 74 | /** 75 | * default value is {@link #DEFAULT_EVALUATE_PERIOD} 76 | */ 77 | @CheckReturnValue 78 | @Nonnull 79 | public ConcurrencyAwarePoolBuilder evaluatePeriod(@Nonnull Duration duration){ 80 | this.evaluatePeriod = duration; 81 | return this; 82 | } 83 | 84 | /** 85 | * @param extendThreshold if min concurrency reach this threshold, the pool would extend. 86 | * @param shrinkThreshold if the second min concurrency below extendThreshold*shrinkThreshold, the pool would shrink. 87 | */ 88 | @CheckReturnValue 89 | @Nonnull 90 | public ConcurrencyAwarePoolBuilder simpleThresholdStrategy(@Nonnegative int extendThreshold, 91 | @Nonnegative double shrinkThreshold) { 92 | return strategy(new SimpleConcurrencyAdjustStrategy(extendThreshold, shrinkThreshold, 93 | DEFAULT_CONTINUOUS_EXTEND_THRESHOLD, DEFAULT_CONTINUOUS_SHRINK_THRESHOLD)); 94 | } 95 | 96 | /** 97 | * @param extendThreshold if min concurrency reach this threshold, the pool would extend. 98 | * @param shrinkThreshold if the second min concurrency below extendThreshold*shrinkThreshold, the pool would shrink. 99 | */ 100 | @CheckReturnValue 101 | @Nonnull 102 | public ConcurrencyAwarePoolBuilder simpleThresholdStrategy(@Nonnegative int extendThreshold, 103 | @Nonnegative double shrinkThreshold, @Nonnegative int continuousExtendThreshold, 104 | @Nonnegative int continuousShrinkThreshold) { 105 | return strategy(new SimpleConcurrencyAdjustStrategy(extendThreshold, shrinkThreshold, 106 | continuousExtendThreshold, continuousShrinkThreshold)); 107 | } 108 | 109 | /** 110 | * @throws IllegalArgumentException when maxSize is smaller than minIdle 111 | */ 112 | @Nonnull 113 | public Pool build(@Nonnull ThrowableSupplier value) { 114 | this.factory = checkNotNull(value); 115 | ensure(); 116 | return new LazyPool<>(() -> new ConcurrencyAwarePool<>(this)); 117 | } 118 | 119 | private void ensure() { 120 | if (maxSize < minIdle) { 121 | throw new IllegalArgumentException( 122 | "maxSize[" + maxSize + "] must be larger than minIdle[" + minIdle + "]."); 123 | } 124 | if (strategy == null) { 125 | logger.warn("no strategy found. pool would run as static mode."); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/test/java/com/github/phantomthief/pool/impl/ConcurrencyAwarePoolTest.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool.impl; 2 | 3 | import static com.github.phantomthief.pool.impl.ConcurrencyAwarePool.CURRENT_COUNT; 4 | import static com.google.common.util.concurrent.MoreExecutors.shutdownAndAwaitTermination; 5 | import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly; 6 | import static java.time.Duration.ofSeconds; 7 | import static java.util.concurrent.Executors.newFixedThreadPool; 8 | import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; 9 | import static java.util.concurrent.TimeUnit.DAYS; 10 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 11 | import static java.util.concurrent.TimeUnit.SECONDS; 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | import static org.junit.jupiter.api.Assertions.assertThrows; 14 | import static org.junit.jupiter.api.Assertions.assertTrue; 15 | import static org.junit.jupiter.api.Assertions.fail; 16 | 17 | import java.util.Set; 18 | import java.util.concurrent.CopyOnWriteArraySet; 19 | import java.util.concurrent.ExecutorService; 20 | import java.util.concurrent.ThreadLocalRandom; 21 | import java.util.concurrent.atomic.AtomicInteger; 22 | 23 | import org.junit.jupiter.api.Assertions; 24 | import org.junit.jupiter.api.Test; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | 28 | import com.github.phantomthief.pool.Pool; 29 | import com.google.common.util.concurrent.UncheckedTimeoutException; 30 | 31 | /** 32 | * @author w.vela 33 | * Created on 09/09/2016. 34 | */ 35 | class ConcurrencyAwarePoolTest { 36 | 37 | private static final Logger logger = LoggerFactory.getLogger(ConcurrencyAwarePoolTest.class); 38 | private volatile boolean afterRun = false; 39 | private AtomicInteger executorCounter = new AtomicInteger(); 40 | 41 | private Set executorSet = new CopyOnWriteArraySet<>(); 42 | private int maxCount; 43 | private volatile boolean closing; 44 | 45 | @Test 46 | void test() { 47 | maxCount = 20; 48 | int extendThreshold = 10; 49 | int minIdleCount = 2; 50 | double shrinkThreshold = 0.5D; 51 | 52 | newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> { 53 | if (closing) { 54 | return; 55 | } 56 | try { 57 | if (afterRun) { 58 | assertTrue(executorSet.size() >= minIdleCount); 59 | } 60 | assertTrue(executorSet.size() <= maxCount); 61 | executorSet.forEach(Assertions::assertNotNull); 62 | } catch (Throwable e) { 63 | e.printStackTrace(); 64 | } 65 | }, 50, 50, MILLISECONDS); 66 | 67 | Pool pool = ConcurrencyAwarePool. builder() 68 | .destroy(Executor::close) 69 | .maxSize(maxCount) 70 | .minIdle(minIdleCount) 71 | .evaluatePeriod(ofSeconds(1)) 72 | .simpleThresholdStrategy(extendThreshold, shrinkThreshold) 73 | .build(Executor::new); 74 | logger.info("after create pool."); 75 | pool.run(o -> {}); 76 | afterRun = true; 77 | 78 | ExecutorService executorService = newFixedThreadPool(60); 79 | for (int i = 0; i < 500; i++) { 80 | int j = i; 81 | executorService.execute(() -> runWithTest(pool, j)); 82 | } 83 | logger.info("current count:{}", pool.getStats(CURRENT_COUNT)); 84 | logger.info("waiting closing..."); 85 | shutdownAndAwaitTermination(executorService, 1, DAYS); 86 | logger.info("after 1 round."); 87 | sleepUninterruptibly(15, SECONDS); 88 | logger.info("executor:{}", executorSet.size()); 89 | assertEquals(executorSet.size(), minIdleCount); 90 | 91 | executorService = newFixedThreadPool(300); 92 | for (int i = 0; i < 3000; i++) { 93 | int j = i; 94 | executorService.execute(() -> runWithTest(pool, j)); 95 | } 96 | shutdownAndAwaitTermination(executorService, 1, DAYS); 97 | 98 | executorService = newFixedThreadPool(30); 99 | for (int i = 0; i < 1000; i++) { 100 | int j = i; 101 | executorService.execute(() -> runWithTest(pool, j)); 102 | } 103 | shutdownAndAwaitTermination(executorService, 1, DAYS); 104 | 105 | logger.info("start closing..."); 106 | closing = true; 107 | pool.close(); 108 | logger.info("after closed..."); 109 | assertEquals(0, executorSet.size()); 110 | logger.info("after 2 round."); 111 | } 112 | 113 | @Test 114 | void testIllegal() { 115 | Pool pool = ConcurrencyAwarePool. builder() 116 | .build(() -> "test"); 117 | pool.run(s -> logger.info("{}", s)); 118 | pool.close(); 119 | assertThrows(IllegalStateException.class, pool::borrow); 120 | assertThrows(IllegalArgumentException.class, () -> ConcurrencyAwarePool. builder() 121 | .minIdle(10).maxSize(5).build(() -> "test")); 122 | } 123 | 124 | private void runWithTest(Pool pool, int j) { 125 | try { 126 | pool.supply(e -> e.convert(j)); 127 | } catch (UncheckedTimeoutException e) { 128 | int size = executorSet.size(); 129 | if (size < maxCount) { 130 | fail("have more max to create:" + size); 131 | } 132 | } 133 | } 134 | 135 | private class Executor implements AutoCloseable { 136 | 137 | private final int count; 138 | private volatile boolean closed; 139 | private AtomicInteger concurrency = new AtomicInteger(); 140 | 141 | Executor() { 142 | count = executorCounter.getAndIncrement(); 143 | executorSet.add(this); 144 | if (executorSet.size() > maxCount) { 145 | fail("out of executor."); 146 | } 147 | logger.info("create obj, after count:{}", executorSet.size()); 148 | } 149 | 150 | String convert(int i) { 151 | if (closed) { 152 | fail("executor has been closed."); 153 | } 154 | int current = concurrency.incrementAndGet(); 155 | try { 156 | sleepUninterruptibly(ThreadLocalRandom.current().nextInt(5 * 1000), MILLISECONDS); 157 | if (i % 100 == 0) { 158 | logger.info("executor:{}, {}, concurrency:{}, total obj:{}", count, i, current, 159 | executorSet.size()); 160 | } 161 | return i + ""; 162 | } finally { 163 | concurrency.decrementAndGet(); 164 | } 165 | } 166 | 167 | @Override 168 | public void close() { 169 | closed = true; 170 | assertTrue(executorSet.remove(this)); 171 | logger.info("closing:{}, after closing:{}", count, executorSet.size()); 172 | } 173 | } 174 | } -------------------------------------------------------------------------------- /src/main/java/com/github/phantomthief/pool/impl/ConcurrencyAwarePool.java: -------------------------------------------------------------------------------- 1 | package com.github.phantomthief.pool.impl; 2 | 3 | import static com.github.phantomthief.pool.impl.SharedResource.cleanupExecutor; 4 | import static com.google.common.base.Preconditions.checkNotNull; 5 | import static com.google.common.base.Throwables.throwIfUnchecked; 6 | import static com.google.common.util.concurrent.MoreExecutors.shutdownAndAwaitTermination; 7 | import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly; 8 | import static java.lang.Math.min; 9 | import static java.util.Comparator.comparingInt; 10 | import static java.util.Optional.ofNullable; 11 | import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; 12 | import static java.util.concurrent.TimeUnit.DAYS; 13 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 14 | import static java.util.concurrent.TimeUnit.MINUTES; 15 | import static java.util.concurrent.TimeUnit.SECONDS; 16 | 17 | import java.util.ArrayList; 18 | import java.util.ConcurrentModificationException; 19 | import java.util.IdentityHashMap; 20 | import java.util.Iterator; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.concurrent.ScheduledExecutorService; 24 | import java.util.concurrent.atomic.AtomicInteger; 25 | 26 | import javax.annotation.CheckReturnValue; 27 | import javax.annotation.Nonnull; 28 | import javax.annotation.concurrent.ThreadSafe; 29 | 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | 33 | import com.github.phantomthief.pool.Pool; 34 | import com.github.phantomthief.pool.Pooled; 35 | import com.github.phantomthief.pool.StatsKey; 36 | import com.github.phantomthief.pool.impl.ConcurrencyAdjustStrategy.AdjustResult; 37 | import com.github.phantomthief.util.ThrowableConsumer; 38 | import com.github.phantomthief.util.ThrowableSupplier; 39 | import com.google.common.base.Supplier; 40 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 41 | 42 | /** 43 | * @author w.vela 44 | * Created on 06/09/2016. 45 | */ 46 | @ThreadSafe 47 | public class ConcurrencyAwarePool implements Pool { 48 | 49 | private static final Logger logger = LoggerFactory.getLogger(ConcurrencyAwarePool.class); 50 | 51 | public static final StatsKey CURRENT_COUNT = new SimpleStatsKey<>(Integer.class); 52 | public static final StatsKey CURRENT_CONCURRENCY = new SimpleStatsKey<>(Integer.class); 53 | 54 | private final ThrowableConsumer destroy; 55 | 56 | private final List currentAvailable; 57 | 58 | private final ScheduledExecutorService scheduledExecutor = newSingleThreadScheduledExecutor( 59 | new ThreadFactoryBuilder() 60 | .setNameFormat("concurrency-pool-adjust-%d") 61 | .build()); 62 | 63 | private final Map, Supplier> stats; 64 | 65 | private volatile boolean closing = false; 66 | 67 | /** 68 | * see {@link ConcurrencyAwarePool#builder()} 69 | */ 70 | ConcurrencyAwarePool(ConcurrencyAwarePoolBuilder builder) { 71 | this.destroy = builder.destroy; 72 | 73 | ThrowableSupplier factory = builder.factory; 74 | int minIdle = builder.minIdle; 75 | int maxSize = builder.maxSize; 76 | 77 | currentAvailable = new ArrayList<>(maxSize); 78 | 79 | for (int i = 0; i < minIdle; i++) { 80 | try { 81 | currentAvailable.add(new CounterWrapper(factory.get())); 82 | } catch (Throwable e) { 83 | throwIfUnchecked(e); 84 | throw new RuntimeException(e); 85 | } 86 | } 87 | 88 | long periodInMs = builder.evaluatePeriod.toMillis(); 89 | ConcurrencyAdjustStrategy strategy = builder.strategy; 90 | scheduledExecutor.scheduleWithFixedDelay(() -> { 91 | List toClosed = null; 92 | try { 93 | if (strategy != null) { 94 | AdjustResult adjust = strategy.adjust(currentAvailable); 95 | if (adjust == null) { 96 | return; 97 | } 98 | int realToCreate = min(adjust.getCreate(), maxSize - currentAvailable.size()); 99 | for (int i = 0; i < realToCreate; i++) { 100 | currentAvailable.add(new CounterWrapper(factory.get())); 101 | } 102 | 103 | if (adjust.getEvict() != null) { 104 | int toRemoveCount = Math.max(0, currentAvailable.size() - minIdle); 105 | for (ConcurrencyInfo item : adjust.getEvict()) { 106 | if (toRemoveCount <= 0) { 107 | break; 108 | } 109 | if (currentAvailable.removeIf(it -> it == item)) { 110 | toRemoveCount--; 111 | if (toClosed == null) { 112 | toClosed = new ArrayList<>(); 113 | } 114 | toClosed.add(CounterWrapper.class.cast(item)); 115 | } 116 | } 117 | } 118 | } 119 | } catch (Throwable e) { 120 | logger.error("", e); 121 | } finally { 122 | closePending(toClosed); 123 | } 124 | }, periodInMs, periodInMs, MILLISECONDS); 125 | 126 | stats = buildStats(); 127 | } 128 | 129 | private Map, Supplier> buildStats() { 130 | Map, Supplier> map = new IdentityHashMap<>(); 131 | map.put(CURRENT_COUNT, currentAvailable::size); 132 | map.put(CURRENT_CONCURRENCY, this::estimateCurrentConcurrency); 133 | return map; 134 | } 135 | 136 | private int estimateCurrentConcurrency() { 137 | do { 138 | try { 139 | return currentAvailable.stream().mapToInt(CounterWrapper::currentConcurrency).sum(); 140 | } catch (ConcurrentModificationException e) { 141 | // ignore and retry. 142 | } 143 | } while (true); 144 | } 145 | 146 | private void closePending(List toClosed) { 147 | if (toClosed == null) { 148 | return; 149 | } 150 | for (CounterWrapper item : toClosed) { 151 | cleanupExecutor().execute(() -> { 152 | try { 153 | item.close(); 154 | } catch (Throwable e) { 155 | logger.error("", e); 156 | } 157 | }); 158 | } 159 | } 160 | 161 | @Nonnull 162 | @Override 163 | public Pooled borrow() { 164 | if (closing) { 165 | throw new IllegalStateException("pool is closed."); 166 | } 167 | CounterWrapper counterWrapper; 168 | do { 169 | try { 170 | counterWrapper = currentAvailable.stream() 171 | .filter(it -> !it.isClosing()) 172 | .min(comparingInt(CounterWrapper::currentConcurrency)) 173 | .orElseThrow(() -> new IllegalStateException("pool is closed.")); 174 | break; 175 | } catch (ConcurrentModificationException e) { 176 | // ignore the exception 177 | } 178 | } while (true); 179 | counterWrapper.enter(); 180 | return counterWrapper; 181 | } 182 | 183 | @Override 184 | public V getStats(@Nonnull StatsKey key) { 185 | checkNotNull(key); 186 | if (key instanceof SimpleStatsKey) { 187 | SimpleStatsKey simpleStatsKey = (SimpleStatsKey) key; 188 | return ofNullable(stats.get(key)) 189 | .map(Supplier::get) 190 | .map(simpleStatsKey::cast) 191 | .orElse(null); 192 | } else { 193 | return null; 194 | } 195 | } 196 | 197 | @Override 198 | public void returnObject(@Nonnull Pooled pooled) { 199 | checkNotNull(pooled); 200 | if (pooled instanceof ConcurrencyAwarePool.CounterWrapper) { 201 | ((CounterWrapper) pooled).leave(); 202 | } else { 203 | logger.warn("invalid pooled object:{}", pooled); 204 | } 205 | } 206 | 207 | @Override 208 | public void close() { 209 | closing = true; 210 | shutdownAndAwaitTermination(scheduledExecutor, 1, DAYS); 211 | Iterator iterator = currentAvailable.iterator(); 212 | Throwable toThrow = null; 213 | while (iterator.hasNext()) { 214 | CounterWrapper wrapper = iterator.next(); 215 | iterator.remove(); 216 | try { 217 | wrapper.close(); 218 | } catch (Throwable e) { 219 | toThrow = e; 220 | } 221 | } 222 | if (toThrow != null) { 223 | throwIfUnchecked(toThrow); 224 | throw new RuntimeException(toThrow); 225 | } 226 | } 227 | 228 | @CheckReturnValue 229 | @Nonnull 230 | public static ConcurrencyAwarePoolBuilder builder() { 231 | return new ConcurrencyAwarePoolBuilder<>(); 232 | } 233 | 234 | private class CounterWrapper implements Pooled, AutoCloseable, ConcurrencyInfo { 235 | 236 | private final T obj; 237 | private final AtomicInteger concurrency = new AtomicInteger(); 238 | 239 | private volatile boolean closing = false; 240 | 241 | CounterWrapper(@Nonnull T obj) { 242 | this.obj = checkNotNull(obj); 243 | } 244 | 245 | @Nonnull 246 | @Override 247 | public T get() { 248 | return obj; 249 | } 250 | 251 | /** 252 | * would blocking until no using. 253 | */ 254 | @Override 255 | public void close() throws Exception { 256 | closing = true; 257 | synchronized (concurrency) { 258 | while (concurrency.intValue() > 0) { 259 | concurrency.wait(MINUTES.toMillis(1)); 260 | } 261 | } 262 | if (destroy != null) { 263 | // sleep for one more second for safety. 264 | sleepUninterruptibly(1, SECONDS); 265 | destroy.accept(obj); 266 | } 267 | } 268 | 269 | private boolean isClosing() { 270 | return closing; 271 | } 272 | 273 | @Override 274 | public int currentConcurrency() { 275 | return concurrency.intValue(); 276 | } 277 | 278 | private void enter() { 279 | concurrency.getAndIncrement(); 280 | } 281 | 282 | private void leave() { 283 | int after = concurrency.decrementAndGet(); 284 | if (closing && after == 0) { 285 | synchronized (concurrency) { 286 | concurrency.notifyAll(); 287 | } 288 | } 289 | } 290 | } 291 | 292 | private static class SimpleStatsKey implements StatsKey { 293 | 294 | private final Class type; 295 | 296 | SimpleStatsKey(Class type) { 297 | this.type = type; 298 | } 299 | 300 | V cast(Object obj) { 301 | return type.cast(obj); 302 | } 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.github.phantomthief 4 | simple-pool 5 | 0.1.20-SNAPSHOT 6 | 7 | 8 | 3.0.2 9 | 1.7.21 10 | 28.1-jre 11 | 0.1.49 12 | 13 | 5.6.2 14 | 1.1.8 15 | 1.23 16 | 17 | 4.3.0 18 | 0.8.5 19 | 1.6.8 20 | 3.8.1 21 | 3.2.1 22 | 3.2.0 23 | 3.0.0-M4 24 | 2.2.6 25 | 3.2.0 26 | 27 | 28 | 29 | org.sonatype.oss 30 | oss-parent 31 | 9 32 | 33 | 34 | Simple Pool 35 | A simple Java Pool 36 | 37 | https://github.com/PhantomThief/simple-pool 38 | 39 | 40 | 41 | w.vela 42 | 43 | 44 | 45 | 46 | scm:git:git@github.com:PhantomThief/simple-pool.git 47 | https://github.com/PhantomThief/simple-pool.git 48 | scm:git:git@github.com:PhantomThief/simple-pool.git 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.junit 56 | junit-bom 57 | ${junit.version} 58 | pom 59 | import 60 | 61 | 62 | 63 | 64 | 65 | 66 | com.google.code.findbugs 67 | jsr305 68 | ${jsr305.version} 69 | provided 70 | 71 | 72 | org.slf4j 73 | slf4j-api 74 | ${slf4j-api.version} 75 | 76 | 77 | com.google.guava 78 | guava 79 | ${guava.version} 80 | 81 | 82 | com.github.phantomthief 83 | more-lambdas 84 | ${more-lambdas.version} 85 | 86 | 87 | 88 | org.junit.jupiter 89 | junit-jupiter-api 90 | test 91 | 92 | 93 | ch.qos.logback 94 | logback-classic 95 | ${logback-classic.version} 96 | test 97 | 98 | 99 | org.openjdk.jmh 100 | jmh-core 101 | ${jmh.version} 102 | test 103 | 104 | 105 | org.openjdk.jmh 106 | jmh-generator-annprocess 107 | ${jmh.version} 108 | test 109 | 110 | 111 | 112 | 113 | 114 | 115 | org.apache.maven.plugins 116 | maven-compiler-plugin 117 | ${maven-compiler-plugin.version} 118 | 119 | 1.8 120 | 1.8 121 | true 122 | true 123 | UTF-8 124 | true 125 | 126 | -parameters 127 | 128 | 129 | 130 | 131 | maven-surefire-plugin 132 | ${maven-surefire-plugin.version} 133 | 134 | 135 | org.apache.maven.plugins 136 | maven-source-plugin 137 | ${maven-source-plugin.version} 138 | 139 | 140 | attach-sources 141 | 142 | jar 143 | 144 | 145 | 146 | 147 | 148 | org.apache.maven.plugins 149 | maven-javadoc-plugin 150 | ${maven-javadoc-plugin.version} 151 | 152 | none 153 | 8 154 | 155 | 156 | 157 | attach-javadocs 158 | 159 | jar 160 | 161 | 162 | 163 | 164 | 165 | org.sonatype.plugins 166 | nexus-staging-maven-plugin 167 | ${nexus-staging-maven-plugin.version} 168 | true 169 | 170 | sonatype-nexus-staging 171 | https://oss.sonatype.org/ 172 | true 173 | 174 | 175 | 176 | org.jacoco 177 | jacoco-maven-plugin 178 | ${jacoco-maven-plugin.version} 179 | 180 | 181 | prepare-agent 182 | 183 | prepare-agent 184 | 185 | 186 | 187 | 188 | 189 | org.eluder.coveralls 190 | coveralls-maven-plugin 191 | ${coveralls-maven-plugin.version} 192 | 193 | 194 | pl.project13.maven 195 | git-commit-id-plugin 196 | ${git-commit-id-plugin.version} 197 | 198 | 199 | get-the-git-infos 200 | 201 | revision 202 | 203 | 204 | 205 | 206 | false 207 | 8 208 | yyyyMMddHHmmssSSS 209 | false 210 | false 211 | true 212 | 213 | true 214 | 215 | 216 | git.branch 217 | git.build 218 | git.commit.id 219 | git.commit.time 220 | git.commit.user 221 | git.remote.origin.url 222 | 223 | 224 | 225 | 226 | org.apache.maven.plugins 227 | maven-jar-plugin 228 | ${maven-jar-plugin.version} 229 | 230 | 231 | 232 | true 233 | 234 | 235 | ${git.commit.id} 236 | ${git.build.time} 237 | ${git.branch} 238 | ${java.version} 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | sonatype-nexus-snapshots 249 | https://oss.sonatype.org/content/repositories/snapshots 250 | 251 | 252 | sonatype-nexus-staging 253 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 254 | 255 | 256 | 257 | --------------------------------------------------------------------------------