/* 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() + "]";
}
}
}