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 javax.websocket.ContainerProvider;
020import javax.websocket.Session;
021import javax.websocket.WebSocketContainer;
022
023import org.springframework.beans.BeansException;
024import org.springframework.beans.factory.BeanFactory;
025import org.springframework.beans.factory.BeanFactoryAware;
026import org.springframework.core.task.SimpleAsyncTaskExecutor;
027import org.springframework.core.task.TaskExecutor;
028import org.springframework.util.Assert;
029import org.springframework.web.socket.client.ConnectionManagerSupport;
030import org.springframework.web.socket.handler.BeanCreatingHandlerProvider;
031
032/**
033 * A WebSocket connection manager that is given a URI, a
034 * {@link javax.websocket.ClientEndpoint}-annotated endpoint, connects to a
035 * WebSocket server through the {@link #start()} and {@link #stop()} methods.
036 * If {@link #setAutoStartup(boolean)} is set to {@code true} this will be
037 * done automatically when the Spring ApplicationContext is refreshed.
038 *
039 * @author Rossen Stoyanchev
040 * @since 4.0
041 */
042public class AnnotatedEndpointConnectionManager extends ConnectionManagerSupport implements BeanFactoryAware {
043
044        private final Object endpoint;
045
046        private final BeanCreatingHandlerProvider<Object> endpointProvider;
047
048        private WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer();
049
050        private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("AnnotatedEndpointConnectionManager-");
051
052        private volatile Session session;
053
054
055        public AnnotatedEndpointConnectionManager(Object endpoint, String uriTemplate, Object... uriVariables) {
056                super(uriTemplate, uriVariables);
057                this.endpointProvider = null;
058                this.endpoint = endpoint;
059        }
060
061        public AnnotatedEndpointConnectionManager(Class<?> endpointClass, String uriTemplate, Object... uriVariables) {
062                super(uriTemplate, uriVariables);
063                this.endpointProvider = new BeanCreatingHandlerProvider<Object>(endpointClass);
064                this.endpoint = null;
065        }
066
067
068        public void setWebSocketContainer(WebSocketContainer webSocketContainer) {
069                this.webSocketContainer = webSocketContainer;
070        }
071
072        public WebSocketContainer getWebSocketContainer() {
073                return this.webSocketContainer;
074        }
075
076        @Override
077        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
078                if (this.endpointProvider != null) {
079                        this.endpointProvider.setBeanFactory(beanFactory);
080                }
081        }
082
083        /**
084         * Set a {@link TaskExecutor} to use to open the connection.
085         * By default {@link SimpleAsyncTaskExecutor} is used.
086         */
087        public void setTaskExecutor(TaskExecutor taskExecutor) {
088                Assert.notNull(taskExecutor, "TaskExecutor must not be null");
089                this.taskExecutor = taskExecutor;
090        }
091
092        /**
093         * Return the configured {@link TaskExecutor}.
094         */
095        public TaskExecutor getTaskExecutor() {
096                return this.taskExecutor;
097        }
098
099
100        @Override
101        protected void openConnection() {
102                this.taskExecutor.execute(new Runnable() {
103                        @Override
104                        public void run() {
105                                try {
106                                        if (logger.isInfoEnabled()) {
107                                                logger.info("Connecting to WebSocket at " + getUri());
108                                        }
109                                        Object endpointToUse = (endpoint != null) ? endpoint : endpointProvider.getHandler();
110                                        session = webSocketContainer.connectToServer(endpointToUse, getUri());
111                                        logger.info("Successfully connected to WebSocket");
112                                }
113                                catch (Throwable ex) {
114                                        logger.error("Failed to connect to WebSocket", ex);
115                                }
116                        }
117                });
118        }
119
120        @Override
121        protected void closeConnection() throws Exception {
122                try {
123                        if (isConnected()) {
124                                this.session.close();
125                        }
126                }
127                finally {
128                        this.session = null;
129                }
130        }
131
132        @Override
133        protected boolean isConnected() {
134                return (this.session != null && this.session.isOpen());
135        }
136
137}