View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.push;
20  
21  import org.apache.myfaces.push.cdi.WebsocketApplicationSessionHolder;
22  import java.io.IOException;
23  import java.io.Serializable;
24  import javax.enterprise.inject.spi.BeanManager;
25  import javax.enterprise.inject.spi.CDI;
26  import javax.enterprise.util.AnnotationLiteral;
27  import javax.faces.event.WebsocketEvent;
28  import javax.faces.event.WebsocketEvent.Closed;
29  import javax.faces.event.WebsocketEvent.Opened;
30  import javax.websocket.CloseReason;
31  import javax.websocket.CloseReason.CloseCodes;
32  import javax.websocket.Endpoint;
33  import javax.websocket.EndpointConfig;
34  import javax.websocket.Session;
35  
36  /**
37   *
38   */
39  public class EndpointImpl extends Endpoint
40  {
41  
42      public static final String JAVAX_FACES_PUSH_PATH = "/javax.faces.push/{channel}";
43  
44      public static final String PUSH_CHANNEL_PARAMETER = "channel";
45      
46      private static final AnnotationLiteral<Opened> OPENED = 
47              new AnnotationLiteral<Opened>() 
48              {
49                  private static final long serialVersionUID = 2789324L;
50              };
51      private static final AnnotationLiteral<Closed> CLOSED = 
52              new AnnotationLiteral<Closed>() 
53              {
54                  private static final long serialVersionUID = 38450203L;
55              };
56  
57      @Override
58      public void onOpen(Session session, EndpointConfig config)
59      {
60          // Get the channel and the channel id
61          String channel = session.getPathParameters().get(PUSH_CHANNEL_PARAMETER);
62          String channelToken = session.getQueryString();
63  
64          // Locate holder
65          // Note in this point there is no session scope because there is no HttpSession available,
66          // but on the handshake there is. So, everything below should use CDI application scoped
67          // beans only.
68  
69          if (Boolean.TRUE.equals(config.getUserProperties().get(WebsocketConfigurator.WEBSOCKET_VALID)) &&
70                  WebsocketApplicationSessionHolder.addOrUpdateSession(channelToken, session))
71          {
72              session.setMaxIdleTimeout((Long) config.getUserProperties().getOrDefault(
73                      WebsocketConfigurator.MAX_IDLE_TIMEOUT, 300000L));
74  
75              Serializable user = (Serializable) session.getUserProperties().get(WebsocketConfigurator.WEBSOCKET_USER);
76  
77              BeanManager beanManager = CDI.current().getBeanManager();
78              beanManager.fireEvent(new WebsocketEvent(channel, user, null), OPENED);
79              
80              session.getUserProperties().put(
81                      WebsocketSessionClusterSerializedRestore.WEBSOCKET_SESSION_SERIALIZED_RESTORE, 
82                      new WebsocketSessionClusterSerializedRestore(channelToken));
83          }
84          else
85          {
86              try
87              {
88                  session.close(new CloseReason(CloseCodes.UNEXPECTED_CONDITION,
89                          "Websocket connection not registered in current session"));
90              }
91              catch (IOException ex)
92              {
93                  onError(session, ex);
94              }
95          }
96      }
97  
98      @Override
99      public void onClose(Session session, CloseReason closeReason)
100     {
101         // Get the channel and the channel id
102         String channel = session.getPathParameters().get(PUSH_CHANNEL_PARAMETER);
103         String channelToken = session.getQueryString();
104 
105         Serializable user = (Serializable) session.getUserProperties().get(WebsocketConfigurator.WEBSOCKET_USER);
106 
107         // In this point in some cases (close reason 1006) CDI does not work and you cannot lookup application
108         // scope beans, because the context could not be properly initialized. In that case we should try to
109         // propagate the event but if an error happens, just ensure the Session is properly disposed.
110         try
111         {
112             BeanManager beanManager = CDI.current().getBeanManager();
113             beanManager.fireEvent(
114                     new WebsocketEvent(channel, user, closeReason.getCloseCode()),  CLOSED);
115         }
116         catch(Exception e)
117         {
118             //No op because it is expected something could go wrong.
119         }
120         finally
121         {
122             WebsocketApplicationSessionHolder.removeSession(channelToken);
123         }
124     }
125 
126     @Override
127     public void onError(Session session, Throwable ex)
128     {
129         if (session.isOpen())
130         {
131             session.getUserProperties().put(Throwable.class.getName(), ex);
132         }
133     }
134 }