/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Alex Rojkov
*/
package com.caucho.jsf.dev;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.util.L10N;
import com.caucho.jsf.webapp.*;
import javax.el.ELContext;
import javax.el.ValueExpression;
import javax.faces.FactoryFinder;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
public class JsfDeveloperAidServlet
extends GenericServlet
{
private static final Logger log
= Logger.getLogger(FacesServletImpl.class.getName());
private static final L10N L
= new L10N(com.caucho.jsf.dev.JsfDeveloperAid.class);
private ServletContext _webApp;
private FacesContextFactory _facesContextFactory;
private Lifecycle _lifecycle;
public void init(ServletConfig config)
throws ServletException
{
_webApp = config.getServletContext();
_facesContextFactory = (FacesContextFactory)
FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
LifecycleFactory lifecycleFactory = (LifecycleFactory)
FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
String name = config.getInitParameter("javax.faces.LIFECYCLE_ID");
if (name == null)
name = _webApp.getInitParameter("javax.faces.LIFECYCLE_ID");
if (name == null)
name = LifecycleFactory.DEFAULT_LIFECYCLE;
_lifecycle = lifecycleFactory.getLifecycle(name);
}
private void printControls(PrintWriter out,
HttpServletRequest request,
HttpSession session)
{
if (request.getParameter("viewId") != null)
out.println(" <br/><br/><a href=\"" +
request.getContextPath() +
"/caucho.jsf.developer.aid" +
(session == null ? "" : ";jsessionid=" + session.getId()) +
"\"><em>" +
"Show Available Views" +
"</em></a>");
out.println(" <br/><br/><a href=\"" +
request.getContextPath() +
"/caucho.jsf.developer.aid" +
(session == null ? "" : ";jsessionid=" + session.getId()) +
"?save=\"><em>" +
"Save to file" +
"</em></a>");
out.println(
" <br/><br/><form enctype=\"multipart/form-data\" method=\"POST\"><em>Load from file</em><input name=\"file\" type=\"file\"/><input type=\"submit\" value=\"Upload\"/></form>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
ServletInputStream in = request.getInputStream();
byte []data = null;
int position = 0;
Scan scan = new Scan();
int count;
boolean found = false;
byte []buffer = new byte[4096];
while ((count = in.read(buffer, position, buffer.length - position)) >
0) {
if (found)
continue;
if (count + position == buffer.length) {
final byte []newData = new byte[buffer.length * 2];
System.arraycopy(buffer, 0, newData, 0, buffer.length);
buffer = newData;
}
if (scan.boundaryEnd == -1) {
for (int i = 0; i < position + count; i++) {
if (buffer[i] == '\n') {
scan.boundaryEnd = i - 1;
scan.pointer = i;
break;
}
}
}
position = position + count;
found = find(buffer, scan, position);
if (found) {
data = buffer;
buffer = new byte[256];
position = 0;
}
}
if (found) {
int start = -1;
for (int i = scan.boundaryEnd; i < data.length; i++) {
if (data[i] == '\n' && data[i - 2] == '\n') {
start = i;
break;
}
}
Hessian2Input input = new Hessian2Input(new ByteArrayInputStream(data,
start +
1,
scan
.pointer -
start -
3));
Object obj = input.readObject();
HttpSession session = request.getSession(true);
session.setAttribute("caucho.jsf.developer.aid", obj);
}
else {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
private class Scan {
private int pointer = 0;
private int boundaryIdx = 0;
private int boundaryEnd = -1;
}
public boolean find(byte []data, Scan scan, int limit)
{
for (; scan.pointer < limit; scan.pointer++) {
for (; scan.boundaryIdx < scan.boundaryEnd; scan.boundaryIdx++) {
if (limit >= (scan.pointer + scan.boundaryIdx)) {
if (data[scan.pointer + scan.boundaryIdx] !=
data[scan.boundaryIdx]) {
scan.boundaryIdx = 0;
break;
}
}
else
break;
}
if (scan.boundaryIdx == scan.boundaryEnd)
return true;
}
return false;
}
public void service(ServletRequest req, ServletResponse res)
throws IOException, ServletException
{
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
final PrintWriter out = res.getWriter();
if ("POST".equalsIgnoreCase(request.getMethod())) {
doPost(request, response);
}
final HttpSession session = request.getSession();
final Map<String, com.caucho.jsf.dev.JsfDeveloperAid.JsfRequestSnapshot> aidMap;
if (session != null)
aidMap = (Map<String, JsfDeveloperAid.JsfRequestSnapshot>)
session.getAttribute("caucho.jsf.developer.aid");
else
aidMap = null;
if (req.getParameter("save") != null) {
if (aidMap != null)
serveAidMap(aidMap, res);
else
response.sendError(HttpServletResponse.SC_NO_CONTENT);
return;
}
final FacesContext oldContext = FacesContext.getCurrentInstance();
FacesContext context = null;
try {
context = _facesContextFactory.getFacesContext(_webApp,
req,
res,
_lifecycle);
res.setCharacterEncoding("UTF-8");
res.setContentType("text/html");
out.println(
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
out.println(
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">");
if (session == null || aidMap == null) {
out.println("<body>");
printControls(out, request, null);
out.println("</body>");
out.println("</html>");
return;
}
final String viewId = req.getParameter("viewId");
if (viewId == null) {
out.println(" <head>");
out.print(" <title>");
out.print("Available Views");
out.println("</title>");
out.println(" </head>");
out.println(" <body>");
out.println(" <em>Available Views</em>");
out.println(" <ul>");
for (String view : aidMap.keySet()) {
out.println(" <li>");
out.println(" <a href=\"" +
request.getContextPath() +
"/caucho.jsf.developer.aid;jsessionid=" +
session.getId() +
"?viewId=" +
view + "\">" +
view +
"</a>");
out.println(" </li>");
}
out.println(" </ul>");
printControls(out, request, session);
out.println(" </body>");
out.println("</html>");
return;
}
else {
out.println("<head>");
out.print("<title>View: ");
out.print(viewId);
out.println("</title>");
out.println("<style type=\"text/css\" media=\"all\">");
out.println("#header ul {list-style: none;padding: 0;margin: 0;}");
out.println(
"#header li {float: left;border: 1px solid;border-bottom-width: 0;margin: 0 0.5em 0 0; font-weight: bold}");
out.println("#header a {display: block;padding: 0 1em;}");
out.println(
"#header #selected {position: relative;top: 1px;background: white; font-weight: normal}");
out.println("#content {border: 1px solid;clear: both;}");
out.println("#view {padding: 10px, 10px, 10px, 10px}");
out.println("table {width: 100%}");
out.println("td {border: 1px dotted}");
out.println("h1 {margin: 0;padding: 0 0 1em 0;}");
out.println("</style>");
out.println("</head>");
//
out.println("<body>");
out.println(" <div id=\"header\">");
out.println(" <ul>");
final String phaseId = req.getParameter("phaseId");
String valueExpression = req.getParameter("valueExpression");
if (valueExpression != null)
valueExpression = URLDecoder.decode(valueExpression, "UTF-8");
com.caucho.jsf.dev.JsfDeveloperAid.JsfRequestSnapshot snapshot
= aidMap.get(viewId);
out.print(" <li" + (phaseId == null ? " id=\"selected\"" : "") + ">");
out.print("<a href=\"" +
request.getContextPath() +
"/caucho.jsf.developer.aid;jsessionid=" +
session.getId() +
"?viewId=" +
viewId +
"\">" +
"Request Info" +
"</a>");
out.println("</li>");
com.caucho.jsf.dev.JsfDeveloperAid.ViewRoot viewRoot = null;
com.caucho.jsf.dev.JsfDeveloperAid.ViewRoot []viewRoots
= snapshot.getPhases();
for (com.caucho.jsf.dev.JsfDeveloperAid.ViewRoot root : viewRoots) {
String phase = root.getPhase();
boolean selected = false;
if (phase.equals(phaseId) && valueExpression == null) {
selected = true;
viewRoot = root;
}
out.print(" <li" + (selected ? " id=\"selected\"" : "") + ">");
out.print("<a href=\"" +
request.getContextPath() +
"/caucho.jsf.developer.aid;jsessionid=" +
session.getId() +
"?viewId=" +
viewId +
"&phaseId=" +
phase +
"\">" +
phase +
"</a>");
out.println("</li>");
}
if (valueExpression != null) {
out.print(" <li id=\"selected\">");
out.print("<a href=\"" +
request.getContextPath() +
"/caucho.jsf.developer.aid;jsessionid=" +
session.getId() +
"?viewId=" + viewId +
"&phaseId=" + phaseId +
"&valueExpression=" +
URLEncoder.encode(valueExpression, "UTF-8") +
"\">" +
valueExpression +
"</a>");
out.println("</li>");
}
out.println(" </ul>");
out.println(" </div>");
out.println(" <div id=\"content\">");
if (valueExpression != null) {
com.caucho.jsf.dev.JsfDeveloperAid.ViewRoot root = viewRoots[1];
UIViewRoot uiViewRoot = new UIViewRoot();
uiViewRoot.setLocale(root.getLocale());
uiViewRoot.setRenderKitId(root.getRenderKitId());
//need view for resolving property bundles.
context.setViewRoot(uiViewRoot);
printEvaluated(context,
request,
out,
valueExpression,
viewId,
phaseId);
out.println("<br/>");
out.println("<em><a href=\"" +
request.getContextPath() +
"/caucho.jsf.developer.aid;jsessionid=" +
session.getId() +
"?viewId=" + viewId +
"&phaseId=" + phaseId + "\">" +
"<<< Back" +
"</a></em>");
}
else if (phaseId == null) {
out.println("<table>");
out.println("<thead>");
out.println(
"<tr><td colspan=\"2\" align=\"center\"><strong>Snoop</strong></td></tr>");
out.println(
"<tr><td><strong>Name</strong></td><td><strong>Value</strong></td></tr>");
out.println("</thead>");
out.println("<tbody>");
//headers
out.println(
"<tr><td colspan=\"2\" align=\"center\"><em>Headers</em></td></tr>");
Map<String, String> headers = snapshot.getHeaderMap();
for (String header : headers.keySet()) {
String value = headers.get(header);
out.println("<tr><td><em>" +
header +
"</em></td><td><em>" +
value +
"</em></td></tr>");
}
//parameters
out.println(
"<tr><td colspan=\"2\" align=\"center\"><em>Parameters</em></td></tr>");
Map<String, String> parameters = snapshot.getParameterMap();
for (String parameter : parameters.keySet()) {
String value = parameters.get(parameter);
out.println("<tr><td><em>" +
parameter +
"</em></td><td><em>" +
value +
"</em></td></tr>");
}
out.println("</tbody>");
out.println("</table>");
}
else {
out.println(" <div id=\"view\">");
printComponentTree(request, out, viewRoot, null, viewId, phaseId, 0);
out.println(" </div>");
//snoop
out.println(" <table>");
out.println(" <thead>");
out.println(
" <tr><td colspan=\"2\" align=\"center\"><strong>Snoop</strong></td></tr>");
out.println(
" <tr><td><strong>Name</strong></td><td><strong>Value</strong></td></tr>");
out.println(" </thead>");
out.println(" <tbody>");
//request
out.println(
" <tr><td colspan=\"2\" align=\"center\"><em>Request</em></td></tr>");
printBeanMap(out, viewRoot.getRequestMap());
//session
out.println(
" <tr><td colspan=\"2\" align=\"center\"><em>Session</em></td></tr>");
printBeanMap(out, viewRoot.getSessionMap());
//application
out.println(
" <tr><td colspan=\"2\" align=\"center\"><em>Application</em></td></tr>");
printBeanMap(out, viewRoot.getApplicationMap());
out.println(" </tbody>");
out.println(" </table>");
}
out.println(" </div>");
printControls(out, request, session);
out.println(" </body>");
out.println("</html>");
}
out.flush();
}
catch (IOException e) {
throw e;
}
finally {
if (context != null)
context.release();
}
}
private void printBeanMap(PrintWriter out, Map<String, JsfDeveloperAid.Bean> map)
{
for (String key : map.keySet()) {
JsfDeveloperAid.Bean bean = map.get(key);
if (bean == null) {
out.println(" <tr><td><em>" +
key +
"</em></td><td><em>null</em></td></tr>");
}
else if (bean.isSimple()) {
out.println(" <tr><td><em>" +
key +
"</em></td><td><em>" +
bean.getClassName() + '(' + bean.getToString() + ')' +
"</em></td></tr>");
}
else if (bean.isArray()) {
out.println(" <tr><td><em>" +
key +
"</em></td><td><em>" +
bean.getClassName() + '[' + bean.getLength() + ']' +
"</em></td></tr>");
}
else {
out.print(" <tr><td><em>" +
key +
"</em></td><td><em>" +
bean.getClassName() + '(' + bean.getToString() + ')');
out.print(": </em>");
Map<String, String> beanAttributes = bean.getAttributes();
if (beanAttributes != null) {
for (String attribute : beanAttributes.keySet()) {
out.print("<br/> <em>" +
attribute +
"</em>=");
String value = beanAttributes.get(attribute);
if (value == null) {
out.print("<em>null</em>");
}
else {
out.print("<em>");
printEscaped(out, value);
out.print("</em>");
}
}
}
out.println("</td></tr>");
}
}
}
private void serveAidMap(Map<String, com.caucho.jsf.dev.JsfDeveloperAid.JsfRequestSnapshot> aidMap,
ServletResponse res)
throws IOException
{
res.setContentType("application/x-caucho-jsf-developer-aid");
Hessian2Output out = new Hessian2Output(res.getOutputStream());
out.writeObject(aidMap);
out.flush();
}
private void printEvaluated(FacesContext context,
HttpServletRequest request,
PrintWriter out,
String expression,
String viewId,
String phaseId
)
throws UnsupportedEncodingException
{
ELContext elContext = context.getELContext();
ValueExpression valueExpression = context.getApplication()
.getExpressionFactory()
.createValueExpression(elContext, expression, Object.class);
Object obj = valueExpression.getValue(elContext);
out.print("<strong>");
out.print(expression);
out.print("</strong>=");
if (obj == null) {
out.println("null");
out.println("<br/>");
}
else {
if (obj instanceof String
|| obj instanceof Boolean
|| obj instanceof Character
|| obj instanceof Number
|| obj instanceof Date
) {
out.println(obj.toString());
}
else {
out.print("<strong>");
out.print(obj.getClass().toString());
out.print("[" + obj.toString() + "]");
out.println("</strong>");
Field []fields = obj.getClass().getDeclaredFields();
out.println("<br/>");
for (Field field : fields) {
try {
field.setAccessible(true);
Object value = field.get(obj);
out.print(" ");
printAttribute(request,
out,
field.getName(),
String.valueOf(value),
viewId,
phaseId);
out.println("<br/>");
}
catch (IllegalAccessException e) {
}
}
}
}
}
private void printComponentTree(HttpServletRequest request,
PrintWriter out,
com.caucho.jsf.dev.JsfDeveloperAid.Component component,
String facetName,
String viewId,
String phaseId,
int depth)
throws UnsupportedEncodingException
{
for (int i = 0; i < depth * 3; i++)
out.print(" ");
out.print("<<strong>" + component.getUiComponentClass() + "</strong>");
printAttribute(request,
out,
"clientId",
component.getClientId(),
viewId,
phaseId);
if (component.isValueHolder()) {
printAttribute(request,
out,
"value",
component.getValue(),
viewId,
phaseId);
printAttribute(request,
out,
"localValue",
component.getLocalValue(),
viewId, phaseId);
}
if (component.isEditableValueHolder())
printAttribute(request, out, "submittedValue",
component.getSubmittedValue(), viewId, phaseId
);
if (facetName != null)
printAttribute(request,
out,
"enclosingFacet",
facetName,
viewId,
phaseId);
Map<String, String> attributes = component.getAttributes();
if (attributes != null)
for (String attr : attributes.keySet()) {
String value = attributes.get(attr);
if (value != null)
printAttribute(request, out, attr, value, viewId, phaseId);
}
out.println("><br/>");
List<com.caucho.jsf.dev.JsfDeveloperAid.Component> children
= component.getChildren();
Map<String, com.caucho.jsf.dev.JsfDeveloperAid.Component> facets = component
.getFacets();
if (children != null)
for (com.caucho.jsf.dev.JsfDeveloperAid.Component child : children)
printComponentTree(request,
out,
child,
null,
viewId,
phaseId,
depth + 1);
if (facets != null)
for (String facet : facets.keySet())
printComponentTree(request,
out,
facets.get(facet),
facet,
viewId,
phaseId,
depth + 1);
}
private void printAttribute(HttpServletRequest request,
PrintWriter out,
String name,
String value,
String viewId,
String phaseId)
throws UnsupportedEncodingException
{
out.print(' ');
out.print("<em>" + name);
out.print("</em>=\"");
if (value == null)
out.print("null");
else if (value.indexOf("#{") > -1 && value.indexOf("}") > -1) {
out.print("<a href=\"" +
request.getContextPath() +
"/caucho.jsf.developer.aid;jsessionid=" +
request.getSession().getId() +
"?viewId=" +
viewId +
"&phaseId=" +
phaseId +
"&valueExpression=" +
URLEncoder.encode(value, "UTF-8") +
"\">" +
value +
"</a>");
}
else
printEscaped(out, value);
out.print("\"");
}
public void printEscaped(PrintWriter out, String value)
{
char []valueChars = value.toCharArray();
boolean wasSpace = false;
for (char valueChar : valueChars) {
switch (valueChar) {
case ' ':
wasSpace = true;
break;
case '\n':
if (wasSpace)
out.print(' ');
wasSpace = false;
out.print('\n');
break;
case '\r':
if (wasSpace)
out.print(' ');
wasSpace = false;
out.print('\r');
break;
case '<':
if (wasSpace)
out.print(' ');
wasSpace = false;
out.print("<");
break;
default:
if (wasSpace)
out.print(' ');
wasSpace = false;
out.write(valueChar);
}
}
}
}