/* * Copyright (c) 2016 Couchbase, Inc. * * 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.couchbase.client.core.state; import com.couchbase.client.core.logging.CouchbaseLogger; import com.couchbase.client.core.logging.CouchbaseLoggerFactory; import rx.Observable; import rx.subjects.BehaviorSubject; import rx.subjects.Subject; /** * Abstract {@link Stateful} implementation which acts like a simple state machine. * * This class is thread safe, so state transitions can be issued from any thread without any further synchronization. * * @author Michael Nitschinger * @since 1.0 */ public class AbstractStateMachine<S extends Enum> implements Stateful<S> { /** * The logger used. */ private static final CouchbaseLogger LOGGER = CouchbaseLoggerFactory.getInstance(Stateful.class); /** * The observable which emits all the subsequent state changes. */ private final Subject<S, S> observable; /** * The current state of the state machine. */ private volatile S currentState; /** * Creates a new state machine. * * @param initialState the initial state of the state machine. */ protected AbstractStateMachine(final S initialState) { currentState = initialState; observable = BehaviorSubject.create(currentState).toSerialized(); } @Override public final Observable<S> states() { return observable; } @Override public final S state() { return currentState; } @Override public final boolean isState(final S state) { return currentState == state; } @Override public boolean hasSubscribers() { return observable.hasObservers(); } /** * Transition into a new state. * * This method is intentionally not public, because the subclass should only be responsible for the actual * transitions, the other components only react on those transitions eventually. * * @param newState the states to transition into. */ protected void transitionState(final S newState) { if (newState != currentState) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("State (" + getClass().getSimpleName() + ") " + currentState + " -> " + newState); } currentState = newState; observable.onNext(newState); } } }