2016-09-18 5 views
0

私は任意のAPIに対するリクエストを抑制するインターセプタを持っています。私は任意のメソッドがレート制限されるようにTPS値を差し込むことをサポートする注釈を書こうとしています。Guiceメソッドインターセプタ(bindInterceptor中のヌルポインタ例外)のハングを取得できません

import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 

@Target({ ElementType.METHOD}) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface RateLimitMethodAnnotation { 

    // Permissible transactions per second. 
    long tps() default Long.MAX_VALUE; 

    // The identifier for the rate limiter. A distinct token bucket is defined 
    // per id. 
    String id(); 
} 

次のようにインターセプタの実装はあるものの: -

import org.aopalliance.intercept.MethodInterceptor; 
import org.aopalliance.intercept.MethodInvocation; 
import org.isomorphism.util.TokenBucket; 
import org.isomorphism.util.TokenBuckets; 

import java.util.concurrent.ConcurrentHashMap; 
import java.util.concurrent.TimeUnit; 

/** 
* Implementation of the rate limiter. 
*/ 
public class RateLimitMethodAnnotationInterceptor implements MethodInterceptor { 

    private static final ConcurrentHashMap<String, TokenBucket> 
      TOKEN_BUCKET_MAP = new ConcurrentHashMap<String, TokenBucket>(); 

    public Object invoke(MethodInvocation methodInvocation) throws Throwable { 
     final RateLimitMethodAnnotation rateLimitMethod = 
      methodInvocation.getMethod().getAnnotation(RateLimitMethodAnnotation.class); 

     final String rateLimitId = rateLimitMethod.id(); 
     final long tps = rateLimitMethod.tps(); 

     boolean proceedMethodCall = tryProceed(rateLimitId, tps); 

     while(!proceedMethodCall) { 
      Thread.sleep(getDurationTillRefillInMilliSecond(rateLimitId, tps)); 
      proceedMethodCall = tryProceed(rateLimitId, tps); 
     } 

     return methodInvocation.proceed(); 
    } 

    private boolean tryProceed(final String tokenBucketId, final long tps) { 

     final TokenBucket tokenBucket = TOKEN_BUCKET_MAP.get(tokenBucketId); 

     if (tokenBucket == null) { 
      TOKEN_BUCKET_MAP.put(tokenBucketId, buildTokenBucket(tps)); 
     } 

     return tokenBucket.tryConsume(); 
    } 

    private long getDurationTillRefillInMilliSecond(final String tokenBucketId, long tps) { 
     final TokenBucket tokenBucket = TOKEN_BUCKET_MAP.get(tokenBucketId); 

     if (tokenBucket == null) { 
      TOKEN_BUCKET_MAP.put(tokenBucketId, buildTokenBucket(tps)); 
     } 

     return tokenBucket.getDurationUntilNextRefill(TimeUnit.MILLISECONDS); 

    } 

    private TokenBucket buildTokenBucket(final long tps) { 
     return TokenBuckets.builder().withCapacity(tps) 
       .withFixedIntervalRefillStrategy(1, 1, TimeUnit.SECONDS) 
       .build(); 
    } 
} 

今は、私は次のコードを使用しているバインディングを定義するために、: -

import com.google.inject.AbstractModule; 
import com.google.inject.matcher.Matchers; 

/** 
* Configuration for rate limiting. 
*/ 
public class RateLimitConfig extends AbstractModule { 

    public void configure() { 
     bindInterceptor(Matchers.any(), 
      Matchers.annotatedWith(RateLimitMethodAnnotation.class), 
      new RateLimitMethodAnnotationInterceptor()); 
    } 
} 

を私が書きました注射の設定を証明するための非常にシンプルな健全性テストは、次のように機能します: -

私はNPE

Running TestRateLimit 
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.073 sec <<< FAILURE! 
testRateLimitInterceptorSanityTest(TestRateLimit) Time elapsed: 0.013 sec <<< ERROR! 
java.lang.NullPointerException 
    at com.google.inject.AbstractModule.bindInterceptor(AbstractModule.java:167) 
at org.isomorphism.annotation.RateLimitConfig.configure(RateLimitConfig.java:12) 
    at org.isomorphism.annotation.TestRateLimit.testRateLimitInterceptorSanityTest(TestRateLimit.java:17) 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:498) 
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) 
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) 

になってしまった私はここのコードhttps://github.com/google/guice/blob/master/core/src/com/google/inject/AbstractModule.javaに見えたが、便利な何かを見つけることができませんでした。私はコードにデバッグしましたが、私はデータ構造を理解することができません(そして、私は単純な作業のためにしたくないフレームワークを完全に理解するために時間を費やす必要があります)。

1. Even for a simple task like this, Guice should not throw an NPE even if 0 methods were annotated with the annotation I have in mind. 
2. Is the configure method never supposed to be called directly in code? If so there is no API given in AbstractModule nor documentation how to configure bindInterceptors. Taking the code out of RateLimitConfig did not work (after putting it into the test suite). 

誰でも私を助けてくれますか?

@Test 
public void testRateLimitInterceptorSanityTest() { 
    final RateLimitConfig config = new RateLimitConfig(); 
    Injector injector = Guice.createInjector(config); 
    TestRateLimit testInstance = injector.getInstance(TestRateLimit.class); 

    int retVal = testInstance.stubMethod(); 
    assertTrue(retVal == TEST_VAL); 
} 

答えて

1
。 configureメソッドがパブリックアクセスであるかどうか疑問に思っていました。 Javacは上書きについて不平を言っていないので、私はそれを逃した。
+1

ああおかげ:あなたは、あなたのテストケースで任意のインジェクターを作成していない

関連する問題