All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.messaging.rsocket.annotation.support.RSocketFrameTypeMessageCondition Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2019 the original author or authors.
 *
 * 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
 *
 *      https://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.springframework.messaging.rsocket.annotation.support;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import io.rsocket.frame.FrameType;

import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.AbstractMessageCondition;
import org.springframework.util.Assert;

/**
 * A condition to assist with mapping onto handler methods based on the RSocket
 * frame type. This helps to separate the handling of connection-level frame
 * types, i.e. {@code SETUP} and {@code METADATA_PUSH}, from the handling of
 * stream requests.
 *
 * @author Rossen Stoyanchev
 * @since 5.2
 */
public class RSocketFrameTypeMessageCondition extends AbstractMessageCondition {

	/** The name of the header that contains the RSocket frame type being processed. */
	public static final String FRAME_TYPE_HEADER = "rsocketFrameType";


	/** Match connection-level frames "SETUP" or "METADATA_PUSH". */
	public static final RSocketFrameTypeMessageCondition CONNECT_CONDITION =
			new RSocketFrameTypeMessageCondition(FrameType.SETUP, FrameType.METADATA_PUSH);

	/** Match RSocket frames "REQUEST_FNF" or "REQUEST_RESPONSE". */
	public static final RSocketFrameTypeMessageCondition REQUEST_FNF_OR_RESPONSE_CONDITION =
			new RSocketFrameTypeMessageCondition(FrameType.REQUEST_FNF, FrameType.REQUEST_RESPONSE);

	/** Match RSocket frame "REQUEST_RESPONSE". */
	public static final RSocketFrameTypeMessageCondition REQUEST_RESPONSE_CONDITION =
			new RSocketFrameTypeMessageCondition(FrameType.REQUEST_RESPONSE);

	/** Match RSocket frame "REQUEST_STREAM". */
	public static final RSocketFrameTypeMessageCondition REQUEST_STREAM_CONDITION =
			new RSocketFrameTypeMessageCondition(FrameType.REQUEST_STREAM);

	/** Match RSocket frame "REQUEST_CHANNEL". */
	public static final RSocketFrameTypeMessageCondition REQUEST_CHANNEL_CONDITION =
			new RSocketFrameTypeMessageCondition(FrameType.REQUEST_CHANNEL);

	/** Empty condition that does not match to any RSocket frames (e.g. for type-level mappings) */
	public static final RSocketFrameTypeMessageCondition EMPTY_CONDITION = new RSocketFrameTypeMessageCondition();


	/**
	 * Condition to match "REQUEST_FNF", "REQUEST_RESPONSE", "REQUEST_STREAM",
	 * and "REQUEST_CHANNEL".
	 * @deprecated as of 5.2.2 because matching to all interaction types is too
	 * flexible. Please use one of the other constants in this class that match
	 * to specific frames.
	 */
	@Deprecated
	public static final RSocketFrameTypeMessageCondition REQUEST_CONDITION =
			new RSocketFrameTypeMessageCondition(
					FrameType.REQUEST_FNF,
					FrameType.REQUEST_RESPONSE,
					FrameType.REQUEST_STREAM,
					FrameType.REQUEST_CHANNEL);

	/** Per FrameType cache to return ready instances from getMatchingCondition. */
	private static final Map frameTypeConditionCache;

	static {
		frameTypeConditionCache = new HashMap<>(FrameType.values().length);
		for (FrameType type : FrameType.values()) {
			frameTypeConditionCache.put(type.name(), new RSocketFrameTypeMessageCondition(type));
		}
	}


	private final Set frameTypes;


	public RSocketFrameTypeMessageCondition(FrameType... frameType) {
		this(Arrays.asList(frameType));
	}

	public RSocketFrameTypeMessageCondition(Collection frameTypes) {
		Assert.notEmpty(frameTypes, "`frameTypes` are required");
		this.frameTypes = Collections.unmodifiableSet(new LinkedHashSet<>(frameTypes));
	}

	private RSocketFrameTypeMessageCondition() {
		this.frameTypes = Collections.emptySet();
	}


	public Set getFrameTypes() {
		return this.frameTypes;
	}

	@Override
	protected Collection getContent() {
		return this.frameTypes;
	}

	@Override
	protected String getToStringInfix() {
		return " || ";
	}

	/**
	 * Find the RSocket frame type in the message headers.
	 * @param message the current message
	 * @return the frame type or {@code null} if not found
	 */
	@SuppressWarnings("ConstantConditions")
	@Nullable
	public static FrameType getFrameType(Message message) {
		return (FrameType) message.getHeaders().get(RSocketFrameTypeMessageCondition.FRAME_TYPE_HEADER);
	}


	@Override
	public RSocketFrameTypeMessageCondition combine(RSocketFrameTypeMessageCondition other) {
		if (this.frameTypes.equals(other.frameTypes)) {
			return other;
		}
		Set set = new LinkedHashSet<>(this.frameTypes);
		set.addAll(other.frameTypes);
		return new RSocketFrameTypeMessageCondition(set);
	}

	@Override
	public RSocketFrameTypeMessageCondition getMatchingCondition(Message message) {
		FrameType actual = message.getHeaders().get(FRAME_TYPE_HEADER, FrameType.class);
		if (actual != null) {
			for (FrameType type : this.frameTypes) {
				if (actual == type) {
					return frameTypeConditionCache.get(type.name());
				}
			}
		}
		return  null;
	}

	@Override
	public int compareTo(RSocketFrameTypeMessageCondition other, Message message) {
		return other.frameTypes.size() - this.frameTypes.size();
	}


	/**
	 * Return a condition for matching the RSocket request interaction type with
	 * that is selected based on the delcared request and response cardinality
	 * of some handler method.
	 * 

The table below shows the selections made: *

* * * * * * * * * * * * * * * * * * * * * * * * * *
Request CardinalityResponse CardinalityInteraction Types
0,10Fire-And-Forget, Request-Response
0,11Request-Response
0,12Request-Stream
2AnyRequest-Channel
* @param cardinalityIn -- the request cardinality: 1 for a single payload, * 2 for many payloads, and 0 if input is not handled. * @param cardinalityOut -- the response cardinality: 0 for no output * payloads, 1 for a single payload, and 2 for many payloads. * @return a condition to use for matching the interaction type * @since 5.2.2 */ public static RSocketFrameTypeMessageCondition getCondition(int cardinalityIn, int cardinalityOut) { switch (cardinalityIn) { case 0: case 1: switch (cardinalityOut) { case 0: return REQUEST_FNF_OR_RESPONSE_CONDITION; case 1: return REQUEST_RESPONSE_CONDITION; case 2: return REQUEST_STREAM_CONDITION; default: throw new IllegalStateException("Invalid cardinality: " + cardinalityOut); } case 2: return REQUEST_CHANNEL_CONDITION; default: throw new IllegalStateException("Invalid cardinality: " + cardinalityIn); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy