001/*
002 * Copyright 2002-2016 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.client.standard;
018
019import java.util.Arrays;
020import java.util.List;
021import javax.websocket.ClientEndpointConfig;
022import javax.websocket.ClientEndpointConfig.Configurator;
023import javax.websocket.ContainerProvider;
024import javax.websocket.Decoder;
025import javax.websocket.Encoder;
026import javax.websocket.Endpoint;
027import javax.websocket.Extension;
028import javax.websocket.Session;
029import javax.websocket.WebSocketContainer;
030
031import org.springframework.beans.factory.BeanFactory;
032import org.springframework.beans.factory.BeanFactoryAware;
033import org.springframework.core.task.SimpleAsyncTaskExecutor;
034import org.springframework.core.task.TaskExecutor;
035import org.springframework.util.Assert;
036import org.springframework.web.socket.client.ConnectionManagerSupport;
037import org.springframework.web.socket.handler.BeanCreatingHandlerProvider;
038
039/**
040 * A WebSocket connection manager that is given a URI, an {@link Endpoint}, connects to a
041 * WebSocket server through the {@link #start()} and {@link #stop()} methods. If
042 * {@link #setAutoStartup(boolean)} is set to {@code true} this will be done automatically
043 * when the Spring ApplicationContext is refreshed.
044 *
045 * @author Rossen Stoyanchev
046 * @since 4.0
047 * @see AnnotatedEndpointConnectionManager
048 */
049public class EndpointConnectionManager extends ConnectionManagerSupport implements BeanFactoryAware {
050
051        private final Endpoint endpoint;
052
053        private final BeanCreatingHandlerProvider<Endpoint> endpointProvider;
054
055        private final ClientEndpointConfig.Builder configBuilder = ClientEndpointConfig.Builder.create();
056
057        private WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer();
058
059        private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("EndpointConnectionManager-");
060
061        private volatile Session session;
062
063
064        public EndpointConnectionManager(Endpoint endpoint, String uriTemplate, Object... uriVariables) {
065                super(uriTemplate, uriVariables);
066                Assert.notNull(endpoint, "endpoint must not be null");
067                this.endpointProvider = null;
068                this.endpoint = endpoint;
069        }
070
071        public EndpointConnectionManager(Class<? extends Endpoint> endpointClass, String uriTemplate, Object... uriVars) {
072                super(uriTemplate, uriVars);
073                Assert.notNull(endpointClass, "endpointClass must not be null");
074                this.endpointProvider = new BeanCreatingHandlerProvider<Endpoint>(endpointClass);
075                this.endpoint = null;
076        }
077
078
079        public void setSupportedProtocols(String... protocols) {
080                this.configBuilder.preferredSubprotocols(Arrays.asList(protocols));
081        }
082
083        public void setExtensions(Extension... extensions) {
084                this.configBuilder.extensions(Arrays.asList(extensions));
085        }
086
087        public void setEncoders(List<Class<? extends Encoder>> encoders) {
088                this.configBuilder.encoders(encoders);
089        }
090
091        public void setDecoders(List<Class<? extends Decoder>> decoders) {
092                this.configBuilder.decoders(decoders);
093        }
094
095        public void setConfigurator(Configurator configurator) {
096                this.configBuilder.configurator(configurator);
097        }
098
099        public void setWebSocketContainer(WebSocketContainer webSocketContainer) {
100                this.webSocketContainer = webSocketContainer;
101        }
102
103        public WebSocketContainer getWebSocketContainer() {
104                return this.webSocketContainer;
105        }
106
107        @Override
108        public void setBeanFactory(BeanFactory beanFactory) {
109                if (this.endpointProvider != null) {
110                        this.endpointProvider.setBeanFactory(beanFactory);
111                }
112        }
113
114        /**
115         * Set a {@link TaskExecutor} to use to open connections.
116         * By default {@link SimpleAsyncTaskExecutor} is used.
117         */
118        public void setTaskExecutor(TaskExecutor taskExecutor) {
119                Assert.notNull(taskExecutor, "TaskExecutor must not be null");
120                this.taskExecutor = taskExecutor;
121        }
122
123        /**
124         * Return the configured {@link TaskExecutor}.
125         */
126        public TaskExecutor getTaskExecutor() {
127                return this.taskExecutor;
128        }
129
130
131        @Override
132        protected void openConnection() {
133                this.taskExecutor.execute(new Runnable() {
134                        @Override
135                        public void run() {
136                                try {
137                                        if (logger.isInfoEnabled()) {
138                                                logger.info("Connecting to WebSocket at " + getUri());
139                                        }
140                                        Endpoint endpointToUse = (endpoint != null) ? endpoint : endpointProvider.getHandler();
141                                        ClientEndpointConfig endpointConfig = configBuilder.build();
142                                        session = getWebSocketContainer().connectToServer(endpointToUse, endpointConfig, getUri());
143                                        logger.info("Successfully connected to WebSocket");
144                                }
145                                catch (Throwable ex) {
146                                        logger.error("Failed to connect to WebSocket", ex);
147                                }
148                        }
149                });
150        }
151
152        @Override
153        protected void closeConnection() throws Exception {
154                try {
155                        if (isConnected()) {
156                                this.session.close();
157                        }
158                }
159                finally {
160                        this.session = null;
161                }
162        }
163
164        @Override
165        protected boolean isConnected() {
166                return (this.session != null && this.session.isOpen());
167        }
168
169}