🚚 Switch namespace
This commit is contained in:
parent
0064093dbf
commit
c74cda3a0f
207 changed files with 2689 additions and 611 deletions
21
cloud-services/LICENSE
Normal file
21
cloud-services/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 IntellectualSites
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
433
cloud-services/README.md
Normal file
433
cloud-services/README.md
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
# Rörledning
|
||||
|
||||
[](https://www.codefactor.io/repository/github/sauilitired/rorledning/overview/master)
|
||||
|
||||
This is a library that allows you to create services, that can have several different implementations.
|
||||
A service in this case, is anything that takes in a context, and spits out some sort of result, achieving
|
||||
some pre-determined task.
|
||||
|
||||
Examples of services would be generators and caches.
|
||||
|
||||
## Links
|
||||
|
||||
- Discord: https://discord.gg/KxkjDVg
|
||||
- JavaDoc: https://plotsquared.com/docs/rörledning/
|
||||
|
||||
## Maven
|
||||
|
||||
Rörledning is available from [IntellectualSites](https://intellectualsites.com)' maven repository:
|
||||
|
||||
```xml
|
||||
<repository>
|
||||
<id>intellectualsites-snapshots</id>
|
||||
<url>https://mvn.intellectualsites.com/content/repositories/snapshots</url>
|
||||
</repository>
|
||||
```
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>cloud.commandframework</groupId>
|
||||
<artifactId>Pipeline</artifactId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### ServicePipeline
|
||||
|
||||
All requests start in the `ServicePipeline`. To get an instance of the `ServicePipeline`, simply use
|
||||
the service pipeline builder.
|
||||
|
||||
**Example:**
|
||||
|
||||
```java
|
||||
final ServicePipeline servicePipeline = ServicePipeline.builder().build();
|
||||
```
|
||||
|
||||
### Service
|
||||
|
||||
To implement a service, simply create an interface that extends `Service<Context, Result>`.
|
||||
The context is the type that gets pumped into the service (i.e, the value you provide), and the result
|
||||
is the type that gets produced by the service.
|
||||
|
||||
The pipeline will attempt to generate a result from each service, until a service produces a non-null result.
|
||||
Thus, if a service cannot (or shouldn't) produce a result for a given context, it can simply return null.
|
||||
|
||||
However, there's a catch to this. At least one service must always provide a result for every input.
|
||||
To ensure that this is the case, a default implementation of the service must be registered together
|
||||
with the service type. This implementation is not allowed to return null.
|
||||
|
||||
**Examples:**
|
||||
|
||||
Example Service:
|
||||
|
||||
```java
|
||||
public interface MockService extends Service<MockService.MockContext, MockService.MockResult> {
|
||||
|
||||
class MockContext {
|
||||
|
||||
private final String string;
|
||||
|
||||
public MockContext(@Nonnull final String string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
@Nonnull public String getString() {
|
||||
return this.string;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MockResult {
|
||||
|
||||
private final int integer;
|
||||
|
||||
public MockResult(final int integer) {
|
||||
this.integer = integer;
|
||||
}
|
||||
|
||||
public int getInteger() {
|
||||
return this.integer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Example Implementation:
|
||||
|
||||
```java
|
||||
public class DefaultMockService implements MockService {
|
||||
|
||||
@Nullable @Override public MockResult handle(@Nonnull final MockContext mockContext) {
|
||||
return new MockResult(32);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Example Registration:
|
||||
|
||||
```java
|
||||
servicePipeline.registerServiceType(TypeToken.of(MockService.class), new DefaultMockService());
|
||||
```
|
||||
|
||||
Example Usage:
|
||||
|
||||
```java
|
||||
final int result = servicePipeline.pump(new MockService.MockContext("Hello"))
|
||||
.through(MockService.class)
|
||||
.getResult()
|
||||
.getInteger();
|
||||
```
|
||||
|
||||
### SideEffectService
|
||||
|
||||
Some services may just alter the state of the incoming context, without generating any (useful) result.
|
||||
These services should extend `SideEffectService`.
|
||||
|
||||
SideEffectService returns a State instead of a result. The service may either accept a context, in
|
||||
which case the execution chain is interrupted. It can also reject the context, in which case the
|
||||
other services in the execution chain will get a chance to consume it.
|
||||
|
||||
**Example:**
|
||||
|
||||
```java
|
||||
public interface MockSideEffectService extends SideEffectService<MockSideEffectService.MockPlayer> {
|
||||
|
||||
class MockPlayer {
|
||||
|
||||
private int health;
|
||||
|
||||
public MockPlayer(final int health) {
|
||||
this.health = health;
|
||||
}
|
||||
|
||||
public int getHealth() {
|
||||
return this.health;
|
||||
}
|
||||
|
||||
public void setHealth(final int health) {
|
||||
this.health = health;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class DefaultSideEffectService implements MockSideEffectService {
|
||||
|
||||
@Nonnull @Override public State handle(@Nonnull final MockPlayer mockPlayer) {
|
||||
mockPlayer.setHealth(0);
|
||||
return State.ACCEPTED;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### Asynchronous Execution
|
||||
|
||||
The pipeline results can be evaluated asynchronously. Simple use `getResultAsynchronously()`
|
||||
instead of `getResult()`. By default, a single threaded executor is used. A different executor
|
||||
can be supplied to the pipeline builder.
|
||||
|
||||
### Filters
|
||||
|
||||
Sometimes you may not want your service to respond to certain contexts. Instead of always
|
||||
returning null in those cases, filters can be used. These are simply predicates that take in your
|
||||
context type, and should be registered together with your implementation.
|
||||
|
||||
**Example:**
|
||||
|
||||
Example Filter:
|
||||
```java
|
||||
public class FilteredMockService implements MockService, Predicate<MockService.MockContext> {
|
||||
|
||||
@Nullable @Override public MockResult handle(@Nonnull final MockContext mockContext) {
|
||||
return new MockResult(999);
|
||||
}
|
||||
|
||||
@Override public boolean test(final MockContext mockContext) {
|
||||
return mockContext.getString().equalsIgnoreCase("potato");
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Example Registration:
|
||||
|
||||
```java
|
||||
final FilteredMockService service = new FilteredMockService();
|
||||
final List<Predicate<MockService.MockContext>> predicates = Collections.singletonList(service);
|
||||
servicePipeline.registerServiceImplementation(MockService.class, service, predicates);
|
||||
```
|
||||
|
||||
### Forwarding
|
||||
|
||||
Sometimes it may be useful to use the result produced by a service as the context for another service.
|
||||
To make this easier, the concept of forwarding was introduced. When using `getResult()`, one can instead
|
||||
use `forward()`, to pump the result back into the pipeline.
|
||||
|
||||
**Examples:**
|
||||
|
||||
```java
|
||||
servicePipeline.pump(new MockService.MockContext("huh"))
|
||||
.through(MockService.class)
|
||||
.forward()
|
||||
.through(MockResultConsumer.class)
|
||||
.getResult();
|
||||
```
|
||||
|
||||
This can also be done asynchronously:
|
||||
|
||||
```java
|
||||
servicePipeline.pump(new MockService.MockContext("Something"))
|
||||
.through(MockService.class)
|
||||
.forwardAsynchronously()
|
||||
.thenApply(pump -> pump.through(MockResultConsumer.class))
|
||||
.thenApply(ServiceSpigot::getResult)
|
||||
.get();
|
||||
```
|
||||
|
||||
### Priority/Ordering
|
||||
|
||||
By default, all service implementations will be executed in first-in-last-out order. That is,
|
||||
the earlier the implementation was registered, the lower the priority it gets in the execution chain.
|
||||
|
||||
This may not always be ideal, and it is therefore possibly to override the natural ordering
|
||||
of the implementations by using the @Optional annotation.
|
||||
|
||||
**Example:**
|
||||
|
||||
```java
|
||||
@Order(ExecutionOrder.FIRST)
|
||||
public class MockOrderedFirst implements MockService {
|
||||
|
||||
@Nullable @Override public MockResult handle(@Nonnull final MockContext mockContext) {
|
||||
return new MockResult(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Order(ExecutionOrder.LAST)
|
||||
public class MockOrderedLast implements MockService {
|
||||
|
||||
@Nullable @Override public MockResult handle(@Nonnull final MockContext mockContext) {
|
||||
return new MockResult(2);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
No matter in which order MockOrderedFirst and MockOrderedLast are added, MockOrderedFirst will be
|
||||
handled before MockOrderedLast.
|
||||
|
||||
The default order for all services is `SOON`.
|
||||
|
||||
### Annotated Methods
|
||||
|
||||
You can also implement services by using instance methods, like such:
|
||||
|
||||
```java
|
||||
@ServiceImplementation(MockService.class)
|
||||
public MockService.MockResult handle(@Nonnull final MockService.MockContext context) {
|
||||
return new MockService.MockResult(context.getString().length());
|
||||
}
|
||||
```
|
||||
|
||||
The methods can also be annotated with the order annotation. Is is very important
|
||||
that the method return type and parameter type match up wit the service context and
|
||||
result types, or you will get runtime exceptions when using the pipeline.
|
||||
|
||||
These methods are registered in ServicePipeline, using `registerMethods(yourClassInstance);`
|
||||
|
||||
### ConsumerService
|
||||
|
||||
Consumer services effectively turns the service pipeline into an event bus. Each implementation
|
||||
will get a chance to consume the incoming context, unless an implementation forcefully interrupts
|
||||
the execution, by calling `ConsumerService.interrupt()`
|
||||
|
||||
**Examples:**
|
||||
|
||||
```java
|
||||
public interface MockConsumerService extends ConsumerService<MockService.MockContext> {
|
||||
}
|
||||
|
||||
public class InterruptingMockConsumer implements MockConsumerService {
|
||||
|
||||
@Override public void accept(@Nonnull final MockService.MockContext mockContext) {
|
||||
ConsumerService.interrupt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class StateSettingConsumerService implements MockConsumerService {
|
||||
|
||||
@Override public void accept(@Nonnull final MockService.MockContext mockContext) {
|
||||
mockContext.setState("");
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### Partial Result Services
|
||||
|
||||
Sometimes you may need to get results for multiple contexts, but there is no guarantee
|
||||
that a single service will be able to generate all the needed results. It is then possible
|
||||
to make use of `PartialResultService`.
|
||||
|
||||
The partial result service interface uses the `ChunkedRequestContext` class as the input, and
|
||||
outputs a map of request-response pairs.
|
||||
|
||||
**Example:**
|
||||
|
||||
Example Request Type:
|
||||
|
||||
```java
|
||||
public class MockChunkedRequest extends ChunkedRequestContext<MockChunkedRequest.Animal, MockChunkedRequest.Sound> {
|
||||
|
||||
public MockChunkedRequest(@Nonnull final Collection<Animal> requests) {
|
||||
super(requests);
|
||||
}
|
||||
|
||||
|
||||
public static class Animal {
|
||||
|
||||
private final String name;
|
||||
|
||||
public Animal(@Nonnull final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Nonnull public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Sound {
|
||||
|
||||
private final String sound;
|
||||
|
||||
public Sound(@Nonnull final String sound) {
|
||||
this.sound = sound;
|
||||
}
|
||||
|
||||
@Nonnull public String getSound() {
|
||||
return this.sound;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Example Service:
|
||||
```java
|
||||
public interface MockPartialResultService extends
|
||||
PartialResultService<MockChunkedRequest.Animal, MockChunkedRequest.Sound, MockChunkedRequest> {
|
||||
}
|
||||
```
|
||||
|
||||
Example Implementations:
|
||||
```java
|
||||
public class DefaultPartialRequestService implements MockPartialResultService {
|
||||
|
||||
@Nonnull @Override
|
||||
public Map<MockChunkedRequest.Animal, MockChunkedRequest.Sound> handleRequests(
|
||||
@Nonnull final List<MockChunkedRequest.Animal> requests) {
|
||||
final Map<MockChunkedRequest.Animal, MockChunkedRequest.Sound> map = new HashMap<>(requests.size());
|
||||
for (final MockChunkedRequest.Animal animal : requests) {
|
||||
map.put(animal, new MockChunkedRequest.Sound("unknown"));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class CompletingPartialResultService implements MockPartialResultService {
|
||||
|
||||
@Nonnull @Override public Map<MockChunkedRequest.Animal, MockChunkedRequest.Sound> handleRequests(
|
||||
@Nonnull List<MockChunkedRequest.Animal> requests) {
|
||||
final Map<MockChunkedRequest.Animal, MockChunkedRequest.Sound> map = new HashMap<>();
|
||||
for (final MockChunkedRequest.Animal animal : requests) {
|
||||
if (animal.getName().equals("cow")) {
|
||||
map.put(animal, new MockChunkedRequest.Sound("moo"));
|
||||
} else if (animal.getName().equals("dog")) {
|
||||
map.put(animal, new MockChunkedRequest.Sound("woof"));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### Exception Handling
|
||||
|
||||
Exceptions thrown during result retrieval and implementation filtering will be wrapped by
|
||||
`PipelineException`. You can use `PipelineException#getCause` to get the exception that was wrapped.
|
||||
|
||||
**Example:**
|
||||
|
||||
```java
|
||||
try {
|
||||
final Result result = pipeline.pump(yourContext).through(YourService.class).getResult();
|
||||
} catch (final PipelineException exception) {
|
||||
final Exception cause = exception.getCause();
|
||||
}
|
||||
```
|
||||
|
||||
You may also make use of `ServicePipeline#getException(BiConsumer<Result, Throwable>)`. This method
|
||||
will unwrap any pipeline exceptions before passing them to the consumer.
|
||||
|
||||
**Example:**
|
||||
|
||||
```java
|
||||
pipeline.getResult((result, exception) -> {
|
||||
if (exception != null) {
|
||||
exception.printStackTrace();
|
||||
} else {
|
||||
// consume result
|
||||
}
|
||||
});
|
||||
```
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import cloud.commandframework.services.annotations.Order;
|
||||
import cloud.commandframework.services.types.Service;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
class AnnotatedMethodService<Context, Result> implements Service<Context, Result> {
|
||||
|
||||
private final ExecutionOrder executionOrder;
|
||||
private final MethodHandle methodHandle;
|
||||
private final Method method;
|
||||
private final Object instance;
|
||||
|
||||
AnnotatedMethodService(@Nonnull final Object instance, @Nonnull final Method method)
|
||||
throws Exception {
|
||||
ExecutionOrder executionOrder = ExecutionOrder.SOON;
|
||||
try {
|
||||
final Order order = method.getAnnotation(Order.class);
|
||||
if (order != null) {
|
||||
executionOrder = order.value();
|
||||
}
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
this.instance = instance;
|
||||
this.executionOrder = executionOrder;
|
||||
method.setAccessible(true);
|
||||
this.methodHandle = MethodHandles.lookup().unreflect(method);
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Result handle(@Nonnull final Context context) {
|
||||
try {
|
||||
return (Result) this.methodHandle.invoke(this.instance, context);
|
||||
} catch (final Throwable throwable) {
|
||||
new IllegalStateException(String
|
||||
.format("Failed to call method service implementation '%s' in class '%s'",
|
||||
method.getName(), instance.getClass().getCanonicalName()), throwable)
|
||||
.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ExecutionOrder order() {
|
||||
return this.executionOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final AnnotatedMethodService<?, ?> that = (AnnotatedMethodService<?, ?>) o;
|
||||
return Objects.equal(this.methodHandle, that.methodHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(this.methodHandle);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import cloud.commandframework.services.annotations.ServiceImplementation;
|
||||
import cloud.commandframework.services.types.Service;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
enum AnnotatedMethodServiceFactory {
|
||||
INSTANCE;
|
||||
|
||||
Map<? extends Service<?, ?>, TypeToken<? extends Service<?, ?>>> lookupServices(
|
||||
@Nonnull final Object instance) throws Exception {
|
||||
final Map<Service<?, ?>, TypeToken<? extends Service<?, ?>>> map = new HashMap<>();
|
||||
final Class<?> clazz = instance.getClass();
|
||||
for (final Method method : clazz.getDeclaredMethods()) {
|
||||
final ServiceImplementation serviceImplementation =
|
||||
method.getAnnotation(ServiceImplementation.class);
|
||||
if (serviceImplementation == null) {
|
||||
continue;
|
||||
}
|
||||
if (method.getParameterCount() != 1) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Method '%s' in class '%s'" + " has wrong parameter count. Expected 1, got %d",
|
||||
method.getName(), instance.getClass().getCanonicalName(),
|
||||
method.getParameterCount()));
|
||||
|
||||
}
|
||||
map.put(new AnnotatedMethodService<>(instance, method),
|
||||
TypeToken.of(serviceImplementation.value()));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services;
|
||||
|
||||
/**
|
||||
* Used to specify the relative priority of a service implementation
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public enum ExecutionOrder {
|
||||
LAST, LATER, LATE, SOON, SOONER, FIRST
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Wrapper for exceptions thrown during pipeline execution.
|
||||
*
|
||||
* @see #getCause() Use {@link #getCause()} to get the wrapped exception
|
||||
*/
|
||||
public final class PipelineException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Construct a new pipeline exception
|
||||
*
|
||||
* @param cause Cause of the exception
|
||||
*/
|
||||
public PipelineException(@Nonnull final Exception cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new pipeline exception
|
||||
*
|
||||
* @param message Message explaining the exception
|
||||
* @param cause Cause of the exception
|
||||
*/
|
||||
public PipelineException(@Nonnull final String message, @Nonnull final Exception cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services;
|
||||
|
||||
import cloud.commandframework.services.types.Service;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
enum ServiceFilterHandler {
|
||||
INSTANCE;
|
||||
|
||||
<Context> boolean passes(
|
||||
@Nonnull final ServiceRepository<Context, ?>.ServiceWrapper<? extends Service<Context, ?>> service,
|
||||
@Nonnull final Context context) {
|
||||
if (!service.isDefaultImplementation()) {
|
||||
for (final Predicate<Context> predicate : service.getFilters()) {
|
||||
try {
|
||||
if (!predicate.test(context)) {
|
||||
return false;
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
throw new PipelineException(String
|
||||
.format("Failed to evaluate filter '%s' for '%s'",
|
||||
predicate.getClass().getCanonicalName(), service.toString()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import cloud.commandframework.services.annotations.Order;
|
||||
import cloud.commandframework.services.types.Service;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Repository that contains implementations for a given service type
|
||||
*
|
||||
* @param <Context> Service context
|
||||
* @param <Response> Service response type
|
||||
*/
|
||||
public final class ServiceRepository<Context, Response> {
|
||||
|
||||
private final Object lock = new Object();
|
||||
private final TypeToken<? extends Service<Context, Response>> serviceType;
|
||||
private final List<ServiceWrapper<? extends Service<Context, Response>>> implementations;
|
||||
|
||||
private int registrationOrder = 0;
|
||||
|
||||
/**
|
||||
* Create a new service repository for a given service type
|
||||
*
|
||||
* @param serviceType Service type
|
||||
*/
|
||||
ServiceRepository(@Nonnull final TypeToken<? extends Service<Context, Response>> serviceType) {
|
||||
this.serviceType = serviceType;
|
||||
this.implementations = new LinkedList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new implementation for the service
|
||||
*
|
||||
* @param service Implementation
|
||||
* @param filters Filters that will be used to determine whether or not the service gets used
|
||||
* @param <T> Type of the implementation
|
||||
*/
|
||||
<T extends Service<Context, Response>> void registerImplementation(@Nonnull final T service,
|
||||
@Nonnull final Collection<Predicate<Context>> filters) {
|
||||
synchronized (this.lock) {
|
||||
this.implementations.add(new ServiceWrapper<>(service, filters));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a queue containing all implementations
|
||||
*
|
||||
* @return Queue containing all implementations
|
||||
*/
|
||||
@Nonnull
|
||||
LinkedList<ServiceWrapper<? extends Service<Context, Response>>> getQueue() {
|
||||
synchronized (this.lock) {
|
||||
return new LinkedList<>(this.implementations);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used to store {@link Service} implementations together with their state
|
||||
*
|
||||
* @param <T> Service type
|
||||
*/
|
||||
final class ServiceWrapper<T extends Service<Context, Response>>
|
||||
implements Comparable<ServiceWrapper<T>> {
|
||||
|
||||
private final boolean defaultImplementation;
|
||||
private final T implementation;
|
||||
private final Collection<Predicate<Context>> filters;
|
||||
|
||||
private final int registrationOrder = ServiceRepository.this.registrationOrder++;
|
||||
private final ExecutionOrder executionOrder;
|
||||
|
||||
private ServiceWrapper(@Nonnull final T implementation,
|
||||
@Nonnull final Collection<Predicate<Context>> filters) {
|
||||
this.defaultImplementation = implementations.isEmpty();
|
||||
this.implementation = implementation;
|
||||
this.filters = filters;
|
||||
ExecutionOrder executionOrder = implementation.order();
|
||||
if (executionOrder == null) {
|
||||
final Order order = implementation.getClass().getAnnotation(Order.class);
|
||||
if (order != null) {
|
||||
executionOrder = order.value();
|
||||
} else {
|
||||
executionOrder = ExecutionOrder.SOON;
|
||||
}
|
||||
}
|
||||
this.executionOrder = executionOrder;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
T getImplementation() {
|
||||
return this.implementation;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
Collection<Predicate<Context>> getFilters() {
|
||||
return Collections.unmodifiableCollection(this.filters);
|
||||
}
|
||||
|
||||
boolean isDefaultImplementation() {
|
||||
return this.defaultImplementation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String
|
||||
.format("ServiceWrapper{type=%s,implementation=%s}", serviceType.toString(),
|
||||
TypeToken.of(implementation.getClass()).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@Nonnull final ServiceWrapper<T> other) {
|
||||
return Comparator.<ServiceWrapper<T>>comparingInt(
|
||||
wrapper -> wrapper.isDefaultImplementation()
|
||||
? Integer.MIN_VALUE
|
||||
: Integer.MAX_VALUE).thenComparingInt(wrapper -> wrapper.executionOrder.ordinal())
|
||||
.thenComparingInt(wrapper -> wrapper.registrationOrder).compare(this, other);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import cloud.commandframework.services.types.ConsumerService;
|
||||
import cloud.commandframework.services.types.Service;
|
||||
import cloud.commandframework.services.types.SideEffectService;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Class that outputs results from the given context, using the specified service type
|
||||
*
|
||||
* @param <Context> Context type
|
||||
* @param <Result> Result type
|
||||
*/
|
||||
public final class ServiceSpigot<Context, Result> {
|
||||
|
||||
private final Context context;
|
||||
private final ServicePipeline pipeline;
|
||||
private final ServiceRepository<Context, Result> repository;
|
||||
|
||||
ServiceSpigot(@Nonnull final ServicePipeline pipeline, @Nonnull final Context context,
|
||||
@Nonnull final TypeToken<? extends Service<Context, Result>> type) {
|
||||
this.context = context;
|
||||
this.pipeline = pipeline;
|
||||
this.repository = pipeline.getRepository(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first result that is generated for the given context. This cannot return {@code null}.
|
||||
* If nothing manages to produce a result, an exception will be thrown. If the pipeline has been
|
||||
* constructed properly, this will never happen.
|
||||
*
|
||||
* @return Generated result
|
||||
* @throws IllegalStateException If no result was found. This only happens if the pipeline has not
|
||||
* been constructed properly. The most likely cause is a faulty
|
||||
* default implementation
|
||||
* @throws IllegalStateException If a {@link SideEffectService} returns {@code null}
|
||||
* @throws PipelineException Any exceptions thrown during result retrieval from the
|
||||
* implementations will be wrapped by {@link PipelineException}. Use
|
||||
* {@link PipelineException#getCause()} to get the exception that
|
||||
* was thrown.
|
||||
* @throws PipelineException Any exceptions thrown during filtering will be wrapped by {@link
|
||||
* PipelineException}. Use {@link PipelineException#getCause()} to
|
||||
* get the exception that was thrown.
|
||||
* @see PipelineException PipelineException wraps exceptions thrown during filtering and result
|
||||
* retrieval
|
||||
*/
|
||||
@Nonnull
|
||||
@SuppressWarnings("unchecked")
|
||||
public Result getResult()
|
||||
throws IllegalStateException, PipelineException {
|
||||
final LinkedList<? extends ServiceRepository<Context, Result>.ServiceWrapper<? extends Service<Context, Result>>>
|
||||
queue = this.repository.getQueue();
|
||||
queue.sort(null); // Sort using the built in comparator method
|
||||
ServiceRepository<Context, Result>.ServiceWrapper<? extends Service<Context, Result>>
|
||||
wrapper;
|
||||
boolean consumerService = false;
|
||||
while ((wrapper = queue.pollLast()) != null) {
|
||||
consumerService = wrapper.getImplementation() instanceof ConsumerService;
|
||||
if (!ServiceFilterHandler.INSTANCE.passes(wrapper, this.context)) {
|
||||
continue;
|
||||
}
|
||||
final Result result;
|
||||
try {
|
||||
result = wrapper.getImplementation().handle(this.context);
|
||||
} catch (final Exception e) {
|
||||
throw new PipelineException(
|
||||
String.format("Failed to retrieve result from %s", wrapper.toString()), e);
|
||||
}
|
||||
if (wrapper.getImplementation() instanceof SideEffectService) {
|
||||
if (result == null) {
|
||||
throw new IllegalStateException(
|
||||
String.format("SideEffectService '%s' returned null", wrapper.toString()));
|
||||
} else if (result == State.ACCEPTED) {
|
||||
return result;
|
||||
}
|
||||
} else if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// This is hack to make it so that the default
|
||||
// consumer implementation does not have to call #interrupt
|
||||
if (consumerService) {
|
||||
return (Result) State.ACCEPTED;
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"No service consumed the context. This means that the pipeline was not constructed properly.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first result that is generated for the given context. If nothing manages to produce a
|
||||
* result, an exception will be thrown. If the pipeline has been constructed properly, this will
|
||||
* never happen. The exception passed to the consumer will be unwrapped, in the case that it's a
|
||||
* {@link PipelineException}. Thus, the actual exception will be given instead of the wrapper.
|
||||
*
|
||||
* @param consumer Result consumer. If an exception was wrong, the result will be {@code null},
|
||||
* otherwise the exception will be non-null and the exception will be {@code
|
||||
* null}.
|
||||
* @throws IllegalStateException If no result was found. This only happens if the pipeline has not
|
||||
* been constructed properly. The most likely cause is a faulty
|
||||
* default implementation
|
||||
* @throws IllegalStateException If a {@link SideEffectService} returns {@code null}
|
||||
*/
|
||||
public void getResult(@Nonnull final BiConsumer<Result, Throwable> consumer) {
|
||||
try {
|
||||
consumer.accept(getResult(), null);
|
||||
} catch (final PipelineException pipelineException) {
|
||||
consumer.accept(null, pipelineException.getCause());
|
||||
} catch (final Exception e) {
|
||||
consumer.accept(null, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first result that is generated for the given context. This cannot return null. If
|
||||
* nothing manages to produce a result, an exception will be thrown. If the pipeline has been
|
||||
* constructed properly, this will never happen.
|
||||
*
|
||||
* @return Generated result
|
||||
*/
|
||||
@Nonnull
|
||||
public CompletableFuture<Result> getResultAsynchronously() {
|
||||
return CompletableFuture.supplyAsync(this::getResult, this.pipeline.getExecutor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward the request through the original pipeline.
|
||||
*
|
||||
* @return New pump, for the result of this request
|
||||
*/
|
||||
@Nonnull
|
||||
public ServicePump<Result> forward() {
|
||||
return this.pipeline.pump(this.getResult());
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward the request through the original pipeline.
|
||||
*
|
||||
* @return New pump, for the result of this request
|
||||
*/
|
||||
@Nonnull
|
||||
public CompletableFuture<ServicePump<Result>> forwardAsynchronously() {
|
||||
return this.getResultAsynchronously().thenApply(pipeline::pump);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services;
|
||||
|
||||
/**
|
||||
* This indicates how a {@link cloud.commandframework.services.types.Service} responded to a given context
|
||||
*/
|
||||
public enum State {
|
||||
/**
|
||||
* The service consumed the context successfully and the execution should stop
|
||||
*/
|
||||
ACCEPTED,
|
||||
/**
|
||||
* The service did not consume the context and the execution should continue
|
||||
*/
|
||||
REJECTED
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.annotations;
|
||||
|
||||
import cloud.commandframework.services.types.Service;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Used to bind methods to services, like such:
|
||||
*
|
||||
* <pre>{@code
|
||||
* {@literal @}Nullable
|
||||
* {@literal @}ServiceImplementation(YourService.class)
|
||||
* public YourResult handle(YourContext) {
|
||||
* return result;
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ServiceImplementation {
|
||||
|
||||
/**
|
||||
* The service class that the method implements
|
||||
*
|
||||
* @return Service to implement
|
||||
*/
|
||||
Class<? extends Service<?, ?>> value();
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
/**
|
||||
* IntellectualSites service pipeline system
|
||||
*/
|
||||
package cloud.commandframework.services;
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.types;
|
||||
|
||||
import cloud.commandframework.services.State;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Service type where each implementation gets a chance to consume the context. This service type
|
||||
* effectively turns the pipeline into an event bus.
|
||||
* <p>
|
||||
* A service can forcefully terminate execution by calling {@link #interrupt()}
|
||||
*
|
||||
* @param <Context> Context
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ConsumerService<Context>
|
||||
extends SideEffectService<Context>, Consumer<Context> {
|
||||
|
||||
/**
|
||||
* Immediately terminate the execution and return {@link State#ACCEPTED}
|
||||
*
|
||||
* @throws PipeBurst Pipe burst
|
||||
*/
|
||||
static void interrupt() throws PipeBurst {
|
||||
throw new PipeBurst();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
default State handle(@Nonnull final Context context) {
|
||||
try {
|
||||
this.accept(context);
|
||||
} catch (final PipeBurst burst) {
|
||||
return State.ACCEPTED;
|
||||
}
|
||||
return State.REJECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept the context. Call {@link #interrupt()} to interrupt the entire pipeline and immediately
|
||||
* return {@link State#ACCEPTED} to the sink
|
||||
*
|
||||
* @param context Context to consume
|
||||
*/
|
||||
@Override
|
||||
void accept(@Nonnull Context context);
|
||||
|
||||
|
||||
class PipeBurst extends RuntimeException {
|
||||
|
||||
private PipeBurst() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Throwable fillInStackTrace() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Throwable initCause(final Throwable cause) {
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.types;
|
||||
|
||||
import cloud.commandframework.services.ChunkedRequestContext;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Service type that allows service to generate partial results for bigger requests
|
||||
*
|
||||
* @param <Context> Context type
|
||||
* @param <Result> Result type
|
||||
* @param <Chunked> Chunk request context
|
||||
*/
|
||||
public interface PartialResultService<Context, Result, Chunked extends ChunkedRequestContext<Context, Result>>
|
||||
extends Service<Chunked, Map<Context, Result>> {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
default Map<Context, Result> handle(@Nonnull final Chunked context) {
|
||||
if (!context.isCompleted()) {
|
||||
this.handleRequests(context.getRemaining()).forEach(context::storeResult);
|
||||
}
|
||||
if (context.isCompleted()) {
|
||||
return context.getAvailableResults();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to generate results for a list of requests, and return a map of all successful
|
||||
* requests
|
||||
*
|
||||
* @param requests Requests
|
||||
* @return Map of request-result pairs
|
||||
*/
|
||||
@Nonnull
|
||||
Map<Context, Result> handleRequests(@Nonnull List<Context> requests);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.types;
|
||||
|
||||
import cloud.commandframework.services.ExecutionOrder;
|
||||
import cloud.commandframework.services.PipelineException;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A service is anything that can take in a context, and produce a response. Most service
|
||||
* implementations will be side effect free, although some service implementations will have side
|
||||
* effects. Those that do, should be clearly labeled
|
||||
*
|
||||
* @param <Context> Context type, this will be the input that is used to generate the response
|
||||
* @param <Result> Response type, this is what is produced by the service ("provided")
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Service<Context, Result> extends Function<Context, Result> {
|
||||
|
||||
/**
|
||||
* Provide a response for the given context. If the service implementation cannot provide a
|
||||
* response for the given context, it should return {@code null}
|
||||
*
|
||||
* @param context Context used in the generation of the response
|
||||
* @return Response. If the response isn't {@code null}, the next service in the service chain
|
||||
* will get to act on the context. Otherwise the execution halts, and the provided response is the
|
||||
* final response.
|
||||
* @throws Exception Any exception that occurs during the handling can be thrown, and will be
|
||||
* wrapped by a {@link PipelineException}
|
||||
*/
|
||||
@Nullable
|
||||
Result handle(@Nonnull Context context) throws Exception;
|
||||
|
||||
@Override
|
||||
default Result apply(@Nonnull Context context) {
|
||||
try {
|
||||
return this.handle(context);
|
||||
} catch (final Exception exception) {
|
||||
throw new PipelineException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the execution order of the service. This should not be overridden, unless you know what you
|
||||
* are doing
|
||||
*
|
||||
* @return Execution order
|
||||
*/
|
||||
@Nullable
|
||||
default ExecutionOrder order() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.types;
|
||||
|
||||
import cloud.commandframework.services.State;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Service implementation that alters the state of the owning application in some way. A
|
||||
* SideEffectService does not return a generated result, instead it returns a response, indicating
|
||||
* whether or not the context was consumed
|
||||
*
|
||||
* @param <Context> Context type.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SideEffectService<Context> extends Service<Context, State> {
|
||||
|
||||
/**
|
||||
* Consumes the context, if possible. Returns {@link State#ACCEPTED} if the input was consumed,
|
||||
* else {@link State#REJECTED}
|
||||
*
|
||||
* @param context context used in the generation of the response
|
||||
* @return Response. If the response isn't {@link State#ACCEPTED}, the next service in the service
|
||||
* chain will get to act on the context. Otherwise the execution halts, and the provided response
|
||||
* is the final response.
|
||||
* @throws Exception Any exception that occurs during the handling can be thrown, and will be
|
||||
* wrapped by a {@link cloud.commandframework.services.PipelineException}
|
||||
*/
|
||||
@Override
|
||||
@Nonnull
|
||||
State handle(@Nonnull Context context) throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
/**
|
||||
* Service implementation types
|
||||
*/
|
||||
package cloud.commandframework.services.types;
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import cloud.commandframework.services.mock.*;
|
||||
import cloud.commandframework.services.types.Service;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ServicesTest {
|
||||
|
||||
@Test
|
||||
public void testPipeline() throws Exception {
|
||||
final ServicePipeline servicePipeline = ServicePipeline.builder().build();
|
||||
Assertions.assertNotNull(servicePipeline);
|
||||
servicePipeline
|
||||
.registerServiceType(TypeToken.of(MockService.class), new DefaultMockService());
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> servicePipeline
|
||||
.registerServiceType(TypeToken.of(MockService.class), new DefaultMockService()));
|
||||
final SecondaryMockService secondaryMockService = new SecondaryMockService();
|
||||
servicePipeline
|
||||
.registerServiceImplementation(TypeToken.of(MockService.class), secondaryMockService,
|
||||
Collections.singleton(secondaryMockService));
|
||||
servicePipeline.registerServiceImplementation(MockService.class,
|
||||
mockContext -> new MockService.MockResult(-91),
|
||||
Collections.singleton(mockContext -> mockContext.getString().startsWith("-91")));
|
||||
Assertions.assertEquals(32,
|
||||
servicePipeline.pump(new MockService.MockContext("Hello")).through(MockService.class)
|
||||
.getResult().getInteger());
|
||||
servicePipeline.pump(new MockService.MockContext("Hello")).through(MockService.class)
|
||||
.getResult(
|
||||
(mockResult, throwable) -> Assertions.assertEquals(32, mockResult.getInteger()));
|
||||
Assertions.assertEquals(999,
|
||||
servicePipeline.pump(new MockService.MockContext("potato")).through(MockService.class)
|
||||
.getResult().getInteger());
|
||||
Assertions.assertEquals(-91,
|
||||
servicePipeline.pump(new MockService.MockContext("-91")).through(MockService.class)
|
||||
.getResult().getInteger());
|
||||
Assertions.assertNotNull(
|
||||
servicePipeline.pump(new MockService.MockContext("oi")).through(MockService.class)
|
||||
.getResultAsynchronously().get());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSideEffectServices() {
|
||||
final ServicePipeline servicePipeline = ServicePipeline.builder().build();
|
||||
servicePipeline.registerServiceType(TypeToken.of(MockSideEffectService.class),
|
||||
new DefaultSideEffectService());
|
||||
final MockSideEffectService.MockPlayer mockPlayer =
|
||||
new MockSideEffectService.MockPlayer(20);
|
||||
Assertions.assertEquals(20, mockPlayer.getHealth());
|
||||
Assertions.assertEquals(State.ACCEPTED,
|
||||
servicePipeline.pump(mockPlayer).through(MockSideEffectService.class).getResult());
|
||||
Assertions.assertEquals(0, mockPlayer.getHealth());
|
||||
mockPlayer.setHealth(20);
|
||||
servicePipeline.registerServiceImplementation(MockSideEffectService.class,
|
||||
new SecondaryMockSideEffectService(), Collections.emptyList());
|
||||
Assertions.assertThrows(IllegalStateException.class,
|
||||
() -> servicePipeline.pump(mockPlayer).through(MockSideEffectService.class)
|
||||
.getResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForwarding() throws Exception {
|
||||
final ServicePipeline servicePipeline = ServicePipeline.builder().build()
|
||||
.registerServiceType(TypeToken.of(MockService.class), new DefaultMockService())
|
||||
.registerServiceType(TypeToken.of(MockResultConsumer.class), new MockResultConsumer());
|
||||
Assertions.assertEquals(State.ACCEPTED,
|
||||
servicePipeline.pump(new MockService.MockContext("huh")).through(MockService.class)
|
||||
.forward().through(MockResultConsumer.class).getResult());
|
||||
Assertions.assertEquals(State.ACCEPTED,
|
||||
servicePipeline.pump(new MockService.MockContext("Something"))
|
||||
.through(MockService.class).forwardAsynchronously()
|
||||
.thenApply(pump -> pump.through(MockResultConsumer.class))
|
||||
.thenApply(ServiceSpigot::getResult).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSorting() {
|
||||
final ServicePipeline servicePipeline = ServicePipeline.builder().build()
|
||||
.registerServiceType(TypeToken.of(MockService.class), new DefaultMockService());
|
||||
servicePipeline.registerServiceImplementation(MockService.class, new MockOrderedFirst(),
|
||||
Collections.emptyList());
|
||||
servicePipeline.registerServiceImplementation(MockService.class, new MockOrderedLast(),
|
||||
Collections.emptyList());
|
||||
// Test that the annotations worked
|
||||
Assertions.assertEquals(1,
|
||||
servicePipeline.pump(new MockService.MockContext("")).through(MockService.class)
|
||||
.getResult().getInteger());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecognisedTypes() {
|
||||
final ServicePipeline servicePipeline = ServicePipeline.builder().build()
|
||||
.registerServiceType(TypeToken.of(MockService.class), new DefaultMockService());
|
||||
Assertions.assertEquals(1, servicePipeline.getRecognizedTypes().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImplementationGetters() {
|
||||
final ServicePipeline servicePipeline = ServicePipeline.builder().build()
|
||||
.registerServiceType(TypeToken.of(MockService.class), new DefaultMockService());
|
||||
servicePipeline.registerServiceImplementation(MockService.class, new MockOrderedFirst(),
|
||||
Collections.emptyList());
|
||||
servicePipeline.registerServiceImplementation(MockService.class, new MockOrderedLast(),
|
||||
Collections.emptyList());
|
||||
final TypeToken<? extends Service<?, ?>> first = TypeToken.of(MockOrderedFirst.class),
|
||||
last = TypeToken.of(MockOrderedLast.class);
|
||||
final TypeToken<MockService> mockServiceType = TypeToken.of(MockService.class);
|
||||
for (TypeToken<?> typeToken : servicePipeline.getRecognizedTypes()) {
|
||||
Assertions.assertEquals(mockServiceType, typeToken);
|
||||
}
|
||||
final Collection<? extends TypeToken<? extends Service<MockService.MockContext, MockService.MockResult>>>
|
||||
impls = servicePipeline.getImplementations(mockServiceType);
|
||||
Assertions.assertEquals(3, impls.size());
|
||||
final Iterator<? extends TypeToken<? extends Service<MockService.MockContext, MockService.MockResult>>>
|
||||
iterator = impls.iterator();
|
||||
Assertions.assertEquals(first, iterator.next());
|
||||
Assertions.assertEquals(last, iterator.next());
|
||||
Assertions.assertEquals(DefaultMockService.class, iterator.next().getRawType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotatedMethods() throws Exception {
|
||||
final ServicePipeline servicePipeline = ServicePipeline.builder().build()
|
||||
.registerServiceType(TypeToken.of(MockService.class), new DefaultMockService())
|
||||
.registerMethods(new AnnotatedMethodTest());
|
||||
final String testString = UUID.randomUUID().toString();
|
||||
Assertions.assertEquals(testString.length(),
|
||||
servicePipeline.pump(new MockService.MockContext(testString)).through(MockService.class)
|
||||
.getResult().getInteger());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConsumerServices() {
|
||||
final ServicePipeline servicePipeline = ServicePipeline.builder().build()
|
||||
.registerServiceType(TypeToken.of(MockConsumerService.class),
|
||||
new StateSettingConsumerService())
|
||||
.registerServiceImplementation(MockConsumerService.class,
|
||||
new InterruptingMockConsumer(), Collections.emptyList());
|
||||
final MockService.MockContext context = new MockService.MockContext("");
|
||||
servicePipeline.pump(context).through(MockConsumerService.class).getResult();
|
||||
Assertions.assertEquals("", context.getState());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPartialResultServices() {
|
||||
final ServicePipeline servicePipeline = ServicePipeline.builder().build()
|
||||
.registerServiceType(TypeToken.of(MockPartialResultService.class),
|
||||
new DefaultPartialRequestService())
|
||||
.registerServiceImplementation(MockPartialResultService.class,
|
||||
new CompletingPartialResultService(), Collections.emptyList());
|
||||
final MockChunkedRequest.Animal cow = new MockChunkedRequest.Animal("cow");
|
||||
final MockChunkedRequest.Animal dog = new MockChunkedRequest.Animal("dog");
|
||||
final MockChunkedRequest.Animal cat = new MockChunkedRequest.Animal("cat");
|
||||
final Map<MockChunkedRequest.Animal, MockChunkedRequest.Sound> sounds =
|
||||
servicePipeline.pump(new MockChunkedRequest(Arrays.asList(cow, dog, cat)))
|
||||
.through(MockPartialResultService.class).getResult();
|
||||
Assertions.assertEquals("moo", sounds.get(cow).getSound());
|
||||
Assertions.assertEquals("woof", sounds.get(dog).getSound());
|
||||
Assertions.assertEquals("unknown", sounds.get(cat).getSound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExceptions() {
|
||||
final ServicePipeline servicePipeline = ServicePipeline.builder().build();
|
||||
Assertions.assertNotNull(servicePipeline);
|
||||
servicePipeline
|
||||
.registerServiceType(TypeToken.of(MockService.class), new DefaultMockService());
|
||||
final PipelineException pipelineException = Assertions.assertThrows(PipelineException.class,
|
||||
() -> servicePipeline.pump(new MockService.MockContext("pls throw exception"))
|
||||
.through(MockService.class).getResult());
|
||||
Assertions.assertEquals(DefaultMockService.TotallyIntentionalException.class,
|
||||
pipelineException.getCause().getClass());
|
||||
servicePipeline.pump(new MockService.MockContext("pls throw exception"))
|
||||
.through(MockService.class).getResult((result, throwable) -> {
|
||||
Assertions.assertNotNull(throwable);
|
||||
Assertions.assertEquals(DefaultMockService.TotallyIntentionalException.class,
|
||||
throwable.getClass());
|
||||
Assertions.assertNull(result);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.mock;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class DefaultMockService implements MockService {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MockResult handle(@Nonnull final MockContext mockContext)
|
||||
throws Exception {
|
||||
if (mockContext.getString().equals("pls throw exception")) {
|
||||
throw new TotallyIntentionalException();
|
||||
}
|
||||
return new MockResult(32);
|
||||
}
|
||||
|
||||
|
||||
public static class TotallyIntentionalException extends Exception {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.mock;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DefaultPartialRequestService implements MockPartialResultService {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Map<MockChunkedRequest.Animal, MockChunkedRequest.Sound> handleRequests(
|
||||
@Nonnull final List<MockChunkedRequest.Animal> requests) {
|
||||
final Map<MockChunkedRequest.Animal, MockChunkedRequest.Sound> map =
|
||||
new HashMap<>(requests.size());
|
||||
for (final MockChunkedRequest.Animal animal : requests) {
|
||||
map.put(animal, new MockChunkedRequest.Sound("unknown"));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.mock;
|
||||
|
||||
import cloud.commandframework.services.types.ConsumerService;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class InterruptingMockConsumer implements MockConsumerService {
|
||||
|
||||
@Override
|
||||
public void accept(@Nonnull final MockService.MockContext mockContext) {
|
||||
ConsumerService.interrupt();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.mock;
|
||||
|
||||
import cloud.commandframework.services.types.ConsumerService;
|
||||
|
||||
public interface MockConsumerService extends ConsumerService<MockService.MockContext> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.mock;
|
||||
|
||||
import cloud.commandframework.services.ExecutionOrder;
|
||||
import cloud.commandframework.services.annotations.Order;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Order(ExecutionOrder.FIRST)
|
||||
public class MockOrderedFirst implements MockService {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MockResult handle(@Nonnull final MockContext mockContext) {
|
||||
return new MockResult(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.mock;
|
||||
|
||||
import cloud.commandframework.services.ExecutionOrder;
|
||||
import cloud.commandframework.services.annotations.Order;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Order(ExecutionOrder.LAST)
|
||||
public class MockOrderedLast implements MockService {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MockResult handle(@Nonnull final MockContext mockContext) {
|
||||
return new MockResult(2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.mock;
|
||||
|
||||
import cloud.commandframework.services.types.PartialResultService;
|
||||
|
||||
public interface MockPartialResultService extends
|
||||
PartialResultService<MockChunkedRequest.Animal, MockChunkedRequest.Sound, MockChunkedRequest> {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.mock;
|
||||
|
||||
import cloud.commandframework.services.State;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class SecondaryMockSideEffectService implements MockSideEffectService {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public State handle(@Nonnull final MockPlayer mockPlayer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 IntellectualSites
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.services.mock;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class StateSettingConsumerService implements MockConsumerService {
|
||||
|
||||
@Override
|
||||
public void accept(@Nonnull final MockService.MockContext mockContext) {
|
||||
mockContext.setState("");
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue