View Javadoc

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