ws.wamp.jawampa.client.StateController Maven / Gradle / Ivy
/*
* Copyright 2014 Matthias Einwag
*
* The jawampa authors license this file to you 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 ws.wamp.jawampa.client;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import rx.Observable;
import rx.Scheduler;
import rx.schedulers.Schedulers;
import rx.subjects.BehaviorSubject;
import ws.wamp.jawampa.WampClient;
import ws.wamp.jawampa.WampClient.DisconnectedState;
import ws.wamp.jawampa.WampClient.State;
import ws.wamp.jawampa.WampMessages.WampMessage;
public class StateController {
private boolean isCompleted = false;
private ClientState currentState = new InitialState(this);
private ClientConfiguration clientConfig;
private final ScheduledExecutorService scheduler;
private final Scheduler rxScheduler;
/** The current externally visible status */
private State extState = new DisconnectedState(null);
/** Holds the final value with which {@link WampClient#status} will be completed */
private Throwable closeError = null;
/** Observable that provides the external state */
private BehaviorSubject statusObservable = BehaviorSubject.create(extState);
public StateController(ClientConfiguration clientConfig) {
this.clientConfig = clientConfig;
this.scheduler = clientConfig.connectorProvider().createScheduler();
this.rxScheduler = Schedulers.from(scheduler);
}
public ClientConfiguration clientConfig() {
return clientConfig;
}
public ScheduledExecutorService scheduler() {
return scheduler;
}
public Scheduler rxScheduler() {
return rxScheduler;
}
public Observable statusObservable() {
return statusObservable;
}
public void setExternalState(State newState) {
extState = newState;
statusObservable.onNext(extState);
}
public void setCloseError(Throwable closeError) {
this.closeError = closeError;
}
/**
* Tries to schedule a runnable on the provided scheduler.
* Rejected executions will be suppressed.
*
* @param action The action to schedule.
*/
public void tryScheduleAction(Runnable action) {
try {
scheduler.submit(action);
} catch (RejectedExecutionException e) {}
}
public ClientState currentState() {
return currentState;
}
public void setState(ClientState newState) {
ClientState lastState = currentState;
if (lastState != null) lastState.onLeave(newState);
currentState = newState;
newState.onEnter(lastState);
}
/**
* Is called when the underlying connection received a message from the remote side.
* @param message The received message
*/
void onMessage(WampMessage message) {
if (currentState instanceof SessionEstablishedState)
((SessionEstablishedState)currentState).onMessage(message);
else if (currentState instanceof HandshakingState)
((HandshakingState)currentState).onMessage(message);
}
/**
* Is called if the underlying connection was closed from the remote side.
* Won't be called if the user issues the close, since the client will then move
* to the {@link WaitingForDisconnectState} directly.
* @param closeReason An optional reason why the connection closed.
*/
void onConnectionClosed(Throwable closeReason) {
if (currentState instanceof SessionEstablishedState)
((SessionEstablishedState)currentState).onConnectionClosed(closeReason);
else if (currentState instanceof HandshakingState)
((HandshakingState)currentState).onConnectionClosed(closeReason);
}
/**
* Initiates the open process.
* If open was initiated before nothing will happen.
*/
public void open() {
scheduler.execute(new Runnable() {
@Override
public void run() {
if (!(currentState instanceof InitialState)) return;
// Try to connect afterwards
// This guarantees that the external state will always
// switch to connecting, even when the attempt immediately
// fails
int nrConnects = clientConfig.totalNrReconnects();
if (nrConnects == 0) nrConnects = 1;
ConnectingState newState =
new ConnectingState(StateController.this, nrConnects);
setState(newState);
}
});
}
/**
* Initiates the close process.
* Will be called on {@link WampClient#close()} of the client
*/
public void initClose() {
tryScheduleAction(new Runnable() {
@Override
public void run() {
if (isCompleted) return;// Check if already closed
isCompleted = true;
// Initialize the close sequence
// The state will try to move to the final state
currentState.initClose();
}
});
}
/**
* Performs the shutdown once the statemachine is in it's terminal state.
*/
public void performShutdown() {
if (closeError != null)
statusObservable.onError(closeError);
else
statusObservable.onCompleted();
scheduler.shutdown();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy