All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ajax4jsf.framework.ajax.AjaxContext Maven / Gradle / Ivy

Go to download

Ajax4jsf is an open source extension to the JavaServer Faces standard that adds AJAX capability to JSF applications without requiring the writing of any JavaScript.

The newest version!
/**
 * Licensed under the Common Development and Distribution License,
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.sun.com/cddl/
 *   
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
package org.ajax4jsf.framework.ajax;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.StateManager;
import javax.faces.application.StateManager.SerializedView;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PhaseId;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.Renderer;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.ajax4jsf.framework.ajax.xmlfilter.BaseFilter;
import org.ajax4jsf.framework.ajax.xmlfilter.FilterServletResponseWrapper;
import org.ajax4jsf.framework.renderer.AjaxContainerRenderer;
import org.ajax4jsf.framework.renderer.AjaxRendererUtils;
import org.ajax4jsf.framework.renderer.HeaderResourceProducer;
import org.ajax4jsf.framework.skin.Skin;
import org.ajax4jsf.framework.skin.SkinFactory;
import org.ajax4jsf.framework.skin.SkinNotFoundException;
import org.ajax4jsf.framework.util.message.Messages;
import org.apache.commons.collections.Buffer;
import org.apache.commons.collections.UnboundedFifoBuffer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This class incapsulated
 * 
 * @author [email protected] (latest modification by $Author: alexsmirnov $)
 * @version $Revision: 1.21 $ $Date: 2007/01/08 17:50:09 $
 * 
 */
public class AjaxContext {
	/**
	 * Key for keep request state information in request-scope attributes.
	 */
	public static final String AJAX_CONTEXT_KEY = "ajaxContext";

	public static final String SCRIPTS_PARAMETER = "org.ajax4jsf.framework.HEADER_SCRIPTS";

	public static final String STYLES_PARAMETER = "org.ajax4jsf.framework.HEADER_STYLES";

	public static final String RESOURCES_PROCESSED = "org.ajax4jsf.framework.HEADER_PROCESSED";

	public static final String RESPONSE_DATA_KEY = "_ajax:data";

	private static final Log log = LogFactory.getLog(AjaxContext.class);

	private static Map contextClasses = new HashMap();

	Set ajaxAreasToRender = new HashSet();

	Set ajaxRenderedAreas = new HashSet();

	boolean ajaxRequest = false;

	boolean ajaxRequestSet = false;

	boolean selfRender = false;

	Integer viewSequence = new Integer(1);

	String submittedRegionClientId = null;

	String submittedRegionId = null;

	UIComponent submittedRegion = null;

	FacesContext context = null;

	int uniqueIdCounter = 0;

	Buffer[] events;

	Buffer ajaxEvents = new UnboundedFifoBuffer();

	ViewIdHolder viewIdHolder = null;

	Map responseDataMap = new HashMap();

	private int eventCount=1;

	private boolean ajaxEventsCountSet=false;

	/**
	 * 
	 */
	public AjaxContext() {
		clearEvents();
	}

	/**
	 * getter for current per-request container state. If not exist, create new
	 * and put it in requestMap
	 * 
	 * @return memento instance for current request
	 */
	public static AjaxContext getCurrentInstance() {
		FacesContext context = FacesContext.getCurrentInstance();
		// All AJAX Areas use same per-request state, since it manipulated
		// viewRoot.
		// String key =
		// AjaxContainer.REQUEST_STATE_KEY;//+component.getClientId(context);
		return getCurrentInstance(context);
	}

	/**
	 * @param context
	 * @return
	 */
	public static AjaxContext getCurrentInstance(FacesContext context) {
		AjaxContext ajaxContext = null;
		Map requestMap = null;
		if (null != context) {
			requestMap = context.getExternalContext().getRequestMap();
			ajaxContext = (AjaxContext) requestMap.get(AJAX_CONTEXT_KEY);
		}
		if (null == ajaxContext) {
			ajaxContext = newInstance();
			if (null != context) {
				requestMap.put(AJAX_CONTEXT_KEY, ajaxContext);
			}
		}
		ajaxContext.context = context;
		return ajaxContext;
	}

	private static AjaxContext newInstance() {
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		Class ajaxContextClass = (Class) contextClasses.get(loader);
		if (null == ajaxContextClass) {
			try {
				String resource = "META-INF/services/"
						+ AjaxContext.class.getName();
				InputStream in = loader.getResourceAsStream(resource);
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(in));
				String ajaxContextClassName = reader.readLine();
				reader.close();
				ajaxContextClass = loader.loadClass(ajaxContextClassName);
				if (log.isDebugEnabled()) {
					log.debug("AjaxContext class set to "
							+ ajaxContextClassName);
				}
			} catch (Exception e) {
				if (log.isDebugEnabled()) {
					log
							.debug("AjaxContext class set to default implementation class");
				}
				ajaxContextClass = AjaxContext.class;
			}
			contextClasses.put(loader, ajaxContextClass);

		}
		try {
			return (AjaxContext) ajaxContextClass.newInstance();
		} catch (Exception e) {
			throw new FacesException("Error to create instance of AjaxContext",
					e);
		}
	}

	/**
	 * @param context
	 * @return
	 */
	public static void clearCurrentInstance(FacesContext context) {
		Map requestMap = null;
		if (null != context) {
			requestMap = context.getExternalContext().getRequestMap();
			requestMap.remove(AJAX_CONTEXT_KEY);
		}
	}

	/**
	 * @param context
	 * @return
	 */
	public static void setCurrentInstance(FacesContext context,
			AjaxContext ajaxContext) {
		Map requestMap = null;
		if (null != context) {
			requestMap = context.getExternalContext().getRequestMap();
			requestMap.put(AJAX_CONTEXT_KEY, ajaxContext);
		}
	}

	public void renderSubmittedAjaxRegion() {
		renderSubmittedAjaxRegion(this.context);
	}

	public void renderSubmittedAjaxRegion(FacesContext context) {
		AjaxContainer ajax = AjaxRendererUtils.getSubmittedAjaxContainer(
				context, null);
		if (null != ajax) {
			renderAjaxRegion(context, (UIComponent) ajax, true);
		}

	}

	/**
	 * @param context
	 * @param useFilterWriter
	 *            TODO
	 * @throws AbortProcessingException
	 */
	public void renderAjaxRegion(FacesContext context, UIComponent component,
			boolean useFilterWriter) throws FacesException {
		if (log.isDebugEnabled()) {
			log.debug(Messages.getMessage(Messages.RENDER_AJAX_REQUEST,
					component.getId()));
		}
		try {
			setSelfRender(true);
			// create response writer.
			ExternalContext extContext = context.getExternalContext();
			RenderKit renderKit = context.getRenderKit();
			String encoding;
			// Depends if we talk about servlets, portlets, ...
			if (extContext.getRequest() instanceof ServletRequest) {
				ServletRequest request = (ServletRequest) extContext.getRequest();
				ServletResponse response = (ServletResponse) extContext
						.getResponse();
				// HACK - bypass MyFaces ( and other ) extensions filter.

				// Setup encoding and content type
				String contentType = "text/xml";
				// get the encoding - must be setup by faces context or filter.
				encoding = request.getCharacterEncoding();
				if (encoding == null) {
					encoding = "UTF-8";
				}
				response.setContentType(contentType + ";charset=" + encoding);
			} else encoding = "UTF-8";
			
			PrintWriter servletWriter;
			if (useFilterWriter
					&& extContext.getRequestMap().containsKey(
							BaseFilter.RESPONSE_WRAPPER_ATTRIBUTE)) {
				// HACK - Special case for MyFaces, since  0) {
					if (log.isDebugEnabled()) {
						StringBuffer buff = new StringBuffer(
								"Scripts for insert into head : \n");
						for (Iterator iter = scripts.iterator(); iter.hasNext();) {
							String script = (String) iter.next();
							buff.append(script).append("\n");
						}
						log.debug(buff.toString());
					}
					requestMap.put(SCRIPTS_PARAMETER, scripts);
				}
				// Set default style sheet for current skin.
				String styleSheetUri = null;
				try {
					styleSheetUri = (String) SkinFactory.getInstance().getSkin(
							context).getParameter(context,
							Skin.generalStyleSheet);
				} catch (SkinNotFoundException e) {
					log.warn("Current Skin is not found", e);
				}
				if (null != styleSheetUri) {
					String resourceURL = context.getApplication()
							.getViewHandler().getResourceURL(context,
									styleSheetUri);
					// TODO - some resources can be non-session aware, we must
					// skip encoding for this case ?
					// But, in common case - static links not need session info,
					// and dynamic resources perform encodings if nessesary
					// resourceURL =
					// context.getExternalContext().encodeResourceURL(resourceURL);
					styles.add(resourceURL);
				}
				if (styles.size() > 0) {
					if (log.isDebugEnabled()) {
						StringBuffer buff = new StringBuffer(
								"Styles for insert into head : \n");
						for (Iterator iter = styles.iterator(); iter.hasNext();) {
							String style = (String) iter.next();
							buff.append(style).append("\n");
						}
						log.debug(buff.toString());
					}
					requestMap.put(STYLES_PARAMETER, styles);
				}
				// Mark as processed.
				requestMap.put(RESOURCES_PROCESSED, Boolean.TRUE);
			}

		}
	}

	/**
	 * Append nessesary scripts and styles from component ( if renderer
	 * implements {@link HeaderResourceProducer}) and recursive process all
	 * facets and childrens.
	 * 
	 * @param context
	 *            TODO
	 * @param root
	 * @param scripts
	 * @param styles
	 * @param renderKit
	 *            TODO
	 */
	private void processHeadResources(FacesContext context, UIComponent root,
			Set scripts, Set styles, RenderKit renderKit) {
		Renderer renderer = getRenderer(context, root, renderKit);
		if (null != renderer) {
			if (renderer instanceof HeaderResourceProducer) {
				HeaderResourceProducer producer = (HeaderResourceProducer) renderer;
				Set set = producer.getHeaderScripts(context, root);
				if (null != set) {
					scripts.addAll(set);
				}
				set = producer.getHeaderStyles(context, root);
				if (null != set) {
					styles.addAll(set);
				}
			}
		}
		for (Iterator iter = root.getFacets().values().iterator(); iter
				.hasNext();) {
			UIComponent child = (UIComponent) iter.next();
			processHeadResources(context, child, scripts, styles, renderKit);
		}
		for (Iterator iter = root.getChildren().iterator(); iter.hasNext();) {
			UIComponent child = (UIComponent) iter.next();
			processHeadResources(context, child, scripts, styles, renderKit);
		}
	}

	/**
	 * Find renderer for given component.
	 * 
	 * @param context
	 * @param comp
	 * @param renderKit
	 * @return
	 */
	private Renderer getRenderer(FacesContext context, UIComponent comp,
			RenderKit renderKit) {

		String rendererType = comp.getRendererType();
		if (rendererType != null) {
			return (renderKit.getRenderer(comp.getFamily(), rendererType));
		} else {
			return (null);
		}

	}

	public void saveViewState(FacesContext context) throws IOException {
		// TODO - for facelets environment, we need to remove transient
		// components.
		try {
			Application.class.getMethod("getExpressionFactory", null);
		} catch (NoSuchMethodException e) {
			// JSF 1.1 !
		}
		ResponseWriter writer = context.getResponseWriter();
		StateManager stateManager = context.getApplication().getStateManager();
		SerializedView serializedView = stateManager
				.saveSerializedView(context);
		if (null != serializedView) {
			StringWriter bufWriter = new StringWriter();
			ResponseWriter cloneWithWriter = writer.cloneWithWriter(bufWriter);
			context.setResponseWriter(cloneWithWriter);
			stateManager.writeState(context, serializedView);
			cloneWithWriter.flush();
			if (bufWriter.getBuffer().length() > 0) {
				context.getExternalContext().getRequestMap().put(
						AjaxViewHandler.SERIALIZED_STATE_KEY,
						bufWriter.toString());
			}
			// Restore original writer.
			context.setResponseWriter(writer);
		}
	}

	/**
	 * @return Returns the counter and increment it.
	 */
	public int getUniqueIdCounter() {
		return uniqueIdCounter++;
	}

	public void resetUniqueIdCounter() {
		uniqueIdCounter = 0;
	}

	/**
	 * @return Returns the ajaxRequest.
	 */
	public boolean isAjaxRequest() {
		if (this.ajaxRequestSet) {
			return ajaxRequest;
		} else {
			return context.getExternalContext().getRequestParameterMap()
					.containsKey(AjaxContainerRenderer.AJAX_PARAMETER_NAME);
		}
	}

	/**
	 * @param ajaxRequest
	 *            The ajaxRequest to set.
	 */
	public void setAjaxRequest(boolean ajaxRequest) {
		this.ajaxRequest = ajaxRequest;
		this.ajaxRequestSet = true;
	}

	/**
	 * Get number of events, collected in client-side queue for ajax request.
	 * @return number of events on client, caused this request.
	 */
	public int getEventCount() {
		if (!this.ajaxEventsCountSet) {
			String countParam = (String) context.getExternalContext()
					.getRequestParameterMap().get("AJAX:EVENTS_COUNT");
			if (null != countParam) {
				eventCount = Integer.parseInt(countParam);
			}
			this.ajaxEventsCountSet = true;

		}
		return eventCount;
	}

	/**
	 * @return Returns the ajaxAreasToRender.
	 */
	public Set getAjaxAreasToRender() {
		return this.ajaxAreasToRender;
	}

	public void addComponentToAjaxRender(UIComponent component) {
		this.ajaxAreasToRender.add(AjaxRendererUtils.getAbsoluteId(component));
	}

	public void addComponentToAjaxRender(UIComponent base, String id) {
		this.ajaxAreasToRender.add(AjaxRendererUtils.convertId(base, id));
	}

	/**
	 * @return Returns the ajaxRenderedAreas.
	 */
	public Set getAjaxRenderedAreas() {
		return ajaxRenderedAreas;
	}

	public void addRenderedArea(String id) {
		ajaxRenderedAreas.add(id);
	}

	public boolean removeRenderedArea(String id) {
		return ajaxRenderedAreas.remove(id);
	}

	/**
	 * @return Returns the submittedClientId.
	 */
	public String getSubmittedRegionClientId() {
		return this.submittedRegionClientId;
	}

	/**
	 * @param submittedClientId
	 *            The submittedClientId to set.
	 */
	public void setSubmittedRegionClientId(String submittedClientId) {
		this.submittedRegionClientId = submittedClientId;
	}

	/**
	 * @return Returns the submittedRegion.
	 */
	public UIComponent getSubmittedRegion() {
		return submittedRegion;
	}

	/**
	 * @param submittedRegion
	 *            The submittedRegion to set.
	 */
	public void setSubmittedRegion(UIComponent submittedRegion) {
		this.submittedRegion = submittedRegion;
	}

	/**
	 * @return Returns the submittedRegionId.
	 */
	public String getSubmittedRegionId() {
		return submittedRegionId;
	}

	/**
	 * @param submittedRegionId
	 *            The submittedRegionId to set.
	 */
	public void setSubmittedRegionId(String submittedRegionId) {
		this.submittedRegionId = submittedRegionId;
	}

	/**
	 * @return Returns the ajaxEvents.
	 */
	Buffer getAjaxEvents() {
		return ajaxEvents;
	}

	/**
	 * @return Returns the events.
	 */
	Buffer[] getEvents() {
		return events;
	}

	/**
	 * Clear evenst queues.
	 */
	void clearEvents() {
		int len;
		events = new Buffer[len = PhaseId.VALUES.size()];
		for (int i = 0; i < len; i++) {
			events[i] = new UnboundedFifoBuffer();
		}

	}

	/**
	 * @return Returns the selfRender.
	 */
	public boolean isSelfRender() {
		return selfRender;
	}

	/**
	 * @param selfRender
	 *            The selfRender to set.
	 */
	public void setSelfRender(boolean selfRender) {
		this.selfRender = selfRender;
	}

	/**
	 * @return the vievIdHolder
	 */
	public ViewIdHolder getViewIdHolder() {
		return viewIdHolder;
	}

	/**
	 * @param viewIdHolder
	 *            the vievIdHolder to set
	 */
	public void setViewIdHolder(ViewIdHolder viewIdHolder) {
		this.viewIdHolder = viewIdHolder;
	}

	/**
	 * @return the responseData
	 */
	public Object getResponseData() {
		return responseDataMap.get(RESPONSE_DATA_KEY);
	}

	/**
	 * @param responseData
	 *            the responseData to set
	 */
	public void setResponseData(Object responseData) {
		this.responseDataMap.put(RESPONSE_DATA_KEY, responseData);
	}

	/**
	 * @return the responseDataMap
	 */
	public Map getResponseDataMap() {
		return responseDataMap;
	}

	/**
	 * @param responseDataMap
	 *            the responseDataMap to set
	 */
	public void setResponseDataMap(Map responseDataMap) {
		this.responseDataMap = responseDataMap;
	}

	/**
	 * Gives back the writer of a Response object.
	 * @param extContext The external context.
	 * @return The writer of the response.
	 * @throws FacesException If the response object has no getWriter() method.
	 */
	protected PrintWriter getWriter(ExternalContext extContext)
		throws FacesException {
		PrintWriter writer = null;
		Object response = extContext.getResponse();		
		try {
			Method gW =
				response.getClass().getMethod("getWriter", new Class[0]);
			writer = (PrintWriter) gW.invoke(response, new Object[0]);
		} catch (Exception e) {
			throw new FacesException(e);
		}		
		return writer;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy