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;
023import javax.websocket.Decoder;
024import javax.websocket.Encoder;
025import javax.websocket.Endpoint;
026import javax.websocket.Extension;
027import javax.websocket.HandshakeResponse;
028import javax.websocket.server.HandshakeRequest;
029import javax.websocket.server.ServerEndpointConfig;
030
031import org.springframework.beans.factory.BeanFactory;
032import org.springframework.beans.factory.BeanFactoryAware;
033import org.springframework.util.Assert;
034import org.springframework.web.socket.handler.BeanCreatingHandlerProvider;
035
036/**
037 * An implementation of {@link javax.websocket.server.ServerEndpointConfig} for use in
038 * Spring-based applications. A {@link ServerEndpointRegistration} bean is detected by
039 * {@link ServerEndpointExporter} and registered with a Java WebSocket runtime at startup.
040 *
041 * <p>Class constructors accept a singleton {@link javax.websocket.Endpoint} instance
042 * or an Endpoint specified by type {@link Class}. When specified by type, the endpoint
043 * will be instantiated and initialized through the Spring ApplicationContext before
044 * each client WebSocket connection.
045 *
046 * <p>This class also extends
047 * {@link javax.websocket.server.ServerEndpointConfig.Configurator} to make it easier
048 * to override methods for customizing the handshake process.
049 *
050 * @author Rossen Stoyanchev
051 * @author Juergen Hoeller
052 * @since 4.0
053 * @see ServerEndpointExporter
054 */
055public class ServerEndpointRegistration extends ServerEndpointConfig.Configurator
056                implements ServerEndpointConfig, BeanFactoryAware {
057
058        private final String path;
059
060        private final Endpoint endpoint;
061
062        private final BeanCreatingHandlerProvider<Endpoint> endpointProvider;
063
064        private List<String> subprotocols = new ArrayList<String>(0);
065
066        private List<Extension> extensions = new ArrayList<Extension>(0);
067
068        private List<Class<? extends Encoder>> encoders = new ArrayList<Class<? extends Encoder>>(0);
069
070        private List<Class<? extends Decoder>> decoders = new ArrayList<Class<? extends Decoder>>(0);
071
072        private final Map<String, Object> userProperties = new HashMap<String, Object>(4);
073
074
075        /**
076         * Create a new {@link ServerEndpointRegistration} instance from an
077         * {@code javax.websocket.Endpoint} instance.
078         * @param path the endpoint path
079         * @param endpoint the endpoint instance
080         */
081        public ServerEndpointRegistration(String path, Endpoint endpoint) {
082                Assert.hasText(path, "Path must not be empty");
083                Assert.notNull(endpoint, "Endpoint must not be null");
084                this.path = path;
085                this.endpoint = endpoint;
086                this.endpointProvider = null;
087        }
088
089        /**
090         * Create a new {@link ServerEndpointRegistration} instance from an
091         * {@code javax.websocket.Endpoint} class.
092         * @param path the endpoint path
093         * @param endpointClass the endpoint class
094         */
095        public ServerEndpointRegistration(String path, Class<? extends Endpoint> endpointClass) {
096                Assert.hasText(path, "Path must not be empty");
097                Assert.notNull(endpointClass, "Endpoint Class must not be null");
098                this.path = path;
099                this.endpoint = null;
100                this.endpointProvider = new BeanCreatingHandlerProvider<Endpoint>(endpointClass);
101        }
102
103
104        // ServerEndpointConfig implementation
105
106        @Override
107        public String getPath() {
108                return this.path;
109        }
110
111        @Override
112        public Class<? extends Endpoint> getEndpointClass() {
113                return (this.endpoint != null ? this.endpoint.getClass() : this.endpointProvider.getHandlerType());
114        }
115
116        public Endpoint getEndpoint() {
117                return (this.endpoint != null) ? this.endpoint : this.endpointProvider.getHandler();
118        }
119
120        public void setSubprotocols(List<String> subprotocols) {
121                this.subprotocols = subprotocols;
122        }
123
124        @Override
125        public List<String> getSubprotocols() {
126                return this.subprotocols;
127        }
128
129        public void setExtensions(List<Extension> extensions) {
130                this.extensions = extensions;
131        }
132
133        @Override
134        public List<Extension> getExtensions() {
135                return this.extensions;
136        }
137
138        public void setEncoders(List<Class<? extends Encoder>> encoders) {
139                this.encoders = encoders;
140        }
141
142        @Override
143        public List<Class<? extends Encoder>> getEncoders() {
144                return this.encoders;
145        }
146
147        public void setDecoders(List<Class<? extends Decoder>> decoders) {
148                this.decoders = decoders;
149        }
150
151        @Override
152        public List<Class<? extends Decoder>> getDecoders() {
153                return this.decoders;
154        }
155
156        public void setUserProperties(Map<String, Object> userProperties) {
157                this.userProperties.clear();
158                this.userProperties.putAll(userProperties);
159        }
160
161        @Override
162        public Map<String, Object> getUserProperties() {
163                return this.userProperties;
164        }
165
166        @Override
167        public Configurator getConfigurator() {
168                return this;
169        }
170
171
172        // ServerEndpointConfig.Configurator implementation
173
174        @SuppressWarnings("unchecked")
175        @Override
176        public final <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
177                return (T) getEndpoint();
178        }
179
180        @Override
181        public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
182                super.modifyHandshake(this, request, response);
183        }
184
185
186        // Remaining methods
187
188        @Override
189        public void setBeanFactory(BeanFactory beanFactory) {
190                if (this.endpointProvider != null) {
191                        this.endpointProvider.setBeanFactory(beanFactory);
192                }
193        }
194
195        @Override
196        public String toString() {
197                return "ServerEndpointRegistration for path '" + getPath() + "': " + getEndpointClass();
198        }
199
200}