/*
* Copyright 2014 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
*
* 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.springframework.integration.websocket.support;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.messaging.SubProtocolHandler;
/**
* The utility class to encapsulate search algorithms for a set of provided {@link SubProtocolHandler}s.
* <p>
* For internal use only.
*
* @author Andy Wilkinson
* @author Artem Bilan
* @since 4.1
* @see org.springframework.integration.websocket.inbound.WebSocketInboundChannelAdapter
* @see org.springframework.integration.websocket.outbound.WebSocketOutboundMessageHandler
*/
public final class SubProtocolHandlerRegistry {
private final static Log logger = LogFactory.getLog(SubProtocolHandlerRegistry.class);
private final Map<String, SubProtocolHandler> protocolHandlers =
new TreeMap<String, SubProtocolHandler>(String.CASE_INSENSITIVE_ORDER);
private final SubProtocolHandler defaultProtocolHandler;
public SubProtocolHandlerRegistry(List<SubProtocolHandler> protocolHandlers) {
this(protocolHandlers, null);
}
public SubProtocolHandlerRegistry(SubProtocolHandler defaultProtocolHandler) {
this(null, defaultProtocolHandler);
}
public SubProtocolHandlerRegistry(List<SubProtocolHandler> protocolHandlers,
SubProtocolHandler defaultProtocolHandler) {
Assert.state(!CollectionUtils.isEmpty(protocolHandlers) || defaultProtocolHandler != null,
"One of 'protocolHandlers' or 'defaultProtocolHandler' must be provided");
if (!CollectionUtils.isEmpty(protocolHandlers)) {
for (SubProtocolHandler handler : protocolHandlers) {
List<String> protocols = handler.getSupportedProtocols();
if (CollectionUtils.isEmpty(protocols)) {
logger.warn("No sub-protocols, ignoring handler " + handler);
continue;
}
for (String protocol : protocols) {
SubProtocolHandler replaced = this.protocolHandlers.put(protocol, handler);
if (replaced != null) {
throw new IllegalStateException("Failed to map handler " + handler
+ " to protocol '" + protocol + "', it is already mapped to handler " + replaced);
}
}
}
}
if (this.protocolHandlers.size() == 1 && defaultProtocolHandler == null) {
this.defaultProtocolHandler = this.protocolHandlers.values().iterator().next();
}
else {
this.defaultProtocolHandler = defaultProtocolHandler;
if (this.protocolHandlers.isEmpty() && this.defaultProtocolHandler != null) {
List<String> protocols = this.defaultProtocolHandler.getSupportedProtocols();
for (String protocol : protocols) {
SubProtocolHandler replaced = this.protocolHandlers.put(protocol, this.defaultProtocolHandler);
if (replaced != null) {
throw new IllegalStateException("Failed to map handler " + this.defaultProtocolHandler
+ " to protocol '" + protocol + "', it is already mapped to handler " + replaced);
}
}
}
}
}
/**
* Resolves the {@link SubProtocolHandler} for the given {@code session} using
* its {@link WebSocketSession#getAcceptedProtocol() accepted sub-protocol}.
* @param session The session to resolve the sub-protocol handler for
* @return The sub-protocol handler
* @throws IllegalStateException if a protocol handler cannot be resolved
*/
public SubProtocolHandler findProtocolHandler(WebSocketSession session) {
SubProtocolHandler handler;
String protocol = session.getAcceptedProtocol();
if (StringUtils.hasText(protocol)) {
handler = this.protocolHandlers.get(protocol);
Assert.state(handler != null,
"No handler for sub-protocol '" + protocol + "', handlers = " + this.protocolHandlers);
}
else {
handler = this.defaultProtocolHandler;
Assert.state(handler != null,
"No sub-protocol was requested and a default sub-protocol handler was not configured");
}
return handler;
}
/**
* Resolves the {@code sessionId} for the given {@code message} using
* the {@link SubProtocolHandler#resolveSessionId} algorithm.
* @param message The message to resolve the {@code sessionId} from.
* @return The sessionId or {@code null}, if no one {@link SubProtocolHandler}
* can't resolve it against provided {@code message}.
*/
public String resolveSessionId(Message<?> message) {
for (SubProtocolHandler handler : this.protocolHandlers.values()) {
String sessionId = handler.resolveSessionId(message);
if (sessionId != null) {
return sessionId;
}
}
if (this.defaultProtocolHandler != null) {
String sessionId = this.defaultProtocolHandler.resolveSessionId(message);
if (sessionId != null) {
return sessionId;
}
}
return null;
}
/**
* Return the {@link List} of sub-protocols from provided {@link SubProtocolHandler}.
* @return The the {@link List} of supported sub-protocols.
*/
public List<String> getSubProtocols() {
return new ArrayList<String>(this.protocolHandlers.keySet());
}
}