001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.socket.server.standard;
018
019import java.util.ArrayList;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023
024import javax.websocket.Decoder;
025import javax.websocket.Encoder;
026import javax.websocket.Endpoint;
027import javax.websocket.Extension;
028import javax.websocket.HandshakeResponse;
029import javax.websocket.server.HandshakeRequest;
030import javax.websocket.server.ServerEndpointConfig;
031
032import org.springframework.beans.factory.BeanFactory;
033import org.springframework.beans.factory.BeanFactoryAware;
034import org.springframework.lang.Nullable;
035import org.springframework.util.Assert;
036import org.springframework.web.socket.handler.BeanCreatingHandlerProvider;
037
038/**
039 * An implementation of {@link javax.websocket.server.ServerEndpointConfig} for use in
040 * Spring-based applications. A {@link ServerEndpointRegistration} bean is detected by
041 * {@link ServerEndpointExporter} and registered with a Java WebSocket runtime at startup.
042 *
043 * <p>Class constructors accept a singleton {@link javax.websocket.Endpoint} instance
044 * or an Endpoint specified by type {@link Class}. When specified by type, the endpoint
045 * will be instantiated and initialized through the Spring ApplicationContext before
046 * each client WebSocket connection.
047 *
048 * <p>This class also extends
049 * {@link javax.websocket.server.ServerEndpointConfig.Configurator} to make it easier
050 * to override methods for customizing the handshake process.
051 *
052 * @author Rossen Stoyanchev
053 * @author Juergen Hoeller
054 * @since 4.0
055 * @see ServerEndpointExporter
056 */
057public class ServerEndpointRegistration extends ServerEndpointConfig.Configurator
058                implements ServerEndpointConfig, BeanFactoryAware {
059
060        private final String path;
061
062        @Nullable
063        private final Endpoint endpoint;
064
065        @Nullable
066        private final BeanCreatingHandlerProvider<Endpoint> endpointProvider;
067
068        private List<String> subprotocols = new ArrayList<>(0);
069
070        private List<Extension> extensions = new ArrayList<>(0);
071
072        private List<Class<? extends Encoder>> encoders = new ArrayList<>(0);
073
074        private List<Class<? extends Decoder>> decoders = new ArrayList<>(0);
075
076        private final Map<String, Object> userProperties = new HashMap<>(4);
077
078
079        /**
080         * Create a new {@link ServerEndpointRegistration} instance from an
081         * {@code javax.websocket.Endpoint} instance.
082         * @param path the endpoint path
083         * @param endpoint the endpoint instance
084         */
085        public ServerEndpointRegistration(String path, Endpoint endpoint) {
086                Assert.hasText(path, "Path must not be empty");
087                Assert.notNull(endpoint, "Endpoint must not be null");
088                this.path = path;
089                this.endpoint = endpoint;
090                this.endpointProvider = null;
091        }
092
093        /**
094         * Create a new {@link ServerEndpointRegistration} instance from an
095         * {@code javax.websocket.Endpoint} class.
096         * @param path the endpoint path
097         * @param endpointClass the endpoint class
098         */
099        public ServerEndpointRegistration(String path, Class<? extends Endpoint> endpointClass) {
100                Assert.hasText(path, "Path must not be empty");
101                Assert.notNull(endpointClass, "Endpoint Class must not be null");
102                this.path = path;
103                this.endpoint = null;
104                this.endpointProvider = new BeanCreatingHandlerProvider<>(endpointClass);
105        }
106
107
108        // ServerEndpointConfig implementation
109
110        @Override
111        public String getPath() {
112                return this.path;
113        }
114
115        @Override
116        public Class<? extends Endpoint> getEndpointClass() {
117                if (this.endpoint != null) {
118                        return this.endpoint.getClass();
119                }
120                else {
121                        Assert.state(this.endpointProvider != null, "No endpoint set");
122                        return this.endpointProvider.getHandlerType();
123                }
124        }
125
126        public Endpoint getEndpoint() {
127                if (this.endpoint != null) {
128                        return this.endpoint;
129                }
130                else {
131                        Assert.state(this.endpointProvider != null, "No endpoint set");
132                        return this.endpointProvider.getHandler();
133                }
134        }
135
136        public void setSubprotocols(List<String> subprotocols) {
137                this.subprotocols = subprotocols;
138        }
139
140        @Override
141        public List<String> getSubprotocols() {
142                return this.subprotocols;
143        }
144
145        public void setExtensions(List<Extension> extensions) {
146                this.extensions = extensions;
147        }
148
149        @Override
150        public List<Extension> getExtensions() {
151                return this.extensions;
152        }
153
154        public void setEncoders(List<Class<? extends Encoder>> encoders) {
155                this.encoders = encoders;
156        }
157
158        @Override
159        public List<Class<? extends Encoder>> getEncoders() {
160                return this.encoders;
161        }
162
163        public void setDecoders(List<Class<? extends Decoder>> decoders) {
164                this.decoders = decoders;
165        }
166
167        @Override
168        public List<Class<? extends Decoder>> getDecoders() {
169                return this.decoders;
170        }
171
172        public void setUserProperties(Map<String, Object> userProperties) {
173                this.userProperties.clear();
174                this.userProperties.putAll(userProperties);
175        }
176
177        @Override
178        public Map<String, Object> getUserProperties() {
179                return this.userProperties;
180        }
181
182        @Override
183        public Configurator getConfigurator() {
184                return this;
185        }
186
187
188        // ServerEndpointConfig.Configurator implementation
189
190        @SuppressWarnings("unchecked")
191        @Override
192        public final <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
193                return (T) getEndpoint();
194        }
195
196        @Override
197        public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
198                super.modifyHandshake(this, request, response);
199        }
200
201
202        // Remaining methods
203
204        @Override
205        public void setBeanFactory(BeanFactory beanFactory) {
206                if (this.endpointProvider != null) {
207                        this.endpointProvider.setBeanFactory(beanFactory);
208                }
209        }
210
211        @Override
212        public String toString() {
213                return "ServerEndpointRegistration for path '" + getPath() + "': " + getEndpointClass();
214        }
215
216}