1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package nginx.unit.websocket.server; 18 19 import java.lang.reflect.Modifier; 20 import java.util.HashSet; 21 import java.util.Set; 22 23 import javax.servlet.ServletContainerInitializer; 24 import javax.servlet.ServletContext; 25 import javax.servlet.ServletException; 26 import javax.servlet.annotation.HandlesTypes; 27 import javax.websocket.ContainerProvider; 28 import javax.websocket.DeploymentException; 29 import javax.websocket.Endpoint; 30 import javax.websocket.server.ServerApplicationConfig; 31 import javax.websocket.server.ServerEndpoint; 32 import javax.websocket.server.ServerEndpointConfig; 33 34 /** 35 * Registers an interest in any class that is annotated with 36 * {@link ServerEndpoint} so that Endpoint can be published via the WebSocket 37 * server. 38 */ 39 @HandlesTypes({ServerEndpoint.class, ServerApplicationConfig.class, 40 Endpoint.class}) 41 public class WsSci implements ServletContainerInitializer { 42 43 @Override onStartup(Set<Class<?>> clazzes, ServletContext ctx)44 public void onStartup(Set<Class<?>> clazzes, ServletContext ctx) 45 throws ServletException { 46 47 WsServerContainer sc = init(ctx, true); 48 49 if (clazzes == null || clazzes.size() == 0) { 50 return; 51 } 52 53 // Group the discovered classes by type 54 Set<ServerApplicationConfig> serverApplicationConfigs = new HashSet<>(); 55 Set<Class<? extends Endpoint>> scannedEndpointClazzes = new HashSet<>(); 56 Set<Class<?>> scannedPojoEndpoints = new HashSet<>(); 57 58 try { 59 // wsPackage is "javax.websocket." 60 String wsPackage = ContainerProvider.class.getName(); 61 wsPackage = wsPackage.substring(0, wsPackage.lastIndexOf('.') + 1); 62 for (Class<?> clazz : clazzes) { 63 int modifiers = clazz.getModifiers(); 64 if (!Modifier.isPublic(modifiers) || 65 Modifier.isAbstract(modifiers)) { 66 // Non-public or abstract - skip it. 67 continue; 68 } 69 // Protect against scanning the WebSocket API JARs 70 if (clazz.getName().startsWith(wsPackage)) { 71 continue; 72 } 73 if (ServerApplicationConfig.class.isAssignableFrom(clazz)) { 74 serverApplicationConfigs.add( 75 (ServerApplicationConfig) clazz.getConstructor().newInstance()); 76 } 77 if (Endpoint.class.isAssignableFrom(clazz)) { 78 @SuppressWarnings("unchecked") 79 Class<? extends Endpoint> endpoint = 80 (Class<? extends Endpoint>) clazz; 81 scannedEndpointClazzes.add(endpoint); 82 } 83 if (clazz.isAnnotationPresent(ServerEndpoint.class)) { 84 scannedPojoEndpoints.add(clazz); 85 } 86 } 87 } catch (ReflectiveOperationException e) { 88 throw new ServletException(e); 89 } 90 91 // Filter the results 92 Set<ServerEndpointConfig> filteredEndpointConfigs = new HashSet<>(); 93 Set<Class<?>> filteredPojoEndpoints = new HashSet<>(); 94 95 if (serverApplicationConfigs.isEmpty()) { 96 filteredPojoEndpoints.addAll(scannedPojoEndpoints); 97 } else { 98 for (ServerApplicationConfig config : serverApplicationConfigs) { 99 Set<ServerEndpointConfig> configFilteredEndpoints = 100 config.getEndpointConfigs(scannedEndpointClazzes); 101 if (configFilteredEndpoints != null) { 102 filteredEndpointConfigs.addAll(configFilteredEndpoints); 103 } 104 Set<Class<?>> configFilteredPojos = 105 config.getAnnotatedEndpointClasses( 106 scannedPojoEndpoints); 107 if (configFilteredPojos != null) { 108 filteredPojoEndpoints.addAll(configFilteredPojos); 109 } 110 } 111 } 112 113 try { 114 // Deploy endpoints 115 for (ServerEndpointConfig config : filteredEndpointConfigs) { 116 sc.addEndpoint(config); 117 } 118 // Deploy POJOs 119 for (Class<?> clazz : filteredPojoEndpoints) { 120 sc.addEndpoint(clazz); 121 } 122 } catch (DeploymentException e) { 123 throw new ServletException(e); 124 } 125 } 126 127 init(ServletContext servletContext, boolean initBySciMechanism)128 static WsServerContainer init(ServletContext servletContext, 129 boolean initBySciMechanism) { 130 131 WsServerContainer sc = new WsServerContainer(servletContext); 132 133 servletContext.setAttribute( 134 Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE, sc); 135 136 servletContext.addListener(new WsSessionListener(sc)); 137 // Can't register the ContextListener again if the ContextListener is 138 // calling this method 139 if (initBySciMechanism) { 140 servletContext.addListener(new WsContextListener()); 141 } 142 143 return sc; 144 } 145 } 146