type) {
87 | return (T) "result";
88 | }
89 | }
90 |
91 | }
--------------------------------------------------------------------------------
/core/src/main/java/feign/impl/type/TypeUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019-2021 OpenFeign Contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package feign.impl.type;
18 |
19 | /**
20 | * Utility Class for working with Types and Class Name.
21 | */
22 | public class TypeUtils {
23 |
24 | /**
25 | * Create a new Class instance for the class name provided.
26 | *
27 | * @param fullyQualifiedClassName to parse.
28 | * @return the class instance, if found.
29 | * @throws IllegalStateException if the class could not be found.
30 | */
31 | public static Class> getInstance(String fullyQualifiedClassName) {
32 | try {
33 | return Class.forName(fullyQualifiedClassName);
34 | } catch (ClassNotFoundException cnfe) {
35 | if (isPrimitiveType(fullyQualifiedClassName)) {
36 | return getPrimitiveInstance(fullyQualifiedClassName);
37 | }
38 | throw new IllegalStateException("Error occurred obtaining class instance.", cnfe);
39 | }
40 | }
41 |
42 | private static boolean isPrimitiveType(String type) {
43 | return Boolean.TYPE.getName().equalsIgnoreCase(type)
44 | || Character.TYPE.getName().equalsIgnoreCase(type)
45 | || Byte.TYPE.getName().equalsIgnoreCase(type)
46 | || Short.TYPE.getName().equalsIgnoreCase(type)
47 | || Integer.TYPE.getName().equalsIgnoreCase(type)
48 | || Long.TYPE.getName().equalsIgnoreCase(type)
49 | || Float.TYPE.getName().equalsIgnoreCase(type)
50 | || Double.TYPE.getName().equalsIgnoreCase(type)
51 | || Void.TYPE.getName().equalsIgnoreCase(type);
52 | }
53 |
54 | private static Class> getPrimitiveInstance(String type) {
55 | if (Boolean.TYPE.getName().equalsIgnoreCase(type)) {
56 | return Boolean.TYPE;
57 | } else if (Character.TYPE.getName().equalsIgnoreCase(type)) {
58 | return Character.TYPE;
59 | } else if (Byte.TYPE.getName().equalsIgnoreCase(type)) {
60 | return Byte.TYPE;
61 | } else if (Short.TYPE.getName().equalsIgnoreCase(type)) {
62 | return Short.TYPE;
63 | } else if (Integer.TYPE.getName().equalsIgnoreCase(type)) {
64 | return Integer.TYPE;
65 | } else if (Long.TYPE.getName().equalsIgnoreCase(type)) {
66 | return Long.TYPE;
67 | } else if (Float.TYPE.getName().equalsIgnoreCase(type)) {
68 | return Float.TYPE;
69 | } else if (Double.TYPE.getName().equalsIgnoreCase(type)) {
70 | return Double.TYPE;
71 | } else if (Void.TYPE.getName().equalsIgnoreCase(type)) {
72 | return Void.TYPE;
73 | }
74 | throw new IllegalArgumentException("Not a primitive type " + type);
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/core/src/test/java/feign/http/HttpHeaderTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019-2020 OpenFeign Contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package feign.http;
18 |
19 | import static feign.assertions.HttpHeaderAssert.assertThat;
20 | import static org.assertj.core.api.Assertions.assertThat;
21 | import static org.junit.jupiter.api.Assertions.*;
22 |
23 | import java.util.Arrays;
24 | import java.util.Collections;
25 | import org.junit.jupiter.api.Test;
26 |
27 | class HttpHeaderTest {
28 |
29 | @Test
30 | void canCreate_withSingleValue() {
31 | HttpHeader httpHeader =
32 | new HttpHeader("Content-Length", Collections.singletonList("123456"));
33 | assertThat(httpHeader).hasName("Content-Length")
34 | .hasValues("123456");
35 | }
36 |
37 | @Test
38 | void cannotCreate_whenMultipleValuesAreNotAllowed() {
39 | assertThrows(IllegalStateException.class, () ->
40 | new HttpHeader("Content-Length", Arrays.asList("123456", "987654")));
41 | }
42 |
43 | @Test
44 | void canClear() {
45 | HttpHeader httpHeader =
46 | new HttpHeader("Content-Length", Collections.singletonList("123456"));
47 | httpHeader.clear();
48 | assertThat(httpHeader).hasNoValues();
49 | }
50 |
51 | @Test
52 | void canAddNewValue_WhenMultipleIsAllowed() {
53 | HttpHeader httpHeader =
54 | new HttpHeader("Accept", Collections.singletonList("application/json"));
55 | httpHeader.value("text/html");
56 | assertThat(httpHeader).hasValues("application/json", "text/html");
57 | }
58 |
59 | @Test
60 | void cannotAddNewValue_WhenMultipleIsNotAllowed() {
61 | HttpHeader httpHeader =
62 | new HttpHeader("Content-Type", Collections.singletonList("application/json"));
63 | assertThrows(IllegalStateException.class, () -> httpHeader.value("text/html"));
64 | }
65 |
66 | @Test
67 | void headersAreEqual_toItself() {
68 | HttpHeader httpHeader = new HttpHeader("X-Custom");
69 | assertThat(httpHeader).isEqualTo(httpHeader);
70 | }
71 |
72 |
73 | @Test
74 | void headersAreEqual_whenNamesMatch() {
75 | HttpHeader httpHeader = new HttpHeader("X-Custom");
76 | assertThat(httpHeader).isEqualTo(new HttpHeader("X-Custom"));
77 | }
78 |
79 | @Test
80 | void headersAreNotEqual_whenNotAHeader() {
81 | HttpHeader httpHeader = new HttpHeader("X-Custom");
82 | assertThat(httpHeader).isNotEqualTo("String");
83 | assertThat(httpHeader).isNotEqualTo(null);
84 | }
85 |
86 | @Test
87 | void headerToString_isNotNull() {
88 | HttpHeader httpHeader = new HttpHeader("X-Custom");
89 | assertThat(httpHeader.toString()).isNotEmpty();
90 | }
91 |
92 | }
--------------------------------------------------------------------------------
/core/src/main/java/feign/template/expander/MapExpander.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019-2020 OpenFeign Contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package feign.template.expander;
18 |
19 | import feign.support.Assert;
20 | import feign.template.ExpansionPolicy;
21 | import java.util.Map;
22 |
23 | /**
24 | * Expression Expander that operates on associative arrays defined as {@link java.util.Map}s.
25 | */
26 | public class MapExpander extends ListExpander {
27 |
28 | /* Singleton instantiation */
29 | private static final MapExpander instance = new MapExpander();
30 |
31 | public static MapExpander getInstance() {
32 | return instance;
33 | }
34 |
35 | /**
36 | * Returns the underlying entry set for the map.
37 | *
38 | * @param value containing the map.
39 | * @return the underlying entry set for the map.
40 | */
41 | @Override
42 | protected Iterable> getValues(Object value) {
43 | /* verify that the value is a map */
44 | Assert.isTrue(value,
45 | obj -> Map.class.isAssignableFrom(obj.getClass()),
46 | "Type " + value.getClass()
47 | + " is not supported by this expander. Values must be a Map.");
48 |
49 | return ((Map, ?>) value).entrySet();
50 | }
51 |
52 | /**
53 | * {@inheritDoc}
54 | */
55 | @Override
56 | protected String explode(String name, Object value, ExpansionPolicy policy) {
57 | Map.Entry, ?> entry = (Map.Entry, ?>) value;
58 |
59 | /* join pairs with an equals */
60 | return this.expand(
61 | entry.getKey().toString(), entry.getValue().toString(), "=", policy);
62 | }
63 |
64 | /**
65 | * {@inheritDoc}
66 | */
67 | @Override
68 | protected String expand(String name, Object value, ExpansionPolicy policy) {
69 | Map.Entry, ?> entry = (Map.Entry, ?>) value;
70 |
71 | /* join using a comma */
72 | return this.expand(
73 | entry.getKey().toString(), entry.getValue().toString(), ",", policy);
74 | }
75 |
76 | /**
77 | * Expand a name,value pair.
78 | *
79 | * @param name of the pair.
80 | * @param value in the pair.
81 | * @param entrySeparator to use when joining the pair.
82 | * @param policy to use when expanding the value.
83 | * @return the expanded name,value pair.
84 | */
85 | private String expand(String name, String value, String entrySeparator, ExpansionPolicy policy) {
86 | StringBuilder result = new StringBuilder();
87 | result.append(this.encode(name, policy.isAllowReservedCharacters()));
88 | if (value.isEmpty()) {
89 | result.append(policy.getEmptySeparator());
90 | } else {
91 | result.append(entrySeparator);
92 | }
93 | result.append(this.encode(value, policy.isAllowReservedCharacters()));
94 | return result.toString();
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/core/src/main/java/feign/proxy/GuardMethodHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019-2020 OpenFeign Contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package feign.proxy;
18 |
19 | import feign.Target;
20 | import feign.TargetMethodHandler;
21 | import feign.support.Assert;
22 | import java.lang.invoke.MethodHandle;
23 | import java.lang.invoke.MethodHandles.Lookup;
24 | import java.lang.reflect.Constructor;
25 | import java.lang.reflect.InvocationTargetException;
26 | import java.lang.reflect.Method;
27 |
28 | /**
29 | * Target HttpMethod Handler implementation for {@code default} or Guard method.
30 | *
31 | * This class uses certain parts of the JDK reflection API that may be considered unsafe.
32 | * In JDK 9+, this type of access frowned upon and may be explicitly disabled in any
33 | * JDK 11+. Until a more complete solution appears, we will continue to use this approach.
34 | *
35 | */
36 | public class GuardMethodHandler implements TargetMethodHandler {
37 |
38 | private final MethodHandle guardMethodHandle;
39 |
40 | /**
41 | * Creates a new Guard HttpMethod Handler.
42 | *
43 | * @param method to proxy.
44 | * @param target instance this method is for.
45 | * @param proxy to bind this handler to.
46 | */
47 | GuardMethodHandler(Method method, Target> target, Object proxy) {
48 | Assert.isNotNull(method, "method is required.");
49 | Assert.isNotNull(target, "target is required.");
50 | try {
51 | /* attempt to create a new instance of the target type */
52 | Class> targetType = target.type();
53 | Constructor constructor = Lookup.class.getDeclaredConstructor(Class.class);
54 |
55 | /* this is the line that breaks on JDK 9+, it violates the new security rules */
56 | constructor.setAccessible(true);
57 |
58 | /* create a temporary instance of the target and execute the method */
59 | this.guardMethodHandle = constructor.newInstance(targetType)
60 | .in(targetType)
61 | .unreflectSpecial(method, targetType)
62 | .bindTo(proxy);
63 | } catch (InstantiationException | InvocationTargetException | NoSuchMethodException
64 | | IllegalAccessException ie) {
65 | /* either the type does not expose a type that can be instantiated or
66 | * access to the type has been explicitly denied
67 | */
68 | throw new IllegalStateException(ie);
69 | }
70 | }
71 |
72 | /**
73 | * Execute the HttpMethod Handler.
74 | *
75 | * @param args for the method.
76 | * @return the result of the method.
77 | * @throws Throwable in the event of any exceptions during execution.
78 | */
79 | @Override
80 | public Object execute(Object[] args) throws Throwable {
81 | return this.guardMethodHandle.invokeWithArguments(args);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FeignX
2 |
3 | [](https://circleci.com/gh/OpenFeign/feignx/tree/master) 
4 | [](https://snyk.io/test/github/openfeign/feignx)
5 |
6 | FeignX, an experimental version of [Feign](https://github.com/OpenFeign/feign), extending the
7 | core concepts of Feign beyond HTTP and REST including support for pooled, asynchronous, non-blocking,
8 | and reactive execution modes.
9 |
10 | ## Getting Started
11 |
12 | These instructions will get you a copy of the project up and running on your local machine for
13 | development and testing purposes.
14 |
15 | ### Prerequisites
16 |
17 | What things you need:
18 |
19 | * [Recent Java JDK: (11 or higher)](https://adoptopenjdk.net/)
20 | * A clone of this repository:
21 |
22 | ```
23 | git clone https://github.com/openfeign/feignx.git
24 | cd feignx
25 | ```
26 |
27 | ### Building from the Command Line
28 |
29 | To compile, test, and build all artifacts, use:
30 |
31 | ```
32 | ./mvnw install
33 | ```
34 |
35 | The first time you run the build, it may take some time to download Maven and all of the required
36 | dependencies, compile, and run all of the tests. Dependencies will be cached in your `$HOME/.m2`
37 | directory.
38 |
39 | ### Importing into your IDE
40 |
41 | Most popular IDE environments support reading project information from a Maven `pom.xml` file.
42 | Follow the instructions for your preferred IDE:
43 |
44 | * [JetBrains IntelliJ](https://www.jetbrains.com/help/idea/maven-support.html)
45 | * [Eclipse](https://books.sonatype.com/m2eclipse-book/reference/creating-sect-importing-projects.html)
46 | * [Visual Studio Code](https://code.visualstudio.com/docs/java/java-project)
47 |
48 | ## Running the tests
49 |
50 | To test your changes, run the test suite using:
51 |
52 | ```
53 | ./mvnw clean test
54 | ```
55 |
56 | This will run the entire test suite, verifying your changes including formatting, licensing, and
57 | code coverage.
58 |
59 | ## Built With
60 |
61 | * [Maven](https://maven.apache.org/) - Dependency Management
62 | * [Snyk](https://snyk.io/) - Security and Dependency Scanning
63 | * [Circle CI](https://circleci.com) - Continuous Integration and Deployment
64 | * [Codecov](https://codecov.io) - Code Coverage tracking and reporting
65 |
66 | ## Issues
67 |
68 | To report an issue, or check on the status of an issue, see the [Issues](https://github.com/openfeign/feignx/issues)
69 | section of this repository.
70 |
71 | ## Contributing
72 |
73 | Please read [CONTRIBUTING](CONTRIBUTING.md) for details on our [Code of Conduct](CODE_OF_CONDUCT.md),
74 | and the process for submitting [Pull Requests](https://github.com/openfeign/feignx/pulls) to us.
75 |
76 | ## Releases
77 |
78 | For the versions available, see the [tags on this repository](https://github.com/openfeign/feignx/tags)
79 | or [Maven Central](https://search.maven.org/search?q=g:io.github.openfeign)
80 |
81 | ## Contributors
82 |
83 | See also the list of [contributors](https://github.com/openfeign/contributors) who participated in this project.
84 |
85 | ## License
86 |
87 | This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE.md) file for details
88 |
89 | ## Acknowledgments
90 |
91 | * [Netflix OSS](https://netflix.github.io/)
92 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/java,maven,intellij+iml
3 | # Edit at https://www.gitignore.io/?templates=java,maven,intellij+iml
4 |
5 | ### Intellij+iml ###
6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
8 |
9 | # Ignore the entire directory
10 | .idea/
11 |
12 | # Gradle and Maven with auto-import
13 | # When using Gradle or Maven with auto-import, you should exclude module files,
14 | # since they will be recreated, and may cause churn. Uncomment if using
15 | # auto-import.
16 | # .idea/modules.xml
17 | # .idea/*.iml
18 | # .idea/modules
19 |
20 | # CMake
21 | cmake-build-*/
22 |
23 | # Mongo Explorer plugin
24 | .idea/**/mongoSettings.xml
25 |
26 | # File-based project format
27 | *.iws
28 |
29 | # IntelliJ
30 | out/
31 |
32 | # mpeltonen/sbt-idea plugin
33 | .idea_modules/
34 |
35 | # JIRA plugin
36 | atlassian-ide-plugin.xml
37 |
38 | # Cursive Clojure plugin
39 | .idea/replstate.xml
40 |
41 | # Crashlytics plugin (for Android Studio and IntelliJ)
42 | com_crashlytics_export_strings.xml
43 | crashlytics.properties
44 | crashlytics-build.properties
45 | fabric.properties
46 |
47 | # Editor-based Rest Client
48 | .idea/httpRequests
49 |
50 | # Android studio 3.1+ serialized cache file
51 | .idea/caches/build_file_checksums.ser
52 |
53 | ### Intellij+iml Patch ###
54 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
55 |
56 | *.iml
57 | modules.xml
58 | .idea/misc.xml
59 | *.ipr
60 |
61 | ### Java ###
62 | # Compiled class file
63 | *.class
64 |
65 | # Log file
66 | *.log
67 |
68 | # BlueJ files
69 | *.ctxt
70 |
71 | # Mobile Tools for Java (J2ME)
72 | .mtj.tmp/
73 |
74 | # Package Files #
75 | *.jar
76 | *.war
77 | *.nar
78 | *.ear
79 | *.zip
80 | *.tar.gz
81 | *.rar
82 |
83 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
84 | hs_err_pid*
85 |
86 | ### Maven ###
87 | target/
88 | pom.xml.tag
89 | pom.xml.releaseBackup
90 | pom.xml.versionsBackup
91 | pom.xml.next
92 | release.properties
93 | dependency-reduced-pom.xml
94 | buildNumber.properties
95 | .mvn/timing.properties
96 | !.mvn/wrapper/maven-wrapper.jar
97 |
98 | # End of https://www.gitignore.io/api/java,maven,intellij+iml
99 |
100 | # Vagrant
101 | .vagrant/
102 |
103 | ### macOS ###
104 | # General
105 | .DS_Store
106 | .AppleDouble
107 | .LSOverride
108 |
109 | # Icon must end with two \r
110 | Icon
111 |
112 | # Thumbnails
113 | ._*
114 |
115 | # Files that might appear in the root of a volume
116 | .DocumentRevisions-V100
117 | .fseventsd
118 | .Spotlight-V100
119 | .TemporaryItems
120 | .Trashes
121 | .VolumeIcon.icns
122 | .com.apple.timemachine.donotpresent
123 |
124 | # Directories potentially created on remote AFP share
125 | .AppleDB
126 | .AppleDesktop
127 | Network Trash Folder
128 | Temporary Items
129 | .apdisk
130 |
131 | ### Windows ###
132 | # Windows thumbnail cache files
133 | Thumbs.db
134 | Thumbs.db:encryptable
135 | ehthumbs.db
136 | ehthumbs_vista.db
137 |
138 | # Dump file
139 | *.stackdump
140 |
141 | # Folder config file
142 | [Dd]esktop.ini
143 |
144 | # Recycle Bin used on file shares
145 | $RECYCLE.BIN/
146 |
147 | # Windows Installer files
148 | *.cab
149 | *.msi
150 | *.msix
151 | *.msm
152 | *.msp
153 |
154 | # Windows shortcuts
155 | *.lnk
--------------------------------------------------------------------------------
/core/src/main/java/feign/support/Assert.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019-2020 OpenFeign Contributors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package feign.support;
18 |
19 | import java.util.Collection;
20 | import java.util.function.Predicate;
21 |
22 | /**
23 | * Helper Class that provides useful Assertions.
24 | */
25 | public final class Assert {
26 |
27 | /**
28 | * Ensures that the provided value is not {@literal null}.
29 | *
30 | * @param value to evaluate.
31 | * @param message to include in the thrown exception.
32 | * @throws IllegalArgumentException if the value is {@literal null}.
33 | */
34 | public static void isNotNull(Object value, String message) {
35 | if (value == null) {
36 | throw new IllegalArgumentException(message);
37 | }
38 | }
39 |
40 | /**
41 | * Ensures that the provided value is not empty. Strings that are all whitespace are also
42 | * considered empty.
43 | *
44 | * @param value to evaluate.
45 | * @param message to include in the thrown exception.
46 | * @throws IllegalArgumentException if the value is {@literal null}, empty, or all whitespace.
47 | */
48 | public static void isNotEmpty(String value, String message) {
49 | if (value == null || value.trim().isEmpty()) {
50 | throw new IllegalArgumentException(message);
51 | }
52 | }
53 |
54 | /**
55 | * Ensures that the provided collection is not empty.
56 | *
57 | * @param value to evaluate.
58 | * @param message to include in the thrown exception.
59 | * @throws IllegalArgumentException if the value is {@literal null} or empty.
60 | */
61 | public static void isNotEmpty(Collection> value, String message) {
62 | if (value == null || value.isEmpty()) {
63 | throw new IllegalArgumentException(message);
64 | }
65 | }
66 |
67 | /**
68 | * Ensures that the value provided matches the supplied expression.
69 | *
70 | * @param value to evaluate.
71 | * @param expression to use to evaluate the value.
72 | * @param message to include in the thrown exception.
73 | * @param type of the value.
74 | * @throws IllegalStateException if the value does not pass the expression.
75 | */
76 | public static void isTrue(T value, Predicate expression, String message) {
77 | if (!expression.test(value)) {
78 | throw new IllegalStateException(message);
79 | }
80 | }
81 |
82 | /**
83 | * Ensures that the value provided does not match the supplied expression.
84 | *
85 | * @param value to evaluate.
86 | * @param expression to use to evaluate the value.
87 | * @param message to include in the thrown exception.
88 | * @param type of the value.
89 | * @throws IllegalStateException if the value does pass the expression.
90 | */
91 | public static void isFalse(T value, Predicate expression, String message) {
92 | isTrue(value, expression.negate(), message);
93 | }
94 |
95 | }
--------------------------------------------------------------------------------