/* * 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.util.L10N; import com.caucho.jsf.webapp.FacesServletImpl; import com.caucho.server.webapp.WebApp; import javax.el.ValueExpression; import javax.faces.component.EditableValueHolder; import javax.faces.component.UIComponent; import javax.faces.component.UIComponentBase; import javax.faces.component.UIViewRoot; import javax.faces.component.ValueHolder; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.Field; import java.lang.reflect.Array; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; public class JsfDeveloperAid implements PhaseListener { public static final String URL_PATTERN = "caucho.jsf.developer.aid"; private static final Logger log = Logger.getLogger(FacesServletImpl.class.getName()); private static final L10N L = new L10N(JsfDeveloperAid.class); private String _developerAidLinkStyle; public JsfDeveloperAid() { WebApp webApp = WebApp.getCurrent(); _developerAidLinkStyle = webApp.getJsf().getDeveloperAidLinkStyle(); } public void afterPhase(PhaseEvent event) { final FacesContext context = event.getFacesContext(); final ExternalContext exContext = context.getExternalContext(); final Map<String, Object> sessionMap = exContext.getSessionMap(); Map<String, JsfRequestSnapshot> aidMap = (Map<String, JsfRequestSnapshot>) sessionMap.get( "caucho.jsf.developer.aid"); if (aidMap == null) { aidMap = new HashMap<String, JsfRequestSnapshot>(); sessionMap.put("caucho.jsf.developer.aid", aidMap); } try { final UIViewRoot uiViewRoot = context.getViewRoot(); if (uiViewRoot != null) { final String viewId = uiViewRoot.getViewId(); final String phaseId = event.getPhaseId().toString(); final ViewRoot viewRoot = (ViewRoot) reflect(context, uiViewRoot); viewRoot.setPhase(phaseId); //request attributes Map<String, Object> requestMap = exContext.getRequestMap(); Map<String, Bean> requestSnapshot = new HashMap<String, Bean>(); for (String key : requestMap.keySet()) { if (key.startsWith("caucho.") || key.startsWith("com.caucho.") || key.startsWith("javax.")) continue; Bean bean = reflect(requestMap.get(key)); requestSnapshot.put(key, bean); } viewRoot.setRequestMap(requestSnapshot); //session attributes Map<String, Bean> sessionSnapshot = new HashMap<String, Bean>(); for (String key : sessionMap.keySet()) { if (key.startsWith("caucho.") || key.startsWith("com.caucho.") || key.startsWith("javax.")) continue; Bean bean = reflect(sessionMap.get(key)); sessionSnapshot.put(key, bean); } viewRoot.setSessionMap(sessionSnapshot); //application attributes Map<String, Object> applicationMap = exContext.getApplicationMap(); Map<String, Bean> applicationSnapshot = new HashMap<String, Bean>(); for (String key : applicationMap.keySet()) { if (key.startsWith("caucho.") || key.startsWith("com.caucho.") || key.startsWith("javax.")) continue; Bean bean = reflect(applicationMap.get(key)); applicationSnapshot.put(key, bean); } viewRoot.setApplicationMap(applicationSnapshot); JsfRequestSnapshot snapshot; if (PhaseId.RESTORE_VIEW.equals(event.getPhaseId())) { snapshot = new JsfRequestSnapshot(); //headers Map<String, String> map = exContext.getRequestHeaderMap(); snapshot.setHeaderMap(new HashMap<String, String>(map)); //parameters map = exContext.getRequestParameterMap(); snapshot.setParameterMap(new HashMap<String, String>(map)); aidMap.put(viewId, snapshot); } else { snapshot = aidMap.get(viewId); } snapshot.addViewRoot(viewRoot); } } catch (IllegalStateException e) { log.log(Level.FINER, e.getMessage(), e); } catch (Throwable t) { log.log(Level.FINER, t.getMessage(), t); } } public void beforePhase(PhaseEvent event) { if (!PhaseId.RENDER_RESPONSE.equals(event.getPhaseId())) return; UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot(); if (viewRoot == null) return; JsfDeveloperAidLink link = new JsfDeveloperAidLink(); link.setStyle(_developerAidLinkStyle); viewRoot.getChildren().add(link); } public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; } public Component reflect(FacesContext facesContext, UIComponent uiComponent) { final Component result; if (uiComponent instanceof UIViewRoot) { UIViewRoot uiViewRoot = (UIViewRoot) uiComponent; result = new ViewRoot(); ViewRoot viewRoot = (ViewRoot) result; viewRoot.setLocale(uiViewRoot.getLocale()); viewRoot.setRenderKitId(uiViewRoot.getRenderKitId()); } else result = new Component(); result._uiComponentClass = uiComponent.getClass().getSimpleName(); result._clientId = uiComponent.getClientId(facesContext); result._family = uiComponent.getFamily(); final int childCount = uiComponent.getChildCount(); if (childCount > 0) { List<UIComponent> children = uiComponent.getChildren(); result._children = new ArrayList<Component>(children.size()); for (int i = 0; i < childCount; i++) { UIComponent child = children.get(i); if (!(child instanceof JsfDeveloperAidLink)) result._children.add(reflect(facesContext, children.get(i))); } } final int facetCount = uiComponent.getFacetCount(); if (facetCount > 0) { Map<String, UIComponent> facets = uiComponent.getFacets(); result._facets = new HashMap<String, Component>(facets.size()); Set<String> names = facets.keySet(); for (String name : names) { UIComponent child = facets.get(name); result._facets.put(name, reflect(facesContext, child)); } } if (uiComponent instanceof ValueHolder) { result._isValueHolder = true; Object value; try { value = ((ValueHolder) uiComponent).getValue(); } catch (Throwable t) { value = "Failed due to: " + t.getMessage(); } result._value = String.valueOf(value); Object localValue; try { localValue = ((ValueHolder) uiComponent).getLocalValue(); } catch (Throwable t) { localValue = "Failed due to: " + t.getMessage(); } result._localValue = String.valueOf(localValue); } if (uiComponent instanceof EditableValueHolder) { result._isEditableValueHolder = true; Object submittedValue; try { submittedValue = ((EditableValueHolder) uiComponent).getSubmittedValue(); } catch (Throwable t) { submittedValue = "Failed due to: " + t.getMessage(); } if (submittedValue instanceof Object[]) { StringBuilder sb = new StringBuilder('['); Object []values = (Object[]) submittedValue; for (int i = 0; i < values.length; i++) { Object value = values[i]; sb.append(String.valueOf(value)); if ((i + 1) < values.length) sb.append(','); } sb.append(']'); result._submittedValue = sb.toString(); } else { result._submittedValue = String.valueOf(submittedValue); } } for (Method method : uiComponent.getClass().getMethods()) { if (!method.getName().startsWith("get") && !method.getName().startsWith("is")) continue; else if (method.getParameterTypes().length != 0) continue; String name; if (method.getName().startsWith("get")) name = method.getName().substring(3); else if (method.getName().startsWith("is")) name = method.getName().substring(2); else continue; name = Character.toLowerCase(name.charAt(0)) + name.substring(1); ValueExpression expr = uiComponent.getValueExpression(name); Class type = method.getReturnType(); if (expr != null) { result.setAttribute("expr:" + name, expr.getExpressionString()); } else if (method.getDeclaringClass().equals(UIComponent.class) || method.getDeclaringClass().equals(UIComponentBase.class)) { } else if (name.equals("family") || name.equals("value") || name.equals("localValue") || name.equals("submittedValue")) { } else if (String.class.equals(type)) { try { Object value = method.invoke(uiComponent); if (value != null) result.setAttribute(name, String.valueOf(value)); } catch (Exception e) { } } } return result; } public Bean reflect(Object obj) { if (obj == null) return null; final Bean result; if (obj instanceof String || obj instanceof Boolean || obj instanceof Character || obj instanceof Number || obj instanceof Date ) { result = new Bean(); result.setClassName(obj.getClass().getSimpleName()); result.setToString(obj.toString()); result.setSimple(true); } else if (obj instanceof Object[]) { result = new Bean(); result.setArray(true); result.setClassName(obj.getClass().getComponentType().getName()); result.setLength(Array.getLength(obj)); } else { result = new Bean(); result.setClassName(obj.getClass().getName()); result.setToString(obj.toString()); Field []fields = obj.getClass().getDeclaredFields(); Map<String, String> attributes = new HashMap<String, String>(); for (Field field : fields) { try { field.setAccessible(true); Object value = field.get(obj); attributes.put(field.getName(), String.valueOf(value)); } catch (IllegalAccessException e) { } } result.setAttributes(attributes); } return result; } public static class JsfRequestSnapshot implements Serializable { private ViewRoot []_phases; private Map<String, String> _parameterMap; private Map<String, String> _headerMap; public void addViewRoot(ViewRoot viewRoot) { if (_phases == null) { _phases = new ViewRoot[]{viewRoot}; } else { ViewRoot []newPhases = new ViewRoot[_phases.length + 1]; System.arraycopy(_phases, 0, newPhases, 0, _phases.length); newPhases[newPhases.length - 1] = viewRoot; _phases = newPhases; } } public void setPhases(ViewRoot []phases) { _phases = phases; } public ViewRoot[] getPhases() { return _phases; } public Map<String, String> getParameterMap() { return _parameterMap; } public void setParameterMap(Map<String, String> parameterMap) { _parameterMap = parameterMap; } public Map<String, String> getHeaderMap() { return _headerMap; } public void setHeaderMap(Map<String, String> headerMap) { _headerMap = headerMap; } } public static class Bean implements Serializable { private Map<String, String> _attributes; private String _className; private String _toString; private boolean _isArray; private int _length; private boolean _simple; public String getToString() { return _toString; } public void setToString(String toString) { _toString = toString; } public String getClassName() { return _className; } public void setClassName(String className) { _className = className; } public Map<String, String> getAttributes() { return _attributes; } public void setAttributes(Map<String, String> attributes) { _attributes = attributes; } public boolean isArray() { return _isArray; } public void setArray(boolean array) { _isArray = array; } public int getLength() { return _length; } public void setLength(int length) { _length = length; } public boolean isSimple() { return _simple; } public void setSimple(boolean simple) { _simple = simple; } } public static class ViewRoot extends Component { private Locale _locale; private String _renderKitId; private String _phase; private Map<String, Bean> _requestMap; private Map<String, Bean> _sessionMap; private Map<String, Bean> _applicationMap; public Locale getLocale() { return _locale; } public void setLocale(Locale locale) { _locale = locale; } public String getRenderKitId() { return _renderKitId; } public void setRenderKitId(String renderKitId) { _renderKitId = renderKitId; } public String getPhase() { return _phase; } public void setPhase(String phase) { _phase = phase; } public Map<String, Bean> getRequestMap() { return _requestMap; } public void setRequestMap(Map<String, Bean> requestMap) { _requestMap = requestMap; } public Map<String, Bean> getSessionMap() { return _sessionMap; } public void setSessionMap(Map<String, Bean> sessionMap) { _sessionMap = sessionMap; } public Map<String, Bean> getApplicationMap() { return _applicationMap; } public void setApplicationMap(Map<String, Bean> applicationMap) { _applicationMap = applicationMap; } } public static class Component implements Serializable { private String _uiComponentClass; private String _clientId; private String _family; private String _value; private String _localValue; private String _submittedValue; private boolean _isValueHolder; private boolean _isEditableValueHolder; private List<Component> _children; private Map<String, Component> _facets; private Map<String, String> _attributes; public List<Component> getChildren() { return _children; } public void setChildren(List<Component> children) { _children = children; } public Map<String, Component> getFacets() { return _facets; } public void setFacets(Map<String, Component> facets) { _facets = facets; } public String getUiComponentClass() { return _uiComponentClass; } public void setUiComponentClass(String uiComponentClass) { _uiComponentClass = uiComponentClass; } public String getClientId() { return _clientId; } public void setClientId(String clientId) { _clientId = clientId; } public String getFamily() { return _family; } public void setFamily(String family) { _family = family; } public String getValue() { return _value; } public void setValue(String value) { _value = value; } public String getLocalValue() { return _localValue; } public void setLocalValue(String localValue) { _localValue = localValue; } public String getSubmittedValue() { return _submittedValue; } public void setSubmittedValue(String submittedValue) { _submittedValue = submittedValue; } public boolean isValueHolder() { return _isValueHolder; } public void setValueHolder(boolean valueHolder) { _isValueHolder = valueHolder; } public boolean isEditableValueHolder() { return _isEditableValueHolder; } public void setEditableValueHolder(boolean editableValueHolder) { _isEditableValueHolder = editableValueHolder; } public void setAttribute(String name, String value) { if (_attributes == null) _attributes = new HashMap<String, String>(); _attributes.put(name, value); } public Map<String, String> getAttributes() { return _attributes; } public void setAttributes(Map<String, String> attributes) { _attributes = attributes; } } }