/* AbstractExecution.java Purpose: Description: History: Mon Jun 6 12:18:25 2005, Created by tomyeh Copyright (C) 2005 Potix Corporation. All Rights Reserved. {{IS_RIGHT This program is distributed under LGPL Version 2.1 in the hope that it will be useful, but WITHOUT ANY WARRANTY. }}IS_RIGHT */ package org.zkoss.zk.ui.impl; import java.io.IOException; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zkoss.idom.Document; import org.zkoss.util.CollectionsX; import org.zkoss.web.servlet.Servlets; import org.zkoss.xel.VariableResolver; import org.zkoss.xel.VariableResolverX; import org.zkoss.xel.XelContext; import org.zkoss.zk.au.AuResponse; import org.zkoss.zk.au.http.AuRedirect; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.Execution; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.Page; import org.zkoss.zk.ui.Session; import org.zkoss.zk.ui.Sessions; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.metainfo.PageDefinition; import org.zkoss.zk.ui.sys.DesktopCtrl; import org.zkoss.zk.ui.sys.ExecutionCtrl; import org.zkoss.zk.ui.sys.ExecutionInfo; import org.zkoss.zk.ui.sys.UiEngine; import org.zkoss.zk.ui.sys.Visualizer; import org.zkoss.zk.ui.sys.WebAppCtrl; import org.zkoss.zk.ui.util.Callback; /** * A skeletal implementation of {@link Execution}. * * @author tomyeh */ public abstract class AbstractExecution implements Execution, ExecutionCtrl { private static final Logger _zklog = LoggerFactory.getLogger("org.zkoss.zk.log"); private Desktop _desktop; private Page _curpage; private PageDefinition _curpgdef; /* A list of EventInfo within the same priority. */ private final Map<Integer, List<EventInfo>> _evtInfos = new TreeMap<Integer, List<EventInfo>>( new Comparator<Integer>() { public int compare(Integer o1, Integer o2) { // reverse it, returning the greater one first. return o2.compareTo(o1); } }); /** A stack of args being pushed by {@link #pushArg}. */ private List<Map<?, ?>> _args; //private Event _evtInProc; /** Which page is being created, or null if all in update mode. */ private final Page _creating; /** The sequence ID of the current request. */ private String _reqId; /** A collection of the AU responses that shall be generated to client */ private Collection<AuResponse> _resps; /** The information of the event being served, or null if not under event processing. */ private ExecutionInfo _execinf; private List<VariableResolver> _resolvers; protected static final String Add_ON_ACTIVATE = "org.zkoss.zk.ui.executions.addOnActivate"; protected static final String Add_ON_DEACTIVATE = "org.zkoss.zk.ui.executions.addOnDeactivate"; /** Constructs an execution. * @param creating which page is being creating for this execution, or * null if none is being created. * {@link #isAsyncUpdate} returns based on this. */ protected AbstractExecution(Desktop desktop, Page creating) { _desktop = desktop; //it is null if it is created by WebManager.newDesktop _curpage = _creating = creating; if (_curpage == null) _curpage = getPage(desktop); } private static Page getPage(Desktop desktop) { return desktop != null ? desktop.getFirstPage() : null; } //-- Execution --// public final boolean isAsyncUpdate(Page page) { if (page != null) return _creating != page; final Visualizer uv; return (uv = getVisualizer()) != null && uv.isEverAsyncUpdate(); } public Desktop getDesktop() { return _desktop; } public Session getSession() { return _desktop != null ? _desktop.getSession() : Sessions.getCurrent(); } public void postEvent(Event evt) { postEvent(0, evt); } public void postEvent(int priority, Event evt) { if (evt == null) throw new IllegalArgumentException("null"); evt = ((DesktopCtrl) _desktop).beforePostEvent(evt); if (evt == null) return; //done (ignored) List<EventInfo> eventInfos = _evtInfos.get(priority); if (eventInfos != null) { eventInfos.add(new EventInfo(priority, evt)); } else { eventInfos = new LinkedList<EventInfo>(); eventInfos.add(new EventInfo(priority, evt)); _evtInfos.put(priority, eventInfos); } } public void postEvent(int priority, Component realTarget, Event evt) { postEvent(priority, realTarget != evt.getTarget() ? new ProxyEvent(realTarget, evt) : evt); } //-- ExecutionCtrl --// public Object getAttribute(String name, boolean recurse) { Object val = getAttribute(name); Desktop desktop; return val != null || !recurse || (desktop = getDesktop()) == null ? val : desktop.getAttribute(name, true); } public boolean hasAttribute(String name, boolean recurse) { Desktop desktop; return hasAttribute(name) || (recurse && (desktop = getDesktop()) != null && desktop.hasAttribute(name, true)); } public Object setAttribute(String name, Object value, boolean recurse) { if (recurse && !hasAttribute(name)) { Desktop desktop = getDesktop(); if (desktop != null) { if (desktop.hasAttribute(name, true)) return desktop.setAttribute(name, value, true); } } return setAttribute(name, value); } public Object removeAttribute(String name, boolean recurse) { if (recurse && !hasAttribute(name)) { Desktop desktop = getDesktop(); if (desktop != null) { if (desktop.hasAttribute(name, true)) return desktop.removeAttribute(name, true); } return null; } return removeAttribute(name); } public final Page getCurrentPage() { if (_curpage == null) _curpage = getPage(_desktop); return _curpage; } public final void setCurrentPage(Page curpage) { if (_curpage != null && curpage != null && _curpage != curpage) { Desktop _curdt = _curpage.getDesktop(), curdt = curpage.getDesktop(); if (_curdt != null && curdt != null && _curdt != curdt) throw new IllegalStateException("Change current page to another desktop? " + curpage); } _curpage = curpage; } public PageDefinition getCurrentPageDefinition() { return _curpgdef; } public void setCurrentPageDefinition(PageDefinition pgdef) { _curpgdef = pgdef; } public Event getNextEvent() { if (!_evtInfos.isEmpty()) { for (Map.Entry<Integer, List<EventInfo>> me : _evtInfos.entrySet()) { List<EventInfo> value = me.getValue(); EventInfo remove = value.remove(0); if (value.isEmpty()) { _evtInfos.remove(me.getKey()); } return remove.event; } } // ZK-770: EventQueue has extra delay if scope is SESSION ((DesktopCtrl) _desktop).onPiggyback(); if (!_evtInfos.isEmpty()) { for (Map.Entry<Integer, List<EventInfo>> me : _evtInfos.entrySet()) { List<EventInfo> value = me.getValue(); EventInfo remove = value.remove(0); if (value.isEmpty()) { _evtInfos.remove(me.getKey()); } return remove.event; } } return null; } public boolean isActivated() { return getVisualizer() != null; } @SuppressWarnings("unchecked") public void onActivate() { if (_desktop != null) { List<Callback> callbacks = (List<Callback>) _desktop.getAttribute(Add_ON_ACTIVATE); if (callbacks != null) { for (Iterator<Callback> it = callbacks.iterator(); it.hasNext();) { Callback callback = it.next(); callback.call(null); it.remove(); } } } } @SuppressWarnings("unchecked") public void onBeforeDeactivate() { if (_desktop != null) { List<Callback> callbacks = (List<Callback>) _desktop.getAttribute(Add_ON_DEACTIVATE); if (callbacks != null) { for (Iterator<Callback> it = callbacks.iterator(); it.hasNext();) { Callback callback = it.next(); callback.call(null); it.remove(); } } } } public void onDeactivate() { } public boolean isRecovering() { Visualizer uv = getVisualizer(); return uv != null && uv.isRecovering(); } public Visualizer getVisualizer() { return _desktop != null ? ((DesktopCtrl) _desktop).getVisualizer() : null; } public String toAbsoluteURI(String uri, boolean skipInclude) { if (uri != null && uri.length() > 0) { final char cc = uri.charAt(0); if (cc != '/' && cc != '~' && !(skipInclude && isIncluded()) && !Servlets.isUniversalURL(uri)) { final String dir = getDesktop().getCurrentDirectory(); if (dir != null) return dir + uri; } } return uri; //we ignore _creating, because Servlet's include cannot handle //related URI correctly (even though it is by the layout servlet) } private final UiEngine getUiEngine() { return ((WebAppCtrl) _desktop.getWebApp()).getUiEngine(); } public Component createComponents(String uri, Component parent, Map<?, ?> arg) { return createComponents0(uri, parent, null, null, arg); } public Component createComponents(String uri, Component parent, Component insertBefore, VariableResolver resolver) { return createComponents0(uri, parent, insertBefore, resolver, null); } public Component[] createComponents(String uri, Component parent, Component insertBefore, VariableResolver resolver, Map<?, ?> arg) { final Component[] cs = getUiEngine().createComponents(this, getPageDefinition(uri), getCurrentPage(), parent, insertBefore, resolver, arg); return cs.length > 0 ? cs : null; } public Component createComponents(PageDefinition pagedef, Component parent, Map<?, ?> arg) { return createComponents0(pagedef, parent, null, null, arg); } public Component createComponents(PageDefinition pagedef, Component parent, Component insertBefore, VariableResolver resolver) { return createComponents0(pagedef, parent, insertBefore, resolver, null); } public Component[] createComponents(String uri, Map<?, ?> arg) { return getUiEngine().createComponents(this, getPageDefinition(uri), null, null, null, null, arg); } public Component[] createComponents(String uri, Page page, VariableResolver resolver, Map<?, ?> arg) { return getUiEngine().createComponents(this, getPageDefinition(uri), page, null, null, resolver, arg); } public Component[] createComponents(PageDefinition pagedef, Map<?, ?> arg) { if (pagedef == null) throw new IllegalArgumentException("pagedef cannot be null"); return getUiEngine().createComponents(this, pagedef, null, null, null, null, arg); } private Component createComponents0(String uri, Component parent, Component insertBefore, VariableResolver resolver, Map<?, ?> arg) { final Component[] cs = getUiEngine().createComponents(this, getPageDefinition(uri), getCurrentPage(), parent, insertBefore, resolver, arg); return cs.length > 0 ? cs[0] : null; } private Component createComponents0(PageDefinition pagedef, Component parent, Component insertBefore, VariableResolver resolver, Map<?, ?> arg) { if (pagedef == null) throw new IllegalArgumentException("pagedef cannot be null"); final Component[] cs = getUiEngine().createComponents(this, pagedef, getCurrentPage(), parent, insertBefore, resolver, arg); return cs.length > 0 ? cs[0] : null; } public Component createComponentsDirectly(String content, String ext, Component parent, Map<?, ?> arg) { return createComponentsDirectly0(content, ext, parent, null, null, arg); } public Component createComponentsDirectly(String content, String ext, Component parent, Component insertBefore, VariableResolver resolver) { return createComponentsDirectly0(content, ext, parent, insertBefore, resolver, null); } public Component createComponentsDirectly(Document content, String ext, Component parent, Map<?, ?> arg) { return createComponentsDirectly0(content, ext, parent, null, null, arg); } public Component createComponentsDirectly(Document content, String ext, Component parent, Component insertBefore, VariableResolver resolver) { return createComponentsDirectly0(content, ext, parent, insertBefore, resolver, null); } public Component createComponentsDirectly(Reader reader, String ext, Component parent, Map<?, ?> arg) throws IOException { return createComponentsDirectly0(reader, ext, parent, null, null, arg); } public Component createComponentsDirectly(Reader reader, String ext, Component parent, Component insertBefore, VariableResolver resolver) throws IOException { return createComponentsDirectly0(reader, ext, parent, insertBefore, resolver, null); } public Component[] createComponentsDirectly(String content, String ext, Map<?, ?> arg) { return getUiEngine().createComponents(this, getPageDefinitionDirectly(content, ext), null, null, null, null, arg); } public Component[] createComponentsDirectly(Document content, String ext, Map<?, ?> arg) { return getUiEngine().createComponents(this, getPageDefinitionDirectly(content, ext), null, null, null, null, arg); } public Component[] createComponentsDirectly(Reader reader, String ext, Map<?, ?> arg) throws IOException { return getUiEngine().createComponents(this, getPageDefinitionDirectly(reader, ext), null, null, null, null, arg); } private Component createComponentsDirectly0(String content, String ext, Component parent, Component insertBefore, VariableResolver resolver, Map<?, ?> arg) { final Component[] cs = getUiEngine().createComponents(this, getPageDefinitionDirectly(content, ext), getCurrentPage(), parent, insertBefore, resolver, arg); return cs.length > 0 ? cs[0] : null; } private Component createComponentsDirectly0(Document content, String ext, Component parent, Component insertBefore, VariableResolver resolver, Map<?, ?> arg) { final Component[] cs = getUiEngine().createComponents(this, getPageDefinitionDirectly(content, ext), getCurrentPage(), parent, insertBefore, resolver, arg); return cs.length > 0 ? cs[0] : null; } private Component createComponentsDirectly0(Reader reader, String ext, Component parent, Component insertBefore, VariableResolver resolver, Map<?, ?> arg) throws IOException { final Component[] cs = getUiEngine().createComponents(this, getPageDefinitionDirectly(reader, ext), getCurrentPage(), parent, insertBefore, resolver, arg); return cs.length > 0 ? cs[0] : null; } public void sendRedirect(String uri) { getUiEngine().sendRedirect(uri, null); } public void sendRedirect(String uri, String target) { getUiEngine().sendRedirect(uri, target); } public void sendRedirect(String uri, boolean respRedirect) { if (!respRedirect) { sendRedirect(uri); return; } else { uri = uri == null ? "" : uri; HttpServletResponse resp = (HttpServletResponse) getNativeResponse(); try { String destUrl = encodeURL(uri); String destUrlParam = URLEncoder.encode(destUrl, "utf-8"); String updateURI = _desktop.getUpdateURI( AuRedirect.URI_PREFIX + "?" + AuRedirect.REDIRECT_URL_PARAMETER + "=" + destUrlParam); updateURI = resp.encodeRedirectURL(updateURI); resp.setHeader("Location", updateURI); resp.setStatus(HttpServletResponse.SC_FOUND); } catch (UnsupportedEncodingException e) { _zklog.warn("", e); } } } public Map<?, ?> getArg() { if (_args != null) return _args.get(0); return Collections.emptyMap(); } public void pushArg(Map<?, ?> arg) { if (_args == null) _args = new LinkedList<Map<?, ?>>(); _args.add(0, arg); } public void popArg() { if (_args != null) { if (_args.size() == 1) _args = null; else _args.remove(0); } } public void addAuResponse(AuResponse response) { getUiEngine().addResponse(response); } public void addAuResponse(String key, AuResponse response) { getUiEngine().addResponse(key, response); } public void setDesktop(Desktop desktop) { if (desktop == null) throw new IllegalArgumentException("null"); if (_desktop != null && _desktop != desktop) throw new IllegalStateException("assign diff desktop"); _desktop = desktop; } public void setRequestId(String reqId) { _reqId = reqId; } public String getRequestId() { return _reqId; } public Collection<AuResponse> getResponses() { return _resps; } public void setResponses(Collection<AuResponse> responses) { _resps = responses; } public ExecutionInfo getExecutionInfo() { return _execinf; } public void setExecutionInfo(ExecutionInfo execinf) { _execinf = execinf; } public boolean addVariableResolver(VariableResolver resolver) { if (resolver == null) throw new IllegalArgumentException("null"); if (_resolvers == null) _resolvers = new LinkedList<VariableResolver>(); else if (_resolvers.contains(resolver)) return false; _resolvers.add(0, resolver); //FILO order return true; } public boolean removeVariableResolver(VariableResolver resolver) { return _resolvers != null && _resolvers.remove(resolver); } public boolean hasVariableResolver(VariableResolver resolver) { return _resolvers != null && _resolvers.contains(resolver); } public boolean hasVariableResolver(Class<? extends VariableResolver> cls) { if (_resolvers != null) for (final VariableResolver resolver : _resolvers) if (cls.isInstance(resolver)) return true; return false; } public Object getExtraXelVariable(String name) { return getExtraXelVariable(null, null, name); } public Object getExtraXelVariable(XelContext ctx, Object base, Object name) { //Note this method searches only _resolvers if (_resolvers != null) { for (Iterator it = CollectionsX.comodifiableIterator(_resolvers); it.hasNext();) { final VariableResolver vr = (VariableResolver) it.next(); final Object o = vr instanceof VariableResolverX ? ((VariableResolverX) vr).resolveVariable(ctx, base, name) : base == null && name != null ? vr.resolveVariable(name.toString()) : null; if (o != null) return o; } } return null; } public void log(String msg) { if (_desktop != null) _desktop.getWebApp().log(msg); else _zklog.info(msg); } public void log(String msg, Throwable ex) { if (_desktop != null) _desktop.getWebApp().log(msg, ex); else _zklog.error(msg, ex); } /** * Adds a callback method to be executed only once after the execution * activated. * @param callback * @since 7.0.5 */ public void addOnActivate(Callback callback) { Execution exec = Executions.getCurrent(); if (exec == null) throw new IllegalStateException("Execution cannot be null!"); Desktop desktop = exec.getDesktop(); if (desktop != null) { List<Callback> callbacks = (List<Callback>) desktop.getAttribute(Add_ON_ACTIVATE); if (callbacks == null) { callbacks = new LinkedList<Callback>(); desktop.setAttribute(Add_ON_ACTIVATE, callbacks); } callbacks.add(callback); } } /** * Adds a callback method to be executed only once after the execution * deactivated. * @param callback * @since 7.0.5 */ public void addOnDeactivate(Callback callback) { Execution exec = Executions.getCurrent(); if (exec == null) throw new IllegalStateException("Execution cannot be null!"); Desktop desktop = exec.getDesktop(); if (desktop != null) { List<Callback> callbacks = (List<Callback>) desktop.getAttribute(Add_ON_DEACTIVATE); if (callbacks == null) { callbacks = new LinkedList<Callback>(); desktop.setAttribute(Add_ON_DEACTIVATE, callbacks); } callbacks.add(callback); } } //Object// public String toString() { return "[Exec" + System.identityHashCode(this) + ": " + _desktop + ']'; } private static class EventInfo { private final int priority; private final Event event; private EventInfo(int priority, Event event) { this.priority = priority; this.event = event; } public String toString() { return "[" + this.priority + ": " + this.event.toString() + "]"; } } }