Java tutorial
/** * License Agreement. * * JBoss RichFaces - Ajax4jsf Component Library * * Copyright (C) 2007 Exadel, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation. * * This library 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. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.ajax4jsf.context; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.el.ExpressionFactory; import javax.el.ValueExpression; import javax.faces.FacesException; import javax.faces.FactoryFinder; import javax.faces.application.Application; import javax.faces.component.UIComponent; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.render.RenderKit; import javax.faces.render.RenderKitFactory; import javax.faces.render.Renderer; import org.ajax4jsf.component.QueueRegistry; import org.ajax4jsf.io.SAXResponseWriter; import org.ajax4jsf.renderkit.HeaderResourceProducer; import org.ajax4jsf.renderkit.HeaderResourceProducer2; import org.ajax4jsf.renderkit.ProducerContext; import org.ajax4jsf.renderkit.ProducerContextImpl; import org.ajax4jsf.renderkit.UserResourceRenderer; import org.ajax4jsf.renderkit.UserResourceRenderer2; import org.ajax4jsf.renderkit.RendererUtils.HTML; import org.ajax4jsf.resource.InternetResource; import org.ajax4jsf.resource.InternetResourceBase; import org.ajax4jsf.resource.InternetResourceBuilder; import org.ajax4jsf.resource.ResourceNotFoundException; import org.ajax4jsf.resource.ResourceRenderer; import org.ajax4jsf.resource.URIInternetResource; import org.ajax4jsf.util.ELUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.richfaces.skin.Skin; import org.richfaces.skin.SkinFactory; import org.richfaces.skin.SkinNotFoundException; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; /** * @author Nick Belaevski */ public class ViewResources { private static final String INIT_PARAMETER_PREFIX = "_init_parameter_"; private static final Object NULL = new Object(); private String scriptStrategy; private String styleStrategy; private boolean useStdControlsSkinning; private boolean useStdControlsSkinningClasses; private HeadResponseWriter componentWriter; private HeadResponseWriter userWriter; private RenderKit renderKit; private Node[] headNodes = null; private static final String EXTENDED_SKINNING_ON_NO_SCRIPTS_INFO_KEY = ViewResources.class.getName() + "EXTENDED_SKINNING_ON_NO_SCRIPTS_INFO_KEY"; private static final InternetResource EXTENDED_SKINNING_ON_RESOURCE = new InternetResourceBase() { private final String RESOURCE_KEY = this.getClass().getName(); @Override public void encode(FacesContext context, Object data) throws IOException { encode(context, data, Collections.EMPTY_MAP); } @Override public void encode(FacesContext context, Object data, Map<String, Object> attributes) throws IOException { Map<String, Object> requestMap = context.getExternalContext().getRequestMap(); if (requestMap.get(RESOURCE_KEY) == null) { ResponseWriter writer = context.getResponseWriter(); writer.startElement(HTML.SCRIPT_ELEM, null); if (!attributes.containsKey(HTML.TYPE_ATTR)) { writer.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null); } for (Map.Entry<String, Object> entry : attributes.entrySet()) { writer.writeAttribute(entry.getKey(), entry.getValue(), null); } writer.writeText("window.RICH_FACES_EXTENDED_SKINNING_ON=true;", null); writer.endElement(HTML.SCRIPT_ELEM); requestMap.put(RESOURCE_KEY, Boolean.TRUE); } } }; public void setScriptStrategy(String scriptsStrategy) { this.scriptStrategy = scriptsStrategy; } public void setStyleStrategy(String stylesStrategy) { this.styleStrategy = stylesStrategy; } public void setUseStdControlsSkinning(boolean stdControlsSkinning) { this.useStdControlsSkinning = stdControlsSkinning; } public void setUseStdControlsSkinningClasses(boolean stdControlsSkinningClasses) { this.useStdControlsSkinningClasses = stdControlsSkinningClasses; } public void setExtendedSkinningAllowed(boolean extendedSkinningAllowed) { this.extendedSkinningAllowed = extendedSkinningAllowed; } private static String SCRIPT = HTML.SCRIPT_ELEM; private static String SCRIPT_UC = SCRIPT.toUpperCase(Locale.US); private static String SRC = HTML.src_ATTRIBUTE; private static String SRC_UC = SRC.toUpperCase(Locale.US); private void mergeHeadResourceNode(List<Node> nodes, Set<String> renderedScripts, Node node) { boolean shouldAdd = true; String nodeName = node.getNodeName(); if (SCRIPT.equals(nodeName) || SCRIPT_UC.equals(nodeName)) { if (node.getFirstChild() == null) { //no text content etc. NamedNodeMap attributes = node.getAttributes(); if (attributes != null) { Node item = attributes.getNamedItem(SRC); if (item == null) { attributes.getNamedItem(SRC_UC); } if (item != null) { String src = item.getNodeValue(); if (src != null) { if (renderedScripts.contains(src)) { shouldAdd = false; } else { renderedScripts.add(src); } } } } } } if (shouldAdd) { nodes.add(node); } } private Node[] mergeHeadResourceNodes() { List<Node> result = new ArrayList<Node>(); Set<String> scripts = new HashSet<String>(); for (Node node : componentWriter.getNodes()) { mergeHeadResourceNode(result, scripts, node); } for (Node node : userWriter.getNodes()) { mergeHeadResourceNode(result, scripts, node); } return result.toArray(new Node[result.size()]); } public Node[] getHeadEvents() { if (headNodes == null) { headNodes = mergeHeadResourceNodes(); } return headNodes; } private static final Log log = LogFactory.getLog(ViewResources.class); //todo private static final Map<String, Object> EXTENDED_SKINNING = new HashMap<String, Object>(1); static { EXTENDED_SKINNING.put(HTML.media_ATTRIBUTE, "rich-extended-skinning"); } public static final String SKINNING_STYLES_PATH = "/org/richfaces/renderkit/html/css/"; public static final String QUEUE_SCRIPT_RESOURCE = "org.ajax4jsf.renderkit.html.scripts.QueueScript"; private boolean extendedSkinningAllowed; private boolean processScripts; private boolean processStyles; private boolean useSkinning; private InternetResourceBuilder resourceBuilder; private boolean ajaxRequest; private ProducerContext producerContext; public static final String COMPONENT_RESOURCE_LINK_CLASS = "component"; public static final String USER_RESOURCE_LINK_CLASS = "user"; class HeadResponseWriter extends SAXResponseWriter { public Node[] getNodes() { return ((ResponseWriterContentHandler) getXmlConsumer()).getNodes(); } public HeadResponseWriter(String linkClass) { super(new ResponseWriterContentHandler(linkClass)); } } private void encodeResources(FacesContext context, ResourceRenderer renderer, Set<String> set) throws IOException { if (set != null) { URIInternetResource resourceImpl = new URIInternetResource(); for (String uri : set) { resourceImpl.setUri(uri); renderer.encode(resourceImpl, context, null); } } } private boolean encodeSkinningResources(FacesContext context, InternetResourceBuilder resourceBuilder) throws IOException, FacesException { String resourceSuffix = null; if (useStdControlsSkinning) { if (useStdControlsSkinningClasses) { resourceSuffix = "_both.xcss"; } else { resourceSuffix = ".xcss"; } } else { if (useStdControlsSkinningClasses) { resourceSuffix = "_classes.xcss"; } else { //no resources } } if (resourceSuffix != null) { resourceBuilder.createResource(this, SKINNING_STYLES_PATH.concat("basic").concat(resourceSuffix)) .encode(context, null); if (extendedSkinningAllowed) { resourceBuilder.createResource(this, SKINNING_STYLES_PATH.concat("extended").concat(resourceSuffix)) .encode(context, null, EXTENDED_SKINNING); } return true; } return false; } protected void processComponent(FacesContext context, UIComponent component) throws IOException, FacesException { Renderer renderer = getRenderer(context, component); if (null != renderer) { ResponseWriter oldResponseWriter = context.getResponseWriter(); try { if ((processScripts || processStyles) && (renderer instanceof HeaderResourceProducer2 || renderer instanceof HeaderResourceProducer)) { context.setResponseWriter(componentWriter); if (renderer instanceof HeaderResourceProducer2) { HeaderResourceProducer2 producer = (HeaderResourceProducer2) renderer; if (producerContext == null) { producerContext = new ProducerContextImpl(processScripts, processStyles); } producer.encodeToHead(context, component, producerContext); } else if (renderer instanceof HeaderResourceProducer) { HeaderResourceProducer producer = (HeaderResourceProducer) renderer; if (processScripts) { encodeResources(context, resourceBuilder.getScriptRenderer(), producer.getHeaderScripts(context, component)); } if (processStyles) { encodeResources(context, resourceBuilder.getStyleRenderer(), producer.getHeaderStyles(context, component)); } } } else if (renderer instanceof UserResourceRenderer2) { context.setResponseWriter(userWriter); UserResourceRenderer2 producer = (UserResourceRenderer2) renderer; producer.encodeToHead(context, component); } else if (renderer instanceof UserResourceRenderer) { context.setResponseWriter(userWriter); UserResourceRenderer producer = (UserResourceRenderer) renderer; encodeResources(context, resourceBuilder.getScriptRenderer(), producer.getHeaderScripts(context, component)); encodeResources(context, resourceBuilder.getStyleRenderer(), producer.getHeaderStyles(context, component)); } } finally { context.setResponseWriter(oldResponseWriter); } } } /** * Find renderer for given component. * * @param context * @param comp * @param renderKit * @return */ private Renderer getRenderer(FacesContext context, UIComponent comp) { String rendererType = comp.getRendererType(); if (rendererType != null) { return (renderKit.getRenderer(comp.getFamily(), rendererType)); } else { return (null); } } protected void traverse(FacesContext context, UIComponent component) throws IOException, FacesException { if (component != null) { processComponent(context, component); if (component.getChildCount() > 0) { for (UIComponent child : component.getChildren()) { traverse(context, child); } } if (component.getFacetCount() > 0) { for (UIComponent child : component.getFacets().values()) { traverse(context, child); } } } } public void processHeadResources(FacesContext context) throws FacesException { RenderKitFactory rkFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY); renderKit = rkFactory.getRenderKit(context, context.getViewRoot().getRenderKitId()); boolean scriptsOff = false; processStyles = true; processScripts = true; useSkinning = false; ajaxRequest = AjaxContext.getCurrentInstance(context).isAjaxRequest(context); if (log.isDebugEnabled()) { log.debug("Process component tree for collect used scripts and styles"); } String skinStyleSheetUri = null; String skinExtendedStyleSheetUri = null; Skin skin = null; try { skin = SkinFactory.getInstance().getSkin(context); // For a "NULL" skin, do not collect components stylesheets if ("false".equals(skin.getParameter(context, Skin.loadStyleSheets))) { processStyles = false; } // Set default style sheet for current skin. skinStyleSheetUri = (String) skin.getParameter(context, Skin.generalStyleSheet); // Set default style sheet for current skin. skinExtendedStyleSheetUri = (String) skin.getParameter(context, Skin.extendedStyleSheet); } catch (SkinNotFoundException e) { if (log.isWarnEnabled()) { log.warn("Current Skin is not found", e); } } resourceBuilder = InternetResourceBuilder.getInstance(); ResponseWriter oldResponseWriter = context.getResponseWriter(); componentWriter = new HeadResponseWriter("component"); userWriter = new HeadResponseWriter("user"); try { componentWriter.startDocument(); userWriter.startDocument(); context.setResponseWriter(componentWriter); // Check init parameters for a resources processing. if (null != scriptStrategy) { if (InternetResourceBuilder.LOAD_NONE.equals(scriptStrategy)) { scriptsOff = true; processScripts = false; } else if (InternetResourceBuilder.LOAD_ALL.equals(scriptStrategy)) { processScripts = false; // For an "ALL" strategy, it is not necessary to load scripts in the ajax request if (!ajaxRequest) { try { resourceBuilder.createResource(this, InternetResourceBuilder.COMMON_FRAMEWORK_SCRIPT) .encode(context, null); resourceBuilder.createResource(this, InternetResourceBuilder.COMMON_UI_SCRIPT) .encode(context, null); } catch (ResourceNotFoundException e) { if (log.isWarnEnabled()) { log.warn("No aggregated javaScript library found " + e.getMessage()); } } } } } if (InternetResourceBuilder.LOAD_NONE.equals(styleStrategy)) { processStyles = false; } else if (InternetResourceBuilder.LOAD_ALL.equals(styleStrategy)) { processStyles = false; // For an "ALL" strategy, it is not necessary to load styles // in the ajax request if (!ajaxRequest) { try { useSkinning = encodeSkinningResources(context, resourceBuilder); resourceBuilder.createResource(this, InternetResourceBuilder.COMMON_STYLE).encode(context, null); } catch (ResourceNotFoundException e) { if (log.isWarnEnabled()) { log.warn("No stylesheet found " + e.getMessage()); } } } } else { useSkinning = encodeSkinningResources(context, resourceBuilder); } //traverse components traverse(context, context.getViewRoot()); context.setResponseWriter(componentWriter); QueueRegistry queueRegistry = QueueRegistry.getInstance(context); if (Boolean.valueOf(getInitParameterValue(context, "org.richfaces.queue.global.enabled"))) { queueRegistry.setShouldCreateDefaultGlobalQueue(); } if (queueRegistry.hasQueuesToEncode()) { InternetResource queueScriptResource = resourceBuilder.getResource(QUEUE_SCRIPT_RESOURCE); queueScriptResource.encode(context, null); } // Append Skin StyleSheet after a if (null != skinStyleSheetUri) { String resourceURL = context.getApplication().getViewHandler().getResourceURL(context, skinStyleSheetUri); URIInternetResource resourceImpl = new URIInternetResource(); resourceImpl.setUri(resourceURL); resourceImpl.setRenderer(resourceBuilder.getStyleRenderer()); resourceImpl.encode(context, null); useSkinning = true; } if (null != skinExtendedStyleSheetUri && extendedSkinningAllowed) { String resourceURL = context.getApplication().getViewHandler().getResourceURL(context, skinExtendedStyleSheetUri); URIInternetResource resourceImpl = new URIInternetResource(); resourceImpl.setUri(resourceURL); resourceImpl.setRenderer(resourceBuilder.getStyleRenderer()); resourceImpl.encode(context, null, EXTENDED_SKINNING); useSkinning = true; } if (useSkinning && extendedSkinningAllowed) { if (!ajaxRequest) { if (!scriptsOff) { //skinning levels aren't dynamic, page-level setting cannot be changed //by AJAX request EXTENDED_SKINNING_ON_RESOURCE.encode(context, null); } else { Map<String, Object> applicationMap = context.getExternalContext().getApplicationMap(); if (applicationMap.get(EXTENDED_SKINNING_ON_NO_SCRIPTS_INFO_KEY) == null) { //do it once per application life - strategies can be changed dynamically ResponseWriter writer = context.getResponseWriter(); try { StringWriter stringWriter = new StringWriter(); if (oldResponseWriter != null) { context.setResponseWriter(oldResponseWriter.cloneWithWriter(stringWriter)); } else { context.setResponseWriter(this.renderKit.createResponseWriter(stringWriter, "text/html", "US-ASCII")); } EXTENDED_SKINNING_ON_RESOURCE.encode(context, null); stringWriter.flush(); if (log.isInfoEnabled()) { log.info( "Extended skinning is on and NONE scripts loading strategy was detected. " + "Do not forget that one of " + InternetResourceBuilder.SKINNING_SCRIPT + " or " + InternetResourceBuilder.COMMON_FRAMEWORK_SCRIPT + " resources should be presented " + "on the page together with the following code: \n" + stringWriter.getBuffer().toString() + "\nfor extended level of skinning to work."); } } finally { if (writer != null) { context.setResponseWriter(writer); } } applicationMap.put(EXTENDED_SKINNING_ON_NO_SCRIPTS_INFO_KEY, Boolean.TRUE); } } } if (processScripts) { InternetResource resource = resourceBuilder.createResource(null, InternetResourceBuilder.SKINNING_SCRIPT); resource.encode(context, null); } } componentWriter.endDocument(); userWriter.endDocument(); } catch (IOException e) { throw new FacesException(e.getLocalizedMessage(), e); } finally { if (oldResponseWriter != null) { context.setResponseWriter(oldResponseWriter); } } } private static String getInitParameterValue(FacesContext context, String parameterName) { String key = INIT_PARAMETER_PREFIX + parameterName; ExternalContext externalContext = context.getExternalContext(); Map<String, Object> applicationMap = externalContext.getApplicationMap(); Object mutex = externalContext.getRequest(); Object parameterValue = null; synchronized (mutex) { parameterValue = applicationMap.get(key); if (parameterValue == null) { String initParameter = externalContext.getInitParameter(parameterName); if (initParameter != null) { if (ELUtils.isValueReference(initParameter)) { Application application = context.getApplication(); ExpressionFactory expressionFactory = application.getExpressionFactory(); parameterValue = expressionFactory.createValueExpression(context.getELContext(), initParameter, String.class); } else { parameterValue = initParameter; } } else { parameterValue = NULL; } applicationMap.put(key, parameterValue); } } return evaluate(context, parameterValue); } private static String evaluate(FacesContext context, Object parameterValue) { if (parameterValue == NULL || parameterValue == null) { return null; } else if (parameterValue instanceof ValueExpression) { ValueExpression expression = (ValueExpression) parameterValue; return (String) expression.getValue(context.getELContext()); } else { return parameterValue.toString(); } } public void initialize(FacesContext context) { boolean extendedSkinningAllowed = true; String skinningLevel = getInitParameterValue(context, InternetResourceBuilder.CONTROL_SKINNING_LEVEL); if (skinningLevel != null && skinningLevel.length() > 0) { if (InternetResourceBuilder.BASIC.equals(skinningLevel)) { extendedSkinningAllowed = false; } else if (!InternetResourceBuilder.EXTENDED.equals(skinningLevel)) { throw new IllegalArgumentException("Value: " + skinningLevel + " of " + InternetResourceBuilder.CONTROL_SKINNING_LEVEL + " init parameter is invalid! Only " + InternetResourceBuilder.EXTENDED + ", " + InternetResourceBuilder.BASIC + " can be used"); } } this.setExtendedSkinningAllowed(extendedSkinningAllowed); this.setScriptStrategy(getInitParameterValue(context, InternetResourceBuilder.LOAD_SCRIPT_STRATEGY_PARAM)); boolean useStdControlsSkinning = false; String stdControlsSkinning = getInitParameterValue(context, InternetResourceBuilder.STD_CONTROLS_SKINNING_PARAM); if (stdControlsSkinning != null) { useStdControlsSkinning = InternetResourceBuilder.ENABLE.equals(stdControlsSkinning); } this.setUseStdControlsSkinning(useStdControlsSkinning); boolean useStdControlsSkinningClasses = true; String stdControlsSkinningClasses = getInitParameterValue(context, InternetResourceBuilder.STD_CONTROLS_SKINNING_CLASSES_PARAM); if (stdControlsSkinningClasses != null) { useStdControlsSkinningClasses = InternetResourceBuilder.ENABLE.equals(stdControlsSkinningClasses); } this.setUseStdControlsSkinningClasses(useStdControlsSkinningClasses); this.setStyleStrategy(getInitParameterValue(context, InternetResourceBuilder.LOAD_STYLE_STRATEGY_PARAM)); } }