View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.websocket.jsr356.server.deploy;
20  
21  import java.util.HashSet;
22  import java.util.Set;
23  
24  import javax.servlet.ServletContainerInitializer;
25  import javax.servlet.ServletContext;
26  import javax.servlet.ServletException;
27  import javax.servlet.annotation.HandlesTypes;
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  import org.eclipse.jetty.server.handler.ContextHandler;
35  import org.eclipse.jetty.servlet.ServletContextHandler;
36  import org.eclipse.jetty.util.log.Log;
37  import org.eclipse.jetty.util.log.Logger;
38  import org.eclipse.jetty.websocket.jsr356.server.ServerContainer;
39  import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter;
40  
41  @HandlesTypes(
42  { ServerApplicationConfig.class, ServerEndpoint.class, Endpoint.class })
43  public class WebSocketServerContainerInitializer implements ServletContainerInitializer
44  {
45      public static final String ENABLE_KEY = "org.eclipse.jetty.websocket.jsr356";
46      private static final Logger LOG = Log.getLogger(WebSocketServerContainerInitializer.class);
47  
48      public static boolean isJSR356EnabledOnContext(ServletContext context)
49      {
50          Object enable = context.getAttribute(ENABLE_KEY);
51          if (enable instanceof Boolean)
52          {
53              return ((Boolean)enable).booleanValue();
54          }
55  
56          return true;
57      }
58  
59      public static ServerContainer configureContext(ServletContextHandler context)
60      {
61          // Create Filter
62          WebSocketUpgradeFilter filter = WebSocketUpgradeFilter.configureContext(context);
63  
64          // Store reference to the WebSocketUpgradeFilter
65          context.setAttribute(WebSocketUpgradeFilter.class.getName(),filter);
66  
67          // Create the Jetty ServerContainer implementation
68          ServerContainer jettyContainer = new ServerContainer(filter,filter.getFactory(),context.getServer().getThreadPool());
69          context.addBean(jettyContainer);
70  
71          // Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment
72          context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer);
73  
74          return jettyContainer;
75      }
76  
77      @Override
78      public void onStartup(Set<Class<?>> c, ServletContext context) throws ServletException
79      {
80          if (!isJSR356EnabledOnContext(context))
81          {
82              LOG.info("JSR-356 support disabled via attribute on context {} - {}",context.getContextPath(),context);
83              return;
84          }
85  
86          ContextHandler handler = ContextHandler.getContextHandler(context);
87  
88          if (handler == null)
89          {
90              throw new ServletException("Not running on Jetty, JSR support disabled");
91          }
92  
93          if (!(handler instanceof ServletContextHandler))
94          {
95              throw new ServletException("Not running in Jetty ServletContextHandler, JSR support disabled");
96          }
97  
98          ServletContextHandler jettyContext = (ServletContextHandler)handler;
99  
100         // Create the Jetty ServerContainer implementation
101         ServerContainer jettyContainer = configureContext(jettyContext);
102 
103         // Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment
104         context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer);
105 
106         LOG.debug("Found {} classes",c.size());
107 
108         // Now process the incoming classes
109         Set<Class<? extends Endpoint>> discoveredExtendedEndpoints = new HashSet<>();
110         Set<Class<?>> discoveredAnnotatedEndpoints = new HashSet<>();
111         Set<Class<? extends ServerApplicationConfig>> serverAppConfigs = new HashSet<>();
112 
113         filterClasses(c,discoveredExtendedEndpoints,discoveredAnnotatedEndpoints,serverAppConfigs);
114 
115         LOG.debug("Discovered {} extends Endpoint classes",discoveredExtendedEndpoints.size());
116         LOG.debug("Discovered {} @ServerEndpoint classes",discoveredAnnotatedEndpoints.size());
117         LOG.debug("Discovered {} ServerApplicationConfig classes",serverAppConfigs.size());
118 
119         // Process the server app configs to determine endpoint filtering
120         boolean wasFiltered = false;
121         Set<ServerEndpointConfig> deployableExtendedEndpointConfigs = new HashSet<>();
122         Set<Class<?>> deployableAnnotatedEndpoints = new HashSet<>();
123 
124         for (Class<? extends ServerApplicationConfig> clazz : serverAppConfigs)
125         {
126             LOG.debug("Found ServerApplicationConfig: {}",clazz);
127             try
128             {
129                 ServerApplicationConfig config = (ServerApplicationConfig)clazz.newInstance();
130 
131                 Set<ServerEndpointConfig> seconfigs = config.getEndpointConfigs(discoveredExtendedEndpoints);
132                 if (seconfigs != null)
133                 {
134                     wasFiltered = true;
135                     deployableExtendedEndpointConfigs.addAll(seconfigs);
136                 }
137 
138                 Set<Class<?>> annotatedClasses = config.getAnnotatedEndpointClasses(discoveredAnnotatedEndpoints);
139                 if (annotatedClasses != null)
140                 {
141                     wasFiltered = true;
142                     deployableAnnotatedEndpoints.addAll(annotatedClasses);
143                 }
144             }
145             catch (InstantiationException | IllegalAccessException e)
146             {
147                 throw new ServletException("Unable to instantiate: " + clazz.getName(),e);
148             }
149         }
150 
151         // Default behavior if nothing filtered
152         if (!wasFiltered)
153         {
154             deployableAnnotatedEndpoints.addAll(discoveredAnnotatedEndpoints);
155             // Note: it is impossible to determine path of "extends Endpoint" discovered classes
156             deployableExtendedEndpointConfigs = new HashSet<>();
157         }
158 
159         // Deploy what should be deployed.
160         LOG.debug("Deploying {} ServerEndpointConfig(s)",deployableExtendedEndpointConfigs.size());
161         for (ServerEndpointConfig config : deployableExtendedEndpointConfigs)
162         {
163             try
164             {
165                 jettyContainer.addEndpoint(config);
166             }
167             catch (DeploymentException e)
168             {
169                 throw new ServletException(e);
170             }
171         }
172 
173         LOG.debug("Deploying {} @ServerEndpoint(s)",deployableAnnotatedEndpoints.size());
174         for (Class<?> annotatedClass : deployableAnnotatedEndpoints)
175         {
176             try
177             {
178                 jettyContainer.addEndpoint(annotatedClass);
179             }
180             catch (DeploymentException e)
181             {
182                 throw new ServletException(e);
183             }
184         }
185     }
186 
187     @SuppressWarnings("unchecked")
188     private void filterClasses(Set<Class<?>> c, Set<Class<? extends Endpoint>> discoveredExtendedEndpoints, Set<Class<?>> discoveredAnnotatedEndpoints,
189             Set<Class<? extends ServerApplicationConfig>> serverAppConfigs)
190     {
191         for (Class<?> clazz : c)
192         {
193             if (ServerApplicationConfig.class.isAssignableFrom(clazz))
194             {
195                 serverAppConfigs.add((Class<? extends ServerApplicationConfig>)clazz);
196             }
197 
198             if (Endpoint.class.isAssignableFrom(clazz))
199             {
200                 discoveredExtendedEndpoints.add((Class<? extends Endpoint>)clazz);
201             }
202             
203             ServerEndpoint endpoint = clazz.getAnnotation(ServerEndpoint.class);
204 
205             if (endpoint != null)
206             {
207                 discoveredAnnotatedEndpoints.add(clazz);
208             }
209         }
210     }
211 }