001/* 002 * Copyright 2002-2014 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.server.standard; 018 019import java.util.Arrays; 020import java.util.LinkedHashSet; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024import javax.servlet.ServletContext; 025import javax.websocket.DeploymentException; 026import javax.websocket.server.ServerContainer; 027import javax.websocket.server.ServerEndpoint; 028import javax.websocket.server.ServerEndpointConfig; 029 030import org.springframework.beans.factory.InitializingBean; 031import org.springframework.beans.factory.SmartInitializingSingleton; 032import org.springframework.context.ApplicationContext; 033import org.springframework.util.Assert; 034import org.springframework.web.context.support.WebApplicationObjectSupport; 035 036/** 037 * Detects beans of type {@link javax.websocket.server.ServerEndpointConfig} and registers 038 * with the standard Java WebSocket runtime. Also detects beans annotated with 039 * {@link ServerEndpoint} and registers them as well. Although not required, it is likely 040 * annotated endpoints should have their {@code configurator} property set to 041 * {@link SpringConfigurator}. 042 * 043 * <p>When this class is used, by declaring it in Spring configuration, it should be 044 * possible to turn off a Servlet container's scan for WebSocket endpoints. This can be 045 * done with the help of the {@code <absolute-ordering>} element in {@code web.xml}. 046 * 047 * @author Rossen Stoyanchev 048 * @author Juergen Hoeller 049 * @since 4.0 050 * @see ServerEndpointRegistration 051 * @see SpringConfigurator 052 * @see ServletServerContainerFactoryBean 053 */ 054public class ServerEndpointExporter extends WebApplicationObjectSupport 055 implements InitializingBean, SmartInitializingSingleton { 056 057 private List<Class<?>> annotatedEndpointClasses; 058 059 private ServerContainer serverContainer; 060 061 062 /** 063 * Explicitly list annotated endpoint types that should be registered on startup. This 064 * can be done if you wish to turn off a Servlet container's scan for endpoints, which 065 * goes through all 3rd party jars in the, and rely on Spring configuration instead. 066 * @param annotatedEndpointClasses {@link ServerEndpoint}-annotated types 067 */ 068 public void setAnnotatedEndpointClasses(Class<?>... annotatedEndpointClasses) { 069 this.annotatedEndpointClasses = Arrays.asList(annotatedEndpointClasses); 070 } 071 072 /** 073 * Set the JSR-356 {@link ServerContainer} to use for endpoint registration. 074 * If not set, the container is going to be retrieved via the {@code ServletContext}. 075 */ 076 public void setServerContainer(ServerContainer serverContainer) { 077 this.serverContainer = serverContainer; 078 } 079 080 /** 081 * Return the JSR-356 {@link ServerContainer} to use for endpoint registration. 082 */ 083 protected ServerContainer getServerContainer() { 084 return this.serverContainer; 085 } 086 087 @Override 088 protected void initServletContext(ServletContext servletContext) { 089 if (this.serverContainer == null) { 090 this.serverContainer = 091 (ServerContainer) servletContext.getAttribute("javax.websocket.server.ServerContainer"); 092 } 093 } 094 095 @Override 096 protected boolean isContextRequired() { 097 return false; 098 } 099 100 @Override 101 public void afterPropertiesSet() { 102 Assert.state(getServerContainer() != null, "javax.websocket.server.ServerContainer not available"); 103 } 104 105 @Override 106 public void afterSingletonsInstantiated() { 107 registerEndpoints(); 108 } 109 110 111 /** 112 * Actually register the endpoints. Called by {@link #afterSingletonsInstantiated()}. 113 */ 114 protected void registerEndpoints() { 115 Set<Class<?>> endpointClasses = new LinkedHashSet<Class<?>>(); 116 if (this.annotatedEndpointClasses != null) { 117 endpointClasses.addAll(this.annotatedEndpointClasses); 118 } 119 120 ApplicationContext context = getApplicationContext(); 121 if (context != null) { 122 String[] endpointBeanNames = context.getBeanNamesForAnnotation(ServerEndpoint.class); 123 for (String beanName : endpointBeanNames) { 124 endpointClasses.add(context.getType(beanName)); 125 } 126 } 127 128 for (Class<?> endpointClass : endpointClasses) { 129 registerEndpoint(endpointClass); 130 } 131 132 if (context != null) { 133 Map<String, ServerEndpointConfig> endpointConfigMap = context.getBeansOfType(ServerEndpointConfig.class); 134 for (ServerEndpointConfig endpointConfig : endpointConfigMap.values()) { 135 registerEndpoint(endpointConfig); 136 } 137 } 138 } 139 140 private void registerEndpoint(Class<?> endpointClass) { 141 try { 142 if (logger.isInfoEnabled()) { 143 logger.info("Registering @ServerEndpoint class: " + endpointClass); 144 } 145 getServerContainer().addEndpoint(endpointClass); 146 } 147 catch (DeploymentException ex) { 148 throw new IllegalStateException("Failed to register @ServerEndpoint class: " + endpointClass, ex); 149 } 150 } 151 152 private void registerEndpoint(ServerEndpointConfig endpointConfig) { 153 try { 154 if (logger.isInfoEnabled()) { 155 logger.info("Registering ServerEndpointConfig: " + endpointConfig); 156 } 157 getServerContainer().addEndpoint(endpointConfig); 158 } 159 catch (DeploymentException ex) { 160 throw new IllegalStateException("Failed to register ServerEndpointConfig: " + endpointConfig, ex); 161 } 162 } 163 164}