/* SessionsCtrl.java

	Purpose:
		
	Description:
		
	History:
		Mon May 30 22:03:45     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.sys;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.zkoss.zk.ui.Session;
import org.zkoss.zk.ui.Sessions;
import org.zkoss.zk.ui.WebApp;
import org.zkoss.zk.ui.util.Configuration;
import org.zkoss.zk.ui.util.Monitor;

/**
 * An additional utilities for implementation related to {@link Session}.
 *
 * @author tomyeh
 */
public class SessionsCtrl extends Sessions {
	private static final Logger log = LoggerFactory.getLogger(SessionsCtrl.class);

	protected SessionsCtrl() {
	} //prevent from instantiation

	/** Sets the session for the current thread.
	 * Called only internally.
	 */
	public static final void setCurrent(Session sess) {
		_sess.set(sess);
	}

	/** Sets the session for the current thread.
	 * Unlike {@link #setCurrent(Session)}, the session is resolved
	 * later (when {@link #getCurrent} is called).
	 * @since 5.0.0
	 */
	public static final void setCurrent(SessionResolver sr) {
		_sess.set(sr);
	}

	/** Returns the current {@link SessionCtrl}.
	 */
	public static final SessionCtrl getCurrentCtrl() {
		return (SessionCtrl) getCurrent();
	}

	/** Returns the current session or the current session resolver.
	 * The returned value is either {@link Session} or {@link SessionResolver}.
	 * @see #setRawCurrent
	 * @see #setCurrent(Session)
	 * @see #setCurrent(SessionResolver)
	 * @since 5.0.0
	 */
	public static final Object getRawCurrent() {
		return _sess.get();
	}

	/** Sets the current session or the current session resolver.
	 * @param rawsess the raw session. It can be null, an instance of
	 * {@link Session} and {@link SessionResolver}.
	 * Notice: it is usually the return value of {@link #getRawCurrent}.
	 * @see #getRawCurrent
	 * @since 5.0.0
	 */
	public static final void setRawCurrent(Object rawsess) {
		if (rawsess != null && !(rawsess instanceof Session) && !(rawsess instanceof SessionResolver))
			throw new IllegalArgumentException(rawsess.getClass().getName());
		_sess.set(rawsess);
	}

	/** Update the session count.
	 * <p>Called only internally.
	 * @since 5.0.0
	 */
	public static final void updateCount(boolean inc) {
		synchronized (SessionsCtrl.class) {
			if (inc)
				++_cnt;
			else
				--_cnt;
		}
	}

	/** Called when a servlet/portlet starts to serve a request.
	 * It checks whether the number of concurrent requests per session
	 * exceeds the number specified in
	 * {@link org.zkoss.zk.ui.util.Configuration#getSessionMaxRequests}.
	 * If exceeded, false is returned, and the servlet/portlet shall stop
	 * processing and return an error code back the client.
	 *
	 * <p>If not exceeded, true is returned, and the servlet/portlet
	 * can continue the processing and it shall invoke {@link #requestExit}
	 * in the finally clause.
	 *
	 * @since 3.0.1
	 */
	public static boolean requestEnter(Session sess) {
		final Integer v = (Integer) sess.getAttribute(ATTR_REQUEST_COUNT);
		final int i = v != null ? v.intValue() + 1 : 1;
		final int max = sess.getWebApp().getConfiguration().getSessionMaxRequests();
		if (max < 0 || i <= max) {
			sess.setAttribute(ATTR_REQUEST_COUNT, new Integer(i));
			return true;
		}
		return false;
	}

	/**
	 * Called when a servlet/portlet completes the service of a request.
	 * This method must be called if {@link #requestEnter} is called
	 * and returns true. This method shall not be called, otherwise.
	 *
	 * @since 3.0.1
	 */
	public static void requestExit(Session sess) {
		final Integer v = (Integer) sess.getAttribute(ATTR_REQUEST_COUNT);
		final int i = v != null ? v.intValue() - 1 : 0;
		sess.setAttribute(ATTR_REQUEST_COUNT, new Integer(i >= 0 ? i : 0));
	}

	private static final String ATTR_REQUEST_COUNT = "org.zkoss.zk.ui.sys.RequestCount";

	/** Returns the ZK session associated with the specified native session,
	 * or null if not found.
	 *
	 * @param navsess the native session (never null).
	 * If HTTP, it is HttpSession. If portlet, it is PortletSession.
	 * @since 3.0.5
	 */
	public static final Session getSession(WebApp wapp, Object navsess) {
		final SessionCache sc = ((WebAppCtrl) wapp).getSessionCache();
		if (sc == null)
			return null;
		//bug 2668190: happens when destroying app in WebSphere 7

		final Session sess = sc.get(navsess);
		if (sess != null && sess.getNativeSession() != navsess)
			((SessionCtrl) sess).recover(navsess);
		return sess;
	}

	/** Instantiates a ZK session that is associated with the specified
	 * native session and request.
	 *
	 * @param navsess the native session (never null).
	 * If HTTP, it is HttpSession. If portlet, it is PortletSession.
	 * @since 3.0.5
	 */
	public static final Session newSession(WebApp wapp, Object navsess, Object request) {
		final WebAppCtrl wappc = (WebAppCtrl) wapp;
		final Session sess = wappc.getUiFactory().newSession(wapp, navsess, request);
		wappc.getSessionCache().put(sess);

		final Configuration config = wapp.getConfiguration();
		config.invokeSessionInits(sess, request); //it might throw exception

		final Monitor monitor = config.getMonitor();
		if (monitor != null) {
			try {
				monitor.sessionCreated(sess);
			} catch (Throwable ex) {
				log.error("", ex);
			}
		}

		//Note: we set timeout here, because HttpSession might have been created
		//by other servlet or filter
		final int v = wapp.getConfiguration().getSessionMaxInactiveInterval();
		if (v != 0)
			sess.setMaxInactiveInterval(v);
		return sess;
	}
}
