mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-06-16 08:34:50 +02:00
cc636fff21
This is a complete redesign of what was previously XmppNioTcpConnection. The new architecture allows to extend an XMPP client to server (c2s) connection with new transport bindings and other extensions.
180 lines
6.6 KiB
Java
180 lines
6.6 KiB
Java
/**
|
|
*
|
|
* Copyright 2018-2020 Florian Schmaus
|
|
*
|
|
* 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 org.jivesoftware.smack.c2s.internal;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.AuthenticatedAndResourceBoundStateDescriptor;
|
|
import org.jivesoftware.smack.fsm.LoginContext;
|
|
import org.jivesoftware.smack.fsm.State;
|
|
import org.jivesoftware.smack.fsm.StateDescriptor;
|
|
import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex;
|
|
import org.jivesoftware.smack.fsm.StateTransitionResult;
|
|
import org.jivesoftware.smack.util.CollectionUtil;
|
|
import org.jivesoftware.smack.util.Objects;
|
|
|
|
import org.jxmpp.jid.parts.Resourcepart;
|
|
|
|
public final class WalkStateGraphContext {
|
|
private final Class<? extends StateDescriptor> initialStateClass;
|
|
private final Class<? extends StateDescriptor> finalStateClass;
|
|
private final Class<? extends StateDescriptor> mandatoryIntermediateState;
|
|
private final LoginContext loginContext;
|
|
|
|
private final List<State> walkedStateGraphPath = new ArrayList<>();
|
|
|
|
/**
|
|
* A linked Map of failed States with their reason as value.
|
|
*/
|
|
final Map<State, StateTransitionResult> failedStates = new LinkedHashMap<>();
|
|
|
|
boolean mandatoryIntermediateStateHandled;
|
|
|
|
WalkStateGraphContext(Builder builder) {
|
|
initialStateClass = builder.initialStateClass;
|
|
finalStateClass = builder.finalStateClass;
|
|
mandatoryIntermediateState = builder.mandatoryIntermediateState;
|
|
loginContext = builder.loginContext;
|
|
}
|
|
|
|
public void recordWalkTo(State state) {
|
|
walkedStateGraphPath.add(state);
|
|
}
|
|
|
|
public boolean isWalksFinalState(StateDescriptor stateDescriptor) {
|
|
return stateDescriptor.getClass() == finalStateClass;
|
|
}
|
|
|
|
public boolean isFinalStateAuthenticatedAndResourceBound() {
|
|
return finalStateClass == AuthenticatedAndResourceBoundStateDescriptor.class;
|
|
}
|
|
|
|
public GraphVertex<State> maybeReturnMandatoryImmediateState(List<GraphVertex<State>> outgoingStateEdges) {
|
|
for (GraphVertex<State> outgoingStateVertex : outgoingStateEdges) {
|
|
if (outgoingStateVertex.getElement().getStateDescriptor().getClass() == mandatoryIntermediateState) {
|
|
mandatoryIntermediateStateHandled = true;
|
|
return outgoingStateVertex;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public List<State> getWalk() {
|
|
return CollectionUtil.newListWith(walkedStateGraphPath);
|
|
}
|
|
|
|
public int getWalkLength() {
|
|
return walkedStateGraphPath.size();
|
|
}
|
|
|
|
public void appendWalkTo(List<State> walk) {
|
|
walk.addAll(walkedStateGraphPath);
|
|
}
|
|
|
|
public LoginContext getLoginContext() {
|
|
return loginContext;
|
|
}
|
|
|
|
public boolean stateAlreadyVisited(State state) {
|
|
return walkedStateGraphPath.contains(state);
|
|
}
|
|
|
|
public void recordFailedState(State state, StateTransitionResult stateTransitionResult) {
|
|
failedStates.put(state, stateTransitionResult);
|
|
}
|
|
|
|
public Map<State, StateTransitionResult> getFailedStates() {
|
|
return new HashMap<>(failedStates);
|
|
}
|
|
|
|
/**
|
|
* Check if the way to the final state via the given successor state that would loop, i.e., lead over the initial state and
|
|
* thus from a cycle.
|
|
*
|
|
* @param successorStateVertex the successor state to use on the way.
|
|
* @return <code>true</code> if it would loop, <code>false</code> otherwise.
|
|
*/
|
|
public boolean wouldCauseCycle(GraphVertex<State> successorStateVertex) {
|
|
Set<Class<? extends StateDescriptor>> visited = new HashSet<>();
|
|
return wouldCycleRecursive(successorStateVertex, visited);
|
|
}
|
|
|
|
private boolean wouldCycleRecursive(GraphVertex<State> stateVertex, Set<Class<? extends StateDescriptor>> visited) {
|
|
Class<? extends StateDescriptor> stateVertexClass = stateVertex.getElement().getStateDescriptor().getClass();
|
|
|
|
if (stateVertexClass == initialStateClass) {
|
|
return true;
|
|
}
|
|
if (finalStateClass == stateVertexClass || visited.contains(stateVertexClass)) {
|
|
return false;
|
|
}
|
|
|
|
visited.add(stateVertexClass);
|
|
|
|
for (GraphVertex<State> successorStateVertex : stateVertex.getOutgoingEdges()) {
|
|
boolean cycle = wouldCycleRecursive(successorStateVertex, visited);
|
|
if (cycle) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static Builder builder(Class<? extends StateDescriptor> initialStateClass, Class<? extends StateDescriptor> finalStateClass) {
|
|
return new Builder(initialStateClass, finalStateClass);
|
|
}
|
|
|
|
public static final class Builder {
|
|
private final Class<? extends StateDescriptor> initialStateClass;
|
|
private final Class<? extends StateDescriptor> finalStateClass;
|
|
private Class<? extends StateDescriptor> mandatoryIntermediateState;
|
|
private LoginContext loginContext;
|
|
|
|
private Builder(Class<? extends StateDescriptor> initialStateClass, Class<? extends StateDescriptor> finalStateClass) {
|
|
this.initialStateClass = Objects.requireNonNull(initialStateClass);
|
|
this.finalStateClass = Objects.requireNonNull(finalStateClass);
|
|
}
|
|
|
|
public Builder withMandatoryIntermediateState(Class<? extends StateDescriptor> mandatoryIntermedidateState) {
|
|
this.mandatoryIntermediateState = mandatoryIntermedidateState;
|
|
return this;
|
|
}
|
|
|
|
public Builder withLoginContext(String username, String password, Resourcepart resource) {
|
|
LoginContext loginContext = new LoginContext(username, password, resource);
|
|
return withLoginContext(loginContext);
|
|
}
|
|
|
|
public Builder withLoginContext(LoginContext loginContext) {
|
|
this.loginContext = loginContext;
|
|
return this;
|
|
}
|
|
|
|
public WalkStateGraphContext build() {
|
|
return new WalkStateGraphContext(this);
|
|
}
|
|
}
|
|
}
|