1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.push;
20
21 import java.io.IOException;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import javax.enterprise.inject.spi.BeanManager;
26 import javax.faces.FacesWrapper;
27 import javax.faces.component.UIComponent;
28 import javax.faces.component.UIWebsocket;
29 import javax.faces.component.behavior.ClientBehavior;
30 import javax.faces.component.behavior.ClientBehaviorContext;
31 import javax.faces.context.FacesContext;
32 import javax.faces.context.ResponseWriter;
33 import javax.faces.event.ComponentSystemEvent;
34 import javax.faces.event.ComponentSystemEventListener;
35 import javax.faces.event.ListenerFor;
36 import javax.faces.event.PostAddToViewEvent;
37 import javax.faces.render.Renderer;
38 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderer;
39 import org.apache.myfaces.cdi.util.CDIUtils;
40 import org.apache.myfaces.push.cdi.WebsocketApplicationBean;
41 import org.apache.myfaces.push.cdi.WebsocketChannelMetadata;
42 import org.apache.myfaces.push.cdi.WebsocketChannelTokenBuilderBean;
43 import org.apache.myfaces.push.cdi.WebsocketSessionBean;
44 import org.apache.myfaces.push.cdi.WebsocketViewBean;
45 import org.apache.myfaces.shared.renderkit.html.HTML;
46 import org.apache.myfaces.shared.renderkit.html.HtmlRendererUtils;
47 import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
48
49
50
51
52 @JSFRenderer(
53 renderKitId = "HTML_BASIC",
54 family = "javax.faces.Script",
55 type = "javax.faces.Websocket")
56 @ListenerFor(systemEventClass = PostAddToViewEvent.class)
57 public class WebsocketComponentRenderer extends Renderer implements ComponentSystemEventListener
58 {
59
60 @Override
61 public void processEvent(ComponentSystemEvent event)
62 {
63 if (event instanceof PostAddToViewEvent)
64 {
65 FacesContext facesContext = FacesContext.getCurrentInstance();
66 UIWebsocket component = (UIWebsocket) event.getComponent();
67 WebsocketInit initComponent = (WebsocketInit) facesContext.getViewRoot().findComponent(
68 (String) component.getAttributes().get("initComponentId"));
69 if (initComponent == null)
70 {
71 initComponent = (WebsocketInit) facesContext.getApplication().createComponent(facesContext,
72 "org.apache.myfaces.WebsocketInit", "org.apache.myfaces.WebsocketInit");
73 initComponent.setId((String) component.getAttributes().get("initComponentId"));
74 facesContext.getViewRoot().addComponentResource(facesContext,
75 initComponent, "body");
76 }
77 }
78 }
79
80 private HtmlBufferResponseWriterWrapper getResponseWriter(FacesContext context)
81 {
82 return HtmlBufferResponseWriterWrapper.getInstance(context.getResponseWriter());
83 }
84
85 @Override
86 public void decode(FacesContext facesContext, UIComponent component)
87 {
88 HtmlRendererUtils.decodeClientBehaviors(facesContext, component);
89 }
90
91 @Override
92 public void encodeBegin(FacesContext facesContext, UIComponent component) throws IOException
93 {
94 ResponseWriter writer = facesContext.getResponseWriter();
95
96 ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, writer);
97
98
99
100 writer.startElement(HTML.DIV_ELEM, component);
101 writer.writeAttribute(HTML.ID_ATTR, component.getClientId() ,null);
102 writer.writeAttribute(HTML.STYLE_ATTR, "display:none", null);
103 writer.endElement(HTML.DIV_ELEM);
104
105 if (!facesContext.getPartialViewContext().isAjaxRequest())
106 {
107 facesContext.setResponseWriter(getResponseWriter(facesContext));
108 }
109 }
110
111 @Override
112 public void encodeEnd(FacesContext facesContext, UIComponent c) throws IOException
113 {
114 super.encodeEnd(facesContext, c);
115
116 UIWebsocket component = (UIWebsocket) c;
117
118 WebsocketInit init = (WebsocketInit) facesContext.getViewRoot().findComponent(
119 (String) component.getAttributes().get("initComponentId"));
120
121 ResponseWriter writer = facesContext.getResponseWriter();
122
123 String channel = component.getChannel();
124
125
126 BeanManager beanManager = CDIUtils.getBeanManager(facesContext.getExternalContext());
127
128 WebsocketChannelTokenBuilderBean channelTokenBean = CDIUtils.lookup(
129 beanManager,
130 WebsocketChannelTokenBuilderBean.class);
131
132
133 WebsocketViewBean viewTokenBean = CDIUtils.lookup(
134 beanManager,
135 WebsocketViewBean.class);
136 WebsocketSessionBean sessionTokenBean = CDIUtils.lookup(
137 beanManager, WebsocketSessionBean.class);
138
139
140
141 String scope = component.getScope() == null ? "application" : component.getScope();
142 WebsocketChannelMetadata metadata = new WebsocketChannelMetadata(
143 channel, scope, component.getUser(), component.isConnected());
144
145 String channelToken = null;
146
147
148 if (!component.isConnected())
149 {
150 channelToken = viewTokenBean.getChannelToken(metadata);
151 }
152 if (channelToken == null)
153 {
154
155 channelToken = channelTokenBean.createChannelToken(facesContext, channel);
156
157
158 viewTokenBean.registerToken(channelToken, metadata);
159
160
161 sessionTokenBean.registerToken(channelToken, metadata);
162 }
163
164
165 WebsocketApplicationBean appTokenBean = CDIUtils.getInstance(
166 beanManager, WebsocketApplicationBean.class, false);
167
168
169 if (scope.equals("view"))
170 {
171 viewTokenBean.registerWebsocketSession(channelToken, metadata);
172 }
173 else if (scope.equals("session"))
174 {
175 sessionTokenBean = (sessionTokenBean != null) ? sessionTokenBean : CDIUtils.lookup(
176 CDIUtils.getBeanManager(facesContext.getExternalContext()),
177 WebsocketSessionBean.class);
178
179 sessionTokenBean.registerWebsocketSession(channelToken, metadata);
180 }
181 else
182 {
183
184 appTokenBean = (appTokenBean != null) ? appTokenBean : CDIUtils.lookup(
185 CDIUtils.getBeanManager(facesContext.getExternalContext()),
186 WebsocketApplicationBean.class);
187
188 appTokenBean.registerWebsocketSession(channelToken, metadata);
189 }
190
191 writer.startElement(HTML.SCRIPT_ELEM, component);
192 writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);
193
194 StringBuilder sb = new StringBuilder(50);
195 sb.append("jsf.push.init(");
196 sb.append("'"+component.getClientId()+"'");
197 sb.append(",");
198 sb.append("'"+facesContext.getExternalContext().encodeWebsocketURL(
199 facesContext.getApplication().getViewHandler().getWebsocketURL(
200 facesContext, component.getChannel()+"?"+channelToken))+"'");
201 sb.append(",");
202 sb.append("'"+component.getChannel()+"'");
203 sb.append(",");
204 sb.append(component.getOnopen());
205 sb.append(",");
206 sb.append(component.getOnmessage());
207 sb.append(",");
208 sb.append(component.getOnclose());
209 sb.append(",");
210 sb.append(getBehaviorScripts(facesContext, component));
211 sb.append(",");
212 sb.append(component.isConnected());
213 sb.append(");");
214
215 writer.write(sb.toString());
216
217 writer.endElement(HTML.SCRIPT_ELEM);
218
219 if (!facesContext.getPartialViewContext().isAjaxRequest())
220 {
221 ResponseWriter responseWriter = facesContext.getResponseWriter();
222 while (!(responseWriter instanceof HtmlBufferResponseWriterWrapper)
223 && responseWriter instanceof FacesWrapper)
224 {
225 responseWriter = (ResponseWriter) ((FacesWrapper) responseWriter).getWrapped();
226 }
227
228 HtmlBufferResponseWriterWrapper htmlBufferResponseWritter =
229 (HtmlBufferResponseWriterWrapper) responseWriter;
230 init.getUIWebsocketMarkupList().add(htmlBufferResponseWritter.toString());
231
232 facesContext.setResponseWriter(htmlBufferResponseWritter.getInitialWriter());
233 }
234 }
235
236 private String getBehaviorScripts(FacesContext facesContext, UIWebsocket component)
237 {
238 Map<String, List<ClientBehavior>> clientBehaviorsByEvent = component.getClientBehaviors();
239
240 if (clientBehaviorsByEvent.isEmpty())
241 {
242 return "{}";
243 }
244
245 String clientId = component.getClientId(facesContext);
246 StringBuilder scripts = new StringBuilder("{");
247
248 for (Entry<String, List<ClientBehavior>> entry : clientBehaviorsByEvent.entrySet())
249 {
250 String event = entry.getKey();
251 List<ClientBehavior> clientBehaviors = entry.getValue();
252 scripts.append(scripts.length() > 1 ? "," : "").append(event).append(":[");
253
254 for (int i = 0; i < clientBehaviors.size(); i++)
255 {
256 scripts.append(i > 0 ? "," : "").append("function(event){");
257 scripts.append(clientBehaviors.get(i).getScript(
258 ClientBehaviorContext.createClientBehaviorContext(
259 facesContext, component, event, clientId, null)));
260 scripts.append("}");
261 }
262
263 scripts.append("]");
264 }
265
266 return scripts.append("}").toString();
267 }
268
269 }