#region License
/**
* Ingenious MVC : An MVC framework for .NET 2.0
* Copyright (C) 2006, JDP Group
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: - Kent Boogaart (kentcb@internode.on.net)
*/
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Web;
using Ingenious.Mvc;
using Ingenious.Mvc.Configuration;
using Ingenious.Mvc.Util;
using Ingenious.Mvc.ViewManagers;
using Ingenious.Mvc.Web.Forms.Views;
namespace Ingenious.Mvc.Web.Forms.ViewManagers{
/// <include file='FormsViewManager.doc.xml' path='/doc/member[@name="T:FormsViewManager"]/*'/>
[Serializable]
public class FormsViewManager : ViewManagerBase
{
private static IHttpBridge _httpBridge;
private static readonly Log _log = Log.CreateForType(typeof(FormsViewManager));
/// <include file='FormsViewManager.doc.xml' path='/doc/member[@name="P:HttpBridge"]/*'/>
internal static IHttpBridge HttpBridge
{
get
{
if (_httpBridge == null)
{
_httpBridge = new HttpBridgeImpl();
}
return _httpBridge;
}
set
{
_httpBridge = value;
}
}
/// <include file='FormsViewManager.doc.xml' path='/doc/member[@name="M:.ctor(Ingenious.Mvc.Id`1,Ingenious.Mvc.TaskManager)"]/*'/>
public FormsViewManager(Id<IViewManager> id, TaskManager taskManager) : base(id, taskManager)
{
}
/// <include file='FormsViewManager.doc.xml' path='/doc/member[@name="M:Activate(Ingenious.Mvc.Task,Ingenious.Mvc.Id`1,System.Object)"]/*'/>
public override IView Activate(Task task, Id<IView> viewId, object viewData)
{
ArgumentHelper.AssertNotNull(task, "task");
ArgumentHelper.AssertNotEmpty(viewId, "viewId");
bool redirect = true;
if (HttpBridge.IsNewSession)
{
ViewInfo requestedView = GetViewInfo(HttpBridge.PhysicalPath);
if (requestedView != null)
{
INavigator navigator = task.Manager.GetNavigatorForTask(task);
//user is trying to go directly to a specific page
if ((navigator.StartingViewId == requestedView.Id) || (navigator.CanNavigateTo(requestedView.Id)))
{
//moreover, the navigator permits it - let's let them go straight there instead of redirecting afterwards (which causes issues for search
//engines)
_log.Verbose("User requested view with ID '{0}' and navigator allows it.");
viewId = requestedView.Id;
redirect = false;
}
}
else
{
//non I-MVC view - we set up the root task and return no view to allow easy bookmarking
task.Ended += new EventHandler<TaskEventArgs>(task_Ended);
SessionInfo.Current.TaskInfos.Push(new TaskInfo(task));
return null;
}
}
_log.Information("Activating view with ID '{0}' for task with ID '{1}'.", viewId, task.Id);
//get and update the session info as necessary
SessionInfo sessionInfo = SessionInfo.Current;
//new tasks will still have a state of NotStarted
bool newTask = (task.State == TaskState.NotStarted);
if (newTask)
{
_log.Verbose("Task is not yet started - attaching to it.");
//attach to task and add it to the task stack
task.Ended += new EventHandler<TaskEventArgs>(task_Ended);
sessionInfo.TaskInfos.Push(new TaskInfo(task));
}
else
{
//existing task. Make sure it is on top of the stack
_log.Verbose("Task is already started - making sure it is on top of the task stack.");
while ((sessionInfo.TaskInfos.Count > 0) && (task != sessionInfo.TaskInfos.Peek().Task))
{
//ending the task results in it getting popped off the stack elsewhere
_log.Verbose("Ending task with ID '{0}'.", sessionInfo.TaskInfos.Peek().Task.Id);
sessionInfo.TaskInfos.Peek().Task.End();
}
if (sessionInfo.TaskInfos.Count == 0)
{
_log.Error("Task with ID '{0}' was not found in the task stack.", task.Id);
ExceptionHelper.Throw("Activate.taskNotFound", Id, task.Id);
}
}
TaskInfo taskInfo = sessionInfo.CurrentTaskInfo;
//now we update the details of the current view so that the page can pick them up and apply them during initialization
ViewInfo viewInfo = GetViewInfo(task, viewId);
taskInfo.ActiveViewData = viewData;
//the url of the page is stored in its custom configuration
string url = (string) viewInfo.CustomConfiguration;
_log.Verbose("URL for view is '{0}'.", url);
//deactivate old view if relevant
if (ActiveView != null)
{
_log.Verbose("Deactivating previous view with ID '{0}'.", ActiveView.Id);
OnViewDeactivated(ActiveView);
}
if (redirect)
{
//now redirect to that url
try
{
HttpBridge.Redirect(url);
}
catch (ThreadAbortException ex)
{
//log as verbose only - this exception is expected
_log.Exception(LogLevel.Verbose, ex);
}
}
//returning the view implementation is not currently supported by this view manager
return null;
}
public ViewInfo GetViewInfo(string physicalPath)
{
_log.Verbose("Getting view info for physical path '{0}'.", physicalPath);
foreach (ViewInfo viewInfo in TaskManager.ConfigurationInfo.ViewInfos)
{
string url = viewInfo.CustomConfiguration as string;
_log.Verbose("Checking view with ID '{0}', URL '{1}'.", viewInfo.Id, url);
//ASP.NET uses different casing interchangeably so we're kind of forced to do the same
if (string.Compare(HttpBridge.MapPath(url), physicalPath, true) == 0)
{
_log.Verbose("Match found.");
return viewInfo;
}
}
return null;
}
internal void ViewActivated(IView view)
{
OnViewActivated(view);
}
private void task_Ended(object sender, TaskEventArgs e)
{
_log.Verbose("Task with ID '{0}' is ending - cleaning up.", e.Task.Id);
SessionInfo sessionInfo = SessionInfo.Current;
TaskInfo taskInfo = sessionInfo.CurrentTaskInfo;
if (e.Task != taskInfo.Task)
{
_log.Error("Attempting to end task with ID '{0}' but it is not on top of the task stack.", e.Task.Id);
ExceptionHelper.Throw("task_Ended.endingIncorrectTask", e.Task.Id, taskInfo.Task.Id);
}
Debug.Assert(taskInfo.Task.State == TaskState.Ended);
//pop the ended task off the stack
sessionInfo.TaskInfos.Pop();
if (sessionInfo.HasTasks)
{
taskInfo = sessionInfo.TaskInfos.Peek();
_log.Verbose("There are tasks remaining on the stack - activating top task with ID '{0}'.", taskInfo.Task.Id);
//now activate the view that was active prior to the previously running task being started
Activate(taskInfo.Task, taskInfo.ActiveViewId, taskInfo.ActiveViewData);
}
else
{
//all tasks have been ended
_log.Verbose("All tasks have been ended - abandoning session.");
HttpBridge.AbandonSession();
}
}
internal interface IHttpBridge
{
string PhysicalPath
{
get;
}
bool IsNewSession
{
get;
}
void Redirect(string url);
string MapPath(string virtualPath);
object GetSessionValue(string key);
void SetSessionValue(string key, object val);
void AbandonSession();
}
[Serializable]
private sealed class HttpBridgeImpl : IHttpBridge
{
public string PhysicalPath
{
get
{
return HttpContext.Current.Request.PhysicalPath;
}
}
public bool IsNewSession
{
get
{
return HttpContext.Current.Session.IsNewSession;
}
}
public HttpBridgeImpl()
{
ExceptionHelper.ThrowIf(HttpContext.Current == null, "ctor.httpContextRequired");
ExceptionHelper.ThrowIf(HttpContext.Current.ApplicationInstance == null, "ctor.httpApplicationRequired");
}
public void Redirect(string url)
{
HttpContext.Current.Response.Redirect(url, false);
}
public string MapPath(string virtualPath)
{
return HttpContext.Current.Server.MapPath(virtualPath);
}
public object GetSessionValue(string key)
{
return HttpContext.Current.Session[key];
}
public void SetSessionValue(string key, object val)
{
HttpContext.Current.Session[key] = val;
}
public void AbandonSession()
{
HttpContext.Current.Session.Abandon();
}
}
[Serializable]
internal sealed class SessionInfo
{
private Stack<TaskInfo> _taskInfos;
private const string _sessionKey = "Ingenious.Mvc.Web.Forms.ViewManagers.FormsViewManager.SessionInfo";
public Stack<TaskInfo> TaskInfos
{
get
{
return _taskInfos;
}
}
public bool HasTasks
{
get
{
return TaskInfos.Count > 0;
}
}
public TaskInfo CurrentTaskInfo
{
get
{
TaskInfo retVal = null;
if (HasTasks)
{
retVal = TaskInfos.Peek();
}
return retVal;
}
}
public static SessionInfo Current
{
get
{
SessionInfo retVal = (SessionInfo) HttpBridge.GetSessionValue(_sessionKey);
if (retVal == null)
{
retVal = new SessionInfo();
HttpBridge.SetSessionValue(_sessionKey, retVal);
}
return retVal;
}
}
public SessionInfo()
{
_taskInfos = new Stack<TaskInfo>();
}
}
[Serializable]
internal sealed class TaskInfo
{
private Task _task;
private Id<IView> _activeViewId;
private object _activeViewData;
private IController _activeViewController;
public Task Task
{
get
{
return _task;
}
}
public Id<IView> ActiveViewId
{
get
{
return _activeViewId;
}
set
{
_activeViewId = value;
}
}
public object ActiveViewData
{
get
{
return _activeViewData;
}
set
{
_activeViewData = value;
}
}
public IController ActiveViewController
{
get
{
return _activeViewController;
}
set
{
_activeViewController = value;
}
}
public TaskInfo(Task task)
{
_task = task;
}
}
}
}
|