├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── OSSMETADATA ├── README.md ├── build.gradle ├── codequality ├── HEADER └── checkstyle.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── netflix-commons-util └── src │ ├── main │ └── java │ │ └── com │ │ └── netflix │ │ └── util │ │ ├── HashCode.java │ │ ├── MeasuredRate.java │ │ ├── Pair.java │ │ ├── UUIDFactory.java │ │ └── concurrent │ │ ├── ConcurrentUUIDFactory.java │ │ └── ShutdownEnabledTimer.java │ └── test │ └── java │ └── com │ └── netflix │ └── util │ ├── PairTest.java │ └── concurrent │ └── ConcurrentUUIDFactoryTest.java ├── netflix-eventbus-bridge └── src │ ├── main │ └── java │ │ └── com │ │ └── netflix │ │ └── eventbus │ │ └── bridge │ │ ├── AbstractEventBusBridge.java │ │ ├── EventBusBridge.java │ │ ├── EventBusBridgeStats.java │ │ ├── ImmutableEventBusBridgeStats.java │ │ └── SimpleEventBusBridgeStats.java │ └── test │ └── java │ └── com │ └── netflix │ └── eventbus │ ├── AbstractEventBusBridgeTest.java │ └── DummyEventBusBridge.java ├── netflix-eventbus-rx └── src │ ├── main │ └── java │ │ └── com │ │ └── netflix │ │ └── eventbus │ │ └── rx │ │ └── RxEventBus.java │ └── test │ └── java │ └── com │ └── netflix │ └── eventbus │ └── rx │ └── RxEventBusTest.java ├── netflix-eventbus └── src │ ├── main │ └── java │ │ └── com │ │ └── netflix │ │ └── eventbus │ │ ├── filter │ │ ├── AlwaysFalseEventFilter.java │ │ ├── AlwaysTrueEventFilter.java │ │ ├── EventFilterCompiler.java │ │ └── lang │ │ │ ├── FilterLanguageSupport.java │ │ │ ├── InvalidFilterException.java │ │ │ ├── infix │ │ │ ├── InfixEventFilter.java │ │ │ ├── InfixFilterLanguageSupport.java │ │ │ └── package-info.java │ │ │ └── package-info.java │ │ ├── impl │ │ ├── AbstractEventBusStats.java │ │ ├── AgeBatchingQueue.java │ │ ├── DefaultConsumerQueueSupplier.java │ │ ├── EventBatch.java │ │ ├── EventBusImpl.java │ │ ├── EventBusStats.java │ │ ├── EventConsumer.java │ │ ├── EventConsumerStats.java │ │ ├── SizeAndAgeBatchingQueue.java │ │ └── SubscriberValidator.java │ │ ├── spi │ │ ├── CatchAllSubscriber.java │ │ ├── DynamicSubscriber.java │ │ ├── EventBus.java │ │ ├── EventCreator.java │ │ ├── EventFilter.java │ │ ├── FilterLanguage.java │ │ ├── InvalidSubscriberException.java │ │ ├── SerializableEventFilter.java │ │ ├── Subscribe.java │ │ ├── SubscriberConfigProvider.java │ │ ├── SubscriberInfo.java │ │ └── SyncSubscribersGatekeeper.java │ │ └── utils │ │ └── EventBusUtils.java │ └── test │ └── java │ └── com │ └── netflix │ ├── eventbus │ ├── impl │ │ ├── AgeBatchingTest.java │ │ ├── BatchSubscribersTest.java │ │ ├── ConsumerQueueFullTest.java │ │ ├── DynamicSubConfigTest.java │ │ ├── IllegalSubscriberTests.java │ │ ├── NFEventBusTest.java │ │ └── SizeAndAgeBatchingTest.java │ ├── spi │ │ └── SynSubGatekeeperTest.java │ └── test │ │ └── AnonymousInnerClassConsumerSupplier.java │ └── infix │ └── MockAnnotatable.java ├── netflix-infix └── src │ ├── main │ └── java │ │ └── com │ │ └── netflix │ │ └── infix │ │ ├── AlwaysFalsePredicate.java │ │ ├── AlwaysTruePredicate.java │ │ ├── AndPredicate.java │ │ ├── BooleanValuePredicate.java │ │ ├── InfixCompiler.java │ │ ├── NotPredicate.java │ │ ├── NullValuePredicate.java │ │ ├── NumericValuePredicate.java │ │ ├── OrPredicate.java │ │ ├── PathExistsEventFilter.java │ │ ├── PathValueEventFilter.java │ │ ├── PredicateCompiler.java │ │ ├── Predicates.java │ │ ├── RegexValuePredicate.java │ │ ├── StringValuePredicate.java │ │ ├── TimeMillisValuePredicate.java │ │ ├── TimeRangeValuePredicate.java │ │ ├── TimeStringValuePredicate.java │ │ ├── TimeUtil.java │ │ ├── ValuePredicate.java │ │ ├── XPathValuePredicate.java │ │ └── lang │ │ └── infix │ │ └── antlr │ │ ├── AndTreeNode.java │ │ ├── BetweenTimeMillisTreeNode.java │ │ ├── BetweenTimeStringTreeNode.java │ │ ├── BetweenTreeNode.java │ │ ├── ComparableTreeNode.java │ │ ├── EqualityComparisonBaseTreeNode.java │ │ ├── EqualsTreeNode.java │ │ ├── EventFilter.tokens │ │ ├── EventFilterLexer.java │ │ ├── EventFilterParser.java │ │ ├── EventFilterParsingException.java │ │ ├── ExistsTreeNode.java │ │ ├── FalseValueTreeNode.java │ │ ├── MatchesTreeNode.java │ │ ├── NotEqualsTreeNode.java │ │ ├── NotTreeNode.java │ │ ├── NullTreeNode.java │ │ ├── NullValueTreeNode.java │ │ ├── NumberTreeNode.java │ │ ├── NumericInTreeNode.java │ │ ├── OrTreeNode.java │ │ ├── PredicateBaseTreeNode.java │ │ ├── PredicateTranslatable.java │ │ ├── StringInTreeNode.java │ │ ├── StringTreeNode.java │ │ ├── TimeMillisValueTreeNode.java │ │ ├── TimeStringValueTreeNode.java │ │ ├── TreeNodeUtil.java │ │ ├── TrueValueTreeNode.java │ │ ├── UnexpectedTokenException.java │ │ ├── ValueTreeNode.java │ │ └── XPathTreeNode.java │ └── test │ └── java │ └── com │ └── netflix │ └── infix │ ├── AlwaysFalseEventFilterTest.java │ ├── AlwaysTrueEventFilterTest.java │ ├── AndEventFilterTest.java │ ├── DummyAnnotatable.java │ ├── EventFiltersTest.java │ ├── MockRequestTrace.java │ ├── NotEventFilterTest.java │ ├── NullValuePredicateTest.java │ ├── NumericValuePredicateTest.java │ ├── OrEventFilterTest.java │ ├── PathValueEventFilterTest.java │ ├── RegexValuePredicateTest.java │ ├── StringValuePredicateTest.java │ ├── TimeMillisValuePredicateTest.java │ ├── TimeRangeValuePredicateTest.java │ ├── TimeStringValuePredicateTest.java │ ├── VerificationUtil.java │ └── parser │ ├── CompositeEventFilterParsingTest.java │ ├── FilterPredicate.java │ └── SimpleEventFilterParsingTest.java ├── netflix-jersey-guice └── src │ └── main │ └── java │ └── com │ └── netflix │ └── jersey │ └── guice │ └── providers │ └── exception │ ├── CustomThrowableExceptionMapper.java │ ├── DefaultThrowableExceptionMapper.java │ ├── GsonDefaultExceptionMapper.java │ └── ThrowableExceptionMapper.java ├── netflix-lifecycle └── src │ ├── main │ └── java │ │ └── com │ │ └── netflix │ │ └── lifecycle │ │ └── concurrency │ │ ├── ConcurrencyModule.java │ │ └── CoreCountBasedScheduledExecutorServiceProvider.java │ └── test │ └── java │ └── com │ └── netflix │ └── lifecycle │ └── concurrency │ ├── ConcurrencyModuleTest.java │ └── CoreCountBasedScheduledExecutorServiceProviderTest.java ├── netflix-statistics └── src │ └── main │ └── java │ └── com │ └── netflix │ └── stats │ └── distribution │ ├── DataAccumulator.java │ ├── DataBuffer.java │ ├── DataCollector.java │ ├── DataDistribution.java │ ├── DataDistributionMBean.java │ ├── DataPublisher.java │ ├── Distribution.java │ ├── DistributionMBean.java │ ├── Histogram.java │ ├── HistogramMBean.java │ └── package.html └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | 27 | # OS generated files # 28 | ###################### 29 | .DS_Store* 30 | ehthumbs.db 31 | Icon? 32 | Thumbs.db 33 | 34 | # Editor Files # 35 | ################ 36 | *~ 37 | *.swp 38 | 39 | # Gradle Files # 40 | ################ 41 | *.gradle 42 | 43 | # Build output directories # 44 | ############################ 45 | /target 46 | */target 47 | /build 48 | */build 49 | /test-output 50 | */test-output 51 | /bin 52 | */bin 53 | 54 | # IntelliJ specific files/directories # 55 | ####################################### 56 | out 57 | .idea 58 | *.ipr 59 | *.iws 60 | *.iml 61 | atlassian-ide-plugin.xml 62 | 63 | # Eclipse specific files/directories # 64 | ###################################### 65 | .classpath 66 | .project 67 | .settings 68 | .metadata 69 | 70 | # NetBeans specific files/directories # 71 | ####################################### 72 | .nbattrs 73 | 74 | 75 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 0.2.0 2 | ----------------- 3 | * Update to new build system 4 | -------------------------------------------------------------------------------- /OSSMETADATA: -------------------------------------------------------------------------------- 1 | osslifecycle=active 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Miscellaneous libraries to support Netflix OSS Projects 2 | -------------------------------------------------------------------------------- /codequality/HEADER: -------------------------------------------------------------------------------- 1 | Copyright ${year} Netflix, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/netflix-commons/f03bc491bd75e513cface6f19de19037672996de/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /netflix-commons-util/src/main/java/com/netflix/util/MeasuredRate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.util; 19 | 20 | import java.util.concurrent.atomic.AtomicLong; 21 | 22 | /** 23 | * Utility class for getting a count per last X milliseconds 24 | * 25 | * @author stonse 26 | * @author gkim 27 | * 28 | */ 29 | public class MeasuredRate { 30 | private final AtomicLong _lastBucket = new AtomicLong(0); 31 | private final AtomicLong _currentBucket = new AtomicLong(0); 32 | private final long _sampleInterval; 33 | private volatile long _threshold; 34 | 35 | /** 36 | * @param sampleInterval in milliseconds 37 | */ 38 | public MeasuredRate(long sampleInterval){ 39 | _sampleInterval = sampleInterval; 40 | 41 | _threshold = System.currentTimeMillis() + sampleInterval; 42 | } 43 | 44 | /** 45 | * @return the count in the last sample interval 46 | */ 47 | public long getCount() { 48 | checkAndResetWindow(); 49 | return _lastBucket.get(); 50 | } 51 | 52 | /** 53 | * @return the count in the current sample interval which will be incomplete. 54 | * If you are looking for accurate counts/interval - use {@link MeasuredRate#getCount()} 55 | * instead. 56 | */ 57 | public long getCurrentCount() { 58 | checkAndResetWindow(); 59 | return _currentBucket.get(); 60 | } 61 | 62 | /** 63 | * Increments the count in the current sample interval. If the current 64 | * interval has exceeded, assigns the current count to the 65 | * last bucket and zeros out the current bucket 66 | */ 67 | public void increment() { 68 | checkAndResetWindow(); 69 | _currentBucket.incrementAndGet(); 70 | } 71 | 72 | private void checkAndResetWindow() { 73 | long now = System.currentTimeMillis(); 74 | if(_threshold < now) { 75 | _lastBucket.set(_currentBucket.get()); 76 | _currentBucket.set(0); 77 | _threshold = now + _sampleInterval; 78 | } 79 | } 80 | 81 | public String toString(){ 82 | StringBuilder sb = new StringBuilder(); 83 | sb.append("count:" + getCount()); 84 | sb.append("currentCount:" + getCurrentCount()); 85 | return sb.toString(); 86 | } 87 | 88 | public static void main(String args[]){ 89 | MeasuredRate mr = new MeasuredRate(500); 90 | 91 | for(int i=0; i<1000; i++){ 92 | mr.increment(); 93 | try { 94 | Thread.sleep(100); 95 | } catch (InterruptedException e) { 96 | // TODO Auto-generated catch block 97 | e.printStackTrace(); 98 | } 99 | } 100 | 101 | System.out.println("mr:" + mr); 102 | 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /netflix-commons-util/src/main/java/com/netflix/util/Pair.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.netflix.util; 20 | 21 | import java.io.Serializable; 22 | 23 | 24 | /** 25 | * A simple class that holds a pair of values. 26 | * This may be useful for methods that care to 27 | * return two values (instead of just one). 28 | */ 29 | public class Pair implements Serializable { 30 | 31 | // ======================================== 32 | // Static vars: public, protected, then private 33 | // ======================================== 34 | private static final long serialVersionUID = 2L; 35 | 36 | // ======================================== 37 | // Instance vars: public, protected, then private 38 | // ======================================== 39 | 40 | private E1 mFirst; 41 | private E2 mSecond; 42 | 43 | // ======================================== 44 | // Constructors 45 | // ======================================== 46 | 47 | /** 48 | * Construct a new pair 49 | * 50 | * @param first the object to store as the first value 51 | * @param second the object to store as the second value 52 | */ 53 | public Pair(E1 first, E2 second) { 54 | mFirst = first; 55 | mSecond = second; 56 | } 57 | 58 | // ======================================== 59 | // Methods, grouped by functionality, *not* scope 60 | // ======================================== 61 | 62 | /** 63 | * Get the first value from the pair. 64 | * 65 | * @return the first value 66 | */ 67 | public E1 first() { 68 | return mFirst; 69 | } 70 | 71 | /** 72 | * Get the second value from the pair. 73 | * 74 | * @return the second value 75 | */ 76 | public E2 second() { 77 | return mSecond; 78 | } 79 | 80 | /** 81 | * Set the first value of the pair. 82 | * 83 | * @param first the new first value 84 | */ 85 | public void setFirst(E1 first) { 86 | mFirst = first; 87 | } 88 | 89 | /** 90 | * Set the second value of the pair. 91 | * 92 | * @param second the new second value 93 | */ 94 | public void setSecond(E2 second) { 95 | mSecond = second; 96 | } 97 | 98 | // ---------------------------------------- 99 | // Generic Object methods 100 | 101 | /** 102 | * Pair objects are equal iff they have the same content. 103 | */ 104 | @Override 105 | public boolean equals(Object obj) { 106 | if (obj == this) { 107 | return true; 108 | } else if (obj == null || obj.getClass() != getClass()) { 109 | return false; 110 | } 111 | Pair other = (Pair)obj; 112 | return HashCode.equalObjects(mFirst, other.mFirst) 113 | && HashCode.equalObjects(mSecond, other.mSecond); 114 | } 115 | 116 | // The hash code needs to align with the 117 | // definition of equals. 118 | @Override 119 | public int hashCode() { 120 | HashCode h = new HashCode(); 121 | h.addValue(mFirst); 122 | h.addValue(mSecond); 123 | return h.hashCode(); 124 | } 125 | 126 | } // Pair 127 | -------------------------------------------------------------------------------- /netflix-commons-util/src/main/java/com/netflix/util/UUIDFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.netflix.util; 20 | 21 | import java.util.UUID; 22 | 23 | /** 24 | * Common interface to sources of various versions of UUIDs. 25 | */ 26 | public interface UUIDFactory { 27 | /** 28 | * Generates a new version 4 UUID. 29 | * 30 | * @return the newly generated UUID 31 | */ 32 | UUID generateRandomUuid(); 33 | } 34 | -------------------------------------------------------------------------------- /netflix-commons-util/src/main/java/com/netflix/util/concurrent/ConcurrentUUIDFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.netflix.util.concurrent; 20 | 21 | import com.netflix.util.UUIDFactory; 22 | 23 | import javax.inject.Singleton; 24 | import java.util.Random; 25 | import java.util.UUID; 26 | import java.util.concurrent.ThreadLocalRandom; 27 | 28 | /** 29 | * UUIDFactory implementation that will perform reasonably when used by multiple threads under load. 30 | */ 31 | @Singleton 32 | public class ConcurrentUUIDFactory implements UUIDFactory { 33 | @Override 34 | public UUID generateRandomUuid() { 35 | final Random rnd = ThreadLocalRandom.current(); 36 | long mostSig = rnd.nextLong(); 37 | long leastSig = rnd.nextLong(); 38 | 39 | // Identify this as a version 4 UUID, that is one based on a random value. 40 | mostSig &= 0xffffffffffff0fffL; 41 | mostSig |= 0x0000000000004000L; 42 | 43 | // Set the variant identifier as specified for version 4 UUID values. The two 44 | // high order bits of the lower word are required to be one and zero, respectively. 45 | leastSig &= 0x3fffffffffffffffL; 46 | leastSig |= 0x8000000000000000L; 47 | 48 | return new UUID(mostSig, leastSig); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /netflix-commons-util/src/main/java/com/netflix/util/concurrent/ShutdownEnabledTimer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.netflix.util.concurrent; 20 | 21 | import java.util.Timer; 22 | 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * 28 | * ShutdownEnabledTimer class handles runtime shutdown issues. 29 | * 30 | * Apparently, adding a runtime shutdown hook will create a global reference 31 | * which can cause memory leaks if not cleaned up. 32 | * 33 | * This abstraction provides a wrapped mechanism to manage those runtime 34 | * shutdown hooks. 35 | * 36 | * @author jzarfoss 37 | * 38 | */ 39 | public class ShutdownEnabledTimer extends Timer { 40 | 41 | private static final Logger LOGGER = LoggerFactory 42 | .getLogger(ShutdownEnabledTimer.class); 43 | 44 | private Thread cancelThread; 45 | private String name; 46 | 47 | public ShutdownEnabledTimer(String name, boolean daemon) { 48 | super(name, daemon); 49 | 50 | this.name = name; 51 | 52 | this.cancelThread = new Thread(new Runnable() { 53 | public void run() { 54 | ShutdownEnabledTimer.super.cancel(); 55 | } 56 | }); 57 | 58 | LOGGER.info("Shutdown hook installed for: {}", this.name); 59 | 60 | Runtime.getRuntime().addShutdownHook(this.cancelThread); 61 | } 62 | 63 | @Override 64 | public void cancel() { 65 | super.cancel(); 66 | 67 | LOGGER.info("Shutdown hook removed for: {}", this.name); 68 | 69 | try { 70 | Runtime.getRuntime().removeShutdownHook(this.cancelThread); 71 | } catch (IllegalStateException ise) { 72 | LOGGER.info("Exception caught (might be ok if at shutdown)", ise); 73 | } 74 | 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /netflix-commons-util/src/test/java/com/netflix/util/PairTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.util; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | public class PairTest { 8 | 9 | @Test 10 | public void testPair() { 11 | Pair pair = new Pair("a", "b"); 12 | assertNotNull(pair); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /netflix-commons-util/src/test/java/com/netflix/util/concurrent/ConcurrentUUIDFactoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2015 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.netflix.util.concurrent; 20 | 21 | import org.junit.Assert; 22 | import org.junit.Test; 23 | 24 | import java.util.HashSet; 25 | import java.util.Set; 26 | import java.util.UUID; 27 | 28 | /** 29 | * Unit tests for the ConcurrentUUIDFactory class. 30 | */ 31 | public class ConcurrentUUIDFactoryTest { 32 | @Test 33 | public void testGenerateRandomUuidVersionAndVariant() throws Exception { 34 | final UUID uuid = new ConcurrentUUIDFactory().generateRandomUuid(); 35 | 36 | Assert.assertEquals(4, uuid.version()); 37 | Assert.assertEquals(2, uuid.variant()); 38 | } 39 | 40 | @Test 41 | public void testGenerateRandomUuidNonConstant() throws Exception { 42 | final int numValues = 1000; 43 | final Set values = new HashSet<>(); 44 | final ConcurrentUUIDFactory factory = new ConcurrentUUIDFactory(); 45 | 46 | for (int i = 0; i < numValues; ++i) { 47 | final UUID newUuid = factory.generateRandomUuid(); 48 | Assert.assertTrue("Already generated UUID with value: " + newUuid, values.add(newUuid)); 49 | } 50 | Assert.assertEquals(numValues, values.size()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /netflix-eventbus-bridge/src/main/java/com/netflix/eventbus/bridge/EventBusBridge.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.bridge; 2 | 3 | /** 4 | * Base API for creating a bridge between the EventBus and any other 5 | * messaging API. A bridge differs from a Subscriber or event handler 6 | * in that it's purpose is to forward messages rather than doing any 7 | * real processing of the message 8 | * 9 | * @author elandau 10 | */ 11 | public interface EventBusBridge { 12 | /** 13 | * Pause processing messages. Any messages being send to the 14 | * bridge will be discarded. 15 | * 16 | * No error is thrown if already paused. 17 | * @throws Exception 18 | */ 19 | public void pause() throws Exception; 20 | 21 | /** 22 | * Resume processing messages. 23 | * 24 | * 25 | * No error is thrown if already resumed 26 | */ 27 | public void resume() throws Exception; 28 | } 29 | -------------------------------------------------------------------------------- /netflix-eventbus-bridge/src/main/java/com/netflix/eventbus/bridge/EventBusBridgeStats.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.bridge; 2 | 3 | /** 4 | * Interface specifying stats exposed by the EventBusSuroBridge. 5 | * 6 | * @author elandau 7 | * 8 | */ 9 | public interface EventBusBridgeStats { 10 | /** 11 | * @return Return number of successfully bridged events 12 | */ 13 | public long getConsumeCount(); 14 | 15 | /** 16 | * @return Return number of failed consume event calls 17 | */ 18 | public long getConsumeErrorCount(); 19 | 20 | /** 21 | * Called for each successful consume 22 | * @return New count of consumed events 23 | */ 24 | public long incConsumeCount(); 25 | 26 | /** 27 | * Called for each failed consume 28 | * @param e - Exception describing reason for failure 29 | * @return New count of consume errors 30 | */ 31 | public long incConsumeErrorCount(Exception e); 32 | 33 | /** 34 | * @return Last exception provided to {@link incConsumeErrorCount} 35 | */ 36 | public Exception getLastConsumeException(); 37 | } 38 | -------------------------------------------------------------------------------- /netflix-eventbus-bridge/src/main/java/com/netflix/eventbus/bridge/ImmutableEventBusBridgeStats.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.bridge; 2 | 3 | /** 4 | * Decorator for {@link EventBusBridgeStats} that makes it unmodifiable. 5 | * An instance of this is returned by {@link AbstractEventBusBridge#getStats()} 6 | * 7 | * @author elandau 8 | * 9 | */ 10 | public class ImmutableEventBusBridgeStats implements EventBusBridgeStats { 11 | 12 | private EventBusBridgeStats delegate; 13 | 14 | public ImmutableEventBusBridgeStats(EventBusBridgeStats delegate) { 15 | this.delegate = delegate; 16 | } 17 | 18 | @Override 19 | public long getConsumeCount() { 20 | return this.delegate.getConsumeCount(); 21 | } 22 | 23 | @Override 24 | public long getConsumeErrorCount() { 25 | return this.delegate.getConsumeErrorCount(); 26 | } 27 | 28 | @Override 29 | public long incConsumeCount() { 30 | throw new UnsupportedOperationException(); 31 | } 32 | 33 | @Override 34 | public long incConsumeErrorCount(Exception e) { 35 | throw new UnsupportedOperationException(); 36 | } 37 | 38 | @Override 39 | public Exception getLastConsumeException() { 40 | return this.delegate.getLastConsumeException(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /netflix-eventbus-bridge/src/main/java/com/netflix/eventbus/bridge/SimpleEventBusBridgeStats.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.bridge; 2 | 3 | import java.util.concurrent.atomic.AtomicLong; 4 | 5 | public class SimpleEventBusBridgeStats implements EventBusBridgeStats { 6 | private final AtomicLong consumeErrorCount = new AtomicLong(0); 7 | private final AtomicLong consumeCount = new AtomicLong(0); 8 | private volatile Exception lastConsumeException = null; 9 | 10 | @Override 11 | public long getConsumeCount() { 12 | return consumeCount.get(); 13 | } 14 | 15 | @Override 16 | public long getConsumeErrorCount() { 17 | return consumeErrorCount.get(); 18 | } 19 | 20 | @Override 21 | public long incConsumeCount() { 22 | return consumeCount.incrementAndGet(); 23 | } 24 | 25 | @Override 26 | public long incConsumeErrorCount(Exception e) { 27 | this.lastConsumeException = e; 28 | return this.consumeErrorCount.incrementAndGet(); 29 | } 30 | 31 | @Override 32 | public Exception getLastConsumeException() { 33 | return this.lastConsumeException; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /netflix-eventbus-bridge/src/test/java/com/netflix/eventbus/AbstractEventBusBridgeTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import com.netflix.eventbus.impl.EventBusImpl; 11 | import com.netflix.eventbus.spi.EventBus; 12 | 13 | public class AbstractEventBusBridgeTest { 14 | private static final Logger LOG = LoggerFactory.getLogger(AbstractEventBusBridgeTest.class); 15 | 16 | @Test 17 | public void testString() throws Exception { 18 | EventBus eventBus = new EventBusImpl(); 19 | 20 | DummyEventBusBridge bridge = DummyEventBusBridge.builder() 21 | .withEventBus(eventBus) 22 | .withEventType(String.class) 23 | .withExpectedCount(1) 24 | .build(); 25 | 26 | eventBus.publish(new String("Foo")); 27 | Assert.assertTrue(bridge.await(3, TimeUnit.SECONDS)); 28 | Assert.assertEquals(1, bridge.getConsumeCount()); 29 | Assert.assertEquals(0, bridge.getConsumeErrorCount()); 30 | } 31 | 32 | @Test 33 | public void testConsumeErrorStats() throws Exception { 34 | EventBus eventBus = new EventBusImpl(); 35 | 36 | final RuntimeException e = new RuntimeException("Suro failed to send the message"); 37 | 38 | DummyEventBusBridge bridge = DummyEventBusBridge.builder() 39 | .withEventBus(eventBus) 40 | .withEventType(String.class) 41 | .build(); 42 | 43 | bridge.setError(e); 44 | eventBus.publish(new String("Foo")); 45 | 46 | TimeUnit.SECONDS.sleep(1); 47 | Assert.assertEquals(0, bridge.getConsumeCount()); 48 | Assert.assertEquals(1, bridge.getConsumeErrorCount()); 49 | Assert.assertEquals(e, bridge.getLastConsumeException()); 50 | } 51 | 52 | @Test 53 | public void testGetStatusAreImmutable() throws Exception { 54 | EventBus eventBus = new EventBusImpl(); 55 | DummyEventBusBridge bridge = DummyEventBusBridge.builder() 56 | .withEventBus(eventBus) 57 | .withEventType(String.class) 58 | .build(); 59 | 60 | try { 61 | bridge.getStats().incConsumeCount(); 62 | Assert.fail("Stats should be immutable"); 63 | } 64 | catch (UnsupportedOperationException e) { 65 | } 66 | } 67 | 68 | @Test 69 | public void testPauseAndResume() throws Exception { 70 | EventBus eventBus = new EventBusImpl(); 71 | DummyEventBusBridge bridge = DummyEventBusBridge.builder() 72 | .withExpectedCount(2) 73 | .withEventBus(eventBus) 74 | .withEventType(String.class) 75 | .build(); 76 | 77 | eventBus.publish(new String("Foo")); 78 | Assert.assertTrue(waitForConsumeCount(bridge, 1, 1, TimeUnit.SECONDS)); 79 | 80 | bridge.pause(); 81 | eventBus.publish(new String("Foo")); 82 | Assert.assertFalse(waitForConsumeCount(bridge, 2, 1, TimeUnit.SECONDS)); 83 | 84 | bridge.resume(); 85 | eventBus.publish(new String("Foo")); 86 | Assert.assertTrue(waitForConsumeCount(bridge, 2, 1, TimeUnit.SECONDS)); 87 | } 88 | 89 | public boolean waitForConsumeCount(DummyEventBusBridge bridge, long expected, long delay, TimeUnit units) throws Exception { 90 | long intervals = TimeUnit.MILLISECONDS.convert(delay, units)/100; 91 | assert intervals > 0; 92 | for (long i = 0; i < intervals; i++) { 93 | if (bridge.getConsumeCount() == expected) 94 | return true; 95 | TimeUnit.MILLISECONDS.sleep(100); 96 | } 97 | 98 | return false; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /netflix-eventbus-bridge/src/test/java/com/netflix/eventbus/DummyEventBusBridge.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus; 2 | 3 | import com.netflix.eventbus.bridge.AbstractEventBusBridge; 4 | 5 | import java.util.concurrent.CountDownLatch; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.atomic.*; 8 | 9 | public class DummyEventBusBridge extends AbstractEventBusBridge { 10 | 11 | public static class Builder extends AbstractEventBusBridge.Builder { 12 | private CountDownLatch latch; 13 | 14 | public Builder withExpectedCount(int count) { 15 | latch = new CountDownLatch(count); 16 | return this; 17 | } 18 | 19 | @Override 20 | protected Builder self() { 21 | return this; 22 | } 23 | 24 | public DummyEventBusBridge build() throws Exception { 25 | validate(); 26 | return new DummyEventBusBridge(this); 27 | } 28 | } 29 | 30 | public static Builder builder() { 31 | return new Builder(); 32 | } 33 | 34 | private final CountDownLatch latch; 35 | private final AtomicLong counter = new AtomicLong(); 36 | private Exception forcedError; 37 | 38 | protected DummyEventBusBridge(final Builder init) 39 | throws Exception { 40 | super(init); 41 | this.latch = init.latch; 42 | init(); 43 | } 44 | 45 | public boolean await(long timeout, TimeUnit units) throws Exception { 46 | return latch.await(timeout, units); 47 | } 48 | 49 | @Override 50 | protected void sendEvent(Object event) throws Exception { 51 | if (forcedError != null) 52 | throw forcedError; 53 | counter.incrementAndGet(); 54 | latch.countDown(); 55 | } 56 | 57 | public void setError(Exception forcedError) { 58 | this.forcedError = forcedError; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /netflix-eventbus-rx/src/test/java/com/netflix/eventbus/rx/RxEventBusTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.rx; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | import rx.Subscription; 10 | import rx.functions.Action0; 11 | import rx.functions.Action1; 12 | 13 | import com.netflix.eventbus.impl.EventBusImpl; 14 | import com.netflix.eventbus.spi.EventBus; 15 | 16 | public class RxEventBusTest { 17 | @Test 18 | public void testStream() throws InterruptedException { 19 | EventBus eventBus = new EventBusImpl(); 20 | RxEventBus rxEventBus = new RxEventBus(eventBus); 21 | 22 | final CountDownLatch completion = new CountDownLatch(1); 23 | final CountDownLatch counter = new CountDownLatch(10); 24 | 25 | Subscription sub = rxEventBus.asObservable(Long.class) 26 | .doOnCompleted(new Action0() { 27 | @Override 28 | public void call() { 29 | System.out.println("Done"); 30 | completion.countDown(); 31 | } 32 | }) 33 | .subscribe(new Action1() { 34 | @Override 35 | public void call(Long t1) { 36 | System.out.println(t1); 37 | counter.countDown(); 38 | } 39 | }); 40 | 41 | for (long i = 0; i < 10; i++) { 42 | eventBus.publish(i); 43 | } 44 | 45 | Assert.assertTrue(counter.await(1, TimeUnit.SECONDS)); 46 | 47 | sub.unsubscribe(); 48 | 49 | Assert.assertTrue(completion.await(1, TimeUnit.SECONDS)); 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/filter/AlwaysFalseEventFilter.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.filter; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | import com.netflix.eventbus.spi.EventFilter; 6 | 7 | public class AlwaysFalseEventFilter implements EventFilter { 8 | public static final AlwaysFalseEventFilter INSTANCE = new AlwaysFalseEventFilter(); 9 | 10 | // There's no point of creating multiple instance of this class 11 | private AlwaysFalseEventFilter() { 12 | } 13 | 14 | 15 | @Override 16 | public boolean apply(@Nullable Object input) { 17 | return false; 18 | } 19 | 20 | @Override 21 | public String getLanguage() { 22 | return "Constant"; 23 | } 24 | 25 | @Override 26 | public String serialize() { 27 | return "false"; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | StringBuilder builder = new StringBuilder(); 33 | builder.append("AlwaysFalseEventFilter []"); 34 | return builder.toString(); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | return Boolean.FALSE.hashCode(); 40 | } 41 | 42 | @Override 43 | public boolean equals(Object obj){ 44 | return obj instanceof AlwaysFalseEventFilter; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/filter/AlwaysTrueEventFilter.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.filter; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | import com.netflix.eventbus.spi.EventFilter; 6 | 7 | public class AlwaysTrueEventFilter implements EventFilter { 8 | public static final AlwaysTrueEventFilter INSTANCE = new AlwaysTrueEventFilter(); 9 | 10 | // There's no point of creating multiple instance of this class 11 | private AlwaysTrueEventFilter() { 12 | } 13 | 14 | @Override 15 | public boolean apply(@Nullable Object input) { 16 | return true; 17 | } 18 | 19 | @Override 20 | public String getLanguage() { 21 | return "Constant"; 22 | } 23 | 24 | @Override 25 | public String serialize() { 26 | return "true"; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "AlwaysTrueEventFilter []"; 32 | } 33 | 34 | @Override 35 | public int hashCode() { 36 | return Boolean.TRUE.hashCode(); 37 | } 38 | 39 | @Override 40 | public boolean equals(Object obj){ 41 | return obj instanceof AlwaysTrueEventFilter; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/filter/EventFilterCompiler.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.filter; 2 | 3 | import com.netflix.eventbus.filter.lang.FilterLanguageSupport; 4 | import com.netflix.eventbus.filter.lang.InvalidFilterException; 5 | import com.netflix.eventbus.filter.lang.infix.InfixFilterLanguageSupport; 6 | import com.netflix.eventbus.spi.EventFilter; 7 | 8 | /** 9 | * A compiler to compile the event filter from a language specified in {@link com.netflix.eventbus.filter.lang} to an 10 | * {@link com.netflix.eventbus.spi.EventFilter} for consumption by {@link com.netflix.eventbus.spi.EventBus} 11 | * 12 | * @author Nitesh Kant (nkant@netflix.com) 13 | */ 14 | public class EventFilterCompiler { 15 | 16 | private static FilterLanguageSupport infixSupport = new InfixFilterLanguageSupport(); 17 | 18 | /** 19 | * Compiles a filter expressed in infix notation to an {@link EventFilter} instance. 20 | * 21 | * @param filter Filter to compile. 22 | * 23 | * @return {@link EventFilter} instance compiled from the passed filter. 24 | * 25 | * @throws InvalidFilterException If the input filter is invalid. 26 | */ 27 | public static EventFilter compileInfixNotation(String filter) throws InvalidFilterException { 28 | return infixSupport.convert(filter); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/filter/lang/FilterLanguageSupport.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.filter.lang; 2 | 3 | import com.netflix.eventbus.spi.EventFilter; 4 | 5 | /** 6 | * General contract for any filter language which relates to a methodology of converting a language expression to a 7 | * valid {@link com.netflix.eventbus.spi.EventFilter} instance consumable by {@link com.netflix.eventbus.spi.EventBus} 8 | * 9 | * @author Nitesh Kant (nkant@netflix.com) 10 | */ 11 | public interface FilterLanguageSupport { 12 | 13 | /** 14 | * Converts the passed filter object to a valid {@link EventFilter}. 15 | * 16 | * @param filter Filter object to convert. 17 | * 18 | * @return {@link EventFilter} corresponding to the passed filter. 19 | * 20 | * @throws InvalidFilterException If the passed filter was invalid. 21 | */ 22 | public EventFilter convert(T filter) throws InvalidFilterException; 23 | } 24 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/filter/lang/InvalidFilterException.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.filter.lang; 2 | 3 | /** 4 | * A generic exception representing an invalid filter expression. 5 | * 6 | * @author Nitesh Kant (nkant@netflix.com) 7 | */ 8 | public class InvalidFilterException extends Exception { 9 | 10 | private static final long serialVersionUID = -5878696854757828678L; 11 | 12 | private Object filter; 13 | 14 | public InvalidFilterException(String message, Throwable cause, Object filter) { 15 | super(String.format("Invalid filter %s. Error: %s", filter, message), cause); 16 | this.filter = filter; 17 | } 18 | 19 | public InvalidFilterException(Throwable cause, Object filter) { 20 | super(String.format("Invalid filter %s.", filter), cause); 21 | this.filter = filter; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/filter/lang/infix/InfixEventFilter.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.filter.lang.infix; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | import com.google.common.base.Predicate; 8 | import com.netflix.eventbus.spi.EventFilter; 9 | 10 | public class InfixEventFilter implements EventFilter { 11 | public static final String INFIX_LANGUAGE_NAME = "Infix"; 12 | 13 | private final Predicate predicate; 14 | private final String original; 15 | 16 | public InfixEventFilter(Predicate predicate, String original) { 17 | this.predicate = predicate; 18 | this.original = original; 19 | } 20 | 21 | public InfixEventFilter(Predicate predicate) { 22 | this.predicate = predicate; 23 | this.original = null; 24 | } 25 | 26 | @Override 27 | public boolean apply(@Nullable Object input) { 28 | return predicate.apply(input); 29 | } 30 | 31 | @Override 32 | public String getLanguage() { 33 | return INFIX_LANGUAGE_NAME; 34 | } 35 | 36 | @Override 37 | public String serialize() { 38 | return original; 39 | } 40 | 41 | @Override 42 | public int hashCode() { 43 | return original != null ? original.hashCode() : 0; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | final StringBuilder sb = new StringBuilder(); 49 | sb.append("InfixEventFilter"); 50 | sb.append("{input=").append(StringUtils.abbreviate(original, 256)); 51 | sb.append('}'); 52 | return sb.toString(); 53 | } 54 | 55 | @Override 56 | public boolean equals(Object o) { 57 | if (this == o) { 58 | return true; 59 | } 60 | if (o == null || getClass() != o.getClass()) { 61 | return false; 62 | } 63 | 64 | InfixEventFilter that = (InfixEventFilter) o; 65 | 66 | if (predicate != null ? !predicate.equals(that.predicate) : that.predicate != null) { 67 | return false; 68 | } 69 | 70 | return true; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/filter/lang/infix/InfixFilterLanguageSupport.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.filter.lang.infix; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import com.google.common.base.Predicate; 6 | import com.netflix.eventbus.filter.lang.FilterLanguageSupport; 7 | import com.netflix.eventbus.filter.lang.InvalidFilterException; 8 | import com.netflix.eventbus.spi.EventFilter; 9 | import com.netflix.infix.InfixCompiler; 10 | 11 | /** 12 | * @author Nitesh Kant (nkant@netflix.com) 13 | */ 14 | public class InfixFilterLanguageSupport implements FilterLanguageSupport { 15 | 16 | @Override 17 | public EventFilter convert(String filter) throws InvalidFilterException { 18 | try { 19 | final Predicate predicate = new InfixCompiler().compile(filter); 20 | return new InfixEventFilter(predicate, filter); 21 | } catch (Exception e) { 22 | throw new InvalidFilterException("Error compiling filter : " + StringUtils.abbreviate(filter, 100), e, null); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/filter/lang/infix/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * An infix notation based language for event filters. At a higher level, one creates an expression made out of multiple 3 | * "predicate"s chained together using the AND and OR operators. 4 | *

Predicate

A predicate by definition is an expression that results in a boolean value. There are multiple 5 | * predicates available in the language which are described below. On top of these predicates one can also use 6 | * comparison functions, described below, which results in a boolean value. 7 | *

Between Predicate:

A between predicate checks whether a value is between a lower and upper bound. 8 | *

9 | * Example: "xpath("//a/b/c") between (100,150)". Here xpath() is a function which is described below. 10 | * This is only supported for numeric inputs. 11 | *

In Predicate:

An in predicate checks whether a value is in a list of values provided to the function. 12 | * This is supported for alphanumeric inputs. 13 | *

14 | * Example: "xpath("//a/b/c") in ("x", "y", "z")". Here xpath() is a function which is described below. 15 | *

Null Predicate:

A null predicate checks whether the passed value is null. 16 | *

17 | * Example: "xpath("//a/b/c") is null". Here xpath() is a function which is described below. 18 | *

Regex Predicate:

A null predicate checks whether the passed value matches a provided regex. 19 | *

20 | * Example: "xpath("//a/b/c") =~ "<a regex>"". Here xpath() is a function which is described below. 21 | *

Exists Predicate:

A null predicate checks whether the passed value matches a provided regex. 22 | *

23 | * Example: "xpath("//a/b/c") =~ "<a regex>"". Here xpath() is a function which is described below. 24 | * 25 | *

Comparison Functions

This language currently supports the following comparison functions: 26 | *
    27 | *
  • Equals: Example: "xpath("//a/b/c") = "xyz". Here xpath() is a function which is described below.
  • 28 | *
  • Not Equals: Example: "xpath("//a/b/c") != "xyz". Here xpath() is a function which is described below.
  • 29 | *
  • Greater than: Example: "xpath("//a/b/c") > "xyz". Here xpath() is a function which is described below.
  • 30 | *
  • Greater than or equals: Example: "xpath("//a/b/c") >= "xyz". Here xpath() is a function which is described below.
  • 31 | *
  • Lesser than: Example: "xpath("//a/b/c") < "xyz". Here xpath() is a function which is described below.
  • 32 | *
  • Lesser than or equals: Example: "xpath("//a/b/c") <= "xyz". Here xpath() is a function which is described below.
  • 33 | *
34 | * All the above comparison functions are available only for numeric inputs. 35 | * 36 | *

XPath function

This primarily is the only function that fetches runtime values in the filters. The XPath 37 | * will be run on the event object for which the filter is applied. For details about the evaluation and support 38 | * see this 39 | * 40 | *

Other value functions

We also have certain functions that helps convert values in specific formats. These 41 | * functions are listed below: 42 | *

43 | Time millis: Converts milliseconds since epoch to a date-time string. The format of date-time pattern is as 44 | specified here 45 | 46 | Example: "xpath("//a/b/c") = time-millis("yyyy-MM-dd'T'HH:mm:ss:SSS", "2012-08-22T10:14:44:856") . Here xpath() is a function which is described above. 47 | Time string: Converts a stringified time from one format to another. The format of date-time pattern is as 48 | specified here 49 | 50 | Example: "xpath("//a/b/c") = time-string("yyyy-MM-dd'T'hh:mm:ss:SSS","yyyy-MM-dd'T'HH:mm:ss:SSS", "2012-08-22T10:14:44:856") . 51 | 52 | Here xpath() is a function which is described above. 53 | 54 | First format argument is the target format. 55 | 56 | Seconds format argument is the input source format. 57 | */ 58 | package com.netflix.eventbus.filter.lang.infix; 59 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/filter/lang/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * This package defines the various filter language available to create the filters for 3 | * {@link com.netflix.eventbus.spi.EventBus}. Any filter language will eventually create an instance of 4 | * {@link com.netflix.eventbus.spi.EventFilter}, the structure of which is opaque to the event bus. 5 | */ 6 | package com.netflix.eventbus.filter.lang; 7 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/impl/DefaultConsumerQueueSupplier.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.impl; 2 | 3 | import com.netflix.eventbus.spi.SubscriberConfigProvider; 4 | import com.netflix.eventbus.utils.EventBusUtils; 5 | 6 | import java.lang.reflect.Method; 7 | import java.util.concurrent.LinkedBlockingQueue; 8 | import java.util.concurrent.atomic.AtomicLong; 9 | 10 | /** 11 | * The default implementation for {@link com.netflix.eventbus.impl.EventBusImpl.ConsumerQueueSupplier}. Our 12 | * 13 | * @author Nitesh Kant (nkant@netflix.com) 14 | */ 15 | class DefaultConsumerQueueSupplier implements EventBusImpl.ConsumerQueueSupplier { 16 | 17 | @Override 18 | public ConsumerQueue get(Method subscriber, final SubscriberConfigProvider.SubscriberConfig subscriberConfig, final AtomicLong queueSizeCounter) { 19 | switch (subscriberConfig.getBatchingStrategy()) { 20 | case Age: 21 | return new AgeBatchingQueue(subscriber, subscriberConfig, queueSizeCounter); 22 | case SizeOrAge: 23 | return new SizeAndAgeBatchingQueue(subscriber, subscriberConfig, queueSizeCounter); 24 | } 25 | return new ConsumerQueue() { 26 | 27 | private LinkedBlockingQueue delegate = new LinkedBlockingQueue(EventBusUtils.getQueueSize(subscriberConfig)); 28 | 29 | @Override 30 | @SuppressWarnings("unchecked") 31 | public boolean offer(Object event) { 32 | boolean offered = delegate.offer(event); 33 | if(offered) { 34 | queueSizeCounter.incrementAndGet(); 35 | } 36 | return offered; 37 | } 38 | 39 | @Override 40 | public Object nonBlockingTake() { 41 | Object retrievedItem = delegate.poll(); 42 | if (null != retrievedItem) { 43 | queueSizeCounter.decrementAndGet(); 44 | } 45 | return retrievedItem; 46 | } 47 | 48 | @Override 49 | public Object blockingTake() throws InterruptedException { 50 | Object retrieved = delegate.take(); 51 | queueSizeCounter.decrementAndGet(); 52 | return retrieved; 53 | } 54 | 55 | @Override 56 | public void clear() { 57 | delegate.clear(); 58 | queueSizeCounter.set(0); 59 | } 60 | }; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/impl/EventBatch.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.impl; 2 | 3 | /** 4 | * An interface for all event batches generated by eventbus for various batching strategies. 5 | * 6 | * @author Nitesh Kant 7 | */ 8 | public interface EventBatch extends Iterable { 9 | } 10 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/impl/EventBusStats.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.impl; 2 | 3 | import com.netflix.servo.monitor.BasicCounter; 4 | import com.netflix.servo.monitor.Counter; 5 | import com.netflix.servo.monitor.MonitorConfig; 6 | import com.netflix.servo.monitor.Monitors; 7 | import com.netflix.servo.monitor.StatsTimer; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import static com.netflix.eventbus.utils.EventBusUtils.newStatsTimer; 12 | 13 | /** 14 | * Runtime statistics for event bus, this relates to publish data and is always captured for the time period as returned 15 | * by {@link EventBusStats#}. 16 | * 17 | * @author Nitesh Kant (nkant@netflix.com) 18 | */ 19 | class EventBusStats { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(EventBusStats.class); 22 | 23 | final StatsTimer publishStats; 24 | final StatsTimer conditionalPublishStats; 25 | final StatsTimer filterStats; 26 | final Counter publishErrors; 27 | final Counter conditionalPublishErrors; 28 | 29 | public EventBusStats(long collectionDurationInMillis) { 30 | publishStats = newStatsTimer("eventbus_publish", collectionDurationInMillis); 31 | conditionalPublishStats = newStatsTimer("eventbus_conditional_publish", collectionDurationInMillis); 32 | filterStats = newStatsTimer("eventbus_publish_filter_stats", collectionDurationInMillis); 33 | publishErrors = new BasicCounter(MonitorConfig.builder("eventbus_publish_errors").build()); 34 | conditionalPublishErrors = new BasicCounter(MonitorConfig.builder("eventbus_conditional_publish_errors").build()); 35 | try { 36 | Monitors.registerObject(this); 37 | } catch (Throwable th) { 38 | LOGGER.error("Unable to register to event bus stats to servo.", th); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/impl/EventConsumerStats.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.impl; 2 | 3 | import com.netflix.servo.DefaultMonitorRegistry; 4 | import com.netflix.servo.monitor.BasicCounter; 5 | import com.netflix.servo.monitor.BasicGauge; 6 | import com.netflix.servo.monitor.Counter; 7 | import com.netflix.servo.monitor.MonitorConfig; 8 | import com.netflix.servo.monitor.StatsTimer; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.util.concurrent.Callable; 13 | import java.util.concurrent.atomic.AtomicLong; 14 | 15 | import static com.netflix.eventbus.utils.EventBusUtils.newStatsTimer; 16 | 17 | /** 18 | * @author Nitesh Kant (nkant@netflix.com) 19 | */ 20 | public class EventConsumerStats { 21 | 22 | private static final Logger LOGGER = LoggerFactory.getLogger(EventConsumerStats.class); 23 | 24 | final StatsTimer enqueueStats; 25 | final StatsTimer consumptionStats; 26 | final StatsTimer filterStats; 27 | final AtomicLong QUEUE_SIZE_COUNTER; 28 | final BasicGauge QUEUE_SIZE_GAUGE; 29 | final Counter QUEUE_OFFER_RETRY_COUNTER; 30 | final Counter EVENT_ENQUEUE_REJECTED_COUNTER; 31 | 32 | public EventConsumerStats(String consumerName, long collectionDurationInMillis) { 33 | String statsPrefix = "eventbus_consumer_" + consumerName; 34 | QUEUE_SIZE_COUNTER = new AtomicLong(); 35 | QUEUE_SIZE_GAUGE = new BasicGauge(MonitorConfig.builder(statsPrefix + "_queue_size").build(), new Callable() { 36 | @Override 37 | public Long call() throws Exception { 38 | return QUEUE_SIZE_COUNTER.get(); 39 | } 40 | }); 41 | QUEUE_OFFER_RETRY_COUNTER = new BasicCounter(MonitorConfig.builder(statsPrefix + "_queue_retry").build()); 42 | EVENT_ENQUEUE_REJECTED_COUNTER = new BasicCounter(MonitorConfig.builder(statsPrefix + "_enqueue_reject").build()); 43 | 44 | enqueueStats = newStatsTimer(statsPrefix + "_enqueue", collectionDurationInMillis); 45 | consumptionStats = newStatsTimer(statsPrefix + "_consumption", collectionDurationInMillis); 46 | filterStats = newStatsTimer(statsPrefix + "_filter", collectionDurationInMillis); 47 | try { 48 | DefaultMonitorRegistry.getInstance().register(QUEUE_SIZE_GAUGE); 49 | DefaultMonitorRegistry.getInstance().register(QUEUE_OFFER_RETRY_COUNTER); 50 | DefaultMonitorRegistry.getInstance().register(EVENT_ENQUEUE_REJECTED_COUNTER); 51 | DefaultMonitorRegistry.getInstance().register(enqueueStats); 52 | DefaultMonitorRegistry.getInstance().register(consumptionStats); 53 | DefaultMonitorRegistry.getInstance().register(filterStats); 54 | } catch (Throwable th) { 55 | LOGGER.error("Unable to register to event bus consumer stats to servo.", th); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/impl/SizeAndAgeBatchingQueue.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.impl; 2 | 3 | import com.google.common.annotations.VisibleForTesting; 4 | import com.netflix.eventbus.spi.SubscriberConfigProvider; 5 | 6 | import javax.annotation.Nullable; 7 | import java.lang.reflect.Method; 8 | import java.util.concurrent.atomic.AtomicInteger; 9 | import java.util.concurrent.atomic.AtomicLong; 10 | 11 | /** 12 | * Extends the {@link AgeBatchingQueue} to add one more reaping point based on the current batch size. 13 | * 14 | * @author Nitesh Kant (nkant@netflix.com) 15 | */ 16 | class SizeAndAgeBatchingQueue extends AgeBatchingQueue { 17 | 18 | private final int batchSize; 19 | 20 | SizeAndAgeBatchingQueue(Method subscriber, SubscriberConfigProvider.SubscriberConfig subscribe, AtomicLong queueSizeCounter) { 21 | this(subscriber, subscribe, true, queueSizeCounter); 22 | } 23 | 24 | @VisibleForTesting 25 | SizeAndAgeBatchingQueue(Method subscriber, SubscriberConfigProvider.SubscriberConfig subscribe, boolean scheduleReaper, 26 | AtomicLong queueSizeCounter) { 27 | super(subscriber, subscribe, scheduleReaper, queueSizeCounter); 28 | batchSize = subscribe.getBatchSize(); 29 | } 30 | 31 | @Override 32 | protected AgeBatch createNewBatch(@Nullable SubscriberConfigProvider.SubscriberConfig subscribe) { 33 | return new AgeAndSizeBatch((null != subscribe) ? subscribe.getBatchSize() : batchSize); 34 | } 35 | 36 | private class AgeAndSizeBatch extends AgeBatch { 37 | 38 | private final int batchSize; 39 | private AtomicInteger currentBatchSize; 40 | 41 | protected AgeAndSizeBatch(int batchSize) { 42 | super(); 43 | this.batchSize = batchSize; 44 | currentBatchSize = new AtomicInteger(); 45 | } 46 | 47 | @Override 48 | protected boolean addEvent(Object event) { 49 | if(currentBatchSize.get() >= batchSize) { 50 | if(!reapCurrentBatch("Batch size exceeded")) { 51 | return false; 52 | } 53 | } 54 | if(super.addEvent(event)) { 55 | currentBatchSize.incrementAndGet(); 56 | return true; 57 | } 58 | return false; 59 | } 60 | 61 | @Override 62 | protected void clear() { 63 | super.clear(); 64 | currentBatchSize.set(0); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/spi/CatchAllSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.spi; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.concurrent.BlockingQueue; 7 | import java.util.concurrent.atomic.AtomicReference; 8 | 9 | /** 10 | * A special subscriber that receives all events published to {@link EventBus}. This is always registered with eventbus 11 | * but is disabled. In order to enable this subscriber call 12 | * {@link com.netflix.eventbus.spi.EventBus#enableCatchAllSubscriber(java.util.concurrent.BlockingQueue)} 13 | * with the sink (receiver) for all events. 14 | * 15 | * This subscriber will only call {@link java.util.concurrent.BlockingQueue#offer(Object)} on this sink to avoid any sort 16 | * of blocking. Any events for which the offer call fails are rejected. 17 | * 18 | * As any other {@link EventBus} subscriber, this subscriber is async i.e. any events received by this subscriber are 19 | * pushed by the eventbus in async mode. The queue size for this is {@link CatchAllSubscriber#SUBSCRIBER_QUEUE_SIZE} and 20 | * there is no batching done for events to reduce memory overheads. 21 | * 22 | * @author Nitesh Kant (nkant@netflix.com) 23 | */ 24 | public final class CatchAllSubscriber { 25 | 26 | private static final Logger LOGGER = LoggerFactory.getLogger(CatchAllSubscriber.class); 27 | 28 | public static final int SUBSCRIBER_QUEUE_SIZE = 100; 29 | 30 | private AtomicReference sink = new AtomicReference(null); 31 | 32 | @Subscribe(queueSize = SUBSCRIBER_QUEUE_SIZE) 33 | @SuppressWarnings({"unchecked", "unused"}) 34 | public void receive(Object event) { 35 | BlockingQueue sinkNow = this.sink.get(); 36 | if (null != sinkNow && !sinkNow.offer(event)) { 37 | LOGGER.info("CatchAllSubscriber sink full, rejected an event."); 38 | } 39 | } 40 | 41 | public boolean enable(BlockingQueue sink) { 42 | return this.sink.compareAndSet(null, sink); 43 | } 44 | 45 | public void disable() { 46 | this.sink.set(null); 47 | } 48 | 49 | public boolean isEnabled() { 50 | return this.sink.get() != null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/spi/DynamicSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.spi; 2 | 3 | /** 4 | * Eventbus subscribers, by design, are statically tied to a particular type of object i.e. the class of the event it 5 | * is interested in. This in most cases is beneficial and easy to use, however, in some cases (typically event agnostic, 6 | * middlemen, which stores the event for later investigation), the event processing is much the same irrespective of the 7 | * type of event it receives. In such a case, it will be an overhead (even if possible) to define a subscriber for 8 | * each kind of event existing in the system. 9 | * 10 | * This special subscriber is a way to achieve dynamic interests (immutable after registration) in subscribers. Although, 11 | * this subscriber can only listen to one type of event but the type is defined at runtime as opposed to compile time 12 | * in a normal subscriber. 13 | * 14 | * Although, this subscriber does not mandate that the subscriber must have a subscribe method (one annotated with 15 | * {@link Subscribe}) listening to event type {@link Object} but in most of the cases, such a subscribe method will 16 | * prove to be more useful. {@link EventBus} allows specifying the subscribe method with argument {@link Object} 17 | * only for these subscribers. 18 | * 19 | * Implementations must only have a single subscriber method or else the registration with eventbus will be rejected. 20 | * This limitation is imposed as we do not want to make runtime decisions on which subscribe method an event must be 21 | * directed to. 22 | * 23 | * @author Nitesh Kant 24 | */ 25 | public interface DynamicSubscriber { 26 | 27 | /** 28 | * Returns the event type i.e. the class of the event that this subscriber will be interested in. 29 | * 30 | * {@link EventBus} allows specifying the subscribe method with argument {@link Object} only for 31 | * these subscribers. 32 | * 33 | * The implementers of this interface must make sure that the subscribe method accepts the event class returned by 34 | * this method. 35 | * 36 | * @return The event type i.e. the class of the event that this subscriber will be interested in. 37 | */ 38 | Class getEventType(); 39 | } 40 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/spi/EventCreator.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.spi; 2 | 3 | import java.util.List; 4 | import java.util.Set; 5 | 6 | /** 7 | * A contract for delayed creation of event(s) objects for conditional event publishing. 8 | *

9 | * The responsibility of storing enough context to create the event(s) later lies on the implementations. As one would 10 | * imagine, this creator will mostly be stateful and hence be instantiated afresh for every call. 11 | *

12 | * The event(s) will be created iff there exists a listener that will process these event(s). However, due to the concurrent 13 | * nature of the event bus, this may not always be the case, as the listeners may come and go between the existence 14 | * check and actual invocation. 15 | * 16 | *

Multiple events

A single event creator can create multiple events at the same time. This is required to 17 | * batch multiple event types together in one conditional publish to aid optimization of multiple publish calls. The 18 | * event bus takes care of managing the mapping between the event type and listeners that showed interest when 19 | * {@link EventBus#publishIffNotDead(EventCreator, Class[])} was called for this creator. 20 | * 21 | * @author Nitesh Kant (nkant@netflix.com) 22 | */ 23 | public interface EventCreator { 24 | 25 | /** 26 | * A callback when the event bus determines that there is atleast one listener for any of the passed event types to 27 | * the corresponding {@link EventBus#publishIffNotDead(EventCreator, Class[])} call. 28 | *

29 | * The event bus makes sure to establish a mapping between the event types and the live listeners before 30 | * calling this method and publishes the created events only to the associated live listeners (determined before 31 | * this callback) 32 | * 33 | * @param liveEventTypes A subset of event types passed to the corresponding {@link EventBus#publishIffNotDead(EventCreator, Class[])} 34 | * call which have atleast one listener to receive the event. Events created only for these 35 | * event types will be published by the event bus. 36 | * 37 | * @return Newly created events corresponding to the live event types. null if no events are created. 38 | */ 39 | List createEvent(Set> liveEventTypes); 40 | } 41 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/spi/EventFilter.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.spi; 2 | 3 | import com.google.common.base.Predicate; 4 | 5 | /** 6 | * Top level filter used by the event bus. A filter may be created in code 7 | * or defined using a scripting language. 8 | * 9 | * @author elandau 10 | * 11 | */ 12 | public interface EventFilter extends Predicate { 13 | /** 14 | * @return String describing the underlying filter language 15 | */ 16 | public String getLanguage(); 17 | 18 | /** 19 | * @return String representing the complete filter definition. May be null if 20 | * the filter is written in code. 21 | */ 22 | public String serialize(); 23 | } 24 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/spi/FilterLanguage.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.spi; 2 | 3 | /** 4 | * Enumeration of various out-of-the-box filter languages supported by the event bus. 5 | * 6 | * @author Nitesh Kant (nkant@netflix.com) 7 | */ 8 | public enum FilterLanguage { 9 | 10 | Infix, 11 | UserDefined, 12 | Constant 13 | } 14 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/spi/InvalidSubscriberException.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.spi; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.Arrays; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | /** 9 | * Thrown if the subscriber registered with the {@link EventBus} is invalid. A subscriber will be 10 | * invalid if 11 | *
    12 |
  • The method annotated with {@link Subscribe} does not contain one and only one argument.
  • 13 |
  • The method uses a batching strategy other than {@link com.netflix.eventbus.spi.Subscribe.BatchingStrategy#None} and 14 | does not have the argument as {@link Iterable}
  • 15 |
  • If the subscriber uses batching and does not provide a batch size > 1
  • 16 |
  • If the subscriber uses batching and does not provide a batch age.
  • 17 |
18 | * 19 | * @author Nitesh Kant (nkant@netflix.com) 20 | */ 21 | public class InvalidSubscriberException extends Exception { 22 | 23 | private static final long serialVersionUID = 4258884942423525335L; 24 | 25 | private Class subscriberClass; 26 | private Set offendingMethods; 27 | 28 | public InvalidSubscriberException(Class subscriberClass, Map errors) { 29 | super(getErrorMessage(subscriberClass, errors)); 30 | this.subscriberClass = subscriberClass; 31 | this.offendingMethods = errors.keySet(); 32 | } 33 | 34 | public Set getOffendingMethods() { 35 | return offendingMethods; 36 | } 37 | 38 | public Class getSubscriberClass() { 39 | return subscriberClass; 40 | } 41 | 42 | private static String getErrorMessage(Class subscriberClass, Map errors) { 43 | StringBuilder errMsgBuilder = new StringBuilder(); 44 | errMsgBuilder.append("Invalid subscriber class: "); 45 | errMsgBuilder.append(subscriberClass); 46 | errMsgBuilder.append(". Errors: \n"); 47 | for (Map.Entry anEntry : errors.entrySet()) { 48 | errMsgBuilder.append("Method: "); 49 | errMsgBuilder.append(anEntry.getKey().toGenericString()); 50 | errMsgBuilder.append(" is invalid. Error: "); 51 | errMsgBuilder.append(anEntry.getValue()); 52 | errMsgBuilder.append("\n"); 53 | } 54 | return errMsgBuilder.toString(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/spi/SerializableEventFilter.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.spi; 2 | 3 | 4 | /** 5 | * An optional feature for a {@link EventFilter} which indicates that the event filter can be converted back and forth 6 | * from a string. 7 | * 8 | * Typically, if the filter is an expression of a language, as defined by {@link com.netflix.eventbus.filter.lang}, then 9 | * it should implement this interface. On the other hand, if the filter is represented as code, implementation of this 10 | * interface is not required. 11 | * 12 | * Any persistence of an {@link EventFilter} will require the filter to implement this interface, else it will not be 13 | * persisted. Since, the filters created in code will typically be registered by the subscribers themselves, they 14 | * typically will not need any persistence. 15 | * 16 | * One will notice that this filter only supports converting the object representation to a string and not back as the 17 | * conversion from arbitrary string is handled by {@link com.netflix.eventbus.filter.EventFilterCompiler}. Such, factory 18 | * kind of methods are deliberately not provided here so that the filter classes do not have to provide any parsing 19 | * logic. 20 | * 21 | * @author Nitesh Kant (nkant@netflix.com) 22 | */ 23 | public interface SerializableEventFilter extends EventFilter { 24 | 25 | /** 26 | * The language in which this filter is expressed. 27 | * 28 | * @return the language in which this filter is expressed. 29 | */ 30 | FilterLanguage getFilterLanguage(); 31 | 32 | /** 33 | * Serializes this filter into the filter string in the language specified by 34 | * {@link com.netflix.eventbus.spi.SerializableEventFilter#getFilterLanguage()}. 35 | * 36 | * @return String representation of the filter. 37 | */ 38 | String serialize(); 39 | } 40 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/spi/Subscribe.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.spi; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Consumers for {@link EventBus} must annotate their consumer methods with this annotation. 10 | * 11 | * A consumer is asynchronous, however it can choose to batch events consumed at a time using a batching strategy. 12 | * 13 | * A consumer can indicate if it favors synchronous event consumption, provided, it is allowed in the current 14 | * environment, by setting the property {@link SyncSubscribersGatekeeper#ALLOW_SYNC_SUBSCRIBERS} to true. 15 | * 16 | * A consumer is always assumed to be thread-safe. 17 | * 18 | * Any method annotated with this must have one and only one argument which is the event object that it is supposed to 19 | * handle. 20 | * 21 | * @author Nitesh Kant (nkant@netflix.com) 22 | */ 23 | @Retention(RetentionPolicy.RUNTIME) 24 | @Target(ElementType.METHOD) 25 | public @interface Subscribe { 26 | 27 | public static final String NO_NAME = "none"; 28 | 29 | /** 30 | * Batching strategy for event batches. 31 | */ 32 | enum BatchingStrategy { 33 | /** 34 | * The events will be batched based on the time elapsed between the addition of the first entry and now. 35 | * The time is always in milliseconds and is defined by the property {@link com.netflix.eventbus.spi.Subscribe#batchAge} 36 | **/ 37 | Age, 38 | 39 | /** 40 | * The events will be dispatched if the number of events in the batch exceeds the threshold as defined by 41 | * {@link com.netflix.eventbus.spi.Subscribe#batchSize} or the age as defined by 42 | * {@link Subscribe#batchAge}. 43 | */ 44 | SizeOrAge, 45 | 46 | /** 47 | * No batching, the events will be dispatched one at a time. 48 | */ 49 | None 50 | } 51 | 52 | /** 53 | * A name for this subscriber, this is only required if you want to have a dynamic configuration via 54 | * {@link SubscriberConfigProvider} AND each subscriber method in the class has a different configuration. 55 | * 56 | * @return A name, unique in a single subscriber class. This name does not need to be unique amongst all 57 | * subscriber classes. 58 | */ 59 | String name() default NO_NAME; 60 | 61 | /** 62 | * Returns the batching strategy for this subscriber. If a subscriber chooses a batching strategy other than 63 | * {@link BatchingStrategy#None}, the argument to the subscriber method must be an {@link Iterable}. 64 | * Event batches does not support removal. 65 | * 66 | * @return The batching strategy for this subscriber. 67 | */ 68 | BatchingStrategy batchingStrategy() default BatchingStrategy.None; 69 | 70 | /** 71 | * The threshold for the age of the batch in milliseconds since the first entry was added. Only considered if the 72 | * batching strategy is {@link BatchingStrategy#Age} or {@link BatchingStrategy#SizeOrAge} 73 | * 74 | * @return The age of the batch in milliseconds. 75 | */ 76 | int batchAge() default 0; 77 | 78 | /** 79 | * The threshold for the size of the batch. Only considered if the batching strategy is {@link BatchingStrategy#SizeOrAge} 80 | * 81 | * @return The threshold for the size of the batch. 82 | */ 83 | int batchSize() default 1; 84 | 85 | /** 86 | * The queue size for the consumer. In case, the consumer receives batches of events, this will be the number of 87 | * batches and not individual events. 88 | * 89 | * @return The queue size for the consumer. 90 | */ 91 | int queueSize() default -1; 92 | 93 | /** 94 | * A backdoor in the eventbus to allow synchronous events consumption. This mode is controlled by the property 95 | * {@link SyncSubscribersGatekeeper#ALLOW_SYNC_SUBSCRIBERS}, which when set to true, allows registration of synchronous consumers. 96 | * 97 | * Setting this property to true, does NOT guarantee synchronous event consumption. It is only available when 98 | * the property {@link SyncSubscribersGatekeeper#ALLOW_SYNC_SUBSCRIBERS} is set to true at the time of event publishing. 99 | * If the property is set to false the events will be sent to the consumer asynchronously. 100 | * 101 | * @return true if this subscriber favors synchronous consumption of events. false by 102 | * default. 103 | */ 104 | boolean syncIfAllowed() default false; 105 | } 106 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/spi/SubscriberConfigProvider.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.spi; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | /** 6 | * A contract to handle dynamic subscriber configuration. Since, any configuration provided by {@link Subscribe} 7 | * annotation is inherently compile-time & constant, in cases where the configurations are to be taken from a property 8 | * file for instance, a subscriber must implement this interface and provide dynamic configurations. 9 | * 10 | * These configurations will be used once & only once by eventbus at the time the subscriber is registered. 11 | * 12 | * All the subscriber methods in a class can be configured using this provider, by pinning a configuration to a key 13 | * which is attached to a subscriber method by the property {@link com.netflix.eventbus.spi.Subscribe#name()}. 14 | * 15 | * For a subscriber that implements interface, for any subscriber method, eventbus follows the following order to get 16 | * the configuration for that subscriber method: 17 | *
    18 |
  • Calls {@link SubscriberConfigProvider#getConfigForName(String)} with the name as specified by 19 | {@link com.netflix.eventbus.spi.Subscribe#name()}
  • 20 |
  • If the configuration returned above is not null then uses this configuration for any field.
  • 21 |
  • If the configuration returned above is null then uses any configuration provided by the annotation 22 | as is.
  • 23 |
24 | * 25 | * @author Nitesh Kant 26 | */ 27 | public interface SubscriberConfigProvider { 28 | 29 | /** 30 | * Returns the configuration for the passed subscriber name. 31 | * 32 | * @param subscriberName Name of the subscriber. 33 | * 34 | * @return The configuration or null if the configuration for that subscriber is not supplied by this 35 | * provider. 36 | */ 37 | @Nullable 38 | SubscriberConfig getConfigForName(String subscriberName); 39 | 40 | /** 41 | * Configuration for a subscriber. Any property that can be configured by {@link Subscribe} can also be configured 42 | * here. 43 | */ 44 | interface SubscriberConfig { 45 | 46 | Subscribe.BatchingStrategy getBatchingStrategy(); 47 | 48 | int getBatchAge(); 49 | 50 | int getBatchSize(); 51 | 52 | int getQueueSize(); 53 | 54 | boolean syncIfAllowed(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /netflix-eventbus/src/main/java/com/netflix/eventbus/spi/SubscriberInfo.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.spi; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * Metadata about a subscriber. This does not define any characterstics of the subscriber, its just a metadata 7 | * about any subscriber. 8 | * 9 | * This class is designed to be immutable. 10 | * 11 | * @author Nitesh Kant (nkant@netflix.com) 12 | */ 13 | public class SubscriberInfo { 14 | 15 | private final Method subMethod; 16 | private final Object subInstance; 17 | 18 | public SubscriberInfo(Method subMethod, Object subInstance) { 19 | this.subMethod = subMethod; 20 | this.subInstance = subInstance; 21 | } 22 | 23 | /** 24 | * Returns the method in the subscriber class that is subscribing to a particular event. 25 | * 26 | * @return The method in the subscriber class that is subscribing to a particular event. 27 | */ 28 | public Method getSubscriberMethod() { 29 | return subMethod; 30 | } 31 | 32 | /** 33 | * Returns the instance of the class that this subscriber method belongs. 34 | * 35 | * @return The instance of the class that this subscriber method belongs. 36 | */ 37 | public Object getSubscriberInstance() { 38 | return subInstance; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /netflix-eventbus/src/test/java/com/netflix/eventbus/impl/AgeBatchingTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.impl; 2 | 3 | import com.netflix.eventbus.spi.Subscribe; 4 | import com.netflix.eventbus.spi.SubscriberConfigProvider; 5 | import com.netflix.eventbus.utils.EventBusUtils; 6 | import junit.framework.Assert; 7 | import org.junit.Test; 8 | 9 | import java.lang.reflect.Method; 10 | import java.util.concurrent.atomic.AtomicLong; 11 | 12 | /** 13 | * @author Nitesh Kant (nkant@netflix.com) 14 | */ 15 | public class AgeBatchingTest { 16 | 17 | @Test 18 | public void testAgeBatchAged() throws Exception { 19 | MySub mySub = new MySub(); 20 | Method subMethod = mySub.getClass().getMethod("subMe", String.class); 21 | SubscriberConfigProvider.SubscriberConfig subscriberConfig = EventBusUtils.getSubscriberConfig(subMethod, mySub); 22 | AgeBatchingQueue q = newQ(subMethod, subscriberConfig); 23 | Assert.assertTrue("Age batch queue offer failed.", q.offer("Event1")); 24 | q.invokeReaping(); 25 | AgeBatchingQueue.AgeBatch agedBatch = q.blockingTakeWithTimeout(subscriberConfig.getBatchAge()); 26 | Assert.assertNotNull("No batch available after batch age expired.", agedBatch); 27 | } 28 | 29 | @Test 30 | public void testAgeBatchYoung() throws Exception { 31 | MySub mySub = new MySub(); 32 | Method subMethod = mySub.getClass().getMethod("subMeLong", String.class); 33 | SubscriberConfigProvider.SubscriberConfig subscriberConfig = EventBusUtils.getSubscriberConfig(subMethod, mySub); 34 | AgeBatchingQueue q = newQ(subMethod, subscriberConfig); 35 | String event = "Event1"; 36 | Assert.assertTrue("Age batch queue offer failed.", q.offer(event)); 37 | 38 | AgeBatchingQueue.AgeBatch currentBatch = q.getCurrentBatch(); 39 | Assert.assertTrue("Offered event not in current batch", currentBatch.events.contains(event)); 40 | 41 | Object shdBeNull = q.nonBlockingTake(); 42 | Assert.assertNull("Batch available before batch age expiry.", shdBeNull); 43 | } 44 | 45 | @Test 46 | public void testQueueFull() throws Exception { 47 | MySub mySub = new MySub(); 48 | Method subMethod = mySub.getClass().getMethod("subMe", String.class); 49 | SubscriberConfigProvider.SubscriberConfig subscriberConfig = EventBusUtils.getSubscriberConfig(subMethod, mySub); 50 | AgeBatchingQueue q = newQ(subMethod, subscriberConfig); 51 | for (int j=0; j < 3;j++) { 52 | for (int i = 0; i < 10; i++) { 53 | Assert.assertTrue("Age batch queue offer failed.", q.offer("event" + j +i)); 54 | } 55 | q.invokeReaping(); 56 | } 57 | 58 | Assert.assertFalse("Offer not failing when queue is full", q.offer("EventToFail")); 59 | Assert.assertNotNull("No batch available after age expiry.", q.blockingTake()); 60 | Assert.assertNotNull("No batch available after age expiry.", q.blockingTake()); 61 | Assert.assertTrue("Offer failing when queue is not full", q.offer("EventToGoThrough")); 62 | 63 | } 64 | 65 | private AgeBatchingQueue newQ(Method subMethod, SubscriberConfigProvider.SubscriberConfig annotation) { 66 | return new AgeBatchingQueue(subMethod, annotation, false, new AtomicLong()); 67 | } 68 | 69 | public class MySub { 70 | 71 | @Subscribe(batchAge = 1000, queueSize = 2,batchingStrategy = Subscribe.BatchingStrategy.Age) 72 | public void subMe(String event) { 73 | System.out.println("AgeBatchingTest$MySub.subMe"); 74 | } 75 | 76 | @Subscribe(batchAge = 60000, queueSize = 2,batchingStrategy = Subscribe.BatchingStrategy.Age) 77 | public void subMeLong(String event) { 78 | System.out.println("AgeBatchingTest$MySub.subMe"); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /netflix-eventbus/src/test/java/com/netflix/eventbus/impl/BatchSubscribersTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.impl; 2 | 3 | import com.netflix.eventbus.spi.InvalidSubscriberException; 4 | import com.netflix.eventbus.spi.Subscribe; 5 | import com.netflix.eventbus.spi.SubscriberConfigProvider; 6 | import junit.framework.Assert; 7 | import org.junit.Test; 8 | 9 | import java.lang.reflect.Method; 10 | import java.util.List; 11 | import java.util.concurrent.atomic.AtomicInteger; 12 | import java.util.concurrent.atomic.AtomicLong; 13 | 14 | /** 15 | * @author Nitesh Kant 16 | */ 17 | public class BatchSubscribersTest { 18 | 19 | @Test 20 | public void testBatch() throws Exception { 21 | EventBusImpl bus = new EventBusImpl(); 22 | 23 | bus.setConsumerQueueSupplier(new EventBusImpl.ConsumerQueueSupplier() { 24 | @Override 25 | public ConsumerQueue get(Method subscriber, SubscriberConfigProvider.SubscriberConfig subscriberConfig, AtomicLong queueSizeCounter) { 26 | SizeAndAgeBatchingQueue q = 27 | new SizeAndAgeBatchingQueue(subscriber, subscriberConfig, false, queueSizeCounter); 28 | return q; 29 | } 30 | }); 31 | 32 | BatchConsumer subscriber = new BatchConsumer(); 33 | bus.registerSubscriber(subscriber); 34 | 35 | for (int i = 0; i < 3; i++) { 36 | bus.publish("Hey buddy " + i); 37 | } 38 | synchronized (subscriber.mockReceiveMonitor) { 39 | subscriber.mockReceiveMonitor.wait(2000); 40 | } 41 | Assert.assertEquals("Not all events received by the consumer.", 3, subscriber.eventsCounter.get()); 42 | } 43 | 44 | @Test 45 | public void testNestedGenericsRegistration() throws Exception { 46 | try { 47 | new EventBusImpl().registerSubscriber(new NestedGenericsBatchConsumer()); 48 | } catch (InvalidSubscriberException e) { 49 | throw new AssertionError("Batch Subscriber with nested generic event failed to register."); 50 | } 51 | } 52 | 53 | @Test 54 | public void testNonTypedBatchSub() throws Exception { 55 | try { 56 | new EventBusImpl().registerSubscriber(new NoTypeBatchConsumer()); 57 | throw new AssertionError("Batch Subscriber with no generic type successfully registered."); 58 | } catch (InvalidSubscriberException e) { 59 | // expected 60 | } 61 | } 62 | 63 | private class BatchConsumer { 64 | 65 | private final Object mockReceiveMonitor = new Object(); 66 | private AtomicInteger eventsCounter = new AtomicInteger(); 67 | 68 | @Subscribe(batchingStrategy = Subscribe.BatchingStrategy.SizeOrAge, batchSize = 2, batchAge = 60000) 69 | private void consume(Iterable eventBatch) { 70 | for (String event : eventBatch) { 71 | eventsCounter.incrementAndGet(); 72 | } 73 | synchronized (mockReceiveMonitor) { 74 | mockReceiveMonitor.notifyAll(); 75 | } 76 | } 77 | } 78 | 79 | private class NestedGenericsBatchConsumer { 80 | 81 | @Subscribe(batchingStrategy = Subscribe.BatchingStrategy.SizeOrAge, batchSize = 2, batchAge = 60000) 82 | private void consumeComplex(Iterable> eventBatch) { 83 | } 84 | } 85 | 86 | private class NoTypeBatchConsumer { 87 | 88 | @Subscribe(batchingStrategy = Subscribe.BatchingStrategy.SizeOrAge, batchSize = 2, batchAge = 60000) 89 | private void consumeComplex(Iterable eventBatch) { 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /netflix-eventbus/src/test/java/com/netflix/eventbus/impl/IllegalSubscriberTests.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.impl; 2 | 3 | import com.netflix.eventbus.spi.InvalidSubscriberException; 4 | import com.netflix.eventbus.spi.Subscribe; 5 | import org.junit.Test; 6 | 7 | /** 8 | * @author Nitesh Kant (nkant@netflix.com) 9 | */ 10 | public class IllegalSubscriberTests { 11 | 12 | @Test 13 | public void testMultiArgumentsSubscriber() throws Exception { 14 | EventBusImpl bus = new EventBusImpl(); 15 | Object multiArgSub = new Object() { 16 | 17 | @Subscribe 18 | public void subscribe(String s, String s1) { 19 | 20 | } 21 | }; 22 | registerInvalidSub(bus, multiArgSub, "Subscriber with two arguments not invalid!"); 23 | } 24 | 25 | @Test 26 | public void testIllegalBatchSubscriber() throws Exception { 27 | EventBusImpl bus = new EventBusImpl(); 28 | Object multiArgSub = new Object() { 29 | 30 | @Subscribe(batchingStrategy = Subscribe.BatchingStrategy.Age) 31 | public void subscribe(String s) { 32 | 33 | } 34 | }; 35 | registerInvalidSub(bus, multiArgSub, "Batching subscriber with non-iterable argument not invalid!"); 36 | } 37 | 38 | @Test 39 | public void testNoBatchAgeSubscriber() throws Exception { 40 | EventBusImpl bus = new EventBusImpl(); 41 | Object multiArgSub = new Object() { 42 | 43 | @Subscribe(batchingStrategy = Subscribe.BatchingStrategy.Age, batchSize = 100) 44 | public void subscribe(Iterable s) { 45 | 46 | } 47 | }; 48 | registerInvalidSub(bus, multiArgSub, "Batching subscriber with no batch age not invalid!"); 49 | } 50 | 51 | @Test 52 | public void testNoBatchSizeSubscriber() throws Exception { 53 | EventBusImpl bus = new EventBusImpl(); 54 | Object multiArgSub = new Object() { 55 | 56 | @Subscribe(batchingStrategy = Subscribe.BatchingStrategy.SizeOrAge, batchAge = 100) 57 | public void subscribe(Iterable s) { 58 | 59 | } 60 | }; 61 | registerInvalidSub(bus, multiArgSub, "Batching subscriber with no batch age not invalid!"); 62 | } 63 | 64 | private void registerInvalidSub(EventBusImpl bus, Object multiArgSub, String errorMessage) throws InvalidSubscriberException { 65 | try { 66 | bus.registerSubscriber(multiArgSub); 67 | throw new AssertionError(errorMessage); 68 | } catch (InvalidSubscriberException e) { 69 | // expected. 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /netflix-eventbus/src/test/java/com/netflix/eventbus/impl/SizeAndAgeBatchingTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.impl; 2 | 3 | import com.netflix.eventbus.spi.Subscribe; 4 | import com.netflix.eventbus.spi.SubscriberConfigProvider; 5 | import com.netflix.eventbus.utils.EventBusUtils; 6 | import junit.framework.Assert; 7 | import org.junit.Test; 8 | 9 | import java.lang.reflect.Method; 10 | import java.util.concurrent.atomic.AtomicLong; 11 | 12 | /** 13 | * @author Nitesh Kant (nkant@netflix.com) 14 | */ 15 | public class SizeAndAgeBatchingTest { 16 | 17 | @Test 18 | public void testAgeBatchAged() throws Exception { 19 | MySub mySub = new MySub(); 20 | Method subMethod = mySub.getClass().getMethod("subMe", String.class); 21 | SubscriberConfigProvider.SubscriberConfig subscriberConfig = EventBusUtils.getSubscriberConfig(subMethod, mySub); 22 | 23 | AgeBatchingQueue q = newQ(subMethod, subscriberConfig); 24 | Assert.assertTrue("Age batch queue offer failed.", q.offer("Event1")); 25 | q.invokeReaping(); 26 | AgeBatchingQueue.AgeBatch agedBatch = q.blockingTakeWithTimeout(subscriberConfig.getBatchAge()); 27 | Assert.assertNotNull("No batch available after batch age expired.", agedBatch); 28 | } 29 | 30 | @Test 31 | public void testAgeBatchYoung() throws Exception { 32 | MySub mySub = new MySub(); 33 | Method subMethod = mySub.getClass().getMethod("subMeLong", String.class); 34 | SubscriberConfigProvider.SubscriberConfig subscriberConfig = EventBusUtils.getSubscriberConfig(subMethod, mySub); 35 | AgeBatchingQueue q = newQ(subMethod, subscriberConfig); 36 | String event = "Event1"; 37 | Assert.assertTrue("Age batch queue offer failed.", q.offer(event)); 38 | 39 | AgeBatchingQueue.AgeBatch currentBatch = q.getCurrentBatch(); 40 | Assert.assertTrue("Offered event not in current batch", currentBatch.events.contains(event)); 41 | 42 | Object shdBeNull = q.nonBlockingTake(); 43 | Assert.assertNull("Batch available before batch age expiry.", shdBeNull); 44 | } 45 | 46 | @Test 47 | public void testQueueFull() throws Exception { 48 | MySub mySub = new MySub(); 49 | Method subMethod = mySub.getClass().getMethod("subMe", String.class); 50 | SubscriberConfigProvider.SubscriberConfig subscriberConfig = EventBusUtils.getSubscriberConfig(subMethod, mySub); 51 | AgeBatchingQueue q = newQ(subMethod, subscriberConfig); 52 | for (int j=0; j < 2;j++) { 53 | for (int i = 0; i < 3; i++) { 54 | Assert.assertTrue("Age batch queue offer failed.", q.offer("event" + j +i)); 55 | } 56 | } 57 | 58 | while(q.getCurrentBatch().events.size() < subscriberConfig.getBatchSize()) { 59 | q.offer("EventToFill"); 60 | } 61 | 62 | Assert.assertFalse("Offer not failing when queue is full", q.offer("EventToFail")); 63 | Assert.assertNotNull("No batch available after age expiry.", q.blockingTake()); 64 | Assert.assertNotNull("No batch available after age expiry.", q.blockingTake()); 65 | Assert.assertTrue("Offer failing when queue is not full", q.offer("EventToGoThrough")); 66 | 67 | } 68 | 69 | private AgeBatchingQueue newQ(Method subMethod, SubscriberConfigProvider.SubscriberConfig annotation) { 70 | return new SizeAndAgeBatchingQueue(subMethod, annotation, false, new AtomicLong()); 71 | } 72 | 73 | public class MySub { 74 | 75 | @Subscribe(batchAge = 1000, queueSize = 2, batchSize = 2, batchingStrategy = Subscribe.BatchingStrategy.Age) 76 | public void subMe(String event) { 77 | System.out.println("AgeBatchingTest$MySub.subMe"); 78 | } 79 | 80 | @Subscribe(batchAge = 60000, queueSize = 2, batchSize = 2, batchingStrategy = Subscribe.BatchingStrategy.Age) 81 | public void subMeLong(String event) { 82 | System.out.println("AgeBatchingTest$MySub.subMe"); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /netflix-eventbus/src/test/java/com/netflix/eventbus/test/AnonymousInnerClassConsumerSupplier.java: -------------------------------------------------------------------------------- 1 | package com.netflix.eventbus.test; 2 | 3 | import com.netflix.eventbus.impl.NFEventBusTest; 4 | import com.netflix.eventbus.spi.Subscribe; 5 | 6 | /** 7 | * @author Nitesh Kant 8 | */ 9 | public class AnonymousInnerClassConsumerSupplier { 10 | 11 | /** 12 | * This is just a way to provide a anonymous class with non-package private access modifier. 13 | * 14 | * @param event The type of event, consumer should consume. 15 | * @param Type of the event 16 | * 17 | * @return Consumer. 18 | */ 19 | public static Object getAnonymousInnerClassConsumer(Class event) { 20 | return new Object() { 21 | 22 | @Subscribe 23 | protected void consume(T event) { 24 | // Helleluja!!! 25 | } 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /netflix-eventbus/src/test/java/com/netflix/infix/MockAnnotatable.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | public interface MockAnnotatable { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/AlwaysFalsePredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Predicate; 4 | 5 | final public class AlwaysFalsePredicate implements Predicate { 6 | 7 | public static final AlwaysFalsePredicate INSTANCE = new AlwaysFalsePredicate(); 8 | 9 | // There's no point of creating multiple instance of this class 10 | private AlwaysFalsePredicate(){ 11 | } 12 | 13 | @Override 14 | public boolean apply(Object input) { 15 | return false; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return "AlwaysFalsePredicate []"; 21 | } 22 | 23 | @Override 24 | public int hashCode() { 25 | return Boolean.FALSE.hashCode(); 26 | } 27 | 28 | @Override 29 | public boolean equals(Object obj) { 30 | return obj instanceof AlwaysFalsePredicate; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/AlwaysTruePredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Predicate; 4 | 5 | final public class AlwaysTruePredicate implements Predicate { 6 | 7 | public static final AlwaysTruePredicate INSTANCE = new AlwaysTruePredicate(); 8 | 9 | private AlwaysTruePredicate() { 10 | } 11 | 12 | @Override 13 | public boolean apply(Object input) { 14 | return true; 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return "AlwaysTruePredicate []"; 20 | } 21 | 22 | @Override 23 | public int hashCode() { 24 | return Boolean.TRUE.hashCode(); 25 | } 26 | 27 | @Override 28 | public boolean equals(Object obj){ 29 | return obj instanceof AlwaysTruePredicate; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/AndPredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.google.common.base.Predicates; 5 | 6 | public class AndPredicate implements Predicate { 7 | 8 | final private Predicate andPredicate; 9 | 10 | public AndPredicate(Predicate... predicates) { 11 | this.andPredicate = Predicates.and(predicates); 12 | } 13 | 14 | public AndPredicate(Iterable> predicates) { 15 | this.andPredicate = Predicates.and(predicates); 16 | } 17 | 18 | @Override 19 | public boolean apply(Object input) { 20 | return andPredicate.apply(input); 21 | } 22 | 23 | @Override 24 | public boolean equals(Object o) { 25 | if (this == o) { 26 | return true; 27 | } 28 | if (o == null || getClass() != o.getClass()) { 29 | return false; 30 | } 31 | 32 | AndPredicate that = (AndPredicate) o; 33 | 34 | if (andPredicate != null ? !andPredicate.equals(that.andPredicate) : that.andPredicate != null) { 35 | return false; 36 | } 37 | 38 | return true; 39 | } 40 | 41 | @Override 42 | public int hashCode() { 43 | return andPredicate != null ? andPredicate.hashCode() : 0; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | final StringBuilder sb = new StringBuilder(); 49 | sb.append("AndEventFilter"); 50 | sb.append("{andPredicate=").append(andPredicate); 51 | sb.append('}'); 52 | return sb.toString(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/BooleanValuePredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | import com.google.common.base.Objects; 6 | 7 | public class BooleanValuePredicate implements ValuePredicate { 8 | 9 | private Boolean value; 10 | 11 | private BooleanValuePredicate(@Nullable Boolean value){ 12 | this.value = value; 13 | } 14 | 15 | @Override 16 | public boolean apply(@Nullable Boolean input) { 17 | return Objects.equal(value, input); 18 | } 19 | 20 | public Boolean getValue(){ 21 | return value; 22 | } 23 | 24 | public static final BooleanValuePredicate TRUE = new BooleanValuePredicate(Boolean.TRUE); 25 | 26 | public static final BooleanValuePredicate FALSE = new BooleanValuePredicate(Boolean.FALSE); 27 | 28 | @Override 29 | public String toString() { 30 | StringBuilder builder = new StringBuilder(); 31 | builder.append("BooleanValuePredicate [value="); 32 | builder.append(value); 33 | builder.append("]"); 34 | return builder.toString(); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | final int prime = 31; 40 | int result = 1; 41 | result = prime * result + ((value == null) ? 0 : value.hashCode()); 42 | return result; 43 | } 44 | 45 | @Override 46 | public boolean equals(Object obj) { 47 | if (this == obj) { 48 | return true; 49 | } 50 | if (obj == null) { 51 | return false; 52 | } 53 | if (getClass() != obj.getClass()) { 54 | return false; 55 | } 56 | BooleanValuePredicate other = (BooleanValuePredicate) obj; 57 | if (value == null) { 58 | if (other.value != null) { 59 | return false; 60 | } 61 | } else if (!value.equals(other.value)) { 62 | return false; 63 | } 64 | return true; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/InfixCompiler.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.lang.infix.antlr.EventFilterParser; 5 | import com.netflix.infix.lang.infix.antlr.PredicateTranslatable; 6 | 7 | /** 8 | * Compile an INFIX string into a Predicate 9 | * 10 | * @author elandau 11 | */ 12 | public class InfixCompiler implements PredicateCompiler { 13 | public Predicate compile(String input) throws Exception { 14 | EventFilterParser parser = EventFilterParser.createParser(input); 15 | EventFilterParser.filter_return result = parser.filter(); 16 | 17 | return ((PredicateTranslatable) result.getTree()).translate(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/NotPredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.google.common.base.Predicates; 5 | 6 | public class NotPredicate implements Predicate { 7 | 8 | final private Predicate notPredicate; 9 | 10 | public NotPredicate(Predicate predicate) { 11 | this.notPredicate = Predicates.not(predicate); 12 | } 13 | 14 | @Override 15 | public boolean apply(Object input) { 16 | return notPredicate.apply(input); 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | final StringBuilder sb = new StringBuilder(); 22 | sb.append("NotEventFilter"); 23 | sb.append("{notPredicate=").append(notPredicate); 24 | sb.append('}'); 25 | return sb.toString(); 26 | } 27 | 28 | @Override 29 | public boolean equals(Object o) { 30 | if (this == o) { 31 | return true; 32 | } 33 | if (o == null || getClass() != o.getClass()) { 34 | return false; 35 | } 36 | 37 | NotPredicate that = (NotPredicate) o; 38 | 39 | if (notPredicate != null ? !notPredicate.equals(that.notPredicate) : that.notPredicate != null) { 40 | return false; 41 | } 42 | 43 | return true; 44 | } 45 | 46 | @Override 47 | public int hashCode() { 48 | return notPredicate != null ? notPredicate.hashCode() : 0; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/NullValuePredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | 4 | /** 5 | * This predicate returns true if its input is null. That is, it identifies 6 | * null object. 7 | * 8 | */ 9 | final public class NullValuePredicate implements ValuePredicate { 10 | 11 | private static final byte KEY = 0x00; 12 | 13 | private NullValuePredicate(){} 14 | 15 | @Override 16 | public boolean apply(final Object input) { 17 | return input == null; 18 | } 19 | 20 | public static final NullValuePredicate INSTANCE = new NullValuePredicate(); 21 | 22 | @Override 23 | public String toString() { 24 | StringBuilder builder = new StringBuilder(); 25 | builder.append("NullValuePredicate []"); 26 | return builder.toString(); 27 | } 28 | 29 | @Override 30 | public final int hashCode() { 31 | return KEY; 32 | } 33 | 34 | @Override 35 | public final boolean equals(Object obj) { 36 | return obj instanceof NullValuePredicate; 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/OrPredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.google.common.base.Predicates; 5 | 6 | public class OrPredicate implements Predicate { 7 | 8 | final private Predicate orPredicate; 9 | 10 | public OrPredicate(Predicate... predicates) { 11 | this.orPredicate = Predicates.or(predicates); 12 | } 13 | 14 | public OrPredicate(Iterable> filters) { 15 | this.orPredicate = Predicates.or(filters); 16 | } 17 | 18 | @Override 19 | public boolean apply(Object input) { 20 | return orPredicate.apply(input); 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | final StringBuilder sb = new StringBuilder(); 26 | sb.append("OrEventFilter"); 27 | sb.append("{orPredicate=").append(orPredicate); 28 | sb.append('}'); 29 | return sb.toString(); 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) { 35 | return true; 36 | } 37 | if (o == null || getClass() != o.getClass()) { 38 | return false; 39 | } 40 | 41 | OrPredicate that = (OrPredicate) o; 42 | 43 | if (orPredicate != null ? !orPredicate.equals(that.orPredicate) : that.orPredicate != null) { 44 | return false; 45 | } 46 | 47 | return true; 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return orPredicate != null ? orPredicate.hashCode() : 0; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/PathExistsEventFilter.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Predicate; 4 | import org.apache.commons.jxpath.JXPathContext; 5 | import org.apache.commons.jxpath.Pointer; 6 | import org.apache.commons.jxpath.ri.model.beans.NullPointer; 7 | 8 | public class PathExistsEventFilter implements Predicate { 9 | 10 | private String xpath; 11 | 12 | public PathExistsEventFilter(String path) { 13 | this.xpath = path; 14 | } 15 | 16 | @Override 17 | public boolean apply(Object input) { 18 | JXPathContext jxpath = JXPathContext.newContext(input); 19 | // We should allow non-existing path, and let predicate handle it. 20 | jxpath.setLenient(true); 21 | 22 | Pointer pointer = jxpath.getPointer(xpath); 23 | 24 | return pointer != null && !(pointer instanceof NullPointer); 25 | } 26 | 27 | public String getXpath() { 28 | return xpath; 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | StringBuilder builder = new StringBuilder(); 34 | builder.append("PathExistsEventFilter [xpath="); 35 | builder.append(xpath); 36 | builder.append("]"); 37 | return builder.toString(); 38 | } 39 | 40 | 41 | @Override 42 | public int hashCode() { 43 | final int prime = 31; 44 | int result = 1; 45 | result = prime * result + ((xpath == null) ? 0 : xpath.hashCode()); 46 | return result; 47 | } 48 | 49 | 50 | @Override 51 | public boolean equals(Object obj) { 52 | if (this == obj) { 53 | return true; 54 | } 55 | if (obj == null) { 56 | return false; 57 | } 58 | if (getClass() != obj.getClass()) { 59 | return false; 60 | } 61 | PathExistsEventFilter other = (PathExistsEventFilter) obj; 62 | if (xpath == null) { 63 | if (other.xpath != null) { 64 | return false; 65 | } 66 | } else if (!xpath.equals(other.xpath)) { 67 | return false; 68 | } 69 | return true; 70 | } 71 | 72 | 73 | } 74 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/PathValueEventFilter.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Predicate; 4 | import org.apache.commons.jxpath.JXPathContext; 5 | 6 | public class PathValueEventFilter implements Predicate { 7 | 8 | private String xpath; 9 | private ValuePredicate predicate; 10 | 11 | public PathValueEventFilter(String path, ValuePredicate predicate) { 12 | this.xpath = path; 13 | this.predicate = predicate; 14 | } 15 | 16 | 17 | @Override 18 | public boolean apply(Object input) { 19 | JXPathContext jxpath = JXPathContext.newContext(input); 20 | // We should allow non-existing path, and let predicate handle it. 21 | jxpath.setLenient(true); 22 | 23 | @SuppressWarnings("unchecked") 24 | T value = (T)jxpath.getValue(xpath); 25 | 26 | return predicate.apply(value); 27 | } 28 | 29 | public String getXpath() { 30 | return xpath; 31 | } 32 | 33 | public ValuePredicate getPredicate() { 34 | return predicate; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | StringBuilder builder = new StringBuilder(); 40 | builder.append("PathValueEventFilter [xpath="); 41 | builder.append(xpath); 42 | builder.append(", predicate="); 43 | builder.append(predicate); 44 | builder.append("]"); 45 | return builder.toString(); 46 | } 47 | 48 | 49 | @Override 50 | public int hashCode() { 51 | final int prime = 31; 52 | int result = 1; 53 | result = prime * result + ((predicate == null) ? 0 : predicate.hashCode()); 54 | result = prime * result + ((xpath == null) ? 0 : xpath.hashCode()); 55 | return result; 56 | } 57 | 58 | 59 | @Override 60 | public boolean equals(Object obj) { 61 | if (this == obj) { 62 | return true; 63 | } 64 | if (obj == null) { 65 | return false; 66 | } 67 | if (getClass() != obj.getClass()) { 68 | return false; 69 | } 70 | PathValueEventFilter other = (PathValueEventFilter) obj; 71 | if (predicate == null) { 72 | if (other.predicate != null) { 73 | return false; 74 | } 75 | } else if (!predicate.equals(other.predicate)) { 76 | return false; 77 | } 78 | if (xpath == null) { 79 | if (other.xpath != null) { 80 | return false; 81 | } 82 | } else if (!xpath.equals(other.xpath)) { 83 | return false; 84 | } 85 | return true; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/PredicateCompiler.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Predicate; 4 | 5 | public interface PredicateCompiler { 6 | public Predicate compile(String input) throws Exception; 7 | } 8 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/Predicates.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.google.common.collect.ImmutableList; 5 | 6 | /** 7 | * A number of static helper methods to simplify the construction of combined event filters. 8 | */ 9 | public class Predicates { 10 | 11 | private Predicates(){} 12 | 13 | public static Predicate alwaysTrue(){ 14 | return AlwaysTruePredicate.INSTANCE; 15 | } 16 | 17 | public static Predicate alwaysFalse() { 18 | return AlwaysFalsePredicate.INSTANCE; 19 | } 20 | 21 | public static Predicate or(Predicate...filters) { 22 | return new OrPredicate(filters); 23 | } 24 | 25 | public static Predicate or(Iterable> filters) { 26 | return new OrPredicate(ImmutableList.copyOf(filters)); 27 | } 28 | 29 | public static Predicate and(Predicate...filters) { 30 | return new AndPredicate(filters); 31 | } 32 | 33 | public static Predicate and(Iterable> filters){ 34 | return new AndPredicate(ImmutableList.copyOf(filters)); 35 | } 36 | 37 | public static Predicate not(Predicate filter) { 38 | return new NotPredicate(filter); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/RegexValuePredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Preconditions; 4 | 5 | import javax.annotation.Nullable; 6 | import java.util.regex.Pattern; 7 | 8 | public class RegexValuePredicate implements ValuePredicate { 9 | 10 | private String regex; 11 | private Pattern pattern; 12 | private MatchPolicy policy; 13 | 14 | public RegexValuePredicate(String regex, MatchPolicy policy){ 15 | Preconditions.checkArgument(regex != null, String.format("The regex should not be null.")); 16 | this.regex = regex; 17 | this.pattern = Pattern.compile(regex); 18 | this.policy = policy; 19 | } 20 | 21 | @Override 22 | public boolean apply(@Nullable String input) { 23 | if(input == null){ 24 | return false; 25 | } 26 | 27 | if(policy == MatchPolicy.PARTIAL){ 28 | return pattern.matcher(input).find(); 29 | } 30 | 31 | if(policy == MatchPolicy.FULL){ 32 | return pattern.matcher(input).matches(); 33 | } 34 | 35 | throw new UnsupportedOperationException(String.format("the match policy %s is not supported", policy)); 36 | } 37 | 38 | public String getPattern() { 39 | return this.pattern.pattern(); 40 | } 41 | 42 | public MatchPolicy getMatchPolicy() { 43 | return policy; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | StringBuilder builder = new StringBuilder(); 49 | builder.append("RegexValuePredicate [pattern="); 50 | builder.append(regex); 51 | builder.append(", policy="); 52 | builder.append(policy); 53 | builder.append("]"); 54 | return builder.toString(); 55 | } 56 | 57 | public static enum MatchPolicy { 58 | PARTIAL, 59 | FULL 60 | } 61 | 62 | @Override 63 | public int hashCode() { 64 | final int prime = 31; 65 | int result = 1; 66 | result = prime * result + ((policy == null) ? 0 : policy.hashCode()); 67 | result = prime * result + ((regex == null) ? 0 : regex.hashCode()); 68 | return result; 69 | } 70 | 71 | @Override 72 | public boolean equals(Object obj) { 73 | if (this == obj) { 74 | return true; 75 | } 76 | if (obj == null) { 77 | return false; 78 | } 79 | if (getClass() != obj.getClass()) { 80 | return false; 81 | } 82 | RegexValuePredicate other = (RegexValuePredicate) obj; 83 | if (policy != other.policy) { 84 | return false; 85 | } 86 | if (regex == null) { 87 | if (other.regex != null) { 88 | return false; 89 | } 90 | } else if (!regex.equals(other.regex)) { 91 | return false; 92 | } 93 | return true; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/StringValuePredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Objects; 4 | 5 | import javax.annotation.Nullable; 6 | 7 | public class StringValuePredicate implements ValuePredicate { 8 | private String value; 9 | 10 | public StringValuePredicate(@Nullable String value){ 11 | this.value = value; 12 | } 13 | 14 | @Override 15 | public boolean apply(@Nullable Object input) { 16 | return Objects.equal(value, input); 17 | } 18 | 19 | String getValue(){ 20 | return value; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | StringBuilder builder = new StringBuilder(); 26 | builder.append("StringValuePredicate [value="); 27 | builder.append(value); 28 | builder.append("]"); 29 | return builder.toString(); 30 | } 31 | 32 | @Override 33 | public int hashCode() { 34 | final int prime = 31; 35 | int result = 1; 36 | result = prime * result + ((value == null) ? 0 : value.hashCode()); 37 | return result; 38 | } 39 | 40 | @Override 41 | public boolean equals(Object obj) { 42 | if (this == obj) { 43 | return true; 44 | } 45 | if (obj == null) { 46 | return false; 47 | } 48 | if (getClass() != obj.getClass()) { 49 | return false; 50 | } 51 | StringValuePredicate other = (StringValuePredicate) obj; 52 | if (value == null) { 53 | if (other.value != null) { 54 | return false; 55 | } 56 | } else if (!value.equals(other.value)) { 57 | return false; 58 | } 59 | return true; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/TimeMillisValuePredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import org.joda.time.format.DateTimeFormatter; 4 | 5 | import javax.annotation.Nullable; 6 | 7 | public class TimeMillisValuePredicate implements ValuePredicate { 8 | 9 | private String timeFormat; 10 | private String value; 11 | private String fnName; 12 | private NumericValuePredicate longPredicate; 13 | 14 | public TimeMillisValuePredicate(String timeFormat, String value, String fnName){ 15 | this.timeFormat = timeFormat; 16 | this.value = value; 17 | this.fnName = fnName; 18 | 19 | DateTimeFormatter formatter = TimeUtil.toDateTimeFormatter("time format", timeFormat); 20 | 21 | long timeInMs = formatter.parseMillis(value); 22 | this.longPredicate = new NumericValuePredicate(timeInMs, fnName); 23 | } 24 | 25 | @Override 26 | public boolean apply(@Nullable Long input) { 27 | return longPredicate.apply(input); 28 | } 29 | 30 | public String getValue(){ 31 | return value; 32 | } 33 | 34 | public String getTimeFormat(){ 35 | return this.timeFormat; 36 | } 37 | 38 | String getFnName() { 39 | return this.fnName; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | StringBuilder builder = new StringBuilder(); 45 | builder.append("TimeMillisValuePredicate [timeFormat="); 46 | builder.append(timeFormat); 47 | builder.append(", value="); 48 | builder.append(value); 49 | builder.append(", fnName="); 50 | builder.append(fnName); 51 | builder.append(", longPredicate="); 52 | builder.append(longPredicate); 53 | builder.append("]"); 54 | return builder.toString(); 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | final int prime = 31; 60 | int result = 1; 61 | result = prime * result + ((fnName == null) ? 0 : fnName.hashCode()); 62 | result = prime * result + ((timeFormat == null) ? 0 : timeFormat.hashCode()); 63 | result = prime * result + ((value == null) ? 0 : value.hashCode()); 64 | return result; 65 | } 66 | 67 | @Override 68 | public boolean equals(Object obj) { 69 | if (this == obj) { 70 | return true; 71 | } 72 | if (obj == null) { 73 | return false; 74 | } 75 | if (getClass() != obj.getClass()) { 76 | return false; 77 | } 78 | TimeMillisValuePredicate other = (TimeMillisValuePredicate) obj; 79 | if (fnName == null) { 80 | if (other.fnName != null) { 81 | return false; 82 | } 83 | } else if (!fnName.equals(other.fnName)) { 84 | return false; 85 | } 86 | if (timeFormat == null) { 87 | if (other.timeFormat != null) { 88 | return false; 89 | } 90 | } else if (!timeFormat.equals(other.timeFormat)) { 91 | return false; 92 | } 93 | if (value == null) { 94 | if (other.value != null) { 95 | return false; 96 | } 97 | } else if (!value.equals(other.value)) { 98 | return false; 99 | } 100 | return true; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/TimeRangeValuePredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.google.common.base.Strings; 5 | import org.joda.time.Interval; 6 | import org.joda.time.format.DateTimeFormatter; 7 | 8 | import javax.annotation.Nullable; 9 | 10 | public class TimeRangeValuePredicate implements ValuePredicate { 11 | 12 | private String timeFormat; 13 | private String start; 14 | private String end; 15 | private Interval interval; 16 | 17 | public TimeRangeValuePredicate(String timeFormat, String start, String end){ 18 | 19 | Preconditions.checkArgument(!Strings.isNullOrEmpty(timeFormat), "Time format can't be null or empty. "); 20 | Preconditions.checkArgument(!Strings.isNullOrEmpty(start), "The lower bound of time range can't be null or empty."); 21 | Preconditions.checkArgument(!Strings.isNullOrEmpty(end), "The upper bound of time range can't be null or empty. "); 22 | 23 | this.timeFormat = timeFormat; 24 | this.start = start; 25 | this.end = end; 26 | 27 | DateTimeFormatter format = TimeUtil.toDateTimeFormatter("time format", timeFormat); 28 | long startInstant = format.parseMillis(start); 29 | long endInstant = format.parseMillis(end); 30 | 31 | try{ 32 | this.interval = new Interval(startInstant, endInstant); 33 | }catch(IllegalArgumentException e) { 34 | String msg = String.format( 35 | "The lower bound should be smaller than or equal to upper bound for a time range. Given range: [%s, %s)", 36 | start, 37 | end); 38 | IllegalArgumentException iae = new IllegalArgumentException(msg, e.getCause()); 39 | iae.setStackTrace(e.getStackTrace()); 40 | throw iae; 41 | } 42 | } 43 | 44 | @Override 45 | public boolean apply(@Nullable Long input) { 46 | return null != input && this.interval.contains(input); 47 | } 48 | 49 | public String getStart(){ 50 | return start; 51 | } 52 | 53 | public String getTimeFormat(){ 54 | return this.timeFormat; 55 | } 56 | 57 | String getEnd() { 58 | return this.end; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | StringBuilder builder = new StringBuilder(); 64 | builder.append("TimeRangeValuePredicate [timeFormat="); 65 | builder.append(timeFormat); 66 | builder.append(", start="); 67 | builder.append(start); 68 | builder.append(", end="); 69 | builder.append(end); 70 | builder.append(", interval="); 71 | builder.append(interval); 72 | builder.append("]"); 73 | return builder.toString(); 74 | } 75 | 76 | @Override 77 | public int hashCode() { 78 | final int prime = 31; 79 | int result = 1; 80 | result = prime * result + ((end == null) ? 0 : end.hashCode()); 81 | result = prime * result + ((start == null) ? 0 : start.hashCode()); 82 | result = prime * result + ((timeFormat == null) ? 0 : timeFormat.hashCode()); 83 | return result; 84 | } 85 | 86 | @Override 87 | public boolean equals(Object obj) { 88 | if (this == obj) { 89 | return true; 90 | } 91 | if (obj == null) { 92 | return false; 93 | } 94 | if (getClass() != obj.getClass()) { 95 | return false; 96 | } 97 | TimeRangeValuePredicate other = (TimeRangeValuePredicate) obj; 98 | if (end == null) { 99 | if (other.end != null) { 100 | return false; 101 | } 102 | } else if (!end.equals(other.end)) { 103 | return false; 104 | } 105 | if (start == null) { 106 | if (other.start != null) { 107 | return false; 108 | } 109 | } else if (!start.equals(other.start)) { 110 | return false; 111 | } 112 | if (timeFormat == null) { 113 | if (other.timeFormat != null) { 114 | return false; 115 | } 116 | } else if (!timeFormat.equals(other.timeFormat)) { 117 | return false; 118 | } 119 | return true; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/TimeStringValuePredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import org.joda.time.format.DateTimeFormatter; 4 | 5 | import javax.annotation.Nullable; 6 | 7 | public class TimeStringValuePredicate implements ValuePredicate { 8 | 9 | private String valueFormat; 10 | private String inputFormat; 11 | private String value; 12 | private String fnName; 13 | private DateTimeFormatter inputTimeFormatter; 14 | private TimeMillisValuePredicate timePredicate; 15 | 16 | public TimeStringValuePredicate(String valueFormat, String inputFormat, String value, String fnName){ 17 | this.valueFormat = valueFormat; 18 | this.inputFormat = inputFormat; 19 | this.value = value; 20 | this.fnName = fnName; 21 | 22 | this.inputTimeFormatter = TimeUtil.toDateTimeFormatter("input format", inputFormat); 23 | this.timePredicate = new TimeMillisValuePredicate(this.valueFormat, value, fnName); 24 | } 25 | 26 | @Override 27 | public boolean apply(@Nullable String input) { 28 | long timeValue = inputTimeFormatter.parseMillis(input); 29 | return timePredicate.apply(timeValue); 30 | } 31 | 32 | public String getValue(){ 33 | return value; 34 | } 35 | 36 | public String getValueFormat(){ 37 | return this.valueFormat; 38 | } 39 | 40 | public String getInputFormat() { 41 | return this.inputFormat; 42 | } 43 | 44 | String getFnName() { 45 | return this.fnName; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | StringBuilder builder = new StringBuilder(); 51 | builder.append("TimeStringValuePredicate [valueFormat="); 52 | builder.append(valueFormat); 53 | builder.append(", inputFormat="); 54 | builder.append(inputFormat); 55 | builder.append(", value="); 56 | builder.append(value); 57 | builder.append(", fnName="); 58 | builder.append(fnName); 59 | builder.append(", inputTimeFormatter="); 60 | builder.append(inputTimeFormatter); 61 | builder.append(", timePredicate="); 62 | builder.append(timePredicate); 63 | builder.append("]"); 64 | return builder.toString(); 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | final int prime = 31; 70 | int result = 1; 71 | result = prime * result + ((fnName == null) ? 0 : fnName.hashCode()); 72 | result = prime * result + ((inputFormat == null) ? 0 : inputFormat.hashCode()); 73 | result = prime * result + ((value == null) ? 0 : value.hashCode()); 74 | result = prime * result + ((valueFormat == null) ? 0 : valueFormat.hashCode()); 75 | return result; 76 | } 77 | 78 | @Override 79 | public boolean equals(Object obj) { 80 | if (this == obj) { 81 | return true; 82 | } 83 | if (obj == null) { 84 | return false; 85 | } 86 | if (getClass() != obj.getClass()) { 87 | return false; 88 | } 89 | TimeStringValuePredicate other = (TimeStringValuePredicate) obj; 90 | if (fnName == null) { 91 | if (other.fnName != null) { 92 | return false; 93 | } 94 | } else if (!fnName.equals(other.fnName)) { 95 | return false; 96 | } 97 | if (inputFormat == null) { 98 | if (other.inputFormat != null) { 99 | return false; 100 | } 101 | } else if (!inputFormat.equals(other.inputFormat)) { 102 | return false; 103 | } 104 | if (value == null) { 105 | if (other.value != null) { 106 | return false; 107 | } 108 | } else if (!value.equals(other.value)) { 109 | return false; 110 | } 111 | if (valueFormat == null) { 112 | if (other.valueFormat != null) { 113 | return false; 114 | } 115 | } else if (!valueFormat.equals(other.valueFormat)) { 116 | return false; 117 | } 118 | return true; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/TimeUtil.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import org.joda.time.format.DateTimeFormat; 4 | import org.joda.time.format.DateTimeFormatter; 5 | 6 | public class TimeUtil { 7 | 8 | /** 9 | * Converts the given time format string to a {@link org.joda.time.format.DateTimeFormatter} instance. 10 | * 11 | * @param formatName A name used to identify the given time format. This is mainly used for error reporting. 12 | * @param timeFormat The date time format to be converted. 13 | * 14 | * @return A {@link org.joda.time.format.DateTimeFormatter} instance of the given time format. 15 | * 16 | * @throws IllegalArgumentException if the given time format is invalid. 17 | */ 18 | public static DateTimeFormatter toDateTimeFormatter(String formatName, String timeFormat) { 19 | DateTimeFormatter formatter = null; 20 | try{ 21 | formatter = DateTimeFormat.forPattern(timeFormat); 22 | }catch(IllegalArgumentException e){ 23 | //JODA's error message doesn't tell you which value sucked, so we create a better 24 | // error message here. 25 | IllegalArgumentException iae = new IllegalArgumentException( 26 | String.format("Invalid time format for the property %s: '%s'", 27 | formatName, 28 | timeFormat), 29 | e.getCause()); 30 | iae.setStackTrace(e.getStackTrace()); 31 | 32 | throw iae; 33 | } 34 | return formatter; 35 | } 36 | 37 | /** 38 | * Converts the given epoch time in millisecond to a string according to the given format. Note 39 | * each invocation creates a new {@link org.joda.time.format.DateTimeFormatter} instance, which is pretty costly. 40 | * This method is suitable for testing and calls that are not on hot path. 41 | * 42 | * @param millis the epoch time to be converted 43 | * @param format The format the returned time string 44 | * @return A string representation of the given epoch time in the given format 45 | */ 46 | public static String toString(long millis, String format){ 47 | return DateTimeFormat.forPattern(format).print(millis); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/ValuePredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Predicate; 4 | 5 | public interface ValuePredicate extends Predicate{ 6 | 7 | // Emphasize that every {@code ValuePredicate} instance can be used as a key 8 | // in a collection. 9 | public int hashCode(); 10 | public boolean equals(Object o); 11 | } 12 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/XPathValuePredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Objects; 4 | import org.apache.commons.jxpath.JXPathContext; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | public class XPathValuePredicate implements ValuePredicate { 9 | 10 | private String valueXpath; 11 | private String inputXpath; 12 | 13 | public XPathValuePredicate(String valueXpath, String inputXpath){ 14 | this.valueXpath = valueXpath; 15 | this.inputXpath = inputXpath; 16 | } 17 | 18 | @Override 19 | public boolean apply(@Nullable String input) { 20 | JXPathContext context = JXPathContext.newContext(input); 21 | context.setLenient(true); 22 | return Objects.equal(context.getValue(valueXpath), context.getValue(inputXpath)); 23 | } 24 | 25 | public String getInputXpath(){ 26 | return inputXpath; 27 | } 28 | 29 | public String getValueXpath() { 30 | return valueXpath; 31 | } 32 | 33 | @Override 34 | public int hashCode() { 35 | final int prime = 31; 36 | int result = 1; 37 | result = prime * result + ((inputXpath == null) ? 0 : inputXpath.hashCode()); 38 | result = prime * result + ((valueXpath == null) ? 0 : valueXpath.hashCode()); 39 | return result; 40 | } 41 | 42 | @Override 43 | public boolean equals(Object obj) { 44 | if (this == obj) { 45 | return true; 46 | } 47 | if (obj == null) { 48 | return false; 49 | } 50 | if (getClass() != obj.getClass()) { 51 | return false; 52 | } 53 | XPathValuePredicate other = (XPathValuePredicate) obj; 54 | if (inputXpath == null) { 55 | if (other.inputXpath != null) { 56 | return false; 57 | } 58 | } else if (!inputXpath.equals(other.inputXpath)) { 59 | return false; 60 | } 61 | if (valueXpath == null) { 62 | if (other.valueXpath != null) { 63 | return false; 64 | } 65 | } else if (!valueXpath.equals(other.valueXpath)) { 66 | return false; 67 | } 68 | return true; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | StringBuilder builder = new StringBuilder(); 74 | builder.append("PathValuePredicate [valueXpath="); 75 | builder.append(valueXpath); 76 | builder.append(", inputXpath="); 77 | builder.append(inputXpath); 78 | builder.append("]"); 79 | return builder.toString(); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/AndTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Function; 4 | import com.google.common.base.Predicate; 5 | import com.google.common.collect.Lists; 6 | import com.netflix.infix.Predicates; 7 | 8 | import org.antlr.runtime.Token; 9 | import org.antlr.runtime.tree.Tree; 10 | 11 | public class AndTreeNode extends PredicateBaseTreeNode implements PredicateTranslatable { 12 | 13 | @Override 14 | @SuppressWarnings("unchecked") 15 | public Predicate translate() { 16 | return Predicates.and( 17 | Lists.transform(getChildren(), new Function>() { 18 | @Override 19 | public Predicate apply(Object input) { 20 | PredicateTranslatable node = (PredicateTranslatable) input; 21 | return node.translate(); 22 | } 23 | }) 24 | ); 25 | } 26 | 27 | public AndTreeNode(Token t) { 28 | super(t); 29 | } 30 | 31 | public AndTreeNode(AndTreeNode node) { 32 | super(node); 33 | } 34 | 35 | public Tree dupNode() { 36 | return new AndTreeNode(this); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/BetweenTimeMillisTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.Predicates; 5 | import com.netflix.infix.PathValueEventFilter; 6 | import com.netflix.infix.TimeMillisValuePredicate; 7 | 8 | import org.antlr.runtime.Token; 9 | import org.antlr.runtime.tree.Tree; 10 | 11 | public class BetweenTimeMillisTreeNode extends PredicateBaseTreeNode implements PredicateTranslatable { 12 | 13 | @Override 14 | public Predicate translate() { 15 | ValueTreeNode xpathNode = (ValueTreeNode)getChild(0); 16 | String xpath = (String)xpathNode.getValue(); 17 | 18 | TimeMillisValueTreeNode lowerBoundNode = (TimeMillisValueTreeNode)getChild(1); 19 | 20 | TimeMillisValueTreeNode upperBoundNode = (TimeMillisValueTreeNode)getChild(2); 21 | 22 | return Predicates.and( 23 | new PathValueEventFilter( 24 | xpath, 25 | new TimeMillisValuePredicate(lowerBoundNode.getValueFormat(), lowerBoundNode.getValue(), ">=")), 26 | new PathValueEventFilter( 27 | xpath, 28 | new TimeMillisValuePredicate(upperBoundNode.getValueFormat(), upperBoundNode.getValue(), "<")) 29 | ); 30 | 31 | } 32 | 33 | public BetweenTimeMillisTreeNode(Token t) { 34 | super(t); 35 | } 36 | 37 | public BetweenTimeMillisTreeNode(BetweenTimeMillisTreeNode node) { 38 | super(node); 39 | } 40 | 41 | public Tree dupNode() { 42 | return new BetweenTimeMillisTreeNode(this); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/BetweenTimeStringTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.Predicates; 5 | import com.netflix.infix.PathValueEventFilter; 6 | import com.netflix.infix.TimeStringValuePredicate; 7 | 8 | import org.antlr.runtime.Token; 9 | import org.antlr.runtime.tree.Tree; 10 | 11 | public class BetweenTimeStringTreeNode extends PredicateBaseTreeNode implements PredicateTranslatable { 12 | 13 | @Override 14 | public Predicate translate() { 15 | ValueTreeNode xpathNode = (ValueTreeNode)getChild(0); 16 | String xpath = (String)xpathNode.getValue(); 17 | 18 | TimeStringValueTreeNode lowerBoundNode = (TimeStringValueTreeNode)getChild(1); 19 | 20 | TimeStringValueTreeNode upperBoundNode = (TimeStringValueTreeNode)getChild(2); 21 | 22 | return Predicates.and( 23 | new PathValueEventFilter( 24 | xpath, 25 | new TimeStringValuePredicate( 26 | lowerBoundNode.getValueTimeFormat(), 27 | lowerBoundNode.getInputTimeFormat(), 28 | lowerBoundNode.getValue(), 29 | ">=")), 30 | new PathValueEventFilter( 31 | xpath, 32 | new TimeStringValuePredicate( 33 | upperBoundNode.getValueTimeFormat(), 34 | upperBoundNode.getInputTimeFormat(), 35 | upperBoundNode.getValue(), 36 | "<")) 37 | ); 38 | 39 | } 40 | 41 | public BetweenTimeStringTreeNode(Token t) { 42 | super(t); 43 | } 44 | 45 | public BetweenTimeStringTreeNode(BetweenTimeStringTreeNode node) { 46 | super(node); 47 | } 48 | 49 | public Tree dupNode() { 50 | return new BetweenTimeStringTreeNode(this); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/BetweenTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.Predicates; 5 | import com.netflix.infix.NumericValuePredicate; 6 | import com.netflix.infix.PathValueEventFilter; 7 | 8 | import org.antlr.runtime.Token; 9 | import org.antlr.runtime.tree.Tree; 10 | 11 | public class BetweenTreeNode extends PredicateBaseTreeNode implements PredicateTranslatable { 12 | 13 | @Override 14 | public Predicate translate() { 15 | ValueTreeNode xpathNode = (ValueTreeNode)getChild(0); 16 | String xpath = (String)xpathNode.getValue(); 17 | 18 | ValueTreeNode lowerBoundNode = (ValueTreeNode)getChild(1); 19 | Number lowerBound = (Number)lowerBoundNode.getValue(); 20 | 21 | ValueTreeNode upperBoundNode = (ValueTreeNode)getChild(2); 22 | Number upperBound = (Number)upperBoundNode.getValue(); 23 | 24 | return Predicates.and( 25 | new PathValueEventFilter(xpath, new NumericValuePredicate(lowerBound, ">=")), 26 | new PathValueEventFilter(xpath, new NumericValuePredicate(upperBound, "<")) 27 | ); 28 | 29 | } 30 | 31 | public BetweenTreeNode(Token t) { 32 | super(t); 33 | } 34 | 35 | public BetweenTreeNode(BetweenTreeNode node) { 36 | super(node); 37 | } 38 | 39 | public Tree dupNode() { 40 | return new BetweenTreeNode(this); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/ComparableTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.NumericValuePredicate; 5 | import com.netflix.infix.PathValueEventFilter; 6 | import com.netflix.infix.TimeMillisValuePredicate; 7 | import com.netflix.infix.TimeStringValuePredicate; 8 | 9 | import org.antlr.runtime.Token; 10 | import org.antlr.runtime.tree.Tree; 11 | 12 | import static com.netflix.infix.lang.infix.antlr.EventFilterParser.*; 13 | import static com.netflix.infix.lang.infix.antlr.TreeNodeUtil.getXPath; 14 | 15 | public class ComparableTreeNode extends PredicateBaseTreeNode implements PredicateTranslatable { 16 | 17 | @Override 18 | public Predicate translate() { 19 | String xpath = getXPath(getChild(0)); 20 | 21 | Tree valueNode = getChild(1); 22 | 23 | switch(valueNode.getType()){ 24 | case NUMBER: 25 | Number value = (Number)((ValueTreeNode)valueNode).getValue(); 26 | return new PathValueEventFilter(xpath, new NumericValuePredicate(value, getToken().getText())); 27 | case TIME_MILLIS_FUN_NAME: 28 | TimeMillisValueTreeNode timeValueNode = (TimeMillisValueTreeNode)valueNode; 29 | return new PathValueEventFilter( 30 | xpath, 31 | new TimeMillisValuePredicate( 32 | timeValueNode.getValueFormat(), 33 | timeValueNode.getValue(), 34 | getToken().getText())); 35 | case TIME_STRING_FUN_NAME: 36 | TimeStringValueTreeNode timeStringNode = (TimeStringValueTreeNode)valueNode; 37 | 38 | return new PathValueEventFilter( 39 | xpath, 40 | new TimeStringValuePredicate( 41 | timeStringNode.getValueTimeFormat(), 42 | timeStringNode.getInputTimeFormat(), 43 | timeStringNode.getValue(), 44 | getToken().getText())); 45 | default: 46 | throw new UnexpectedTokenException(valueNode, "Number", "time-millis", "time-string"); 47 | } 48 | 49 | } 50 | 51 | 52 | public ComparableTreeNode(Token t) { 53 | super(t); 54 | } 55 | 56 | public ComparableTreeNode(ComparableTreeNode node) { 57 | super(node); 58 | } 59 | 60 | public Tree dupNode() { 61 | return new ComparableTreeNode(this); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/EqualityComparisonBaseTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.*; 5 | 6 | import org.antlr.runtime.Token; 7 | import org.antlr.runtime.tree.Tree; 8 | 9 | import static com.netflix.infix.lang.infix.antlr.EventFilterParser.*; 10 | import static com.netflix.infix.lang.infix.antlr.TreeNodeUtil.getXPath; 11 | 12 | public abstract class EqualityComparisonBaseTreeNode extends PredicateBaseTreeNode { 13 | 14 | public EqualityComparisonBaseTreeNode(Token t) { 15 | super(t); 16 | } 17 | 18 | public EqualityComparisonBaseTreeNode(PredicateBaseTreeNode node) { 19 | super(node); 20 | } 21 | 22 | // TODO this is an ugly workaround. We should really generate ^(NOT ^(Equals...) for NOT_EQUAL 23 | // but I can't get ANTLR to generated nested tree with added node. 24 | protected Predicate getEqualFilter() { 25 | String xpath = getXPath(getChild(0)); 26 | 27 | Tree valueNode = getChild(1); 28 | 29 | switch(valueNode.getType()){ 30 | case NUMBER: 31 | Number value = (Number)((ValueTreeNode)valueNode).getValue(); 32 | return new PathValueEventFilter(xpath, new NumericValuePredicate(value, "=")); 33 | case STRING: 34 | String sValue = (String)((ValueTreeNode)valueNode).getValue(); 35 | return new PathValueEventFilter(xpath, new StringValuePredicate(sValue)); 36 | case TRUE: 37 | return new PathValueEventFilter(xpath, BooleanValuePredicate.TRUE); 38 | case FALSE: 39 | return new PathValueEventFilter(xpath, BooleanValuePredicate.FALSE); 40 | case NULL: 41 | return new PathValueEventFilter(xpath, NullValuePredicate.INSTANCE); 42 | case XPATH_FUN_NAME: 43 | String aPath = (String)((ValueTreeNode)valueNode).getValue(); 44 | return new PathValueEventFilter(xpath, new XPathValuePredicate(aPath, xpath)); 45 | case TIME_MILLIS_FUN_NAME: 46 | TimeMillisValueTreeNode timeNode = (TimeMillisValueTreeNode)valueNode; 47 | return new PathValueEventFilter(xpath, 48 | new TimeMillisValuePredicate( 49 | timeNode.getValueFormat(), 50 | timeNode.getValue(), 51 | "=")); 52 | case TIME_STRING_FUN_NAME: 53 | TimeStringValueTreeNode timeStringNode = (TimeStringValueTreeNode)valueNode; 54 | return new PathValueEventFilter(xpath, 55 | new TimeStringValuePredicate( 56 | timeStringNode.getValueTimeFormat(), 57 | timeStringNode.getInputTimeFormat(), 58 | timeStringNode.getValue(), 59 | "=")); 60 | default: 61 | throw new UnexpectedTokenException(valueNode, "Number", "String", "TRUE", "FALSE"); 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/EqualsTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import org.antlr.runtime.Token; 5 | import org.antlr.runtime.tree.Tree; 6 | 7 | public class EqualsTreeNode extends EqualityComparisonBaseTreeNode implements PredicateTranslatable { 8 | 9 | @Override 10 | public Predicate translate() { 11 | return getEqualFilter(); 12 | } 13 | 14 | public EqualsTreeNode(Token t) { 15 | super(t); 16 | } 17 | 18 | public EqualsTreeNode(EqualsTreeNode node) { 19 | super(node); 20 | } 21 | 22 | public Tree dupNode() { 23 | return new EqualsTreeNode(this); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/EventFilter.tokens: -------------------------------------------------------------------------------- 1 | T__33=33 2 | T__34=34 3 | T__35=35 4 | AND=4 5 | BETWEEN=5 6 | COMMENT=6 7 | EQUALS=7 8 | ESC_SEQ=8 9 | EXISTS=9 10 | EXPONENT=10 11 | FALSE=11 12 | GE=12 13 | GT=13 14 | HEX_DIGIT=14 15 | IN=15 16 | IS=16 17 | LE=17 18 | LT=18 19 | MATCHES=19 20 | NOT=20 21 | NOT_EQUALS=21 22 | NULL=22 23 | NUMBER=23 24 | OCTAL_ESC=24 25 | OR=25 26 | STRING=26 27 | TIME_MILLIS_FUN_NAME=27 28 | TIME_STRING_FUN_NAME=28 29 | TRUE=29 30 | UNICODE_ESC=30 31 | WS=31 32 | XPATH_FUN_NAME=32 33 | '!='=21 34 | '('=33 35 | ')'=34 36 | ','=35 37 | '<'=18 38 | '<='=17 39 | '='=7 40 | '=~'=19 41 | '>'=13 42 | '>='=12 43 | 'and'=4 44 | 'between'=5 45 | 'exists'=9 46 | 'false'=11 47 | 'in'=15 48 | 'is'=16 49 | 'not'=20 50 | 'null'=22 51 | 'or'=25 52 | 'time-millis'=27 53 | 'time-string'=28 54 | 'true'=29 55 | 'xpath'=32 56 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/EventFilterParsingException.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | public class EventFilterParsingException extends RuntimeException { 4 | public EventFilterParsingException(String msg, Throwable cause){ 5 | super(msg, cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/ExistsTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.PathExistsEventFilter; 5 | 6 | import org.antlr.runtime.Token; 7 | import org.antlr.runtime.tree.Tree; 8 | 9 | import static com.netflix.infix.lang.infix.antlr.TreeNodeUtil.getXPath; 10 | 11 | public class ExistsTreeNode extends PredicateBaseTreeNode implements PredicateTranslatable { 12 | 13 | @Override 14 | public Predicate translate() { 15 | return new PathExistsEventFilter(getXPath(getChild(0))); 16 | } 17 | 18 | public ExistsTreeNode(Token t) { 19 | super(t); 20 | } 21 | 22 | public ExistsTreeNode(ExistsTreeNode node) { 23 | super(node); 24 | } 25 | 26 | public Tree dupNode() { 27 | return new ExistsTreeNode(this); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/FalseValueTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.Predicates; 5 | 6 | import org.antlr.runtime.Token; 7 | import org.antlr.runtime.tree.Tree; 8 | 9 | public class FalseValueTreeNode extends PredicateBaseTreeNode implements ValueTreeNode, PredicateTranslatable { 10 | 11 | @Override 12 | public Object getValue() { 13 | return Boolean.FALSE; 14 | } 15 | 16 | public FalseValueTreeNode(Token t) { 17 | super(t); 18 | } 19 | 20 | public FalseValueTreeNode(FalseValueTreeNode node) { 21 | super(node); 22 | } 23 | 24 | public Tree dupNode() { 25 | return new FalseValueTreeNode(this); 26 | } 27 | 28 | @Override 29 | public Predicate translate() { 30 | return Predicates.alwaysFalse(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/MatchesTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.PathValueEventFilter; 5 | import com.netflix.infix.RegexValuePredicate; 6 | 7 | import org.antlr.runtime.Token; 8 | import org.antlr.runtime.tree.Tree; 9 | 10 | import static com.netflix.infix.lang.infix.antlr.TreeNodeUtil.getXPath; 11 | 12 | public class MatchesTreeNode extends PredicateBaseTreeNode implements PredicateTranslatable { 13 | 14 | @Override 15 | public Predicate translate() { 16 | String xpath = getXPath(getChild(0)); 17 | 18 | StringTreeNode valueNode = (StringTreeNode)getChild(1); 19 | 20 | return new PathValueEventFilter(xpath, new RegexValuePredicate(valueNode.getValue(), RegexValuePredicate.MatchPolicy.FULL)); 21 | 22 | } 23 | 24 | public MatchesTreeNode(Token t) { 25 | super(t); 26 | } 27 | 28 | public MatchesTreeNode(MatchesTreeNode node) { 29 | super(node); 30 | } 31 | 32 | public Tree dupNode() { 33 | return new MatchesTreeNode(this); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/NotEqualsTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.Predicates; 5 | 6 | import org.antlr.runtime.Token; 7 | import org.antlr.runtime.tree.Tree; 8 | 9 | public class NotEqualsTreeNode extends EqualityComparisonBaseTreeNode implements PredicateTranslatable { 10 | 11 | @Override 12 | public Predicate translate() { 13 | return Predicates.not(getEqualFilter()); 14 | } 15 | 16 | public NotEqualsTreeNode(Token t) { 17 | super(t); 18 | } 19 | 20 | public NotEqualsTreeNode(NotEqualsTreeNode node) { 21 | super(node); 22 | } 23 | 24 | public Tree dupNode() { 25 | return new NotEqualsTreeNode(this); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/NotTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.Predicates; 5 | 6 | import org.antlr.runtime.Token; 7 | import org.antlr.runtime.tree.Tree; 8 | 9 | public class NotTreeNode extends PredicateBaseTreeNode implements PredicateTranslatable { 10 | 11 | @Override 12 | public Predicate translate() { 13 | Predicate filter = ((PredicateTranslatable)getChild(0)).translate(); 14 | 15 | return Predicates.not(filter); 16 | } 17 | 18 | public NotTreeNode(Token t) { 19 | super(t); 20 | } 21 | 22 | public NotTreeNode(NotTreeNode node) { 23 | super(node); 24 | } 25 | 26 | public Tree dupNode() { 27 | return new NotTreeNode(this); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/NullTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.NullValuePredicate; 5 | import com.netflix.infix.PathValueEventFilter; 6 | 7 | import org.antlr.runtime.Token; 8 | import org.antlr.runtime.tree.Tree; 9 | 10 | import static com.netflix.infix.lang.infix.antlr.TreeNodeUtil.getXPath; 11 | 12 | public class NullTreeNode extends PredicateBaseTreeNode implements PredicateTranslatable { 13 | 14 | @Override 15 | public Predicate translate() { 16 | String xpath = getXPath(getChild(0)); 17 | 18 | return new PathValueEventFilter(xpath, NullValuePredicate.INSTANCE); 19 | } 20 | 21 | public NullTreeNode(Token t) { 22 | super(t); 23 | } 24 | 25 | public NullTreeNode(NullTreeNode node) { 26 | super(node); 27 | } 28 | 29 | public Tree dupNode() { 30 | return new NullTreeNode(this); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/NullValueTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import org.antlr.runtime.Token; 4 | import org.antlr.runtime.tree.Tree; 5 | 6 | public class NullValueTreeNode extends PredicateBaseTreeNode implements ValueTreeNode { 7 | 8 | @Override 9 | public Object getValue() { 10 | return null; 11 | 12 | } 13 | 14 | public NullValueTreeNode(Token t) { 15 | super(t); 16 | } 17 | 18 | public NullValueTreeNode(NullValueTreeNode node) { 19 | super(node); 20 | } 21 | 22 | public Tree dupNode() { 23 | return new NullValueTreeNode(this); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/NumberTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import org.antlr.runtime.Token; 4 | import org.antlr.runtime.tree.Tree; 5 | 6 | public class NumberTreeNode extends PredicateBaseTreeNode implements ValueTreeNode { 7 | 8 | @Override 9 | public Number getValue() { 10 | return Double.valueOf(getText()); 11 | } 12 | 13 | public NumberTreeNode(Token t) { 14 | super(t); 15 | } 16 | 17 | public NumberTreeNode(NumberTreeNode node) { 18 | super(node); 19 | } 20 | 21 | public Tree dupNode() { 22 | return new NumberTreeNode(this); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/NumericInTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Function; 4 | import com.google.common.base.Predicate; 5 | import com.google.common.collect.Lists; 6 | import com.netflix.infix.Predicates; 7 | import com.netflix.infix.NumericValuePredicate; 8 | import com.netflix.infix.PathValueEventFilter; 9 | 10 | import org.antlr.runtime.Token; 11 | import org.antlr.runtime.tree.Tree; 12 | 13 | import java.util.List; 14 | 15 | import static com.netflix.infix.lang.infix.antlr.TreeNodeUtil.getXPath; 16 | 17 | public class NumericInTreeNode extends PredicateBaseTreeNode implements PredicateTranslatable { 18 | 19 | @SuppressWarnings("unchecked") 20 | @Override 21 | public Predicate translate() { 22 | final String xpath = getXPath(getChild(0)); 23 | 24 | List children = getChildren(); 25 | return Predicates.or( 26 | Lists.transform(children.subList(1, children.size()), new Function>() { 27 | @Override 28 | public Predicate apply(Object node) { 29 | Number value = ((NumberTreeNode) node).getValue(); 30 | return new PathValueEventFilter(xpath, new NumericValuePredicate(value, "=")); 31 | } 32 | }) 33 | ); 34 | } 35 | 36 | public NumericInTreeNode(Token t) { 37 | super(t); 38 | } 39 | 40 | public NumericInTreeNode(NumericInTreeNode node) { 41 | super(node); 42 | } 43 | 44 | public Tree dupNode() { 45 | return new NumericInTreeNode(this); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/OrTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Function; 4 | import com.google.common.base.Predicate; 5 | import com.google.common.collect.Lists; 6 | import com.netflix.infix.Predicates; 7 | 8 | import org.antlr.runtime.Token; 9 | import org.antlr.runtime.tree.CommonTree; 10 | import org.antlr.runtime.tree.Tree; 11 | 12 | public class OrTreeNode extends CommonTree implements PredicateTranslatable { 13 | 14 | @Override 15 | @SuppressWarnings("unchecked") 16 | public Predicate translate() { 17 | return Predicates.or( 18 | Lists.transform(getChildren(), new Function>() { 19 | @Override 20 | public Predicate apply(Object input) { 21 | PredicateTranslatable node = (PredicateTranslatable) input; 22 | return node.translate(); 23 | } 24 | }) 25 | ); 26 | } 27 | 28 | public OrTreeNode(Token t) { 29 | super(t); 30 | } 31 | 32 | public OrTreeNode(OrTreeNode node) { 33 | super(node); 34 | } 35 | 36 | public Tree dupNode() { 37 | return new OrTreeNode(this); 38 | } // for dup'ing type 39 | 40 | public String toString() { 41 | return String.format("%s<%s>", token.getText(), getClass().getSimpleName()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/PredicateBaseTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import org.antlr.runtime.Token; 4 | import org.antlr.runtime.tree.CommonTree; 5 | 6 | public abstract class PredicateBaseTreeNode extends CommonTree { 7 | public PredicateBaseTreeNode(Token t) { 8 | super(t); 9 | } 10 | 11 | public PredicateBaseTreeNode(PredicateBaseTreeNode node) { 12 | super(node); 13 | } 14 | 15 | public String toString() { 16 | return String.format("%s<%s>", getText(), getClass().getSimpleName()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/PredicateTranslatable.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | 5 | public interface PredicateTranslatable { 6 | public Predicate translate(); 7 | } 8 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/StringInTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Function; 4 | import com.google.common.base.Predicate; 5 | import com.google.common.collect.Lists; 6 | import com.netflix.infix.Predicates; 7 | import com.netflix.infix.PathValueEventFilter; 8 | import com.netflix.infix.StringValuePredicate; 9 | 10 | import org.antlr.runtime.Token; 11 | import org.antlr.runtime.tree.Tree; 12 | 13 | import java.util.List; 14 | 15 | import static com.netflix.infix.lang.infix.antlr.TreeNodeUtil.getXPath; 16 | 17 | public class StringInTreeNode extends PredicateBaseTreeNode implements PredicateTranslatable { 18 | 19 | @SuppressWarnings("unchecked") 20 | @Override 21 | public Predicate translate() { 22 | final String xpath = getXPath(getChild(0)); 23 | 24 | List children = getChildren(); 25 | return Predicates.or( 26 | Lists.transform(children.subList(1, children.size()), new Function>() { 27 | @Override 28 | public Predicate apply(Object node) { 29 | String value = ((StringTreeNode) node).getValue(); 30 | return new PathValueEventFilter(xpath, new StringValuePredicate(value)); 31 | } 32 | }) 33 | ); 34 | } 35 | 36 | public StringInTreeNode(Token t) { 37 | super(t); 38 | } 39 | 40 | public StringInTreeNode(StringInTreeNode node) { 41 | super(node); 42 | } 43 | 44 | public Tree dupNode() { 45 | return new StringInTreeNode(this); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/StringTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import org.antlr.runtime.Token; 4 | import org.antlr.runtime.tree.Tree; 5 | 6 | public class StringTreeNode extends PredicateBaseTreeNode implements ValueTreeNode { 7 | 8 | @Override 9 | public String getValue() { 10 | return getText(); 11 | } 12 | 13 | public StringTreeNode(Token t) { 14 | super(t); 15 | } 16 | 17 | public StringTreeNode(StringTreeNode node) { 18 | super(node); 19 | } 20 | 21 | public Tree dupNode() { 22 | return new StringTreeNode(this); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/TimeMillisValueTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import org.antlr.runtime.Token; 4 | import org.antlr.runtime.tree.Tree; 5 | 6 | public class TimeMillisValueTreeNode extends PredicateBaseTreeNode implements ValueTreeNode { 7 | 8 | @Override 9 | public String getValue() { 10 | return (String)((ValueTreeNode)getChild(1)).getValue(); 11 | } 12 | 13 | public String getValueFormat() { 14 | return (String)((ValueTreeNode)getChild(0)).getValue(); 15 | } 16 | 17 | public TimeMillisValueTreeNode(Token t) { 18 | super(t); 19 | } 20 | 21 | public TimeMillisValueTreeNode(TimeMillisValueTreeNode node) { 22 | super(node); 23 | } 24 | 25 | public Tree dupNode() { 26 | return new TimeMillisValueTreeNode(this); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/TimeStringValueTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import org.antlr.runtime.Token; 4 | import org.antlr.runtime.tree.Tree; 5 | 6 | public class TimeStringValueTreeNode extends PredicateBaseTreeNode implements ValueTreeNode { 7 | 8 | @Override 9 | public String getValue() { 10 | return (String)((ValueTreeNode)getChild(2)).getValue(); 11 | } 12 | 13 | public String getValueTimeFormat() { 14 | return (String)((ValueTreeNode)getChild(1)).getValue(); 15 | } 16 | 17 | public String getInputTimeFormat() { 18 | return (String)((ValueTreeNode)getChild(0)).getValue(); 19 | } 20 | 21 | public TimeStringValueTreeNode(Token t) { 22 | super(t); 23 | } 24 | 25 | public TimeStringValueTreeNode(TimeStringValueTreeNode node) { 26 | super(node); 27 | } 28 | 29 | public Tree dupNode() { 30 | return new TimeStringValueTreeNode(this); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/TreeNodeUtil.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import org.antlr.runtime.tree.Tree; 4 | 5 | class TreeNodeUtil { 6 | private TreeNodeUtil(){} 7 | 8 | public static String getXPath(Tree pathNode) { 9 | ValueTreeNode xpathNode = (ValueTreeNode)pathNode; 10 | 11 | return (String)xpathNode.getValue(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/TrueValueTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.netflix.infix.Predicates; 5 | 6 | import org.antlr.runtime.Token; 7 | import org.antlr.runtime.tree.Tree; 8 | 9 | public class TrueValueTreeNode extends PredicateBaseTreeNode implements PredicateTranslatable { 10 | 11 | public TrueValueTreeNode(Token t) { 12 | super(t); 13 | } 14 | 15 | public TrueValueTreeNode(TrueValueTreeNode node) { 16 | super(node); 17 | } 18 | 19 | public Tree dupNode() { 20 | return new TrueValueTreeNode(this); 21 | } 22 | 23 | @Override 24 | public Predicate translate() { 25 | return Predicates.alwaysTrue(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/UnexpectedTokenException.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import com.google.common.base.Joiner; 4 | import org.antlr.runtime.tree.Tree; 5 | 6 | public class UnexpectedTokenException extends RuntimeException { 7 | private Tree unexpected; 8 | private String[] expected; 9 | private Joiner joiner = Joiner.on(" or "); 10 | public UnexpectedTokenException(Tree unexpected, String... expected){ 11 | this.unexpected = unexpected; 12 | this.expected = expected; 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | return String.format( 18 | "Unexpected token %s at %d:%d. Expected: %s", 19 | unexpected.getText(), 20 | unexpected.getLine(), 21 | unexpected.getCharPositionInLine(), 22 | joiner.join(expected)); 23 | } 24 | 25 | @Override 26 | public String getMessage(){ 27 | return toString(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/ValueTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | public interface ValueTreeNode { 4 | public Object getValue(); 5 | } 6 | -------------------------------------------------------------------------------- /netflix-infix/src/main/java/com/netflix/infix/lang/infix/antlr/XPathTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.lang.infix.antlr; 2 | 3 | import org.antlr.runtime.Token; 4 | import org.antlr.runtime.tree.Tree; 5 | 6 | public class XPathTreeNode extends PredicateBaseTreeNode implements ValueTreeNode { 7 | 8 | @Override 9 | public Object getValue() { 10 | return getChild(0).getText(); 11 | } 12 | 13 | public XPathTreeNode(Token t) { 14 | super(t); 15 | } 16 | 17 | public XPathTreeNode(XPathTreeNode node) { 18 | super(node); 19 | } 20 | 21 | public Tree dupNode() { 22 | return new XPathTreeNode(this); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/AlwaysFalseEventFilterTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import static com.netflix.infix.VerificationUtil.DUMMY_INPUT; 4 | import static org.junit.Assert.assertFalse; 5 | 6 | import org.junit.After; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | public class AlwaysFalseEventFilterTest { 11 | 12 | @Before 13 | public void setUp() throws Exception { 14 | } 15 | 16 | @After 17 | public void tearDown() throws Exception { 18 | } 19 | 20 | @Test 21 | public void testAlwaysFalse() { 22 | assertFalse(AlwaysFalsePredicate.INSTANCE.apply(DUMMY_INPUT)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/AlwaysTrueEventFilterTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import static com.netflix.infix.VerificationUtil.DUMMY_INPUT; 8 | import static org.junit.Assert.assertTrue; 9 | 10 | public class AlwaysTrueEventFilterTest { 11 | 12 | @Before 13 | public void setUp() throws Exception { 14 | } 15 | 16 | @After 17 | public void tearDown() throws Exception { 18 | } 19 | 20 | @Test 21 | public void test() { 22 | assertTrue(AlwaysTruePredicate.INSTANCE.apply(DUMMY_INPUT)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/AndEventFilterTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.mockito.Mockito.never; 6 | import static org.mockito.Mockito.verify; 7 | 8 | import java.util.List; 9 | 10 | import org.junit.After; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | import com.google.common.base.Predicate; 15 | import com.google.common.collect.ImmutableList; 16 | 17 | public class AndEventFilterTest { 18 | @Before 19 | public void setUp() throws Exception { 20 | } 21 | 22 | @After 23 | public void tearDown() throws Exception { 24 | } 25 | 26 | @Test 27 | public void testAllAcceptancesLeadToAcceptance() { 28 | List> filters = ImmutableList.of( 29 | VerificationUtil.getTrueFilter(), 30 | VerificationUtil.getTrueFilter(), 31 | VerificationUtil.getTrueFilter()); 32 | 33 | AndPredicate filter = new AndPredicate(filters); 34 | assertTrue(filter.apply(VerificationUtil.DUMMY_INPUT)); 35 | } 36 | 37 | @Test 38 | public void testOneRejectionLeadsToRejection() { 39 | List> filters = ImmutableList.of( 40 | VerificationUtil.getTrueFilter(), 41 | VerificationUtil.getTrueFilter(), 42 | VerificationUtil.getFalseFilter() 43 | ); 44 | 45 | AndPredicate filter = new AndPredicate(filters); 46 | assertFalse(filter.apply(VerificationUtil.DUMMY_INPUT)); 47 | } 48 | 49 | @Test 50 | public void testAndEventFilterShortcuts() { 51 | Predicate falseFilter = VerificationUtil.getFalseFilter(); 52 | 53 | Predicate trueFilter = VerificationUtil.getTrueFilter(); 54 | 55 | 56 | List> filters = ImmutableList.of( 57 | falseFilter, trueFilter 58 | ); 59 | 60 | assertFalse(new AndPredicate(filters).apply(VerificationUtil.DUMMY_INPUT)); 61 | verify(trueFilter, never()).apply(VerificationUtil.DUMMY_INPUT); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/DummyAnnotatable.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | public interface DummyAnnotatable { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/EventFiltersTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import static com.netflix.infix.Predicates.alwaysFalse; 4 | import static com.netflix.infix.Predicates.alwaysTrue; 5 | import static com.netflix.infix.Predicates.and; 6 | import static com.netflix.infix.Predicates.not; 7 | import static com.netflix.infix.Predicates.or; 8 | import static com.netflix.infix.VerificationUtil.DUMMY_INPUT; 9 | import static com.netflix.infix.VerificationUtil.getFalseFilter; 10 | import static com.netflix.infix.VerificationUtil.getTrueFilter; 11 | import static org.junit.Assert.assertFalse; 12 | import static org.junit.Assert.assertTrue; 13 | 14 | import java.io.IOException; 15 | 16 | import org.junit.Test; 17 | 18 | import com.google.common.base.Predicate; 19 | import com.netflix.infix.RegexValuePredicate.MatchPolicy; 20 | 21 | public class EventFiltersTest { 22 | @Test 23 | public void testAlwaysFalseReturnsFalse() { 24 | assertFalse(alwaysFalse().apply(DUMMY_INPUT)); 25 | } 26 | 27 | @Test 28 | public void testAlwaysTrueReturnsTrue() { 29 | assertTrue(alwaysTrue().apply(DUMMY_INPUT)); 30 | } 31 | 32 | @Test 33 | public void testNotAlwaysNegates(){ 34 | assertTrue(not(getFalseFilter()).apply(DUMMY_INPUT)); 35 | 36 | assertFalse(not(getTrueFilter()).apply(DUMMY_INPUT)); 37 | } 38 | 39 | @Test 40 | public void testOr() { 41 | assertTrue(or(getFalseFilter(), getFalseFilter(), getTrueFilter()).apply(DUMMY_INPUT)); 42 | 43 | assertFalse(or(getFalseFilter(), getFalseFilter()).apply(DUMMY_INPUT)); 44 | } 45 | 46 | @Test 47 | public void testAnd(){ 48 | assertTrue(and(getTrueFilter(), getTrueFilter()).apply(DUMMY_INPUT)); 49 | 50 | assertFalse(and(getTrueFilter(), getFalseFilter()).apply(DUMMY_INPUT)); 51 | } 52 | 53 | @Test 54 | public void showQuery() throws Exception { 55 | Predicate filter = Predicates.or( 56 | Predicates.and( 57 | new PathValueEventFilter("//path/to/property", new StringValuePredicate("foo")), 58 | new PathValueEventFilter("//path/to/property", new NumericValuePredicate(123, ">")), 59 | new PathValueEventFilter("//path/to/property", new XPathValuePredicate("//path/to/property", "//another/path")) 60 | ), 61 | Predicates.not( 62 | new PathValueEventFilter("//path/to/time", new TimeMillisValuePredicate("yyyy-MM-dd", "1997-08-29", "!=")) 63 | ), 64 | new PathValueEventFilter("//path/to/stringProp", new RegexValuePredicate(".*", MatchPolicy.PARTIAL)), 65 | new PathValueEventFilter("//path/to/stringProp", new RegexValuePredicate(".*", MatchPolicy.FULL)) 66 | ); 67 | 68 | print(filter); 69 | } 70 | 71 | private void print(Predicate filter) throws IOException { 72 | System.out.println(filter.toString()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/NotEventFilterTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import static com.netflix.infix.VerificationUtil.DUMMY_INPUT; 8 | import static com.netflix.infix.VerificationUtil.getFalseFilter; 9 | import static com.netflix.infix.VerificationUtil.getTrueFilter; 10 | import static org.junit.Assert.assertFalse; 11 | import static org.junit.Assert.assertTrue; 12 | 13 | public class NotEventFilterTest { 14 | @Before 15 | public void setUp() throws Exception { 16 | } 17 | 18 | @After 19 | public void tearDown() throws Exception { 20 | } 21 | 22 | @Test 23 | public void testNotTrueIsFalse() { 24 | assertFalse(new NotPredicate(getTrueFilter()).apply(DUMMY_INPUT)); 25 | } 26 | 27 | @Test 28 | public void testNotFalseIsTrue() { 29 | assertTrue(new NotPredicate(getFalseFilter()).apply(DUMMY_INPUT)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/NullValuePredicateTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.assertFalse; 8 | import static org.junit.Assert.assertTrue; 9 | 10 | public class NullValuePredicateTest { 11 | 12 | @Before 13 | public void setUp() throws Exception { 14 | } 15 | 16 | @After 17 | public void tearDown() throws Exception { 18 | } 19 | 20 | @Test 21 | public void testNullIsAccepted() { 22 | assertTrue(NullValuePredicate.INSTANCE.apply(null)); 23 | } 24 | 25 | @Test 26 | public void testNonNullIsRejected() { 27 | assertFalse(NullValuePredicate.INSTANCE.apply(new Object())); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/NumericValuePredicateTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | 9 | public class NumericValuePredicateTest { 10 | 11 | @Before 12 | public void setUp() throws Exception { 13 | } 14 | 15 | @After 16 | public void tearDown() throws Exception { 17 | } 18 | 19 | @Test 20 | public void testEachOperatorWorks() throws Exception { 21 | Object[][] inputs = { 22 | {1, "=", 1L, true}, 23 | {.3, "=", .3, true}, 24 | {3, ">", 2.1, true}, 25 | {2, ">=", 2, true}, 26 | {3, ">=", 2.2f, true}, 27 | {4, "<", 5, true}, 28 | {4, "<=", 4, true}, 29 | {4, "<=", 5, true}, 30 | 31 | // Negative cases 32 | {4, "=", 3L, false}, 33 | {3.3, "=", 3.4f, false}, 34 | {3, ">", 4, false}, 35 | {3, ">=", 4, false}, 36 | {4, "<", 3, false}, 37 | {4.2, "<", 3.5f, false}, 38 | {4L, "<=", 3.2, false}, 39 | }; 40 | 41 | for(Object[] input : inputs){ 42 | Number inputValue = (Number)input[0]; 43 | String fn = (String)input[1]; 44 | Number value = (Number)input[2]; 45 | boolean expected = (Boolean) input[3]; 46 | 47 | verifyValuesOfDifferentFormatsCanBeCompared(inputValue, fn, value, expected); 48 | } 49 | } 50 | 51 | @Test(expected=IllegalArgumentException.class) 52 | public void testInvalidFunctionNameShouldBeRejected() { 53 | 54 | new NumericValuePredicate(4, "~~"); 55 | } 56 | 57 | public void verifyValuesOfDifferentFormatsCanBeCompared ( 58 | Number input, 59 | String fnName, 60 | Number value, 61 | boolean expectedValue) throws Exception { 62 | 63 | NumericValuePredicate pred = new NumericValuePredicate(value, fnName); 64 | boolean result = pred.apply(input); 65 | 66 | assertEquals( 67 | String.format( 68 | "Expected: %s %s %s", 69 | input, fnName, value), 70 | expectedValue, 71 | result); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/OrEventFilterTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.google.common.collect.ImmutableList; 5 | import org.junit.After; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import java.util.List; 10 | 11 | import static com.netflix.infix.VerificationUtil.DUMMY_INPUT; 12 | import static com.netflix.infix.VerificationUtil.getFalseFilter; 13 | import static com.netflix.infix.VerificationUtil.getTrueFilter; 14 | import static org.junit.Assert.assertFalse; 15 | import static org.junit.Assert.assertTrue; 16 | import static org.mockito.Mockito.never; 17 | import static org.mockito.Mockito.verify; 18 | 19 | public class OrEventFilterTest { 20 | @Before 21 | public void setUp() throws Exception { 22 | } 23 | 24 | @After 25 | public void tearDown() throws Exception { 26 | } 27 | 28 | @Test 29 | public void testAllRejectionsLeadToRejection() { 30 | List> filters = ImmutableList.of(getFalseFilter(), getFalseFilter(), getFalseFilter()); 31 | 32 | OrPredicate filter = new OrPredicate(filters); 33 | assertFalse(filter.apply(DUMMY_INPUT)); 34 | } 35 | 36 | @Test 37 | public void testOneAcceptanceLeadsToAcceptance() { 38 | List> filters = ImmutableList.of(getFalseFilter(), getTrueFilter(), getFalseFilter()); 39 | 40 | OrPredicate filter = new OrPredicate(filters); 41 | assertTrue(filter.apply(DUMMY_INPUT)); 42 | } 43 | 44 | @Test 45 | public void testOrEventFilterShortcuts() { 46 | Predicate falseFilter = getFalseFilter(); 47 | 48 | Predicate trueFilter = getTrueFilter(); 49 | 50 | List> filters = ImmutableList.of(trueFilter, falseFilter); 51 | 52 | assertTrue(new OrPredicate(filters).apply(DUMMY_INPUT)); 53 | verify(falseFilter, never()).apply(DUMMY_INPUT); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/PathValueEventFilterTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.mockito.Mockito.when; 6 | 7 | import java.util.Map; 8 | 9 | import org.junit.After; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | import org.mockito.Mockito; 13 | 14 | import com.google.common.base.Predicate; 15 | import com.google.common.collect.Maps; 16 | 17 | public class PathValueEventFilterTest { 18 | 19 | @Before 20 | public void setUp() throws Exception { 21 | } 22 | 23 | @After 24 | public void tearDown() throws Exception { 25 | } 26 | 27 | @Test 28 | public void testPositiveSelection() { 29 | MockRequestTrace requestTrace = Mockito.mock(MockRequestTrace.class); 30 | 31 | when(requestTrace.getClientInfoHostName()).thenReturn("localhost"); 32 | 33 | Map clientInfoMap = Maps.newHashMap(); 34 | clientInfoMap.put("clientName", "client"); 35 | clientInfoMap.put("clientId", (long) 10); 36 | when(requestTrace.getClientInfoMap()).thenReturn(clientInfoMap); 37 | 38 | // Test numerical values can be filtered 39 | Predicate filter = new PathValueEventFilter("//clientInfoMap/clientId", new NumericValuePredicate(5, ">")); 40 | assertTrue("Filter should return true as the client ID is 10, greater than 5", filter.apply(requestTrace)); 41 | 42 | // Test string value can be filtered 43 | filter = new PathValueEventFilter("//clientInfoMap/clientName", new StringValuePredicate("client")); 44 | assertTrue("Filter should return true as client name is 'client'", filter.apply(requestTrace)); 45 | 46 | // Test attribute is supported 47 | filter = new PathValueEventFilter("//@clientInfoHostName", new StringValuePredicate("localhost")); 48 | assertTrue("Filter should return tre as clientInfoHostName is localhost", filter.apply(requestTrace)); 49 | } 50 | 51 | @Test 52 | public void testNonExistentValueShouldBeFiltered(){ 53 | MockRequestTrace requestTrace = Mockito.mock(MockRequestTrace.class); 54 | 55 | Predicate filter = new PathValueEventFilter("//whatever", new StringValuePredicate("whatever")); 56 | assertFalse(filter.apply(requestTrace)); 57 | } 58 | 59 | @Test 60 | public void testNullValueCanBeAccepted(){ 61 | MockRequestTrace requestTrace = Mockito.mock(MockRequestTrace.class); 62 | 63 | Predicate filter = new PathValueEventFilter("//whatever", NullValuePredicate.INSTANCE); 64 | assertTrue(filter.apply(requestTrace)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/RegexValuePredicateTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import com.netflix.infix.RegexValuePredicate.MatchPolicy; 4 | 5 | import org.junit.After; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | 11 | public class RegexValuePredicateTest { 12 | 13 | @Before 14 | public void setUp() throws Exception { 15 | } 16 | 17 | @After 18 | public void tearDown() throws Exception { 19 | } 20 | 21 | @Test 22 | public void testNormalMatch() throws Exception { 23 | Object[][] inputs = { 24 | {"a123cd", "a[0-9]+cd", MatchPolicy.FULL, true}, 25 | {"", "", MatchPolicy.FULL, true}, 26 | {"", "^$", MatchPolicy.FULL, true}, 27 | {"32abde23", "[a-z]+", MatchPolicy.FULL, false}, 28 | {"32abde23", "[a-z]+", MatchPolicy.PARTIAL, true}, 29 | {null, ".*", MatchPolicy.PARTIAL, false}, 30 | {null, ".*", MatchPolicy.FULL, false}, 31 | {null, "", MatchPolicy.PARTIAL, false} 32 | }; 33 | 34 | for(Object[] input : inputs){ 35 | String value = (String)input[0]; 36 | String regex = (String)input[1]; 37 | MatchPolicy policy = (MatchPolicy)input[2]; 38 | boolean expected = (Boolean) input[3]; 39 | 40 | RegexValuePredicate pred = new RegexValuePredicate(regex, policy); 41 | 42 | assertEquals( 43 | String.format("Given regex = %s, isPartial = %s, and input = %s", regex, policy, value), 44 | expected, 45 | pred.apply(value)); 46 | } 47 | 48 | } 49 | 50 | @Test(expected = IllegalArgumentException.class) 51 | public void testNullPatternIsNotAllowed(){ 52 | new RegexValuePredicate(null, MatchPolicy.FULL); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/StringValuePredicateTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | 9 | public class StringValuePredicateTest { 10 | 11 | @Before 12 | public void setUp() throws Exception { 13 | } 14 | 15 | @After 16 | public void tearDown() throws Exception { 17 | } 18 | 19 | @Test 20 | public void testValueComparison() throws Exception { 21 | Object[][] inputs = { 22 | {"abc", "abc", true}, 23 | {"", "", true}, 24 | {"AB", "A", false}, 25 | {null, null, true}, 26 | {null, "", false}, 27 | {"", null, false} 28 | }; 29 | 30 | for(Object[] input : inputs){ 31 | String value = (String)input[0]; 32 | String inputValue = (String)input[1]; 33 | boolean expected = ((Boolean)input[2]).booleanValue(); 34 | 35 | StringValuePredicate pred = new StringValuePredicate(value); 36 | 37 | assertEquals(String.format("Given value = %s, and input = %s", value, inputValue), expected, pred.apply(inputValue)); 38 | } 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/TimeMillisValuePredicateTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import org.joda.time.format.DateTimeFormat; 4 | import org.junit.After; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.Date; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class TimeMillisValuePredicateTest { 13 | 14 | @Before 15 | public void setUp() throws Exception { 16 | } 17 | 18 | @After 19 | public void tearDown() throws Exception { 20 | } 21 | 22 | @Test 23 | public void testEachOperatorWorks() throws Exception { 24 | String valueFormat = "YYYY-MM-dd HH:mm:ss:SSS"; 25 | long now = new Date().getTime(); 26 | 27 | Object[][] inputs = { 28 | { now, "=", now, true}, 29 | {now+1, ">", now, true}, 30 | { now, ">=", now, true}, 31 | {now+2, ">=", now, true}, 32 | {now-1, "<", now, true}, 33 | { now, "<=", now, true}, 34 | {now-1, "<=", now, true}, 35 | 36 | // Negative cases 37 | {now+1, "=", now, false}, 38 | {now-1, "=", now, false}, 39 | {now-1, ">", now, false}, 40 | {now-1, ">=", now, false}, 41 | { now, "<", now, false}, 42 | {now+1, "<", now, false}, 43 | {now+1, "<=", now, false}, 44 | }; 45 | 46 | for(Object[] input : inputs){ 47 | long inputValue = (Long) input[0]; 48 | String fn = (String)input[1]; 49 | long value = (Long) input[2]; 50 | boolean expected = (Boolean) input[3]; 51 | 52 | verifyValuesOfDifferentFormatsCanBeCompared(valueFormat, value, inputValue, fn, expected); 53 | } 54 | } 55 | 56 | @Test(expected=IllegalArgumentException.class) 57 | public void testInvalidFunctionNameShouldBeRejected() { 58 | String valueFormat = "YYYY-MM-dd HH:mm:ss:SSS"; 59 | long now = new Date().getTime(); 60 | 61 | new TimeMillisValuePredicate(valueFormat, toString(now, valueFormat), "~~"); 62 | } 63 | 64 | public void verifyValuesOfDifferentFormatsCanBeCompared ( 65 | String valueFormat, 66 | long value, 67 | long input, 68 | String fnName, 69 | boolean expectedValue) throws Exception { 70 | 71 | String stringValue = toString(value, valueFormat); 72 | 73 | TimeMillisValuePredicate pred = new TimeMillisValuePredicate(valueFormat, stringValue, fnName); 74 | 75 | boolean result = pred.apply(input); 76 | 77 | assertEquals( 78 | String.format( 79 | "Expected: %s %s %s where value format = %s. Expected string value: %s %s %s ", 80 | input, fnName, value, valueFormat, input, fnName, stringValue), 81 | expectedValue, 82 | result); 83 | } 84 | 85 | private String toString(long millis, String format){ 86 | return DateTimeFormat.forPattern(format).print(millis); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/TimeRangeValuePredicateTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.util.Date; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | 11 | public class TimeRangeValuePredicateTest { 12 | private static final String DEFAULT_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss:SSS"; 13 | 14 | @Before 15 | public void setUp() throws Exception { 16 | } 17 | 18 | @After 19 | public void tearDown() throws Exception { 20 | } 21 | 22 | @Test(expected=IllegalArgumentException.class) 23 | public void testNegativeRangeShouldBeRejected() { 24 | verifyTimeRangeIsCalculatedCorrectly(DEFAULT_TIME_FORMAT, 10, 9, 20, false); 25 | } 26 | 27 | @Test 28 | public void testRangeChecks(){ 29 | long time = new Date().getTime(); 30 | // [start, end, input, expected] 31 | Object[][] inputs = { 32 | {time, time, time, false}, 33 | {time, time, time + 1, false}, 34 | {time, time, time - 1, false}, 35 | 36 | {time, time + 10, time, true}, 37 | {time, time + 10, time + 1, true}, 38 | {time, time + 10, time + 3, true}, 39 | {time, time + 10, time + 9, true}, 40 | {time, time + 10, time + 10, false}, 41 | {time, time + 10, time + 12, false}, 42 | {time, time + 10, time - 1, false}, 43 | {time, time + 10, time - 10, false} 44 | }; 45 | 46 | for(Object[] input: inputs){ 47 | long start = (Long) input[0]; 48 | long end = (Long) input[1]; 49 | long inputValue = (Long) input[2]; 50 | boolean expected = (Boolean) input[3]; 51 | 52 | verifyTimeRangeIsCalculatedCorrectly(DEFAULT_TIME_FORMAT, start, end, inputValue, expected); 53 | } 54 | 55 | } 56 | 57 | public void verifyTimeRangeIsCalculatedCorrectly(String timeFormat, long startInstant, long endInstant, long inputValue, boolean expected) { 58 | String start = TimeUtil.toString(startInstant, timeFormat); 59 | String end = TimeUtil.toString(endInstant, timeFormat); 60 | String input = TimeUtil.toString(inputValue, timeFormat); 61 | 62 | TimeRangeValuePredicate pred = new TimeRangeValuePredicate(DEFAULT_TIME_FORMAT, start, end); 63 | boolean result = pred.apply(inputValue); 64 | 65 | String originalValue = String.format("{input = %s, start = %s, end = %s}", inputValue, startInstant, endInstant); 66 | if(result){ 67 | assertEquals(String.format("The input value %s is in the given range [%s, %s). Original Values: %s", input, start, end, originalValue), expected, result); 68 | }else{ 69 | assertEquals(String.format("The input value %s is not in the given range [%s, %s). Original Value: %s", input, start, end, originalValue), expected, result); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/TimeStringValuePredicateTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import org.joda.time.format.DateTimeFormat; 4 | import org.junit.After; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.Date; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class TimeStringValuePredicateTest { 13 | 14 | @Before 15 | public void setUp() throws Exception { 16 | } 17 | 18 | @After 19 | public void tearDown() throws Exception { 20 | } 21 | 22 | @Test 23 | public void testEachOperatorWorks() throws Exception { 24 | String valueFormat = "YYYY-MM-dd HH:mm:ss:SSS"; 25 | long now = new Date().getTime(); 26 | 27 | String inputFormat = "yyyyMMdd'T'HHmmss.SSSZ"; 28 | 29 | Object[][] inputs = { 30 | { now, "=", now, true}, 31 | {now+1, ">", now, true}, 32 | { now, ">=", now, true}, 33 | {now+2, ">=", now, true}, 34 | {now-1, "<", now, true}, 35 | { now, "<=", now, true}, 36 | {now-1, "<=", now, true}, 37 | 38 | // Negative cases 39 | {now+1, "=", now, false}, 40 | {now-1, "=", now, false}, 41 | {now-1, ">", now, false}, 42 | {now-1, ">=", now, false}, 43 | { now, "<", now, false}, 44 | {now+1, "<", now, false}, 45 | {now+1, "<=", now, false}, 46 | }; 47 | 48 | for(Object[] input : inputs){ 49 | long inputValue = (Long) input[0]; 50 | String fn = (String)input[1]; 51 | long value = (Long) input[2]; 52 | boolean expected = (Boolean) input[3]; 53 | 54 | verifyValuesOfDifferentFormatsCanBeCompared(valueFormat, value, inputFormat, inputValue, fn, expected); 55 | } 56 | } 57 | 58 | @Test(expected=IllegalArgumentException.class) 59 | public void testInvalidFunctionNameShouldBeRejected() { 60 | String valueFormat = "YYYY-MM-dd HH:mm:ss:SSS"; 61 | long now = new Date().getTime(); 62 | 63 | String inputFormat = "yyyyMMdd'T'HHmmss.SSSZ"; 64 | 65 | new TimeStringValuePredicate(valueFormat, inputFormat, toString(now, valueFormat), "~~"); 66 | } 67 | 68 | public void verifyValuesOfDifferentFormatsCanBeCompared ( 69 | String valueFormat, 70 | long value, 71 | String inputFormat, 72 | long input, 73 | String fnName, 74 | boolean expectedValue) throws Exception { 75 | 76 | String stringValue = toString(value, valueFormat); 77 | String stringInput = toString(input, inputFormat); 78 | 79 | TimeStringValuePredicate pred = new TimeStringValuePredicate(valueFormat, inputFormat, stringValue, fnName); 80 | 81 | boolean result = pred.apply(stringInput); 82 | 83 | assertEquals( 84 | String.format( 85 | "Expected: %s %s %s where input format = %s, and value format = %s. Expected string value: %s %s %s ", 86 | input, fnName, value, inputFormat, valueFormat, stringInput, fnName, stringValue), 87 | expectedValue, 88 | result); 89 | } 90 | 91 | private String toString(long millis, String format){ 92 | return DateTimeFormat.forPattern(format).print(millis); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/VerificationUtil.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix; 2 | 3 | import org.mockito.Mockito; 4 | 5 | import com.google.common.base.Predicate; 6 | 7 | public class VerificationUtil { 8 | public static final DummyAnnotatable DUMMY_INPUT = Mockito.mock(DummyAnnotatable.class); 9 | 10 | private VerificationUtil(){} 11 | 12 | /** 13 | * Creating mocked filter instead of using AwaysTrueFilter so this test case 14 | * is independent of other test target. 15 | */ 16 | public static Predicate getTrueFilter() { 17 | Predicate trueFilter = Mockito.mock(Predicate.class); 18 | Mockito.when(trueFilter.apply(VerificationUtil.DUMMY_INPUT)).thenReturn(true); 19 | return trueFilter; 20 | } 21 | 22 | public static Predicate getFalseFilter() { 23 | Predicate falseFilter = Mockito.mock(Predicate.class); 24 | Mockito.when(falseFilter.apply(VerificationUtil.DUMMY_INPUT)).thenReturn(false); 25 | return falseFilter; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /netflix-infix/src/test/java/com/netflix/infix/parser/FilterPredicate.java: -------------------------------------------------------------------------------- 1 | package com.netflix.infix.parser; 2 | 3 | import com.google.common.base.Joiner; 4 | 5 | 6 | public enum FilterPredicate { 7 | stringComp("xpath(\"%s\") %s \"%s\""), 8 | numberComp("xpath(\"%s\") %s %s"), 9 | between("xpath(\"%s\") between (%s, %s)") { 10 | public String create(String path, String operator, Object value) { 11 | Object[] values = (Object[])value; 12 | return String.format(getTemplate(), path, values[0], values[1]); 13 | } 14 | }, 15 | isNull("xpath(\"%s\") is null") { 16 | public String create(String path, String operator, Object value) { 17 | return String.format(getTemplate(), path); 18 | } 19 | }, 20 | regex("xpath(\"%s\") =~ \"%s\"") { 21 | public String create(String path, String operator, Object value) { 22 | return String.format(getTemplate(), path, value); 23 | } 24 | }, 25 | existsRight("xpath(\"%s\") exists") { 26 | public String create(String path, String operator, Object value) { 27 | return String.format(getTemplate(), path); 28 | } 29 | }, 30 | existsLeft("exists xpath(\"%s\")") { 31 | public String create(String path, String operator, Object value) { 32 | return String.format(getTemplate(), path); 33 | } 34 | }, 35 | trueValue("true") { 36 | public String create(String path, String operator, Object value) { 37 | return getTemplate(); 38 | } 39 | }, 40 | falseValue("false") { 41 | public String create(String path, String operator, Object value) { 42 | return getTemplate(); 43 | } 44 | }, 45 | timeComp("xpath(\"%s\") %s %s"), 46 | 47 | inPred("xpath(\"%s\") in (%s)") { 48 | public String create(String path, String operator, Object value) { 49 | Object[] values = (Object[])value; 50 | 51 | return String.format(getTemplate(), path, Joiner.on(',').join(values)); 52 | } 53 | } 54 | ; 55 | 56 | final private String template; 57 | private FilterPredicate(String template) { 58 | this.template = template; 59 | } 60 | 61 | public String create(String path, String operator, Object value) { 62 | return String.format(template, path, operator, value); 63 | } 64 | 65 | public String getTemplate() { 66 | return template; 67 | } 68 | public String create(String path) { 69 | return create(path, null); 70 | } 71 | 72 | public String create(String path, Object value) { 73 | return create(path, null, value); 74 | } 75 | 76 | public String create(String path, String operator){ 77 | return create(path, operator, null); 78 | } 79 | 80 | public String create() { 81 | return create(null, null); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /netflix-jersey-guice/src/main/java/com/netflix/jersey/guice/providers/exception/CustomThrowableExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package com.netflix.jersey.guice.providers.exception; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.ws.rs.ext.ExceptionMapper; 5 | 6 | /** 7 | * Interface for implementing a custom mapper. 8 | * 9 | * @see ThrowableExceptionMapper 10 | * @author elandau 11 | * 12 | */ 13 | public interface CustomThrowableExceptionMapper extends ExceptionMapper { 14 | /** 15 | * @param exception The exception 16 | * @param request Request context that can be used to determine the context and response format 17 | * @return Return true if this mapper should be used to map the exception 18 | */ 19 | public boolean canMap(Throwable exception, HttpServletRequest request); 20 | } 21 | -------------------------------------------------------------------------------- /netflix-jersey-guice/src/main/java/com/netflix/jersey/guice/providers/exception/DefaultThrowableExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package com.netflix.jersey.guice.providers.exception; 2 | 3 | import javax.ws.rs.ext.ExceptionMapper; 4 | 5 | import com.google.inject.ImplementedBy; 6 | 7 | @ImplementedBy(GsonDefaultExceptionMapper.class) 8 | public interface DefaultThrowableExceptionMapper extends ExceptionMapper { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /netflix-jersey-guice/src/main/java/com/netflix/jersey/guice/providers/exception/GsonDefaultExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package com.netflix.jersey.guice.providers.exception; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import javax.ws.rs.core.Response; 7 | 8 | import com.google.gson.Gson; 9 | 10 | /** 11 | * Default mapper returns error 500 and a JSON response containing, 12 | * { 13 | * code : 500, 14 | * class : ... 15 | * stack : ... 16 | * } 17 | * 18 | * An alternative default mapper can be used by binding the implementation 19 | * to DefaultThrowableExceptionMapper.class in a guice module. 20 | * 21 | * @author elandau 22 | * 23 | */ 24 | public class GsonDefaultExceptionMapper implements DefaultThrowableExceptionMapper { 25 | 26 | @Override 27 | public Response toResponse(Throwable exception) { 28 | Map info = new HashMap(); 29 | if (exception.getCause() != null) 30 | info.put("class", exception.getCause().getClass().getCanonicalName()); 31 | else 32 | info.put("class", exception.getClass().getCanonicalName()); 33 | info.put("code", 500); 34 | info.put("stack", exception.getStackTrace()); 35 | 36 | return Response.status(500).entity(new Gson().toJson(info)).build(); 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /netflix-jersey-guice/src/main/java/com/netflix/jersey/guice/providers/exception/ThrowableExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package com.netflix.jersey.guice.providers.exception; 2 | 3 | import java.util.Set; 4 | 5 | import javax.inject.Inject; 6 | import javax.inject.Singleton; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.ws.rs.core.Context; 9 | import javax.ws.rs.core.Response; 10 | import javax.ws.rs.ext.ExceptionMapper; 11 | import javax.ws.rs.ext.Provider; 12 | 13 | /** 14 | * Jersey does not provide a mechanism for multiple exception mappers to exist for the same 15 | * Exception type. Consequently it is not possible to implement context specific mapping 16 | * customizations. This is needed for multi-tenant http servers with a single server context 17 | * where each resource may wish to implement a different exception format. 18 | * 19 | * ThrowableExceptionMapper solves this by exposing a Guice multi-binding based 'plugin' 20 | * architecture where a custom mapper can be added via simple Guice binding. When an 21 | * exception occurs each CustomThrowableExceptionMapper is asked to determine if it 22 | * can/should map the exception. A CustomThrowableExceptionMapper may inspect the request 23 | * context (url, content type, etc...) as well as the actual exception and even stack trace 24 | * to determine if it should map the exception and format the response. A 25 | * DefaultThrowableExceptionMapper is called if no custom mapper is matched. 26 | * 27 | * Note that this should ONLY be used for the catch all exception mapping. Specific exceptions 28 | * should be mapped by creating a specific {@code ExceptionMapper. } 29 | * 30 | * To enable a custom mapper just add a binding when bootstrapping guice. 31 | *
32 |  * {@code
33 |  *  new AbstractModule() {
34 |  *      void configuration() {
35 |  *          Multibinder mappers = Multibinder.newSetBinder(binder(), CustomThrowableExceptionMapper.class);
36 |  *          mappers.addBinding().to(MyCustomExceptionMapper.class);
37 |  *      }
38 |  *  }
39 |  * }
40 |  * 
41 | * 42 | * The default mapper may also be replaced via the following Guice binding. 43 | * 44 | *
45 |  * {@code
46 |  *  new AbstractModule() {
47 |  *      void configuration() {
48 |  *          bind(DefaultThrowableExceptionMapper.class).to(MyDefaultThrowableExceptionMapper.class);
49 |  *      }
50 |  *  }
51 |  * }
52 |  * 
53 | * 54 | * @see GsonDefaultExceptionMapper 55 | * @author elandau 56 | * 57 | */ 58 | @Provider 59 | @Singleton 60 | public class ThrowableExceptionMapper implements ExceptionMapper { 61 | private final DefaultThrowableExceptionMapper defaultMapper; 62 | private final Set mappers; 63 | 64 | @Context 65 | ThreadLocal request = new ThreadLocal() ; 66 | 67 | @Inject 68 | public ThrowableExceptionMapper(Set mappers, DefaultThrowableExceptionMapper defaultMapper) { 69 | this.mappers = mappers; 70 | this.defaultMapper = defaultMapper; 71 | } 72 | 73 | @Override 74 | public Response toResponse(Throwable exception) { 75 | HttpServletRequest request = this.request.get(); 76 | for (CustomThrowableExceptionMapper mapper : mappers) { 77 | try { 78 | if (mapper.canMap(exception, request)) { 79 | return mapper.toResponse(exception); 80 | } 81 | } 82 | catch (Throwable t) { 83 | // OK to ignore 84 | } 85 | } 86 | return defaultMapper.toResponse(exception); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /netflix-lifecycle/src/main/java/com/netflix/lifecycle/concurrency/ConcurrencyModule.java: -------------------------------------------------------------------------------- 1 | package com.netflix.lifecycle.concurrency; 2 | 3 | import java.util.concurrent.ScheduledExecutorService; 4 | 5 | import javax.inject.Singleton; 6 | 7 | import com.google.inject.AbstractModule; 8 | import com.netflix.governator.annotations.binding.Background; 9 | 10 | /** 11 | * Provide bindings for concurrency related singletons such as background 12 | * executors. All bindings should be LazySingletons so that they are only 13 | * created when needed. 14 | * 15 | * @author elandau 16 | * 17 | */ 18 | @Singleton 19 | public class ConcurrencyModule extends AbstractModule { 20 | @Override 21 | protected void configure() { 22 | bind(ScheduledExecutorService.class) 23 | .annotatedWith(Background.class) 24 | .toProvider(CoreCountBasedScheduledExecutorServiceProvider.class); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /netflix-lifecycle/src/main/java/com/netflix/lifecycle/concurrency/CoreCountBasedScheduledExecutorServiceProvider.java: -------------------------------------------------------------------------------- 1 | package com.netflix.lifecycle.concurrency; 2 | 3 | import java.util.concurrent.Executors; 4 | import java.util.concurrent.ScheduledExecutorService; 5 | 6 | import javax.annotation.PreDestroy; 7 | 8 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 9 | import com.google.inject.Provider; 10 | import com.netflix.governator.guice.lazy.LazySingleton; 11 | 12 | @LazySingleton 13 | public class CoreCountBasedScheduledExecutorServiceProvider implements Provider { 14 | private ScheduledExecutorService service; 15 | 16 | @Override 17 | public ScheduledExecutorService get() { 18 | service = Executors.newScheduledThreadPool( 19 | Runtime.getRuntime().availableProcessors(), 20 | new ThreadFactoryBuilder() 21 | .setDaemon(true) 22 | .setNameFormat("Background-%d") 23 | .build()); 24 | return service; 25 | } 26 | 27 | @PreDestroy 28 | protected void shutdown() { 29 | if (service != null) 30 | service.shutdown(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /netflix-lifecycle/src/test/java/com/netflix/lifecycle/concurrency/ConcurrencyModuleTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.lifecycle.concurrency; 2 | 3 | import java.util.concurrent.ScheduledExecutorService; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import com.google.inject.ConfigurationException; 9 | import com.google.inject.Injector; 10 | import com.google.inject.Key; 11 | import com.netflix.governator.annotations.binding.Background; 12 | import com.netflix.governator.guice.BootstrapBinder; 13 | import com.netflix.governator.guice.BootstrapModule; 14 | import com.netflix.governator.guice.LifecycleInjector; 15 | 16 | public class ConcurrencyModuleTest { 17 | @Test 18 | public void backgroundShouldBeInjectable() { 19 | Injector injector = LifecycleInjector.builder() 20 | .withRootModule(ConcurrencyModule.class) 21 | .build() 22 | .createInjector(); 23 | 24 | ScheduledExecutorService service = injector.getInstance(Key.get(ScheduledExecutorService.class, Background.class)); 25 | } 26 | 27 | @Test 28 | public void shouldUseOverrideModule() { 29 | Injector injector = LifecycleInjector.builder() 30 | .withRootModule(ConcurrencyModule.class) 31 | .withBootstrapModule(new BootstrapModule() { 32 | @Override 33 | public void configure(BootstrapBinder binder) { 34 | binder.bind(ConcurrencyModule.class).toInstance(new ConcurrencyModule() { 35 | @Override 36 | protected void configure() { 37 | } 38 | }); 39 | } 40 | }) 41 | .build() 42 | .createInjector(); 43 | 44 | try { 45 | ScheduledExecutorService service = injector.getInstance(Key.get(ScheduledExecutorService.class, Background.class)); 46 | Assert.fail("Binding shouldn't exist"); 47 | } 48 | catch (ConfigurationException e) { 49 | 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /netflix-lifecycle/src/test/java/com/netflix/lifecycle/concurrency/CoreCountBasedScheduledExecutorServiceProviderTest.java: -------------------------------------------------------------------------------- 1 | package com.netflix.lifecycle.concurrency; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | import java.util.concurrent.ScheduledFuture; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | public class CoreCountBasedScheduledExecutorServiceProviderTest { 11 | @Test 12 | public void shouldCallAndShutdown() throws Exception { 13 | CoreCountBasedScheduledExecutorServiceProvider provider = new CoreCountBasedScheduledExecutorServiceProvider(); 14 | final CountDownLatch latch = new CountDownLatch(2); 15 | ScheduledFuture future = provider.get().scheduleWithFixedDelay(new Runnable() { 16 | @Override 17 | public void run() { 18 | latch.countDown(); 19 | } 20 | }, 21 | 100, 100, TimeUnit.MILLISECONDS); 22 | 23 | Assert.assertFalse(future.isDone()); 24 | latch.await(); 25 | Assert.assertFalse(future.isDone()); 26 | 27 | provider.shutdown(); 28 | 29 | Assert.assertTrue(future.isDone()); 30 | 31 | 32 | } 33 | 34 | @Test 35 | public void shouldNotCrashOnUnusedShutdown() { 36 | CoreCountBasedScheduledExecutorServiceProvider provider = new CoreCountBasedScheduledExecutorServiceProvider(); 37 | provider.shutdown(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /netflix-statistics/src/main/java/com/netflix/stats/distribution/DataAccumulator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.stats.distribution; 19 | 20 | import java.util.concurrent.locks.Lock; 21 | 22 | 23 | /** 24 | * A double-buffer of {@link DataBuffer} objects. 25 | * One is the "current" buffer, and new data is added to it. 26 | * The other is the "previous" buffer, and is used as a sorce 27 | * of computed statistics. 28 | * 29 | * @see DataPublisher 30 | * 31 | * @author $Author: netflixoss $ 32 | */ 33 | public abstract class DataAccumulator implements DataCollector { 34 | 35 | private DataBuffer current; 36 | private DataBuffer previous; 37 | private final Object swapLock = new Object(); 38 | 39 | /* 40 | * Constructor(s) 41 | */ 42 | 43 | /** 44 | * Creates a new initially empty DataAccumulator. 45 | * 46 | * @param bufferSize the size of the buffers to use 47 | */ 48 | public DataAccumulator(int bufferSize) { 49 | this.current = new DataBuffer(bufferSize); 50 | this.previous = new DataBuffer(bufferSize); 51 | } 52 | 53 | /* 54 | * Accumulating new values 55 | */ 56 | 57 | /** {@inheritDoc} */ 58 | public void noteValue(double val) { 59 | synchronized (swapLock) { 60 | Lock l = current.getLock(); 61 | l.lock(); 62 | try { 63 | current.noteValue(val); 64 | } finally { 65 | l.unlock(); 66 | } 67 | } 68 | } 69 | 70 | /** 71 | * Swaps the data collection buffers, and computes statistics 72 | * about the data collected up til now. 73 | */ 74 | public void publish() { 75 | /* 76 | * Some care is required here to correctly swap the DataBuffers, 77 | * but not hold the synchronization object while compiling stats 78 | * (a potentially long computation). This ensures that continued 79 | * data collection (calls to noteValue()) will not be blocked for any 80 | * significant period. 81 | */ 82 | DataBuffer tmp = null; 83 | Lock l = null; 84 | synchronized (swapLock) { 85 | // Swap buffers 86 | tmp = current; 87 | current = previous; 88 | previous = tmp; 89 | // Start collection in the new "current" buffer 90 | l = current.getLock(); 91 | l.lock(); 92 | try { 93 | current.startCollection(); 94 | } finally { 95 | l.unlock(); 96 | } 97 | // Grab lock on new "previous" buffer 98 | l = tmp.getLock(); 99 | l.lock(); 100 | } 101 | // Release synchronizaton *before* publishing data 102 | try { 103 | tmp.endCollection(); 104 | publish(tmp); 105 | } finally { 106 | l.unlock(); 107 | } 108 | } 109 | 110 | /** 111 | * Called to publish recently collected data. 112 | * When called, the {@link Lock} associated with the "previous" 113 | * buffer is held, so the data will not be changed. 114 | * Other locks have been released, and so new data can be 115 | * collected in the "current" buffer. 116 | * The data in the buffer has also been sorted in increasing order. 117 | * 118 | * @param buf the {@code DataBuffer} that is now "previous". 119 | */ 120 | protected abstract void publish(DataBuffer buf); 121 | 122 | } // DataAccumulator 123 | -------------------------------------------------------------------------------- /netflix-statistics/src/main/java/com/netflix/stats/distribution/DataCollector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.stats.distribution; 19 | 20 | 21 | /** 22 | * An object that collects new values incrementally. 23 | * 24 | * @author netflixoss 25 | * @version $Revision: $ 26 | */ 27 | public interface DataCollector { 28 | 29 | /** 30 | * Adds a value to the collected data. 31 | * This must run very quickly, and so can safely 32 | * be called in time-critical code. 33 | */ 34 | void noteValue(double val); 35 | 36 | } // DataCollector 37 | -------------------------------------------------------------------------------- /netflix-statistics/src/main/java/com/netflix/stats/distribution/DataDistributionMBean.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.stats.distribution; 19 | 20 | 21 | /** 22 | * Abstract MBean interface for objects that hold information about a distribution 23 | * of (double) values. The information includes basic statistics (count, mean, 24 | * min, max) as well as information about the percentile values for some number 25 | * of percent values. 26 | *

27 | * This interface supports the standard MBean management interface, 28 | * so implementing classes will support JMX monitoring. 29 | * 30 | * @author netflixoss $ 31 | * @version $Revision: $ 32 | */ 33 | public interface DataDistributionMBean extends DistributionMBean { 34 | 35 | /** 36 | * Gets a String representation of the time when this data was produced. 37 | */ 38 | String getTimestamp(); 39 | 40 | /** 41 | * Gets the time when this data was produced, in milliseconds since the epoch. 42 | */ 43 | long getTimestampMillis(); 44 | 45 | /** 46 | * Gets the length of time over which the data was collected, 47 | * in milliseconds. 48 | */ 49 | long getSampleIntervalMillis(); 50 | 51 | /** 52 | * Gets the number of values used to compute the percentile values. 53 | * This value may be smaller than the value of {@link #getNumValues} 54 | * depending on how the percentile values were computed. 55 | */ 56 | int getSampleSize(); 57 | 58 | /** 59 | * Gets the array of known percentile percents. 60 | */ 61 | double[] getPercents(); 62 | 63 | /** 64 | * Gets the array of known percentile values. 65 | */ 66 | double[] getPercentiles(); 67 | 68 | } // DataDistributionMBean 69 | -------------------------------------------------------------------------------- /netflix-statistics/src/main/java/com/netflix/stats/distribution/DistributionMBean.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.stats.distribution; 19 | 20 | 21 | /** 22 | * Abstract MBean interface for objects that describe the general 23 | * characteristics of a distribution of (double) values. 24 | * This interface supports the standard MBean management interface, 25 | * so implementing classes will support JMX monitoring. 26 | * 27 | * @author netflixoss $ 28 | * @version $Revision: $ 29 | */ 30 | public interface DistributionMBean { 31 | 32 | /** 33 | * Clears out the distribution, resetting it to its initial state. 34 | */ 35 | void clear(); 36 | 37 | /** 38 | * Get the number of values in the distribution. 39 | */ 40 | long getNumValues(); 41 | 42 | /** 43 | * Get the average value in the distribtion. 44 | */ 45 | double getMean(); 46 | 47 | /** 48 | * Get the variance (the square of the standard deviation) 49 | * of values in the distribution. 50 | */ 51 | double getVariance(); 52 | 53 | /** 54 | * Get the standard deviation of values in the distribution. 55 | */ 56 | double getStdDev(); 57 | 58 | /** 59 | * Get the minimum value found in the distribution. 60 | */ 61 | double getMinimum(); 62 | 63 | /** 64 | * Get the maximum value found in the distribution. 65 | */ 66 | double getMaximum(); 67 | 68 | } // DistributionMBean 69 | -------------------------------------------------------------------------------- /netflix-statistics/src/main/java/com/netflix/stats/distribution/HistogramMBean.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2013 Netflix, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package com.netflix.stats.distribution; 19 | 20 | 21 | /** 22 | * Abstract MBean interface for objects that describe the general 23 | * characteristics of a distribution of (double) values recorded 24 | * as a histogram. 25 | * This interface supports the standard MBean management interface, 26 | * so implementing classes will support JMX monitoring. 27 | * 28 | * @author $Author: netflixoss $ 29 | * @version $Revision: $ 30 | */ 31 | public interface HistogramMBean extends DistributionMBean { 32 | 33 | /** 34 | * Gets the total number of buckets. 35 | * Note that this includes any buckets added to handle out-of-range values. 36 | */ 37 | int getNumBuckets(); 38 | 39 | /** 40 | * Gets the number of values recorded in each bucket. 41 | */ 42 | long[] getBucketCounts(); 43 | 44 | /** 45 | * Gets the minimum bound for the histogram buckets. 46 | * This is an inclusive minimum; values equal to the 47 | * bucket limit are counted in this bucket. 48 | */ 49 | double[] getBucketMinimums(); 50 | 51 | /** 52 | * Gets the maximum bound for the histogram buckets. 53 | * This is an exclusive maximum; values equal to the 54 | * bucket limit are counted in the subsequent bucket. 55 | */ 56 | double[] getBucketMaximums(); 57 | 58 | /** 59 | * Gets the approximate median value, that is the value where half of the 60 | * observed values are less than the median and half are greater. 61 | */ 62 | double getMedian(); 63 | 64 | /** 65 | * Gets the (approximate) percentile value, that is the value where some 66 | * desired percent of the observed values are less than the percentile 67 | * value and the remainder are greater. 68 | */ 69 | double getPercentile(int percent); 70 | 71 | /** 72 | * Gets the (approximate) percentage of observed values that are 73 | * less than a given value. 74 | */ 75 | long getPercentileRank(double value); 76 | 77 | } // HistogramMBean 78 | -------------------------------------------------------------------------------- /netflix-statistics/src/main/java/com/netflix/stats/distribution/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | This package contains a number of classes that produce statistics 5 | about on-the-fly accumulated data. 6 | Their primary use-case is collecting information about the distribution 7 | of API call response times. 8 | There are a few variations, with differing capabilities and 9 | memory and run-time costs. 10 | These classes all implement the {@link com.netflix.stats.distribution.DataCollector} 11 | interface for adding new data, 12 | so that code that generates data need not know about the particular data collection implementation. 13 | 14 |

    15 | 16 |
  • 17 | The {@link com.netflix.stats.distribution.Distribution} class is the lightest 18 | weight {@code DataCollector}. 19 | It reports basic information about the collected data, 20 | but gives no view into the shape of the distribution of that data. 21 | The reported information is updated and is available immediately. 22 | 23 |
  • 24 | The {@link com.netflix.stats.distribution.Histogram} class is slightly more expensive: 25 | It maintains counts of values in pre-defined ranges. 26 | This gives more information, but requires the user to have some idea of the proper ranges. 27 | Like the {@code Distribution}, reported information is updated and is available immediately. 28 | 29 | 30 |
  • 31 | The class {@link com.netflix.stats.distribution.DataDistribution} implements a 32 | double buffering scheme to 33 | support simultaneous accumulation of new data and analysis of the previous buffered data. 34 | This provides accurate percentile data without prior knowledge of the distribution of data, 35 | but requires more memory than the other collection types. 36 | Also, reports are generated when the buffers are swapped, and so are not immediately available. 37 | The class {@link com.netflix.stats.distribution.DataPublisher} manages a background 38 | task that periodically swaps the data buffers and publishes the now "previous" data 39 | into a {@link com.netflix.stats.distribution.DataDistribution} object. 40 | 41 |
42 | 43 |

44 | These classes all implement MBean interfaces, and so can be viewed via JMX. 45 | 46 | 47 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name='netflix-commons' 2 | include 'netflix-commons-util' 3 | include 'netflix-statistics' 4 | include 'netflix-lifecycle' 5 | include 'netflix-infix' 6 | include 'netflix-eventbus' 7 | include 'netflix-eventbus-bridge' 8 | include 'netflix-eventbus-rx' 9 | include 'netflix-jersey-guice' 10 | --------------------------------------------------------------------------------