🚚 Switch namespace

This commit is contained in:
Alexander Söderberg 2020-09-27 23:04:15 +02:00
parent 0064093dbf
commit c74cda3a0f
No known key found for this signature in database
GPG key ID: C0207FF7EA146678
207 changed files with 2689 additions and 611 deletions

21
cloud-services/LICENSE Normal file
View 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
View file

@ -0,0 +1,433 @@
# Rörledning
[![CodeFactor](https://www.codefactor.io/repository/github/sauilitired/rorledning/badge/master)](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 &#64;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
}
});
```

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}

View file

@ -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
}

View file

@ -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();
}

View file

@ -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;

View file

@ -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;
}
}
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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);
});
}
}

View file

@ -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 {
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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> {
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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> {
}

View file

@ -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;
}
}

View file

@ -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("");
}
}