cloud.commandframework.services.ServiceSpigot Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cloud-services Show documentation
Show all versions of cloud-services Show documentation
Command framework and dispatcher for the JVM
//
// MIT License
//
// Copyright (c) 2022 Alexander Söderberg & Contributors
//
// 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.ConsumerService;
import cloud.commandframework.services.types.Service;
import cloud.commandframework.services.types.SideEffectService;
import io.leangen.geantyref.TypeToken;
import java.util.LinkedList;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Class that outputs results from the given context, using the specified service type
*
* @param Context type
* @param Result type
*/
public final class ServiceSpigot {
private final Context context;
private final ServicePipeline pipeline;
private final ServiceRepository repository;
ServiceSpigot(
final @NonNull ServicePipeline pipeline,
final @NonNull Context context,
final @NonNull TypeToken extends Service<@NonNull Context, @NonNull 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
*/
@SuppressWarnings("unchecked")
public @NonNull Result getResult()
throws IllegalStateException, PipelineException {
final LinkedList extends ServiceRepository<@NonNull Context, @NonNull Result>
.ServiceWrapper extends Service<@NonNull Context, @NonNull Result>>>
queue = this.repository.getQueue();
queue.sort(null); // Sort using the built in comparator method
ServiceRepository.ServiceWrapper extends Service>
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), e);
}
if (wrapper.getImplementation() instanceof SideEffectService) {
if (result == null) {
throw new IllegalStateException(String.format("SideEffectService '%s' returned null", wrapper));
} 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(final @NonNull BiConsumer consumer) {
try {
consumer.accept(this.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
*/
public @NonNull CompletableFuture 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
*/
public @NonNull ServicePump forward() {
return this.pipeline.pump(this.getResult());
}
/**
* Forward the request through the original pipeline.
*
* @return New pump, for the result of this request
*/
public @NonNull CompletableFuture> forwardAsynchronously() {
return this.getResultAsynchronously().thenApply(this.pipeline::pump);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy