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}