1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.tobago.internal.ajax;
21
22 import org.apache.myfaces.tobago.component.Attributes;
23 import org.apache.myfaces.tobago.internal.lifecycle.TobagoLifecycle;
24 import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
25 import org.apache.myfaces.tobago.internal.util.ResponseUtils;
26 import org.apache.myfaces.tobago.internal.webapp.JsonResponseWriter;
27 import org.apache.myfaces.tobago.util.ComponentUtils;
28 import org.apache.myfaces.tobago.util.EncodeAjaxCallback;
29 import org.apache.myfaces.tobago.util.RequestUtils;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 import javax.faces.FactoryFinder;
34 import javax.faces.application.StateManager;
35 import javax.faces.component.UIComponent;
36 import javax.faces.component.UIViewRoot;
37 import javax.faces.context.ExternalContext;
38 import javax.faces.context.FacesContext;
39 import javax.faces.context.ResponseWriter;
40 import javax.faces.render.RenderKit;
41 import javax.faces.render.RenderKitFactory;
42 import javax.servlet.http.HttpServletResponse;
43 import java.io.IOException;
44 import java.io.PrintWriter;
45 import java.util.Map;
46
47 public class AjaxResponseRenderer {
48
49 public static final int CODE_SUCCESS = 200;
50
51
52
53 @Deprecated
54 public static final int CODE_NOT_MODIFIED = 304;
55 public static final int CODE_RELOAD_REQUIRED = 309;
56 public static final int CODE_ERROR = 500;
57 public static final String CONTENT_TYPE = "application/json";
58 private static final Logger LOG = LoggerFactory.getLogger(AjaxResponseRenderer.class);
59
60 private EncodeAjaxCallback callback;
61
62 public AjaxResponseRenderer() {
63 callback = new EncodeAjaxCallback();
64 }
65
66 public void renderResponse(FacesContext facesContext) throws IOException {
67 final UIViewRoot viewRoot = facesContext.getViewRoot();
68 RenderKitFactory renderFactory = (RenderKitFactory)
69 FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
70 RenderKit renderKit = renderFactory.getRenderKit(
71 facesContext, viewRoot.getRenderKitId());
72
73 UIViewRoot incomingViewRoot = (UIViewRoot)
74 facesContext.getExternalContext().getRequestMap().get(TobagoLifecycle.VIEW_ROOT_KEY);
75 LOG.error("TODO: Reimplement AJAX stuff for JSF 2.0");
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 writeResponse(facesContext, renderKit, false);
105
106 }
107
108 private void renderComponent(FacesContext facesContext, RenderKit renderKit, String clientId, UIComponent component)
109 throws IOException {
110 final PrintWriter writer = getPrintWriter(facesContext);
111 final JsonResponseWriter jsonWriter = getJsonResponseWriter(renderKit, writer);
112
113 facesContext.setResponseWriter(jsonWriter);
114
115 if (LOG.isDebugEnabled()) {
116 LOG.debug("write ajax response for {}", component);
117 }
118 writer.write("{\n \"ajaxId\": \"");
119 writer.write(clientId);
120 writer.write("\",\n");
121
122 writer.write(" \"html\": \"");
123 ComponentUtils.invokeOnComponent(facesContext, facesContext.getViewRoot(), clientId, callback);
124 writer.write("\",\n");
125
126 writer.write(" \"responseCode\": ");
127 writer.write(Integer.toString(CODE_SUCCESS));
128
129 writer.write(",\n");
130 writer.write(" \"script\": \"function() { ");
131 writer.write(jsonWriter.getJavascript());
132 writer.write(" }\"");
133
134 writer.write("\n }");
135 }
136
137 private void saveState(FacesContext facesContext, RenderKit renderKit) throws IOException {
138
139 ResponseWriter stateWriter = renderKit.createResponseWriter(getPrintWriter(facesContext), CONTENT_TYPE, null);
140 facesContext.setResponseWriter(stateWriter);
141
142 StateManager stateManager = facesContext.getApplication().getStateManager();
143 StateManager.SerializedView serializedView = stateManager.saveSerializedView(facesContext);
144 stateManager.writeState(facesContext, serializedView);
145 }
146
147 private static void ensureContentTypeHeader(FacesContext facesContext, String charset, String contentType) {
148 StringBuilder sb = new StringBuilder(contentType);
149 if (charset == null) {
150 charset = "UTF-8";
151 }
152 sb.append("; charset=");
153 sb.append(charset);
154 ResponseUtils.ensureContentTypeHeader(facesContext, sb.toString());
155 }
156
157 private void writeResponse(FacesContext facesContext, RenderKit renderKit, boolean reloadRequired)
158 throws IOException {
159
160 RequestUtils.ensureEncoding(facesContext);
161 ResponseUtils.ensureNoCacheHeader(facesContext);
162 UIComponent page = ComponentUtils.findPage(facesContext);
163 String charset;
164 if (page != null) {
165 charset = (String) page.getAttributes().get(Attributes.CHARSET);
166 } else {
167 charset = "UTF-8";
168 }
169 ensureContentTypeHeader(facesContext, charset, CONTENT_TYPE);
170
171 PrintWriter writer = getPrintWriter(facesContext);
172 writer.write("{\n \"tobagoAjaxResponse\": true,\n");
173 writer.write(" \"responseCode\": ");
174 writer.write(reloadRequired ? Integer.toString(CODE_RELOAD_REQUIRED) : Integer.toString(CODE_SUCCESS));
175
176 Map<String, UIComponent> ajaxComponents = AjaxInternalUtils.getAjaxComponents(facesContext);
177 if (ajaxComponents != null) {
178 int i = 0;
179 for (Map.Entry<String, UIComponent> entry : ajaxComponents.entrySet()) {
180 writer.write(",\n");
181 writer.write(" \"ajaxPart_");
182 writer.write(Integer.toString(i++));
183 writer.write("\": ");
184
185 UIComponent component = entry.getValue();
186 FacesContextUtils.setAjaxComponentId(facesContext, entry.getKey());
187 renderComponent(facesContext, renderKit, entry.getKey(), component);
188 }
189 }
190
191 if (!reloadRequired) {
192 writer.write(",\n");
193 writer.write(" \"jsfState\": \"");
194 saveState(facesContext, renderKit);
195 writer.write("\"");
196 }
197
198 writer.write("\n}\n");
199 writer.flush();
200 writer.close();
201 }
202
203 private PrintWriter getPrintWriter(FacesContext facesContext) throws IOException {
204 ExternalContext externalContext = facesContext.getExternalContext();
205
206 if (externalContext.getResponse() instanceof HttpServletResponse) {
207 final HttpServletResponse httpServletResponse
208 = (HttpServletResponse) externalContext.getResponse();
209 return httpServletResponse.getWriter();
210 }
211 throw new IOException("No ResponseWriter found for ExternalContext " + externalContext);
212 }
213
214 private JsonResponseWriter getJsonResponseWriter(RenderKit renderKit, PrintWriter writer) {
215
216 ResponseWriter newWriter = renderKit.createResponseWriter(writer, CONTENT_TYPE, null);
217 if (newWriter instanceof JsonResponseWriter) {
218 return (JsonResponseWriter) newWriter;
219 } else {
220
221 return new JsonResponseWriter(newWriter, CONTENT_TYPE, newWriter.getCharacterEncoding());
222 }
223 }
224 }