package org.qfox.jestful.form;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by yangchangpei on 17/8/17.
 */
public class ConcurrentHashMapTokenManager implements TokenManager {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private long duration = 30L * 60L * 1000L; // 半小时失效
    private TimeUnit unit = TimeUnit.MILLISECONDS;
    private TokenGenerator generator = new UUIDTokenGenerator();
    private TokenFactory factory = new ConcurrentLinkedQueueTokenFactory();
    private Map<String, Token> map = new ConcurrentHashMap<String, Token>();
    private AtomicInteger size = new AtomicInteger(0);
    private int maxBuffSize;
    private long cleanInterval = 5L * 1000L;

    private Thread cleaner = new Thread() {

        {
            this.setDaemon(true);
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(cleanInterval);
                    Iterator<Map.Entry<String, Token>> iterator = map.entrySet().iterator();
                    while (iterator.hasNext()) {
                        Map.Entry<String, Token> entry = iterator.next();
                        if (entry.getValue().expired()) {
                            iterator.remove();
                        }
                    }

                } catch (Exception e) {
                    logger.warn("error occur when cleaning form tokens:", e);
                }
            }
        }

    };

    public ConcurrentHashMapTokenManager() {
        this(2048);
    }

    public ConcurrentHashMapTokenManager(int maxBuffSize) {
        this.maxBuffSize = maxBuffSize;
        this.cleaner.start();
    }

    @Override
    public String create() throws TokenExceedException {
        return create(duration, unit);
    }

    @Override
    public String create(long duration, TimeUnit unit) throws TokenExceedException {
        if (size.get() < maxBuffSize) {

            String key = generator.generate();
            long timeExpired = System.currentTimeMillis() + unit.toMillis(duration);
            Token token = factory.produce(key, timeExpired);
            map.put(key, token);
            return key;
        } else {
            throw new TokenExceedException();
        }
    }

    @Override
    public void verify(String key) throws TokenExpiredException, TokenMissedException {
        Token token = map.remove(key);
        if (token == null) throw new TokenMissedException(key);
        if (token.expired()) throw new TokenExpiredException(key);
        factory.recover(token);
    }

    public long getDuration() {
        return duration;
    }

    public void setDuration(long duration) {
        this.duration = duration;
    }

    public TimeUnit getUnit() {
        return unit;
    }

    public void setUnit(TimeUnit unit) {
        this.unit = unit;
    }

    public TokenGenerator getGenerator() {
        return generator;
    }

    public void setGenerator(TokenGenerator generator) {
        this.generator = generator;
    }
}

@@ -1521,33 +1521,32 @@
               %7D%0A
-%0A
                 
@@ -1643,24 +1643,53 @@
 kens:%22, e);%0A
+                %7D finally %7B%0A%0A
             
@@ -2172,16 +2172,28 @@
 size.get
+AndIncrement
 () %3C max
@@ -2200,25 +2200,24 @@
 BuffSize) %7B%0A
-%0A
             
@@ -2471,16 +2471,52 @@
  else %7B%0A
+            size.getAndDecrement();%0A
         
@@ -2707,24 +2707,25 @@
 emove(key);%0A
+%0A
         if (
@@ -2768,32 +2768,98 @@
 Exception(key);%0A
+%0A        size.getAndDecrement();%0A        factory.recover(token);%0A%0A
         if (toke
@@ -2913,40 +2913,8 @@
 y);%0A
-        factory.recover(token);%0A
     

Add Form Parameters Bind to Request Supports