View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2015 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;
20  
21  import java.io.IOException;
22  import java.net.URI;
23  import java.security.Principal;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Objects;
31  import java.util.Set;
32  
33  import javax.websocket.CloseReason;
34  import javax.websocket.EndpointConfig;
35  import javax.websocket.Extension;
36  import javax.websocket.MessageHandler;
37  import javax.websocket.RemoteEndpoint.Async;
38  import javax.websocket.RemoteEndpoint.Basic;
39  import javax.websocket.Session;
40  import javax.websocket.WebSocketContainer;
41  
42  import org.eclipse.jetty.util.log.Log;
43  import org.eclipse.jetty.util.log.Logger;
44  import org.eclipse.jetty.websocket.api.BatchMode;
45  import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
46  import org.eclipse.jetty.websocket.common.LogicalConnection;
47  import org.eclipse.jetty.websocket.common.SessionListener;
48  import org.eclipse.jetty.websocket.common.WebSocketSession;
49  import org.eclipse.jetty.websocket.common.events.EventDriver;
50  import org.eclipse.jetty.websocket.jsr356.endpoints.AbstractJsrEventDriver;
51  import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
52  import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
53  import org.eclipse.jetty.websocket.jsr356.metadata.MessageHandlerMetadata;
54  
55  /**
56   * Session for the JSR.
57   */
58  public class JsrSession extends WebSocketSession implements javax.websocket.Session, Configurable
59  {
60      private static final Logger LOG = Log.getLogger(JsrSession.class);
61      private final ClientContainer container;
62      private final String id;
63      private final EndpointConfig config;
64      private final EndpointMetadata metadata;
65      private final DecoderFactory decoderFactory;
66      private final EncoderFactory encoderFactory;
67      /** Factory for MessageHandlers */
68      private final MessageHandlerFactory messageHandlerFactory;
69      /** Array of MessageHandlerWrappers, indexed by {@link MessageType#ordinal()} */
70      private final MessageHandlerWrapper wrappers[];
71      private Set<MessageHandler> messageHandlerSet;
72      private List<Extension> negotiatedExtensions;
73      private Map<String, String> pathParameters = new HashMap<>();
74      private JsrAsyncRemote asyncRemote;
75      private JsrBasicRemote basicRemote;
76  
77      public JsrSession(ClientContainer container, String id, URI requestURI, EventDriver websocket, LogicalConnection connection, SessionListener... sessionListeners)
78      {
79          super(container, requestURI, websocket, connection, sessionListeners);
80          if (!(websocket instanceof AbstractJsrEventDriver))
81          {
82              throw new IllegalArgumentException("Cannot use, not a JSR WebSocket: " + websocket);
83          }
84          AbstractJsrEventDriver jsr = (AbstractJsrEventDriver)websocket;
85          this.config = jsr.getConfig();
86          this.metadata = jsr.getMetadata();
87          this.container = container;
88          this.id = id;
89          this.decoderFactory = new DecoderFactory(this,metadata.getDecoders(),container.getDecoderFactory());
90          this.encoderFactory = new EncoderFactory(this,metadata.getEncoders(),container.getEncoderFactory());
91          this.messageHandlerFactory = new MessageHandlerFactory();
92          this.wrappers = new MessageHandlerWrapper[MessageType.values().length];
93          this.messageHandlerSet = new HashSet<>();
94      }
95  
96      @Override
97      public void addMessageHandler(MessageHandler handler) throws IllegalStateException
98      {
99          Objects.requireNonNull(handler, "MessageHandler cannot be null");
100 
101         synchronized (wrappers)
102         {
103             for (MessageHandlerMetadata metadata : messageHandlerFactory.getMetadata(handler.getClass()))
104             {
105                 DecoderFactory.Wrapper wrapper = decoderFactory.getWrapperFor(metadata.getMessageClass());
106                 if (wrapper == null)
107                 {
108                     StringBuilder err = new StringBuilder();
109                     err.append("Unable to find decoder for type <");
110                     err.append(metadata.getMessageClass().getName());
111                     err.append("> used in <");
112                     err.append(metadata.getHandlerClass().getName());
113                     err.append(">");
114                     throw new IllegalStateException(err.toString());
115                 }
116 
117                 MessageType key = wrapper.getMetadata().getMessageType();
118                 MessageHandlerWrapper other = wrappers[key.ordinal()];
119                 if (other != null)
120                 {
121                     StringBuilder err = new StringBuilder();
122                     err.append("Encountered duplicate MessageHandler handling message type <");
123                     err.append(wrapper.getMetadata().getObjectType().getName());
124                     err.append(">, ").append(metadata.getHandlerClass().getName());
125                     err.append("<");
126                     err.append(metadata.getMessageClass().getName());
127                     err.append("> and ");
128                     err.append(other.getMetadata().getHandlerClass().getName());
129                     err.append("<");
130                     err.append(other.getMetadata().getMessageClass().getName());
131                     err.append("> both implement this message type");
132                     throw new IllegalStateException(err.toString());
133                 }
134                 else
135                 {
136                     MessageHandlerWrapper handlerWrapper = new MessageHandlerWrapper(handler,metadata,wrapper);
137                     wrappers[key.ordinal()] = handlerWrapper;
138                 }
139             }
140 
141             // Update handlerSet
142             updateMessageHandlerSet();
143         }
144     }
145 
146     @Override
147     public void close(CloseReason closeReason) throws IOException
148     {
149         close(closeReason.getCloseCode().getCode(),closeReason.getReasonPhrase());
150     }
151 
152     @Override
153     public Async getAsyncRemote()
154     {
155         if (asyncRemote == null)
156         {
157             asyncRemote = new JsrAsyncRemote(this);
158         }
159         return asyncRemote;
160     }
161 
162     @Override
163     public Basic getBasicRemote()
164     {
165         if (basicRemote == null)
166         {
167             basicRemote = new JsrBasicRemote(this);
168         }
169         return basicRemote;
170     }
171 
172     @Override
173     public WebSocketContainer getContainer()
174     {
175         return this.container;
176     }
177 
178     public DecoderFactory getDecoderFactory()
179     {
180         return decoderFactory;
181     }
182 
183     public EncoderFactory getEncoderFactory()
184     {
185         return encoderFactory;
186     }
187 
188     public EndpointConfig getEndpointConfig()
189     {
190         return config;
191     }
192 
193     public EndpointMetadata getEndpointMetadata()
194     {
195         return metadata;
196     }
197 
198     @Override
199     public String getId()
200     {
201         return this.id;
202     }
203 
204     @Override
205     public int getMaxBinaryMessageBufferSize()
206     {
207         return getPolicy().getMaxBinaryMessageSize();
208     }
209 
210     @Override
211     public long getMaxIdleTimeout()
212     {
213         return getPolicy().getIdleTimeout();
214     }
215 
216     @Override
217     public int getMaxTextMessageBufferSize()
218     {
219         return getPolicy().getMaxTextMessageSize();
220     }
221 
222     public MessageHandlerFactory getMessageHandlerFactory()
223     {
224         return messageHandlerFactory;
225     }
226 
227     @Override
228     public Set<MessageHandler> getMessageHandlers()
229     {
230         // Always return copy of set, as it is common to iterate and remove from the real set.
231         return new HashSet<MessageHandler>(messageHandlerSet);
232     }
233 
234     public MessageHandlerWrapper getMessageHandlerWrapper(MessageType type)
235     {
236         synchronized (wrappers)
237         {
238             return wrappers[type.ordinal()];
239         }
240     }
241 
242     @Override
243     public List<Extension> getNegotiatedExtensions()
244     {
245         if (negotiatedExtensions == null)
246         {
247             negotiatedExtensions = new ArrayList<Extension>();
248             for (ExtensionConfig cfg : getUpgradeResponse().getExtensions())
249             {
250                 negotiatedExtensions.add(new JsrExtension(cfg));
251             }
252         }
253         return negotiatedExtensions;
254     }
255 
256     @Override
257     public String getNegotiatedSubprotocol()
258     {
259         String acceptedSubProtocol = getUpgradeResponse().getAcceptedSubProtocol();
260         if (acceptedSubProtocol == null)
261         {
262             return "";
263         }
264         return acceptedSubProtocol;
265     }
266 
267     @Override
268     public Set<Session> getOpenSessions()
269     {
270         return container.getOpenSessions();
271     }
272 
273     @Override
274     public Map<String, String> getPathParameters()
275     {
276         return Collections.unmodifiableMap(pathParameters);
277     }
278 
279     @Override
280     public String getQueryString()
281     {
282         return getUpgradeRequest().getRequestURI().getQuery();
283     }
284 
285     @Override
286     public Map<String, List<String>> getRequestParameterMap()
287     {
288         return getUpgradeRequest().getParameterMap();
289     }
290 
291     @Override
292     public Principal getUserPrincipal()
293     {
294         return getUpgradeRequest().getUserPrincipal();
295     }
296 
297     @Override
298     public Map<String, Object> getUserProperties()
299     {
300         return config.getUserProperties();
301     }
302 
303     @Override
304     public void init(EndpointConfig config)
305     {
306         // Initialize encoders
307         encoderFactory.init(config);
308         // Initialize decoders
309         decoderFactory.init(config);
310     }
311 
312     @Override
313     public void removeMessageHandler(MessageHandler handler)
314     {
315         synchronized (wrappers)
316         {
317             try
318             {
319                 for (MessageHandlerMetadata metadata : messageHandlerFactory.getMetadata(handler.getClass()))
320                 {
321                     DecoderMetadata decoder = decoderFactory.getMetadataFor(metadata.getMessageClass());
322                     MessageType key = decoder.getMessageType();
323                     wrappers[key.ordinal()] = null;
324                 }
325                 updateMessageHandlerSet();
326             }
327             catch (IllegalStateException e)
328             {
329                 LOG.warn("Unable to identify MessageHandler: " + handler.getClass().getName(),e);
330             }
331         }
332     }
333 
334     @Override
335     public void setMaxBinaryMessageBufferSize(int length)
336     {
337         getPolicy().setMaxBinaryMessageSize(length);
338         getPolicy().setMaxBinaryMessageBufferSize(length);
339     }
340 
341     @Override
342     public void setMaxIdleTimeout(long milliseconds)
343     {
344         getPolicy().setIdleTimeout(milliseconds);
345         super.setIdleTimeout(milliseconds);
346     }
347 
348     @Override
349     public void setMaxTextMessageBufferSize(int length)
350     {
351         getPolicy().setMaxTextMessageSize(length);
352         getPolicy().setMaxTextMessageBufferSize(length);
353     }
354 
355     public void setPathParameters(Map<String, String> pathParams)
356     {
357         this.pathParameters.clear();
358         if (pathParams != null)
359         {
360             this.pathParameters.putAll(pathParams);
361         }
362     }
363 
364     private void updateMessageHandlerSet()
365     {
366         messageHandlerSet.clear();
367         for (MessageHandlerWrapper wrapper : wrappers)
368         {
369             if (wrapper == null)
370             {
371                 // skip empty
372                 continue;
373             }
374             messageHandlerSet.add(wrapper.getHandler());
375         }
376     }
377 
378     @Override
379     public BatchMode getBatchMode()
380     {
381         // JSR 356 specification mandates default batch mode to be off.
382         return BatchMode.OFF;
383     }
384 }