Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.spotify.mobius.Mobius Maven / Gradle / Ivy
/*
* -\-\-
* Mobius
* --
* Copyright (c) 2017-2018 Spotify AB
* --
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* -/-/-
*/
package com.spotify.mobius;
import static com.spotify.mobius.internal_util.Preconditions.checkNotNull;
import com.spotify.mobius.functions.Consumer;
import com.spotify.mobius.functions.Producer;
import com.spotify.mobius.internal_util.ImmutableUtil;
import com.spotify.mobius.runners.WorkRunner;
import com.spotify.mobius.runners.WorkRunners;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class Mobius {
private Mobius() {
// prevent instantiation
}
private static final Connectable, ?> NOOP_EVENT_SOURCE =
new Connectable() {
@Nonnull
@Override
public Connection connect(Consumer output)
throws ConnectionLimitExceededException {
return new Connection() {
@Override
public void accept(Object value) {}
@Override
public void dispose() {}
};
}
};
private static final MobiusLoop.Logger, ?, ?> NOOP_LOGGER =
new MobiusLoop.Logger() {
@Override
public void beforeInit(Object model) {
/* noop */
}
@Override
public void afterInit(Object model, First result) {
/* noop */
}
@Override
public void exceptionDuringInit(Object model, Throwable exception) {
System.err.println("error initialising from model: '" + model + "' - " + exception);
exception.printStackTrace(System.err);
}
@Override
public void beforeUpdate(Object model, Object event) {
/* noop */
}
@Override
public void afterUpdate(Object model, Object event, Next result) {
/* noop */
}
@Override
public void exceptionDuringUpdate(Object model, Object event, Throwable exception) {
System.err.println(
"error updating model: '" + model + "' with event: '" + event + "' - " + exception);
exception.printStackTrace(System.err);
}
};
/**
* Create a {@link MobiusLoop.Builder} to help you configure a MobiusLoop before starting it.
*
* Once done configuring the loop you can start the loop using {@link
* MobiusLoop.Factory#startFrom(Object)}.
*
* @param update the {@link Update} function of the loop
* @param effectHandler the {@link Connectable} effect handler of the loop
* @return a {@link MobiusLoop.Builder} instance that you can further configure before starting
* the loop
*/
public static MobiusLoop.Builder loop(
Update update, Connectable effectHandler) {
//noinspection unchecked
return new Builder<>(
update,
effectHandler,
null,
(Connectable) NOOP_EVENT_SOURCE,
(MobiusLoop.Logger) NOOP_LOGGER,
new Producer() {
@Nonnull
@Override
public WorkRunner get() {
return WorkRunners.from(Executors.newSingleThreadExecutor(Builder.THREAD_FACTORY));
}
},
new Producer() {
@Nonnull
@Override
public WorkRunner get() {
return WorkRunners.from(Executors.newCachedThreadPool(Builder.THREAD_FACTORY));
}
});
}
/**
* Create a {@link MobiusLoop.Controller} that allows you to start, stop, and restart MobiusLoops.
*
* @param loopFactory a factory for creating loops
* @param defaultModel the model the controller should start from
* @return a new controller
*/
public static MobiusLoop.Controller controller(
MobiusLoop.Factory loopFactory, M defaultModel) {
return new MobiusLoopController<>(loopFactory, defaultModel, null, WorkRunners.immediate());
}
/**
* Create a {@link MobiusLoop.Controller} that allows you to start, stop, and restart MobiusLoops.
*
* @param loopFactory a factory for creating loops
* @param defaultModel the model the controller should start from
* @param init the init function to run when a loop starts
* @return a new controller
*/
public static MobiusLoop.Controller controller(
MobiusLoop.Factory loopFactory, M defaultModel, Init init) {
return new MobiusLoopController<>(loopFactory, defaultModel, init, WorkRunners.immediate());
}
/**
* Create a {@link MobiusLoop.Controller} that allows you to start, stop, and restart MobiusLoops.
*
* @param loopFactory a factory for creating loops
* @param defaultModel the model the controller should start from
* @param modelRunner the WorkRunner to use when observing model changes
* @return a new controller
*/
public static MobiusLoop.Controller controller(
MobiusLoop.Factory loopFactory, M defaultModel, WorkRunner modelRunner) {
return new MobiusLoopController<>(loopFactory, defaultModel, null, modelRunner);
}
/**
* Create a {@link MobiusLoop.Controller} that allows you to start, stop, and restart MobiusLoops.
*
* @param loopFactory a factory for creating loops
* @param defaultModel the model the controller should start from
* @param init the init function to run when a loop starts
* @param modelRunner the WorkRunner to use when observing model changes
* @return a new controller
*/
public static MobiusLoop.Controller controller(
MobiusLoop.Factory loopFactory,
M defaultModel,
Init init,
WorkRunner modelRunner) {
return new MobiusLoopController<>(loopFactory, defaultModel, init, modelRunner);
}
private static final class Builder implements MobiusLoop.Builder {
private static final MyThreadFactory THREAD_FACTORY = new MyThreadFactory();
private final Update update;
private final Connectable effectHandler;
@Nullable private final Init init;
private final Connectable eventSource;
private final Producer eventRunner;
private final Producer effectRunner;
private final MobiusLoop.Logger logger;
private Builder(
Update update,
Connectable effectHandler,
@Nullable Init init,
Connectable eventSource,
MobiusLoop.Logger logger,
Producer eventRunner,
Producer effectRunner) {
this.update = checkNotNull(update);
this.effectHandler = checkNotNull(effectHandler);
this.init = init;
this.eventSource = checkNotNull(eventSource);
this.eventRunner = checkNotNull(eventRunner);
this.effectRunner = checkNotNull(effectRunner);
this.logger = checkNotNull(logger);
}
@Override
@Nonnull
public MobiusLoop.Builder init(Init init) {
return new Builder<>(
update,
effectHandler,
checkNotNull(init),
eventSource,
logger,
eventRunner,
effectRunner);
}
@Override
@Nonnull
public MobiusLoop.Builder eventSource(Connectable eventSource) {
return new Builder<>(
update, effectHandler, init, eventSource, logger, eventRunner, effectRunner);
}
@Override
@Nonnull
public MobiusLoop.Builder eventSource(EventSource eventSource) {
return new Builder<>(
update,
effectHandler,
init,
EventSourceConnectable.create(eventSource),
logger,
eventRunner,
effectRunner);
}
@Nonnull
@Override
public MobiusLoop.Builder eventSources(
EventSource eventSource, EventSource... eventSources) {
EventSource mergedSource = MergedEventSource.from(eventSource, eventSources);
return new Builder<>(
update,
effectHandler,
init,
EventSourceConnectable.create(mergedSource),
logger,
eventRunner,
effectRunner);
}
@Override
@Nonnull
public MobiusLoop.Builder logger(MobiusLoop.Logger logger) {
return new Builder<>(
update, effectHandler, init, eventSource, logger, eventRunner, effectRunner);
}
@Override
@Nonnull
public MobiusLoop.Builder eventRunner(Producer eventRunner) {
return new Builder<>(
update, effectHandler, init, eventSource, logger, eventRunner, effectRunner);
}
@Override
@Nonnull
public MobiusLoop.Builder effectRunner(Producer effectRunner) {
return new Builder<>(
update, effectHandler, init, eventSource, logger, eventRunner, effectRunner);
}
@Override
@Nonnull
public MobiusLoop startFrom(final M startModel) {
M firstModel = startModel;
Set firstEffects = ImmutableUtil.emptySet();
if (init != null) {
LoggingInit loggingInit = new LoggingInit<>(init, logger);
First first = loggingInit.init(checkNotNull(startModel));
firstModel = first.model();
firstEffects = first.effects();
}
return startFromInternal(firstModel, firstEffects);
}
@Override
@Nonnull
public MobiusLoop startFrom(M startModel, Set startEffects) {
if (init != null) {
throw new IllegalArgumentException(
"cannot pass in start effects when a loop has init defined");
}
return startFromInternal(startModel, startEffects);
}
private MobiusLoop startFromInternal(M startModel, Set startEffects) {
LoggingUpdate loggingUpdate = new LoggingUpdate<>(update, logger);
return MobiusLoop.create(
loggingUpdate,
startModel,
startEffects,
effectHandler,
eventSource,
checkNotNull(eventRunner.get()),
checkNotNull(effectRunner.get()));
}
private static class MyThreadFactory implements ThreadFactory {
private static final AtomicLong threadCount = new AtomicLong(0);
@Override
public Thread newThread(Runnable runnable) {
Thread thread = Executors.defaultThreadFactory().newThread(checkNotNull(runnable));
thread.setName(
String.format(Locale.ENGLISH, "mobius-thread-%d", threadCount.incrementAndGet()));
return thread;
}
}
}
}