diff --git a/spring-websocket/src/main/java/org/springframework/websocket/client/AbstractEndpointConnectionManager.java b/spring-websocket/src/main/java/org/springframework/websocket/client/AbstractEndpointConnectionManager.java new file mode 100644 index 00000000000..1cb0d35102e --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/websocket/client/AbstractEndpointConnectionManager.java @@ -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. + *
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;
+ }
+ }
+
+}
diff --git a/spring-websocket/src/main/java/org/springframework/websocket/client/AnnotatedEndpointConnectionManager.java b/spring-websocket/src/main/java/org/springframework/websocket/client/AnnotatedEndpointConnectionManager.java
new file mode 100644
index 00000000000..ccc4c3f4a0a
--- /dev/null
+++ b/spring-websocket/src/main/java/org/springframework/websocket/client/AnnotatedEndpointConnectionManager.java
@@ -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());
+ }
+
+}
diff --git a/spring-websocket/src/main/java/org/springframework/websocket/client/EndpointConnectionManager.java b/spring-websocket/src/main/java/org/springframework/websocket/client/EndpointConnectionManager.java
new file mode 100644
index 00000000000..472a9df942e
--- /dev/null
+++ b/spring-websocket/src/main/java/org/springframework/websocket/client/EndpointConnectionManager.java
@@ -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