View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 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.TypeUtil;
37  import org.eclipse.jetty.util.log.Log;
38  import org.eclipse.jetty.util.log.Logger;
39  import org.eclipse.jetty.websocket.jsr356.server.ServerContainer;
40  import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter;
41  
42  @HandlesTypes(
43  { ServerApplicationConfig.class, ServerEndpoint.class, Endpoint.class })
44  public class WebSocketServerContainerInitializer implements ServletContainerInitializer
45  {
46      public static final String ENABLE_KEY = "org.eclipse.jetty.websocket.jsr356";
47      private static final Logger LOG = Log.getLogger(WebSocketServerContainerInitializer.class);
48  
49  
50      public static ServerContainer configureContext(ServletContextHandler context)
51      {
52          // Create Filter
53          WebSocketUpgradeFilter filter = WebSocketUpgradeFilter.configureContext(context);
54  
55          // Store reference to the WebSocketUpgradeFilter
56          context.setAttribute(WebSocketUpgradeFilter.class.getName(),filter);
57  
58          // Create the Jetty ServerContainer implementation
59          ServerContainer jettyContainer = new ServerContainer(filter,filter.getFactory(),context.getServer().getThreadPool());
60          context.addBean(jettyContainer);
61  
62          // Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment
63          context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer);
64  
65          return jettyContainer;
66      }
67  
68      @Override
69      public void onStartup(Set<Class<?>> c, ServletContext context) throws ServletException
70      {
71          Object enable = context.getAttribute(ENABLE_KEY);
72          
73          // Disable if explicitly disabled
74          if (TypeUtil.isFalse(enable))
75          {
76              if (c.isEmpty())
77                  LOG.debug("JSR-356 support disabled via attribute on context {} - {}",context.getContextPath(),context);
78              else
79                  LOG.warn("JSR-356 support disabled via attribute on context {} - {}",context.getContextPath(),context);
80              return;
81          }
82          
83          // Disabled if not explicitly enabled and there are no discovered annotations or interfaces
84          if (!TypeUtil.isTrue(enable) && c.isEmpty())
85          {
86              LOG.debug("No JSR-356 annotations or interfaces discovered. JSR-356 support disabled",context.getContextPath(),context);
87              return;
88          }
89  
90          ContextHandler handler = ContextHandler.getContextHandler(context);
91  
92          if (handler == null)
93          {
94              throw new ServletException("Not running on Jetty, JSR-356 support disabled");
95          }
96  
97          if (!(handler instanceof ServletContextHandler))
98          {
99              throw new ServletException("Not running in Jetty ServletContextHandler, JSR-356 support disabled");
100         }
101 
102         ServletContextHandler jettyContext = (ServletContextHandler)handler;
103 
104         // Create the Jetty ServerContainer implementation
105         ServerContainer jettyContainer = configureContext(jettyContext);
106 
107         // Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment
108         context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer);
109 
110         LOG.debug("Found {} classes",c.size());
111 
112         // Now process the incoming classes
113         Set<Class<? extends Endpoint>> discoveredExtendedEndpoints = new HashSet<>();
114         Set<Class<?>> discoveredAnnotatedEndpoints = new HashSet<>();
115         Set<Class<? extends ServerApplicationConfig>> serverAppConfigs = new HashSet<>();
116 
117         filterClasses(c,discoveredExtendedEndpoints,discoveredAnnotatedEndpoints,serverAppConfigs);
118 
119         LOG.debug("Discovered {} extends Endpoint classes",discoveredExtendedEndpoints.size());
120         LOG.debug("Discovered {} @ServerEndpoint classes",discoveredAnnotatedEndpoints.size());
121         LOG.debug("Discovered {} ServerApplicationConfig classes",serverAppConfigs.size());
122 
123         // Process the server app configs to determine endpoint filtering
124         boolean wasFiltered = false;
125         Set<ServerEndpointConfig> deployableExtendedEndpointConfigs = new HashSet<>();
126         Set<Class<?>> deployableAnnotatedEndpoints = new HashSet<>();
127 
128         for (Class<? extends ServerApplicationConfig> clazz : serverAppConfigs)
129         {
130             LOG.debug("Found ServerApplicationConfig: {}",clazz);
131             try
132             {
133                 ServerApplicationConfig config = clazz.newInstance();
134 
135                 Set<ServerEndpointConfig> seconfigs = config.getEndpointConfigs(discoveredExtendedEndpoints);
136                 if (seconfigs != null)
137                 {
138                     wasFiltered = true;
139                     deployableExtendedEndpointConfigs.addAll(seconfigs);
140                 }
141 
142                 Set<Class<?>> annotatedClasses = config.getAnnotatedEndpointClasses(discoveredAnnotatedEndpoints);
143                 if (annotatedClasses != null)
144                 {
145                     wasFiltered = true;
146                     deployableAnnotatedEndpoints.addAll(annotatedClasses);
147                 }
148             }
149             catch (InstantiationException | IllegalAccessException e)
150             {
151                 throw new ServletException("Unable to instantiate: " + clazz.getName(),e);
152             }
153         }
154 
155         // Default behavior if nothing filtered
156         if (!wasFiltered)
157         {
158             deployableAnnotatedEndpoints.addAll(discoveredAnnotatedEndpoints);
159             // Note: it is impossible to determine path of "extends Endpoint" discovered classes
160             deployableExtendedEndpointConfigs = new HashSet<>();
161         }
162 
163         // Deploy what should be deployed.
164         LOG.debug("Deploying {} ServerEndpointConfig(s)",deployableExtendedEndpointConfigs.size());
165         for (ServerEndpointConfig config : deployableExtendedEndpointConfigs)
166         {
167             try
168             {
169                 jettyContainer.addEndpoint(config);
170             }
171             catch (DeploymentException e)
172             {
173                 throw new ServletException(e);
174             }
175         }
176 
177         LOG.debug("Deploying {} @ServerEndpoint(s)",deployableAnnotatedEndpoints.size());
178         for (Class<?> annotatedClass : deployableAnnotatedEndpoints)
179         {
180             try
181             {
182                 jettyContainer.addEndpoint(annotatedClass);
183             }
184             catch (DeploymentException e)
185             {
186                 throw new ServletException(e);
187             }
188         }
189     }
190 
191     @SuppressWarnings("unchecked")
192     private void filterClasses(Set<Class<?>> c, Set<Class<? extends Endpoint>> discoveredExtendedEndpoints, Set<Class<?>> discoveredAnnotatedEndpoints,
193             Set<Class<? extends ServerApplicationConfig>> serverAppConfigs)
194     {
195         for (Class<?> clazz : c)
196         {
197             if (ServerApplicationConfig.class.isAssignableFrom(clazz))
198             {
199                 serverAppConfigs.add((Class<? extends ServerApplicationConfig>)clazz);
200             }
201 
202             if (Endpoint.class.isAssignableFrom(clazz))
203             {
204                 discoveredExtendedEndpoints.add((Class<? extends Endpoint>)clazz);
205             }
206             
207             ServerEndpoint endpoint = clazz.getAnnotation(ServerEndpoint.class);
208 
209             if (endpoint != null)
210             {
211                 discoveredAnnotatedEndpoints.add(clazz);
212             }
213         }
214     }
215 }