7 changed files with 402 additions and 39 deletions
@ -0,0 +1,227 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2013 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.websocket.client; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.net.URI; |
||||||
|
|
||||||
|
import javax.websocket.ContainerProvider; |
||||||
|
import javax.websocket.DeploymentException; |
||||||
|
import javax.websocket.Session; |
||||||
|
import javax.websocket.WebSocketContainer; |
||||||
|
|
||||||
|
import org.apache.commons.logging.Log; |
||||||
|
import org.apache.commons.logging.LogFactory; |
||||||
|
import org.springframework.beans.BeansException; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
import org.springframework.context.ApplicationContextAware; |
||||||
|
import org.springframework.context.SmartLifecycle; |
||||||
|
import org.springframework.core.task.SimpleAsyncTaskExecutor; |
||||||
|
import org.springframework.core.task.TaskExecutor; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
import org.springframework.web.util.UriComponentsBuilder; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author Rossen Stoyanchev |
||||||
|
* @since 4.0 |
||||||
|
*/ |
||||||
|
public abstract class AbstractEndpointConnectionManager implements ApplicationContextAware, SmartLifecycle { |
||||||
|
|
||||||
|
protected final Log logger = LogFactory.getLog(getClass()); |
||||||
|
|
||||||
|
private final Class<?> endpointClass; |
||||||
|
|
||||||
|
private final Object endpointBean; |
||||||
|
|
||||||
|
private final URI uri; |
||||||
|
|
||||||
|
private boolean autoStartup = false; |
||||||
|
|
||||||
|
private int phase = Integer.MAX_VALUE; |
||||||
|
|
||||||
|
private final WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer(); |
||||||
|
|
||||||
|
private Session session; |
||||||
|
|
||||||
|
private ApplicationContext applicationContext; |
||||||
|
|
||||||
|
private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("EndpointConnectionManager-"); |
||||||
|
|
||||||
|
private final Object lifecycleMonitor = new Object(); |
||||||
|
|
||||||
|
|
||||||
|
public AbstractEndpointConnectionManager(Class<?> endpointClass, String uriTemplate, Object... uriVariables) { |
||||||
|
Assert.notNull(endpointClass, "endpointClass is required"); |
||||||
|
this.endpointClass = endpointClass; |
||||||
|
this.endpointBean = null; |
||||||
|
this.uri = initUri(uriTemplate, uriVariables); |
||||||
|
} |
||||||
|
|
||||||
|
public AbstractEndpointConnectionManager(Object endpointBean, String uriTemplate, Object... uriVariables) { |
||||||
|
Assert.notNull(endpointBean, "endpointBean is required"); |
||||||
|
this.endpointClass = null; |
||||||
|
this.endpointBean = endpointBean; |
||||||
|
this.uri = initUri(uriTemplate, uriVariables); |
||||||
|
} |
||||||
|
|
||||||
|
private static URI initUri(String uri, Object... uriVariables) { |
||||||
|
return UriComponentsBuilder.fromUriString(uri).buildAndExpand(uriVariables).encode().toUri(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setAsyncSendTimeout(long timeoutInMillis) { |
||||||
|
this.webSocketContainer.setAsyncSendTimeout(timeoutInMillis); |
||||||
|
} |
||||||
|
|
||||||
|
public void setMaxSessionIdleTimeout(long timeoutInMillis) { |
||||||
|
this.webSocketContainer.setDefaultMaxSessionIdleTimeout(timeoutInMillis); |
||||||
|
} |
||||||
|
|
||||||
|
public void setMaxTextMessageBufferSize(int bufferSize) { |
||||||
|
this.webSocketContainer.setDefaultMaxTextMessageBufferSize(bufferSize); |
||||||
|
} |
||||||
|
|
||||||
|
public void setMaxBinaryMessageBufferSize(Integer bufferSize) { |
||||||
|
this.webSocketContainer.setDefaultMaxBinaryMessageBufferSize(bufferSize); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set whether to auto-connect to the {@link #setDefaultUri(URI) default URI} after |
||||||
|
* this endpoint connection factory has been initialized and the Spring context has |
||||||
|
* been refreshed. |
||||||
|
* <p>Default is "false". |
||||||
|
*/ |
||||||
|
public void setAutoStartup(boolean autoStartup) { |
||||||
|
this.autoStartup = autoStartup; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return the value for the 'autoStartup' property. If "true", this endpoint |
||||||
|
* connection factory will connect to the {@link #setDefaultUri(URI) default URI} upon |
||||||
|
* a ContextRefreshedEvent. |
||||||
|
*/ |
||||||
|
public boolean isAutoStartup() { |
||||||
|
return this.autoStartup; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Specify the phase in which this endpoint connection factory should be |
||||||
|
* auto-connected and closed. The startup order proceeds from lowest to highest, and |
||||||
|
* the shutdown order is the reverse of that. By default this value is |
||||||
|
* Integer.MAX_VALUE meaning that this endpoint connection factory connects as late as |
||||||
|
* possible and is closed as soon as possible. |
||||||
|
*/ |
||||||
|
public void setPhase(int phase) { |
||||||
|
this.phase = phase; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return the phase in which this endpoint connection factory will be auto-connected |
||||||
|
* and stopped. |
||||||
|
*/ |
||||||
|
public int getPhase() { |
||||||
|
return this.phase; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
||||||
|
this.applicationContext = applicationContext; |
||||||
|
} |
||||||
|
|
||||||
|
protected URI getUri() { |
||||||
|
return this.uri; |
||||||
|
} |
||||||
|
|
||||||
|
protected WebSocketContainer getWebSocketContainer() { |
||||||
|
return this.webSocketContainer; |
||||||
|
} |
||||||
|
|
||||||
|
protected Object getEndpoint() { |
||||||
|
if (this.endpointClass != null) { |
||||||
|
return this.applicationContext.getAutowireCapableBeanFactory().createBean(this.endpointClass); |
||||||
|
} |
||||||
|
else { |
||||||
|
return this.endpointBean; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Auto-connects to the configured {@link #setDefaultUri(URI) default URI}. |
||||||
|
*/ |
||||||
|
public void start() { |
||||||
|
synchronized (this.lifecycleMonitor) { |
||||||
|
if (!isRunning()) { |
||||||
|
this.taskExecutor.execute(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
synchronized (lifecycleMonitor) { |
||||||
|
try { |
||||||
|
logger.info("Connecting to endpoint at URI " + uri); |
||||||
|
session = connect(getEndpoint()); |
||||||
|
logger.info("Successfully connected"); |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
logger.error("Failed to connect to endpoint at " + uri, ex); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract Session connect(Object endpoint) throws DeploymentException, IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Deactivates the configured message endpoint. |
||||||
|
*/ |
||||||
|
public void stop() { |
||||||
|
synchronized (this.lifecycleMonitor) { |
||||||
|
if (isRunning()) { |
||||||
|
try { |
||||||
|
this.session.close(); |
||||||
|
} |
||||||
|
catch (IOException e) { |
||||||
|
// ignore
|
||||||
|
} |
||||||
|
} |
||||||
|
this.session = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void stop(Runnable callback) { |
||||||
|
synchronized (this.lifecycleMonitor) { |
||||||
|
this.stop(); |
||||||
|
callback.run(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return whether the configured message endpoint is currently active. |
||||||
|
*/ |
||||||
|
public boolean isRunning() { |
||||||
|
synchronized (this.lifecycleMonitor) { |
||||||
|
if ((this.session != null) && this.session.isOpen()) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
this.session = null; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,46 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2013 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.websocket.client; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import javax.websocket.DeploymentException; |
||||||
|
import javax.websocket.Session; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author Rossen Stoyanchev |
||||||
|
* @since 4.0 |
||||||
|
*/ |
||||||
|
public class AnnotatedEndpointConnectionManager extends AbstractEndpointConnectionManager { |
||||||
|
|
||||||
|
|
||||||
|
public AnnotatedEndpointConnectionManager(Class<?> endpointClass, String uriTemplate, Object... uriVariables) { |
||||||
|
super(endpointClass, uriTemplate, uriVariables); |
||||||
|
} |
||||||
|
|
||||||
|
public AnnotatedEndpointConnectionManager(Object endpointBean, String uriTemplate, Object... uriVariables) { |
||||||
|
super(endpointBean, uriTemplate, uriVariables); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Session connect(Object endpoint) throws DeploymentException, IOException { |
||||||
|
return getWebSocketContainer().connectToServer(endpoint, getUri()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,78 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2013 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.websocket.client; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import javax.websocket.ClientEndpointConfig; |
||||||
|
import javax.websocket.ClientEndpointConfig.Configurator; |
||||||
|
import javax.websocket.Decoder; |
||||||
|
import javax.websocket.DeploymentException; |
||||||
|
import javax.websocket.Encoder; |
||||||
|
import javax.websocket.Endpoint; |
||||||
|
import javax.websocket.Extension; |
||||||
|
import javax.websocket.Session; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @author Rossen Stoyanchev |
||||||
|
* @since 4.0 |
||||||
|
*/ |
||||||
|
public class EndpointConnectionManager extends AbstractEndpointConnectionManager { |
||||||
|
|
||||||
|
private final ClientEndpointConfig.Builder configBuilder = ClientEndpointConfig.Builder.create(); |
||||||
|
|
||||||
|
|
||||||
|
public EndpointConnectionManager(Class<? extends Endpoint> endpointClass, String uriTemplate, Object... uriVariables) { |
||||||
|
super(endpointClass, uriTemplate, uriVariables); |
||||||
|
} |
||||||
|
|
||||||
|
public EndpointConnectionManager(Endpoint endpointBean, String uriTemplate, Object... uriVariables) { |
||||||
|
super(endpointBean, uriTemplate, uriVariables); |
||||||
|
} |
||||||
|
|
||||||
|
public void setSubProtocols(String... subprotocols) { |
||||||
|
this.configBuilder.preferredSubprotocols(Arrays.asList(subprotocols)); |
||||||
|
} |
||||||
|
|
||||||
|
public void setExtensions(Extension... extensions) { |
||||||
|
this.configBuilder.extensions(Arrays.asList(extensions)); |
||||||
|
} |
||||||
|
|
||||||
|
public void setEncoders(List<Class<? extends Encoder>> encoders) { |
||||||
|
this.configBuilder.encoders(encoders); |
||||||
|
} |
||||||
|
|
||||||
|
public void setDecoders(List<Class<? extends Decoder>> decoders) { |
||||||
|
this.configBuilder.decoders(decoders); |
||||||
|
} |
||||||
|
|
||||||
|
public void setConfigurator(Configurator configurator) { |
||||||
|
this.configBuilder.configurator(configurator); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Session connect(Object endpoint) throws DeploymentException, IOException { |
||||||
|
Endpoint typedEndpoint = (Endpoint) endpoint; |
||||||
|
ClientEndpointConfig endpointConfig = this.configBuilder.build(); |
||||||
|
return getWebSocketContainer().connectToServer(typedEndpoint, endpointConfig, getUri()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue