entry : violations.entrySet()) {
builder.append(entry.getKey()).append(": ").append(entry.getValue().getMessage()).append(separator);
}
int n = builder.length();
builder.delete(n - separator.length(), n);
return builder;
}
public String violationMessage(String separator) {
return buildViolationMessage(S.newBuffer(), separator).toString();
}
public String violationMessage() {
return violationMessage("\n");
}
public ActionContext flashViolationMessage() {
return flashViolationMessage("\n");
}
public ActionContext flashViolationMessage(String separator) {
if (violations().isEmpty()) return this;
flash().error(violationMessage(separator));
return this;
}
public String actionPath() {
return actionPath;
}
public ActionContext actionPath(String path) {
actionPath = path;
return this;
}
@Override
public String methodPath() {
return actionPath;
}
public void startIntercepting() {
state = State.INTERCEPTING;
}
public void startHandling() {
state = State.HANDLING;
}
/**
* Update the context session to mark a user logged in
* @param username the username
*/
public void login(String username) {
session().put(config().sessionKeyUsername(), username);
}
/**
* Login the user and redirect back to original URL
* @param username
* the username
*/
public void loginAndRedirectBack(String username) {
login(username);
RedirectToLoginUrl.redirectToOriginalUrl(this);
}
/**
* Login the user and redirect back to original URL. If no
* original URL found then redirect to `defaultLandingUrl`.
*
* @param username
* The username
* @param defaultLandingUrl
* the URL to be redirected if original URL not found
*/
public void loginAndRedirectBack(String username, String defaultLandingUrl) {
login(username);
RedirectToLoginUrl.redirectToOriginalUrl(this, defaultLandingUrl);
}
/**
* Login the user and redirect to specified URL
* @param username
* the username
* @param url
* the URL to be redirected to
*/
public void loginAndRedirect(String username, String url) {
login(username);
redirect(url);
}
/**
* Logout the current session. After calling this method,
* the session will be cleared
*/
public void logout() {
session().clear();
}
/**
* Initialize params/renderArgs/attributes and then
* resolve session and flash from cookies
*/
public void resolve() {
E.illegalStateIf(state != State.CREATED);
boolean sessionFree = handler.sessionFree();
attribute(ATTR_WAS_UNAUTHENTICATED, true);
if (!sessionFree) {
resolveSession();
resolveFlash();
}
localeResolver.resolve();
state = State.SESSION_RESOLVED;
if (!sessionFree) {
handler.prepareAuthentication(this);
EventBus eventBus = app().eventBus();
eventBus.emit(new PreFireSessionResolvedEvent(session, this));
Act.sessionManager().fireSessionResolved(this);
eventBus.emit(new SessionResolvedEvent(session, this));
if (isLoggedIn()) {
attribute(ATTR_WAS_UNAUTHENTICATED, false);
}
}
}
@Override
public Locale locale(boolean required) {
if (required) {
if (null == locale()) {
localeResolver.resolve();
}
}
return super.locale(required);
}
/**
* Dissolve session and flash into cookies.
* Note this method must be called
* before any content has been committed to
* response output stream/writer
*/
public void dissolve() {
if (state == State.SESSION_DISSOLVED) {
return;
}
if (handler.sessionFree()) {
return;
}
if (null == session) {
// only case is when CSRF token check failed
// while resolving session
// we need to generate new session anyway
// because it is required to cache the
// original URL
// see RedirectToLoginUrl
session = new H.Session();
}
localeResolver.dissolve();
app().eventBus().emit(new SessionWillDissolveEvent(this));
try {
dissolveFlash();
dissolveSession();
state = State.SESSION_DISSOLVED;
} finally {
app().eventBus().emit(new SessionDissolvedEvent(this));
}
}
/**
* Clear all internal data store/cache and then
* remove this context from thread local
*/
@Override
protected void releaseResources() {
super.releaseResources();
PropertySpec.current.remove();
if (this.state != State.DESTROYED) {
this.allParams = null;
this.extraParams = null;
this.requestParamCache = null;
this.router = null;
this.handler = null;
// xio impl might need this this.request = null;
// xio impl might need this this.response = null;
this.flash = null;
this.session = null;
this.controllerInstances = null;
clearLocal();
this.uploads.clear();
}
this.state = State.DESTROYED;
}
public void saveLocal() {
_local.set(this);
}
public static void clearLocal() {
clearCurrent();
}
private Set> requestParamCache() {
if (null != requestParamCache) {
return requestParamCache;
}
requestParamCache = new HashSet<>();
Map map = new HashMap<>();
// url queries
Iterator paramNames = request.paramNames().iterator();
while (paramNames.hasNext()) {
final String key = paramNames.next();
final String[] val = request.paramVals(key);
MapUtil.mergeValueInMap(map, key, val);
}
// post bodies
Map map2 = bodyParams();
for (String key : map2.keySet()) {
String[] val = map2.get(key);
if (null != val) {
MapUtil.mergeValueInMap(map, key, val);
}
}
requestParamCache.addAll(map.entrySet());
return requestParamCache;
}
private void _init() {
uploads = new HashMap<>();
extraParams = new HashMap<>();
final Set> paramEntrySet = new AbstractSet>() {
@Override
public Iterator> iterator() {
final Iterator> extraItr = new Iterator>() {
Iterator> parent = extraParams.entrySet().iterator();
@Override
public boolean hasNext() {
return parent.hasNext();
}
@Override
public Map.Entry next() {
final Map.Entry parentEntry = parent.next();
return new Map.Entry() {
@Override
public String getKey() {
return parentEntry.getKey();
}
@Override
public String[] getValue() {
return new String[]{parentEntry.getValue()};
}
@Override
public String[] setValue(String[] value) {
throw E.unsupport();
}
};
}
@Override
public void remove() {
throw E.unsupport();
}
};
final Iterator> reqParamItr = requestParamCache().iterator();
return new Iterator>() {
@Override
public boolean hasNext() {
return extraItr.hasNext() || reqParamItr.hasNext();
}
@Override
public Map.Entry next() {
if (extraItr.hasNext()) return extraItr.next();
return reqParamItr.next();
}
@Override
public void remove() {
throw E.unsupport();
}
};
}
@Override
public int size() {
int size = extraParams.size();
if (null != request) {
size += requestParamCache().size();
}
return size;
}
};
allParams = new AbstractMap() {
@Override
public Set> entrySet() {
return paramEntrySet;
}
};
}
private void resolveSession() {
this.session = Act.sessionManager().resolveSession(this);
}
private void resolveFlash() {
this.flash = Act.sessionManager().resolveFlash(this);
}
private void dissolveSession() {
Cookie c = Act.sessionManager().dissolveSession(this);
if (null != c) {
config().sessionMapper().serializeSession(c, this);
}
}
private void dissolveFlash() {
Cookie c = Act.sessionManager().dissolveFlash(this);
if (null != c) {
config().sessionMapper().serializeFlash(c, this);
}
}
private static ContextLocal _local = $.contextLocal();
public static final String METHOD_GET_CURRENT = "current";
public static ActionContext current() {
return _local.get();
}
public static void clearCurrent() {
_local.remove();
}
/**
* Create an new {@code AppContext} and return the new instance
*/
public static ActionContext create(App app, H.Request request, ActResponse resp) {
return new ActionContext(app, request, resp);
}
public enum State {
CREATED,
SESSION_RESOLVED,
SESSION_DISSOLVED,
INTERCEPTING,
HANDLING,
DESTROYED;
public boolean isHandling() {
return this == HANDLING;
}
public boolean isIntercepting() {
return this == INTERCEPTING;
}
}
public static class ActionContextEvent extends ActEvent implements SystemEvent {
public ActionContextEvent(ActionContext source) {
super(source);
}
public ActionContext context() {
return source();
}
}
private static class SessionEvent extends ActionContextEvent {
private H.Session session;
public SessionEvent(H.Session session, ActionContext source) {
super(source);
this.session = session;
}
public H.Session session() {
return session;
}
}
/**
* This event is fired after session resolved and before any
* {@link act.util.SessionManager.Listener} get called
*/
public static class PreFireSessionResolvedEvent extends SessionEvent {
public PreFireSessionResolvedEvent(H.Session session, ActionContext context) {
super(session, context);
}
}
/**
* This event is fired after session resolved and after all
* {@link act.util.SessionManager.Listener} get notified and
* in turn after all event listeners that listen to the
* {@link PreFireSessionResolvedEvent} get notified
*/
public static class SessionResolvedEvent extends SessionEvent {
public SessionResolvedEvent(H.Session session, ActionContext context) {
super(session, context);
}
}
public static class SessionWillDissolveEvent extends ActionContextEvent {
public SessionWillDissolveEvent(ActionContext source) {
super(source);
}
}
public static class SessionDissolvedEvent extends ActionContextEvent {
public SessionDissolvedEvent(ActionContext source) {
super(source);
}
}
}