Conversation Workaround (Core Implementation)
This commit is contained in:
5
src/Spring/Spring.ConversationWA.NH32/AssemblyInfo.cs
Normal file
5
src/Spring/Spring.ConversationWA.NH32/AssemblyInfo.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyTitle("Spring.ConversationWA.NH32. NHibernate 3.2 support.")]
|
||||
[assembly: AssemblyDescription("Interfaces and classes that provide 'Conversation Workaround' support in Spring.Net")]
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using Spring.Context.Support;
|
||||
using Common.Logging;
|
||||
using Spring.Data.NHibernate.Support;
|
||||
using System.Web.UI;
|
||||
using Spring.Context;
|
||||
|
||||
namespace Spring.ConversationWA.HttpModule
|
||||
{
|
||||
/// <summary>
|
||||
/// HttpModule for end Conversation with Timeout exceeded.
|
||||
/// </summary>
|
||||
/// <author>Hailton de Castro</author>
|
||||
public class ConversationModule : IHttpModule, IApplicationContextAware
|
||||
{
|
||||
private static readonly ILog LOG = LogManager.GetLogger(typeof(ConversationModule));
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor.
|
||||
/// </summary>
|
||||
public ConversationModule(){ }
|
||||
|
||||
private IList<String> conversationManagerName;
|
||||
/// <summary>
|
||||
/// Name for the IConversationManager on the spring context.
|
||||
/// </summary>
|
||||
public IList<String> ConversationManagerNameList
|
||||
{
|
||||
get { return conversationManagerName; }
|
||||
set { conversationManagerName = value; }
|
||||
}
|
||||
|
||||
#region IHttpModule Members
|
||||
|
||||
/// <summary>
|
||||
/// Add PostRequestHandlerExecute event to clear conversations with timeout exceeded.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
public void Init(HttpApplication context)
|
||||
{
|
||||
context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
|
||||
context.PostRequestHandlerExecute += new EventHandler(context_PostRequestHandlerExecute);
|
||||
context.EndRequest += new EventHandler(context_EndRequest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NOOP.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
//noop
|
||||
}
|
||||
|
||||
void context_PreRequestHandlerExecute(object sender, EventArgs e)
|
||||
{
|
||||
if (HttpContext.Current.Handler is Page)
|
||||
{
|
||||
Page page = (Page)HttpContext.Current.Handler;
|
||||
page.Unload += new EventHandler(page_Unload);
|
||||
|
||||
if (HttpContext.Current.Session != null)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug("context_PreRequestHandlerExecute: HttpContext.Current.Session is NOT null");
|
||||
foreach (String convMngName in this.ConversationManagerNameList)
|
||||
{
|
||||
IConversationManager convMng = (IConversationManager)this.applicationContext.GetObject(convMngName);
|
||||
convMng.EndOnTimeOut();
|
||||
convMng.FreeEnded();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug("context_PreRequestHandlerExecute: HttpContext.Current.Session IS null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Necessary for Redirect or Abort for some reason.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
void page_Unload(object sender, EventArgs e)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug("page_Unload HttpContext.Current.Session is null: " + (HttpContext.Current.Session == null));
|
||||
foreach (String convMngName in this.ConversationManagerNameList)
|
||||
{
|
||||
IConversationManager convMng = (IConversationManager)this.applicationContext.GetObject(convMngName);
|
||||
convMng.EndOnTimeOut();
|
||||
convMng.FreeEnded();
|
||||
convMng.PauseConversations();
|
||||
}
|
||||
}
|
||||
|
||||
void context_EndRequest(object sender, EventArgs e)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug("context_EndRequest HttpContext.Current.Session is null: " + (HttpContext.Current.Session == null));
|
||||
}
|
||||
|
||||
void context_PostRequestHandlerExecute(object sender, EventArgs e)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug("context_PostRequestHandlerExecute HttpContext.Current.Session is null: " + (HttpContext.Current.Session == null));
|
||||
if (HttpContext.Current.Session != null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IApplicationContextAware Members
|
||||
private IApplicationContext applicationContext;
|
||||
/// <summary>
|
||||
/// Used to obtain the instances of "IConversationManager".
|
||||
/// </summary>
|
||||
public IApplicationContext ApplicationContext
|
||||
{
|
||||
set { this.applicationContext = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NHibernate;
|
||||
|
||||
namespace Spring.ConversationWA
|
||||
{
|
||||
/// <summary>
|
||||
/// manager for Conversations.
|
||||
/// </summary>
|
||||
/// <author>Hailton de Castro</author>
|
||||
public interface IConversationManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the conversation if it is still alive, otherwise it returns null.
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
IConversationState GetConversationById(String id);
|
||||
|
||||
/// <summary>
|
||||
/// Ends all conversations with the timeout exceeded.
|
||||
/// </summary>
|
||||
void EndOnTimeOut();
|
||||
|
||||
/// <summary>
|
||||
/// Close IDbConnection's for <see cref="IConversationState"/> that
|
||||
/// use 'session-per-conversation'. It calls
|
||||
/// <see cref="IConversationState.PauseConversation"/> in all conversations.
|
||||
/// </summary>
|
||||
void PauseConversations();
|
||||
|
||||
/// <summary>
|
||||
/// Release the ended conversatons And remove it.
|
||||
/// If the conversation support 'session-per-conversation' close the session.
|
||||
/// </summary>
|
||||
void FreeEnded();
|
||||
|
||||
/// <summary>
|
||||
/// Add conversation. If <see cref="IConversationManager"/> is null
|
||||
/// it is setted with 'this'.
|
||||
/// </summary>
|
||||
/// <param name="conversation"></param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// If <paramref name="conversation"/> already has another manager.
|
||||
/// </exception>
|
||||
void AddConversation(IConversationState conversation);
|
||||
|
||||
/// <summary>
|
||||
/// Makes the 'root conversation' of <paramref name="conversation"/>
|
||||
/// the current active conversation and open/reopen the
|
||||
/// <see cref="IConversationState.RootSessionPerConversation"/> if
|
||||
/// the conversation supports 'session-per-conversation'. Close all
|
||||
/// the connection for all session before.
|
||||
/// If <see cref="EndPaused"/> is <c>true</c> will end all
|
||||
/// paused conversations.
|
||||
/// </summary>
|
||||
void SetActiveConversation(IConversationState conversation);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the active conversation if exists, otherwise returns null.
|
||||
/// It depends on <see cref="SetActiveConversation"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IConversationState ActiveConversation{ get; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>If this is non-null run pattern 'session-per-conversation'.
|
||||
/// Must be the same SessionFactory of the managed conversations.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
ISessionFactory SessionFactory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ends the "paused conversations" in call to <see cref="ActiveConversation"/>.
|
||||
/// Important: Unexpected behavior may occur if there are nested conversations,
|
||||
/// as in 'StartResumeConversation' only the own conversation and their parents
|
||||
/// are started, the 'conversations children' remain paused, so these will be ended.
|
||||
/// Defaul value: <c>false</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When it is true, "start/resume a conversation" will cause the other to be
|
||||
/// ended and cleaned up.
|
||||
/// </para>
|
||||
/// <para>This is useful to avoid memory leak where there are many conversations.
|
||||
/// This leak can be very considerable, as the conversation may keep a "NHibernate session"
|
||||
/// that can contain many objects in its cache from the database queries.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
bool EndPaused { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Configuration;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
using System.Web.UI.WebControls.WebParts;
|
||||
using System.Web.UI.HtmlControls;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using NHibernate;
|
||||
using Spring.Data.Common;
|
||||
|
||||
namespace Spring.ConversationWA
|
||||
{
|
||||
/// <summary>
|
||||
/// Port to conversation. If the object is not found in the current
|
||||
/// conversation, will be tried on the parent if the parent is
|
||||
/// different not null.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// If <see cref="Id"/> is different from spring name for this instance.
|
||||
/// </exception>
|
||||
/// <author>Hailton de Castro</author>
|
||||
public interface IConversationState: IDictionary<String, Object>
|
||||
{
|
||||
/// <summary>
|
||||
/// Conversation id.
|
||||
/// </summary>
|
||||
String Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Starts or resumes the conversation and the <see cref="ParenteConversation"/>.
|
||||
/// <para>If <see cref="RootSessionPerConversation"/> is not null, so
|
||||
/// <see cref="SessionFactory"/>.GetCurrentSession() is called to
|
||||
/// Raise SessionHolder for make the reconnection.
|
||||
/// </para>
|
||||
/// <para>Make <see cref="IsNew"/> return false.
|
||||
/// </para>
|
||||
/// <para>Update the <see cref="LastAccess"/>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// <list type="bullet">
|
||||
/// <item>If this conversation is ended.
|
||||
/// </item>
|
||||
/// <item>If <see cref="RootSessionPerConversation"/> is not null and
|
||||
/// <see cref="RootSessionPerConversation"/> different from
|
||||
/// <see cref="SessionFactory"/>.GetCurrentSession()
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </exception>
|
||||
void StartResumeConversation();
|
||||
|
||||
/// <summary>
|
||||
/// Return true until <see cref="StartResumeConversation"/> is called.
|
||||
/// </summary>
|
||||
bool IsNew { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Ends the conversation. End each the 'inner conversation' in
|
||||
/// <see cref="InnerConversations"/>. Returns false if the
|
||||
/// conversation and all <see cref="IConversationState"/> of
|
||||
/// <see cref="InnerConversations"/> has already been ended.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// <list type="bullet">
|
||||
/// <item>If <see cref="System.Web.HttpContext.Current"/>.
|
||||
/// <see cref="System.Web.SessionState.HttpSessionState">Session</see>["spring.objects"]
|
||||
/// is null.
|
||||
/// </item>
|
||||
/// <item>The 'spring session scopes' are not located in the key
|
||||
/// 'spring.objects' of HttpSessionState.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </exception>
|
||||
void EndConversation();
|
||||
|
||||
/// <summary>
|
||||
/// Return true if this conversation is ended.
|
||||
/// </summary>
|
||||
bool Ended { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Inner conversation. After added if the <see cref="ParenteConversation"/>
|
||||
/// is null it will be setted with 'this'.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">at
|
||||
/// <see cref="T:System.Collections.Generic.ICollection`1.Add(T)"/>,
|
||||
/// <see cref="T:System.Collections.Generic.IList`1.this[int]"/>,
|
||||
/// <see cref="T:System.Collections.Generic.IList`1.Insert(int, T)"/>
|
||||
/// if Circular Dependence is detected.</exception>
|
||||
IList<IConversationState> InnerConversations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Conversation parent.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// <list type="bullet">
|
||||
/// <item>If this conversation already has a different parent.
|
||||
/// </item>
|
||||
/// <item>If this Conversation is not new.
|
||||
/// </item>
|
||||
/// <item>If Circular Dependence is detected.
|
||||
/// </item>
|
||||
/// <item>The Parent conversation is not new.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </exception>
|
||||
IConversationState ParenteConversation { get; set;}
|
||||
|
||||
/// <summary>
|
||||
/// TimeOut for the conversation in milliseconds.
|
||||
/// If <c>0</c> means it will be ignored.
|
||||
/// </summary>
|
||||
Int32 TimeOut { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Last acces for a value into this Conversation or Inner Conversation.
|
||||
/// It is reseted for DateTime.Now each time <see cref="StartResumeConversation()"/>
|
||||
/// is called.
|
||||
/// </summary>
|
||||
DateTime LastAccess { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Conversation Manager. When this is setted if
|
||||
/// <see cref="IConversationManager.GetConversationById(String)"/>
|
||||
/// returns null so AddConversation is called.
|
||||
/// </summary>
|
||||
IConversationManager ConversationManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para><see cref="ISession"/> that is stored in the root conversation.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <see cref="ConversationManager"/> must support 'session-per-conversation'.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
ISession RootSessionPerConversation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>If this is non-null run pattern c.
|
||||
/// It also depends on <see cref="DbProvider"/> and <see cref="ConversationManager"/>.
|
||||
/// <see cref="ConversationManager"/> must support ConversationManager.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
ISessionFactory SessionFactory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>If this is non-null run pattern 'session-per-conversation'.
|
||||
/// It also depends on <see cref="SessionFactory"/> and <see cref="ConversationManager"/>.
|
||||
/// <see cref="ConversationManager"/> must support ConversationManager.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
IDbProvider DbProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the conversation is paused.
|
||||
/// </summary>
|
||||
bool IsPaused { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Starts or resumes the conversation and each 'inner conversation' in
|
||||
/// <see cref="InnerConversations"/>.
|
||||
/// It is not about 'Session-per-conversation' because it is done by
|
||||
/// <see cref="IConversationManager"/>.
|
||||
/// </summary>
|
||||
void PauseConversation();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using Common.Logging;
|
||||
using Iesi.Collections.Generic;
|
||||
|
||||
namespace Spring.ConversationWA.Imple
|
||||
{
|
||||
/// <summary>
|
||||
/// List that make validation for Circular Dependence for <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
/// <author>Hailton de Castro</author>
|
||||
public class InnerConversationList: IList<IConversationState>, IList
|
||||
{
|
||||
private static readonly ILog LOG = LogManager.GetLogger(typeof(InnerConversationList));
|
||||
|
||||
private IConversationState conversationOwner;
|
||||
/// <summary>
|
||||
/// Contructor.
|
||||
/// </summary>
|
||||
/// <param name="conversationOwner">The IConversation that owns this InnerConversationList.</param>
|
||||
public InnerConversationList(IConversationState conversationOwner)
|
||||
{
|
||||
if (conversationOwner == null)
|
||||
{
|
||||
String exMsgStr = "'conversationOwner' can not be null";
|
||||
|
||||
LOG.Error(exMsgStr);
|
||||
throw new InvalidOperationException(exMsgStr);
|
||||
}
|
||||
if (LOG.IsDebugEnabled) LOG.Error(String.Format("Creating InnerConversationList for '{0}'", conversationOwner.Id));
|
||||
this.conversationOwner = conversationOwner;
|
||||
}
|
||||
|
||||
private IList<IConversationState> innerList = new List<IConversationState>();
|
||||
|
||||
/// <summary>
|
||||
/// Common Helper to be run before insert.
|
||||
/// </summary>
|
||||
/// <param name="itemAdded"></param>
|
||||
private void addPreAddHelper(IConversationState itemAdded)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("addPreAddHelper: added={0} into {1}", itemAdded, this.conversationOwner));
|
||||
this.validateCircularDependence(itemAdded);
|
||||
if (itemAdded.ParenteConversation != null && itemAdded.ParenteConversation != this.conversationOwner)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
String.Format(WebConversationSpringState.MSG_CONVERSATION_ALREADY_HAS_PARENT, itemAdded.ParenteConversation.Id, this.conversationOwner.Id));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Common Helper to be run after insert.
|
||||
/// </summary>
|
||||
private void addPostAddHelper(IConversationState itemAdded)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("addPreAddHelper: added={0} into {1}", itemAdded, this.conversationOwner));
|
||||
if (itemAdded.ParenteConversation == null)
|
||||
{
|
||||
itemAdded.ParenteConversation = this.conversationOwner;
|
||||
}
|
||||
}
|
||||
|
||||
private void validateCircularDependence(IConversationState itemAdded)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("Validating Circular Dependence: added={0} into {1}", itemAdded, this.conversationOwner));
|
||||
|
||||
ICollection<IConversationState> visitedColl = new HashedSet<IConversationState>();
|
||||
visitedColl.Add(conversationOwner);
|
||||
|
||||
IConversationState parentAncestor = this.conversationOwner;
|
||||
String path = this.conversationOwner.Id;
|
||||
//string conectorPath = "";
|
||||
|
||||
this.validateCircularDependenceRecursive(itemAdded, visitedColl, path + "->" + itemAdded.Id);
|
||||
}
|
||||
|
||||
private void validateCircularDependenceRecursive(IConversationState currentConv, ICollection<IConversationState> visitedColl, String path)
|
||||
{
|
||||
foreach (IConversationState convItem in currentConv.InnerConversations)
|
||||
{
|
||||
if (visitedColl.Contains(convItem))
|
||||
{
|
||||
String exMsgStr =
|
||||
"ConversationState Circular Dependence detected: " +
|
||||
path + "->" + convItem.Id;
|
||||
|
||||
LOG.Error(exMsgStr);
|
||||
throw new InvalidOperationException(exMsgStr);
|
||||
}
|
||||
|
||||
visitedColl.Add(convItem);
|
||||
|
||||
this.validateCircularDependenceRecursive(convItem, visitedColl, path + "->" + convItem.Id);
|
||||
}
|
||||
}
|
||||
|
||||
#region IList<IConversationState> Members
|
||||
/// <summary>
|
||||
/// <see cref="T:IList`1"/>
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public int IndexOf(IConversationState item)
|
||||
{
|
||||
return this.innerList.IndexOf(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IList`1"/>
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="item"></param>
|
||||
public void Insert(int index, IConversationState item)
|
||||
{
|
||||
this.addPreAddHelper(item);
|
||||
this.innerList.Insert(index, item);
|
||||
this.addPostAddHelper(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IList`1"/>
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
this.innerList.RemoveAt(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IList`1"/>
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public IConversationState this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.innerList[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
this.addPreAddHelper(value);
|
||||
this.innerList[index] = value;
|
||||
this.addPostAddHelper(value);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICollection<IConversationState> Members
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
public void Add(IConversationState item)
|
||||
{
|
||||
this.addPreAddHelper(item);
|
||||
this.innerList.Add(item);
|
||||
this.addPostAddHelper(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
this.innerList.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public bool Contains(IConversationState item)
|
||||
{
|
||||
return this.innerList.Contains(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
/// <param name="arrayIndex"></param>
|
||||
public void CopyTo(IConversationState[] array, int arrayIndex)
|
||||
{
|
||||
this.innerList.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get { return this.innerList.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return this.innerList.IsReadOnly; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public bool Remove(IConversationState item)
|
||||
{
|
||||
return this.innerList.Remove(item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<IConversationState> Members
|
||||
/// <summary>
|
||||
/// <see cref="T:IEnumerable`1"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerator<IConversationState> GetEnumerator()
|
||||
{
|
||||
return this.innerList.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IEnumerable"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return this.innerList.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IList Members
|
||||
/// <summary>
|
||||
/// <see cref="T:IList`1"/>
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public int Add(object value)
|
||||
{
|
||||
((IList<IConversationState>)this).Add((IConversationState)value);
|
||||
return this.innerList.Count - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IList`1"/>
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public bool Contains(object value)
|
||||
{
|
||||
return ((IList<IConversationState>)this).Contains((IConversationState)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IList`1"/>
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public int IndexOf(object value)
|
||||
{
|
||||
return ((IList<IConversationState>)this).IndexOf((IConversationState)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IList`1"/>
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="value"></param>
|
||||
public void Insert(int index, object value)
|
||||
{
|
||||
((IList<IConversationState>)this).Insert(index, (IConversationState)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IList`1"/>
|
||||
/// </summary>
|
||||
public bool IsFixedSize
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IList`1"/>
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public void Remove(object value)
|
||||
{
|
||||
((IList<IConversationState>)this).Add((IConversationState)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IList`1"/>
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
object IList.this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((IList<IConversationState>)this)[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
((IList<IConversationState>)this)[index] = (IConversationState)value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICollection Members
|
||||
/// <summary>
|
||||
/// <see cref="ICollection"/>
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
/// <param name="index"></param>
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
IConversationState[] convArr = new IConversationState[array.Length];
|
||||
((IList<IConversationState>)this).CopyTo(convArr, index);
|
||||
convArr.CopyTo(array, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ICollection"/>
|
||||
/// </summary>
|
||||
public bool IsSynchronized
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ICollection"/>
|
||||
/// </summary>
|
||||
public object SyncRoot
|
||||
{
|
||||
get { return this; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using Common.Logging;
|
||||
using Spring.Data.NHibernate.Support;
|
||||
using NHibernate;
|
||||
|
||||
namespace Spring.ConversationWA.Imple
|
||||
{
|
||||
/// <summary>
|
||||
/// This was made to stay under session scope.
|
||||
/// </summary>
|
||||
/// <author>Hailton de Castro</author>
|
||||
public class WebConversationManager : SessionPerConversationScope, IConversationManager
|
||||
{
|
||||
private static readonly ILog LOG = LogManager.GetLogger(typeof(WebConversationManager));
|
||||
|
||||
private static readonly String CONVERSATION_COOKIE_ID = "WebConversationManager.activeConversationId";
|
||||
|
||||
/// <summary>
|
||||
/// Semaphore to synchronize writes to the dictionary.
|
||||
/// </summary>
|
||||
private Mutex mutexEditDic = new Mutex();
|
||||
private IDictionary<String, IConversationState> conversations = new Dictionary<String, IConversationState>();
|
||||
private IConversationState activeConversation = null;
|
||||
|
||||
#region IConversationManager Members
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IConversationManager"/>
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public IConversationState GetConversationById(string id)
|
||||
{
|
||||
if (this.conversations.ContainsKey(id))
|
||||
{
|
||||
return this.conversations[id];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IConversationManager"/>
|
||||
/// </summary>
|
||||
public void EndOnTimeOut()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug("EndOnTimeOut");
|
||||
this.mutexEditDic.WaitOne(5000);
|
||||
foreach (String keyItem in this.conversations.Keys)
|
||||
{
|
||||
IConversationState conversationItem = this.conversations[keyItem];
|
||||
if (conversationItem.TimeOut > 0)
|
||||
{
|
||||
if (DateTime.Now.Subtract(conversationItem.LastAccess).TotalMilliseconds > conversationItem.TimeOut)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("Timeout for conversation '{0}'", conversationItem.Id));
|
||||
conversationItem.EndConversation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.mutexEditDic.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IConversationManager"/>
|
||||
/// </summary>
|
||||
public void PauseConversations()
|
||||
{
|
||||
foreach (IConversationState convItem in this.conversations.Values)
|
||||
{
|
||||
convItem.PauseConversation();
|
||||
}
|
||||
this.Close(this.SessionFactory, this.conversations.Values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IConversationManager"/>
|
||||
/// </summary>
|
||||
public void FreeEnded()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug("EndOnTimeOut");
|
||||
this.mutexEditDic.WaitOne(5000);
|
||||
List<IConversationState> removeList = new List<IConversationState>();
|
||||
foreach (String keyItem in this.conversations.Keys)
|
||||
{
|
||||
IConversationState conversationItem = this.conversations[keyItem];
|
||||
if (conversationItem.Ended)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("FreeEnded: Release conversation '{0}'", conversationItem.Id));
|
||||
removeList.Add(conversationItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (removeList.Count > 0)
|
||||
{
|
||||
this.Close(this.sessionFactory, removeList);
|
||||
}
|
||||
|
||||
foreach (IConversationState conversationItem in removeList)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("FreeEnded: Remove conversation '{0}'", conversationItem.Id));
|
||||
conversationItem.EndConversation();
|
||||
this.RemoveConversation(conversationItem);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.mutexEditDic.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IConversationManager"/>
|
||||
/// </summary>
|
||||
/// <param name="conversation"></param>
|
||||
public void AddConversation(IConversationState conversation)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.mutexEditDic.WaitOne(5000);
|
||||
this.conversations.Add(conversation.Id, conversation);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.mutexEditDic.ReleaseMutex();
|
||||
}
|
||||
|
||||
if (conversation.ConversationManager != null && conversation.ConversationManager != this)
|
||||
{
|
||||
throw new InvalidOperationException(String.Format("Conversation already has another manager. conversation='{0}'", conversation.Id));
|
||||
}
|
||||
|
||||
if (conversation.ConversationManager == null)
|
||||
{
|
||||
conversation.ConversationManager = this;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IConversationManager"/>
|
||||
/// </summary>
|
||||
/// <param name="conversation"></param>
|
||||
public void SetActiveConversation(IConversationState conversation)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("SetActiveConversation('{0}')", conversation.Id));
|
||||
|
||||
//Close connection for the last conversation, if it is open.
|
||||
if (this.activeConversation != null && this.activeConversation != conversation)
|
||||
{
|
||||
this.Close(this.SessionFactory, this.conversations.Values);
|
||||
}
|
||||
this.Open(conversation, this.conversations.Values);
|
||||
|
||||
this.activeConversation = conversation;
|
||||
|
||||
//Ending the paused conversations.
|
||||
if (this.EndPaused)
|
||||
{
|
||||
foreach (IConversationState convItem in this.conversations.Values)
|
||||
{
|
||||
if (convItem.IsPaused)
|
||||
{
|
||||
convItem.EndConversation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IConversationManager"/>
|
||||
/// </summary>
|
||||
[Obsolete("Not used, the active conversation is defined by call 'IConversationManager.SetActiveConversation' on 'IConversationState.StartResumeConversation'")]
|
||||
public void LoadActiveConversation()
|
||||
{
|
||||
//reset this.activeConversation
|
||||
this.activeConversation = null;
|
||||
|
||||
if (LOG.IsDebugEnabled) LOG.Debug("LoadActiveConversation");
|
||||
|
||||
HttpCookie activeConveCookie = HttpContext.Current.Request.Cookies[CONVERSATION_COOKIE_ID];
|
||||
if (activeConveCookie != null && !String.IsNullOrEmpty(activeConveCookie.Value))
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("LoadActiveConversation: cooking found for current active conversation: [{0}]", activeConveCookie.ToString()));
|
||||
if (this.conversations.ContainsKey(activeConveCookie.Value))
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("LoadActiveConversation: active conversation found for id: '{0}'", activeConveCookie.Value));
|
||||
IConversationState conversation = this.conversations[activeConveCookie.Value];
|
||||
if (conversation != null)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("LoadActiveConversation: conversation found: '{0}'", conversation.Id));
|
||||
//find root conversation.
|
||||
IConversationState rootConversation = conversation;
|
||||
while (rootConversation.ParenteConversation != null)
|
||||
{
|
||||
rootConversation = rootConversation.ParenteConversation;
|
||||
}
|
||||
rootConversation.StartResumeConversation();
|
||||
this.SetActiveConversation(rootConversation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("LoadActiveConversation: conversation NOT found for id on the cookie: '{0}'", activeConveCookie.Value));
|
||||
HttpContext.Current.Response.Cookies.Remove(CONVERSATION_COOKIE_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IConversationManager"/>
|
||||
/// </summary>
|
||||
public IConversationState ActiveConversation
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.activeConversation;
|
||||
}
|
||||
}
|
||||
|
||||
private ISessionFactory sessionFactory;
|
||||
/// <summary>
|
||||
/// <see cref="IConversationManager"/>
|
||||
/// </summary>
|
||||
public ISessionFactory SessionFactory
|
||||
{
|
||||
get { return sessionFactory; }
|
||||
set { sessionFactory = value; }
|
||||
}
|
||||
|
||||
private bool endPaused = false;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IConversationManager"/>
|
||||
/// </summary>
|
||||
public bool EndPaused
|
||||
{
|
||||
get { return endPaused; }
|
||||
set { endPaused = value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SessionPerConversationScope Members
|
||||
/// <summary>
|
||||
/// Ends all conversations and Closes all their Session.
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug("Dispose. End all Conversations");
|
||||
foreach (String conversationId in this.conversations.Keys)
|
||||
{
|
||||
this.conversations[conversationId].EndConversation();
|
||||
}
|
||||
|
||||
this.Close(this.SessionFactory, this.conversations.Values);
|
||||
|
||||
this.conversations.Clear();
|
||||
this.sessionFactory = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Remove conversation.
|
||||
/// </summary>
|
||||
/// <param name="conversation"></param>
|
||||
private void RemoveConversation(IConversationState conversation)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.mutexEditDic.WaitOne(5000);
|
||||
if (!this.conversations.Remove(conversation.Id))
|
||||
{
|
||||
throw new InvalidOperationException(String.Format("Conversation '{0}' not exists on this manager", conversation.Id));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.mutexEditDic.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,601 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Configuration;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
using System.Web.UI.WebControls.WebParts;
|
||||
using System.Web.UI.HtmlControls;
|
||||
using Spring.Objects.Factory;
|
||||
using Common.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using Spring.Data.Common;
|
||||
using NHibernate;
|
||||
using Spring.Data.NHibernate.Support;
|
||||
using Spring.Data.NHibernate;
|
||||
|
||||
namespace Spring.ConversationWA.Imple
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of conversation in the infrastructure of Spring.
|
||||
/// It avoid Circular Dependence.
|
||||
/// </summary>
|
||||
/// <author>Hailton de Castro</author>
|
||||
public class WebConversationSpringState : IConversationState, IObjectNameAware
|
||||
{
|
||||
private static readonly ILog LOG = LogManager.GetLogger(typeof(WebConversationSpringState));
|
||||
|
||||
/// <summary>
|
||||
/// Default message for "CONVERSATION ALREADY HAS A PARENT" error.
|
||||
/// </summary>
|
||||
public static readonly String MSG_CONVERSATION_ALREADY_HAS_PARENT =
|
||||
"This conversation already has a different parent." +
|
||||
" Current: '{0}'. Tried: '{1}'";
|
||||
|
||||
private IDictionary<string, object> state = new Dictionary<string, object>();
|
||||
|
||||
#region IConversationState Members
|
||||
|
||||
private String id;
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
public string Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.id;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.id = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void EndConversation()
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("End of Conversation '{0}'", this.id));
|
||||
|
||||
IDictionary springSessionScope = (IDictionary)HttpContext.Current.Session["spring.objects"];
|
||||
if (springSessionScope == null)
|
||||
{
|
||||
throw new InvalidOperationException("The 'spring session scope' are not located in the key 'spring.objects' of HttpSessionState");
|
||||
}
|
||||
|
||||
if (springSessionScope.Contains(this.Id))
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("EndConversation: Id='{0}' Was Found on 'spring session scope'!", this.id));
|
||||
springSessionScope.Remove(this.id);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("EndConversation: Id='{0}' Was NOT Found on 'spring session scope!", this.id));
|
||||
}
|
||||
|
||||
List<IConversationState> innerConversationsListTemp = new List<IConversationState>(this.InnerConversations);
|
||||
foreach (IConversationState innerConversationItem in innerConversationsListTemp)
|
||||
{
|
||||
innerConversationItem.EndConversation();
|
||||
}
|
||||
|
||||
if (this.parenteConversation != null)
|
||||
{
|
||||
this.parenteConversation.InnerConversations.Remove(this);
|
||||
}
|
||||
|
||||
this.ended = true;
|
||||
}
|
||||
|
||||
private bool ended = false;
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
public bool Ended
|
||||
{
|
||||
get { return ended; }
|
||||
}
|
||||
|
||||
private int timeOut = 180000;
|
||||
/// <summary>
|
||||
/// Default 180000.
|
||||
/// </summary>
|
||||
public int TimeOut
|
||||
{
|
||||
get { return timeOut; }
|
||||
set { timeOut = value; }
|
||||
}
|
||||
|
||||
private IList<IConversationState> innerConversations;
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
public IList<IConversationState> InnerConversations
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.innerConversations == null)
|
||||
{
|
||||
this.innerConversations = new InnerConversationList(this);
|
||||
}
|
||||
return this.innerConversations;
|
||||
}
|
||||
}
|
||||
|
||||
private IConversationState parenteConversation;
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
public IConversationState ParenteConversation
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.parenteConversation;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (this.parenteConversation != null && this.parenteConversation != value)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
String.Format(MSG_CONVERSATION_ALREADY_HAS_PARENT, this.parenteConversation.Id, value.Id));
|
||||
}
|
||||
if (!this.IsNew)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
String.Format("This Conversation is not new." +
|
||||
" Conversation.Id: '{0}'. Parent Tried: '{1}'", this.Id, value.Id));
|
||||
}
|
||||
//Perhaps the father need not be new, only the child needs to be.
|
||||
//if (!value.IsNew)
|
||||
//{
|
||||
// throw new InvalidOperationException(
|
||||
// String.Format("The Parent conversation is not new." +
|
||||
// " Conversation.Id: '{0}'. Parent Tried: '{1}'", this.Id, value.Id));
|
||||
//}
|
||||
|
||||
this.parenteConversation = value;
|
||||
if (!this.parenteConversation.InnerConversations.Contains(this))
|
||||
{
|
||||
this.parenteConversation.InnerConversations.Add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime lastAccess = DateTime.Now;
|
||||
/// <summary>
|
||||
/// <see cref="LastAccess"/>.
|
||||
/// </summary>
|
||||
public DateTime LastAccess
|
||||
{
|
||||
get { return lastAccess; }
|
||||
set { lastAccess = value; }
|
||||
}
|
||||
|
||||
private IConversationManager conversationManager;
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
public IConversationManager ConversationManager
|
||||
{
|
||||
get
|
||||
{
|
||||
return conversationManager;
|
||||
}
|
||||
set
|
||||
{
|
||||
conversationManager = value;
|
||||
if (this.conversationManager.GetConversationById(this.Id) == null)
|
||||
{
|
||||
this.conversationManager.AddConversation(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
public void StartResumeConversation()
|
||||
{
|
||||
this.isNew = false;
|
||||
this.isPaused = false;
|
||||
this.lastAccess = DateTime.Now;
|
||||
|
||||
if (this.Ended)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
String.Format(
|
||||
"StartResumeConversation: this conversation is ended." +
|
||||
" Conversation.Id '{0}'", this.Id));
|
||||
}
|
||||
if (this.ConversationManager != null)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("StartResumeConversation('{0}'): ConversationManager is not null.", this.Id));
|
||||
//if this is the root conversation.
|
||||
if (this.ParenteConversation == null)
|
||||
{
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("SetActiveConversation('{0}'): ConversationManager is not null.", this.Id));
|
||||
//if this is the root conversation.
|
||||
this.ConversationManager.SetActiveConversation(this);
|
||||
|
||||
//makes SessionHolder to reopen this session
|
||||
if (this.RootSessionPerConversation != null)
|
||||
{
|
||||
ISession session = this.SessionFactory.GetCurrentSession();
|
||||
if (this.RootSessionPerConversation != session)
|
||||
{
|
||||
//How it is implemented it will never happen, because of the sequence of previous calls:
|
||||
// ConversationManager.SetActiveConversation -> SessionPerConversationScope.Open
|
||||
// 'new InvalidOperationException("Participating in existing Hibernate SessionFactory IS NOT ALOWED.")' happen first.
|
||||
throw new InvalidOperationException(
|
||||
String.Format(
|
||||
"StartResumeConversation: this.SessionFactory.GetCurrentSession()" +
|
||||
" have a different instance than 'RootSessionPerConversation'" +
|
||||
" from conversation '{0}'", this.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ParenteConversation.StartResumeConversation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ISession rootSessionPerConversation;
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
public ISession RootSessionPerConversation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.ParenteConversation != null)
|
||||
{
|
||||
return this.ParenteConversation.RootSessionPerConversation;
|
||||
}
|
||||
else
|
||||
{
|
||||
return rootSessionPerConversation;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
if (this.ParenteConversation != null)
|
||||
{
|
||||
this.ParenteConversation.RootSessionPerConversation = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
rootSessionPerConversation = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ISessionFactory sessionFactory;
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
public ISessionFactory SessionFactory
|
||||
{
|
||||
get { return sessionFactory; }
|
||||
set { sessionFactory = value; }
|
||||
}
|
||||
|
||||
IDbProvider dbProvider;
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
public IDbProvider DbProvider
|
||||
{
|
||||
get { return dbProvider; }
|
||||
set { dbProvider = value; }
|
||||
}
|
||||
|
||||
private bool isNew = true;
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
public bool IsNew
|
||||
{
|
||||
get { return this.isNew; }
|
||||
}
|
||||
|
||||
private bool isPaused = true;
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
public bool IsPaused
|
||||
{
|
||||
get { return isPaused; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IConversationState"/>
|
||||
/// </summary>
|
||||
public void PauseConversation()
|
||||
{
|
||||
this.isPaused = true;
|
||||
foreach (IConversationState innerConv in this.InnerConversations)
|
||||
{
|
||||
innerConv.PauseConversation();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IObjectNameAware Members
|
||||
/// <summary>
|
||||
/// <see cref="IObjectNameAware"/>. It is used to valddate <see cref="Id"/>
|
||||
/// </summary>
|
||||
public string ObjectName
|
||||
{
|
||||
set
|
||||
{
|
||||
if (this.id != value)
|
||||
{
|
||||
throw new InvalidOperationException(String.Format("Id is different from spring name for this instance.. Currents='{0}', springName='{1}'", this.id, value));
|
||||
}
|
||||
if (LOG.IsDebugEnabled) LOG.Debug(String.Format("Begin of Conversation '{0}'", this.id));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDictionary<string,object> Members
|
||||
/// <summary>
|
||||
/// <see cref="T:IDictionary`2"/>
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
public void Add(string key, object value)
|
||||
{
|
||||
this.state.Add(key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IDictionary`2"/>
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
if (this.state.ContainsKey(key))
|
||||
{
|
||||
return this.state.ContainsKey(key);
|
||||
}
|
||||
else if (this.parenteConversation != null)
|
||||
{
|
||||
return this.parenteConversation.ContainsKey(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IDictionary`2"/>
|
||||
/// </summary>
|
||||
public ICollection<string> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.state.Keys;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IDictionary`2"/>
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public bool Remove(string key)
|
||||
{
|
||||
return this.state.Remove(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IDictionary`2"/>
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public bool TryGetValue(string key, out object value)
|
||||
{
|
||||
if (this.state.TryGetValue(key, out value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (this.parenteConversation != null && this.parenteConversation.TryGetValue(key, out value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IDictionary`2"/>
|
||||
/// </summary>
|
||||
public ICollection<object> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.state.Values;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:IDictionary`2"/>
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public object this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.state.ContainsKey(key))
|
||||
{
|
||||
return this.state[key];
|
||||
}
|
||||
else if (this.parenteConversation != null)
|
||||
{
|
||||
return this.parenteConversation[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
this.state[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICollection<KeyValuePair<string,object>> Members
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
public void Add(KeyValuePair<string, object> item)
|
||||
{
|
||||
this.state.Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
this.state.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public bool Contains(KeyValuePair<string, object> item)
|
||||
{
|
||||
if (this.state.Contains(item))
|
||||
{
|
||||
return this.state.Contains(item);
|
||||
}
|
||||
else if (this.parenteConversation != null)
|
||||
{
|
||||
return this.parenteConversation.Contains(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
/// <param name="arrayIndex"></param>
|
||||
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
|
||||
{
|
||||
this.state.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.state.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.state.IsReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="T:ICollection`1"/>
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public bool Remove(KeyValuePair<string, object> item)
|
||||
{
|
||||
return this.state.Remove(item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<KeyValuePair<string,object>> Members
|
||||
/// <summary>
|
||||
/// <see cref="T:IEnumerable`1"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
||||
{
|
||||
return this.state.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
/// <summary>
|
||||
/// <see cref="IEnumerable"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return this.state.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// A String representation from conversation.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
String innerConsversationsStr = "";
|
||||
|
||||
String conector = "";
|
||||
foreach (IConversationState convItem in this.InnerConversations)
|
||||
{
|
||||
innerConsversationsStr += conector + convItem.ToString();
|
||||
}
|
||||
|
||||
return String.Format("{{Id='{0}'; this.ParenteConversation.Id={1}; InnerConversations=[{2}]}}",
|
||||
this.id,
|
||||
this.ParenteConversation != null ? this.ParenteConversation.Id : "<no_parent>",
|
||||
innerConsversationsStr
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HashCode.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
#region Licence
|
||||
|
||||
/*
|
||||
* Copyright <20> 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#endregion
|
||||
|
||||
#region Imports
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Common.Logging;
|
||||
using NHibernate;
|
||||
using Spring.Threading;
|
||||
using Spring.Transaction.Support;
|
||||
using Spring.Util;
|
||||
using System.Data;
|
||||
using Spring.ConversationWA;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Spring.Data.NHibernate.Support
|
||||
{
|
||||
///<summary>
|
||||
///Based on <see cref="Spring.Data.NHibernate.Support.SessionScope"/>
|
||||
/// for dupport to 'session-per-conversation' pattern.
|
||||
///</summary>
|
||||
///<author>Hailton de Castro</author>
|
||||
public class SessionPerConversationScope : IDisposable
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// The logging instance.
|
||||
/// </summary>
|
||||
protected readonly ILog log = LogManager.GetLogger(MethodInfo.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private readonly SessionPerConversationScopeSettings settings;
|
||||
|
||||
// Keys into LogicalThreadContext for runtime values.
|
||||
private readonly string ISOPEN_KEY;
|
||||
private readonly string OPENER_CONVERSATION_ID_KEY;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor (s)
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SessionPerConversationScopeSettings"/> class.
|
||||
/// Uses default values for <see cref="SessionPerConversationScopeSettings"/>
|
||||
/// </summary>
|
||||
public SessionPerConversationScope()
|
||||
: this(new SessionPerConversationScopeSettings())
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SessionPerConversationScopeSettings"/> class.
|
||||
/// </summary>
|
||||
/// <param name="entityInterceptor">Specify the <see cref="IInterceptor"/> to be set on each session provided by this <see cref="SessionPerConversationScope"/> instance.</param>
|
||||
/// <param name="defaultFlushMode">Specify the flushmode to be applied on each session provided by this <see cref="SessionPerConversationScope"/> instance.
|
||||
/// </param>
|
||||
public SessionPerConversationScope(IInterceptor entityInterceptor, FlushMode defaultFlushMode)
|
||||
: this(new SessionPerConversationScopeSettings(entityInterceptor, defaultFlushMode))
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SessionScope"/> class.
|
||||
/// </summary>
|
||||
/// <param name="settings">An <see cref="SessionPerConversationScopeSettings"/> instance holding the scope configuration</param>
|
||||
public SessionPerConversationScope(SessionPerConversationScopeSettings settings)
|
||||
{
|
||||
log = LogManager.GetLogger(this.GetType());
|
||||
this.settings = settings;
|
||||
|
||||
ISOPEN_KEY = UniqueKey.GetInstanceScopedString(this, "IsOpen");
|
||||
OPENER_CONVERSATION_ID_KEY = UniqueKey.GetInstanceScopedString(this, "OpenerConversationId");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flushmode to be applied on each newly created session.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property defaults to <see cref="FlushMode.Never"/> to ensure that modifying objects outside the boundaries
|
||||
/// of a transaction will not be persisted. It is recommended to not change this value but wrap any modifying operation
|
||||
/// within a transaction.
|
||||
/// </remarks>
|
||||
public FlushMode DefaultFlushMode
|
||||
{
|
||||
get { return settings.DefaultFlushMode; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the configured EntityInterceptor
|
||||
/// </summary>
|
||||
public IInterceptor EntityInterceptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return settings.EntityInterceptor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Id for conversation that open the Session.
|
||||
/// </summary>
|
||||
public String OpenerConversationId
|
||||
{
|
||||
get
|
||||
{
|
||||
return (String)LogicalThreadContext.GetData(OPENER_CONVERSATION_ID_KEY);
|
||||
}
|
||||
set
|
||||
{
|
||||
LogicalThreadContext.SetData(OPENER_CONVERSATION_ID_KEY, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag, whether this scope is in "open" state on the current logical thread.
|
||||
/// </summary>
|
||||
public bool IsOpen
|
||||
{
|
||||
get
|
||||
{
|
||||
return (null != LogicalThreadContext.GetData(ISOPEN_KEY));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a flag, whether this scope is in "open" state on the current logical thread.
|
||||
/// </summary>
|
||||
/// <param name="isOpen"></param>
|
||||
private void SetOpen(bool isOpen)
|
||||
{
|
||||
if (isOpen)
|
||||
{
|
||||
LogicalThreadContext.SetData(ISOPEN_KEY, ISOPEN_KEY);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogicalThreadContext.FreeNamedDataSlot(ISOPEN_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
/// <summary>
|
||||
/// NOOP.
|
||||
/// </summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
//no OP
|
||||
log.Warn("I'm not doing anything");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Open a new session or reconect the
|
||||
/// <see cref="IConversationState.RootSessionPerConversation"/> in <paramref name="activeConversation"/>.
|
||||
/// Participates in an existing session registed with spring's <see cref="TransactionSynchronizationManager"/>
|
||||
/// is not alowed.
|
||||
/// </summary>
|
||||
/// <param name="activeConversation"></param>
|
||||
/// <param name="allManagedConversation"></param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// <list type="bullet">
|
||||
/// <item>If there is another conversation with a ISession with opened
|
||||
/// IDbConnection.</item>
|
||||
/// <item>If trie to participating in existing Hibernate SessionFactory
|
||||
/// managed by <see cref="TransactionSynchronizationManager"/>.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </exception>
|
||||
public void Open(IConversationState activeConversation, ICollection<IConversationState> allManagedConversation)
|
||||
{
|
||||
bool isDebugEnabled = log.IsDebugEnabled;
|
||||
|
||||
if (IsOpen)
|
||||
{
|
||||
if (activeConversation.Id != this.OpenerConversationId)
|
||||
{
|
||||
throw new InvalidOperationException("There is another conversation with a ISession with opened IDbConnection.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isDebugEnabled) log.Debug(String.Format("SessionPerConversationScope is already open for this conversation: Id:'{0}'.", activeConversation.Id));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (activeConversation.SessionFactory != null)
|
||||
{
|
||||
if (isDebugEnabled) log.Debug(String.Format("activeConversation with 'session-per-conversation': Id:'{0}'.", activeConversation.Id));
|
||||
|
||||
// single session mode
|
||||
if (TransactionSynchronizationManager.HasResource(activeConversation.SessionFactory))
|
||||
{
|
||||
// Do not modify the Session: just set the participate flag.
|
||||
if (isDebugEnabled) log.Debug("Participating in existing Hibernate SessionFactory IS NOT ALOWED.");
|
||||
throw new InvalidOperationException("Participating in existing Hibernate SessionFactory IS NOT ALOWED.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isDebugEnabled) log.Debug("Opening single Hibernate Session in SessionPerConversationScope");
|
||||
TransactionSynchronizationManager.BindResource(activeConversation.SessionFactory, new LazySessionPerConversationHolder(this, activeConversation, allManagedConversation));
|
||||
|
||||
SetOpen(true);
|
||||
this.OpenerConversationId = activeConversation.Id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isDebugEnabled) log.Debug(String.Format("activeConversation with NO 'session-per-conversation': Id:'{0}'.", activeConversation.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the current view's session and unregisters
|
||||
/// from spring's <see cref="TransactionSynchronizationManager"/>.
|
||||
/// </summary>
|
||||
/// <param name="sessionFactory">The session factory that IConversationState on <paramref name="allManagedConversation"/> use</param>
|
||||
/// <param name="allManagedConversation">A list of conversations which the session can be closed or disconnected</param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// <list type="bullet">
|
||||
/// <item>If start/resume a conversation from a
|
||||
/// IConversationManager when exists a diferent IConversationManager
|
||||
/// with open ISession registered on TransactionSynchronizationManager
|
||||
/// </item>
|
||||
/// <item>If the holder on TransactionSynchronizationManager, is not a LazySessionPerConversationHolder.</item>
|
||||
/// </list>
|
||||
/// </exception>
|
||||
public void Close(ISessionFactory sessionFactory, ICollection<IConversationState> allManagedConversation)
|
||||
{
|
||||
bool isDebugEnabled = log.IsDebugEnabled;
|
||||
if (isDebugEnabled) log.Debug("Trying to close SessionPerConversationScope");
|
||||
|
||||
if (IsOpen)
|
||||
{
|
||||
try
|
||||
{
|
||||
DoClose(sessionFactory, allManagedConversation, isDebugEnabled);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetOpen(false);
|
||||
this.OpenerConversationId = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isDebugEnabled) log.Debug("No open conversation - doing nothing");
|
||||
}
|
||||
}
|
||||
|
||||
private void DoClose(ISessionFactory sessionFactory, ICollection<IConversationState> allManagedConversation, bool isLogDebugEnabled)
|
||||
{
|
||||
// single session mode
|
||||
if (isLogDebugEnabled) log.Debug("DoClose: Closing SessionPerConversationScope");
|
||||
Object holderObj = TransactionSynchronizationManager.UnbindResource(sessionFactory);
|
||||
if (holderObj != null)
|
||||
{
|
||||
if (holderObj is LazySessionPerConversationHolder)
|
||||
{
|
||||
LazySessionPerConversationHolder holder = (LazySessionPerConversationHolder)holderObj;
|
||||
if (holder.Owner == this)
|
||||
{
|
||||
holder.CloseAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Can not close session beacause 'holder owner' is not 'this'." +
|
||||
" You are trying to start/resume a conversation from a" +
|
||||
" IConversationManager when exists a diferent IConversationManager " +
|
||||
" with open ISession registered on TransactionSynchronizationManager.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Can not close session beacause holder, on TransactionSynchronizationManager, is not a LazySessionPerConversationHolder.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isLogDebugEnabled) log.Warn("DoClose: TransactionSynchronizationManager.UnbindResource(sessionFactory) has no SessionHolder. Should I throw error?");
|
||||
}
|
||||
}
|
||||
|
||||
private void DoOpenSession(IConversationState conversation)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
ISession session = null;
|
||||
if (conversation.RootSessionPerConversation == null)
|
||||
{
|
||||
//new session
|
||||
session = (
|
||||
(EntityInterceptor != null)
|
||||
? conversation.SessionFactory.OpenSession(EntityInterceptor)
|
||||
: conversation.SessionFactory.OpenSession()
|
||||
);
|
||||
conversation.RootSessionPerConversation = session;
|
||||
}
|
||||
else
|
||||
{
|
||||
//reconnect existing one.
|
||||
if (conversation.DbProvider != null)
|
||||
{
|
||||
if (log.IsDebugEnabled) log.Debug(String.Format("DoOpenSession: Conversation has a DbProvider: Id='{0}'", conversation.Id));
|
||||
if (!conversation.RootSessionPerConversation.IsConnected)
|
||||
{
|
||||
if (log.IsDebugEnabled) log.Debug(String.Format("DoOpenSession: Conversation is not Connected: Id='{0}'", conversation.Id));
|
||||
|
||||
IDbConnection connection = conversation.DbProvider.CreateConnection();
|
||||
connection.Open();
|
||||
|
||||
conversation.RootSessionPerConversation.Reconnect(connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log.IsDebugEnabled) log.Debug(String.Format("DoOpenSession: Conversation is already Connected: Id='{0}'", conversation.Id));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log.IsDebugEnabled) log.Debug(String.Format("DoOpenSession: Conversation has NO DbProvider: Id='{0}'", conversation.Id));
|
||||
conversation.RootSessionPerConversation.Reconnect();
|
||||
}
|
||||
session = conversation.RootSessionPerConversation;
|
||||
}
|
||||
session.FlushMode = DefaultFlushMode;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LazySessionPerConversationHolder utility class
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This sessionHolder creates a session for the active conversation only if it is
|
||||
/// needed (<see cref="Spring.ConversationWA.IConversationState.StartResumeConversation"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Although a NHibernateSession deferes creation of db-connections until they are really
|
||||
/// needed, instantiation a session is imho still more expensive than this LazySessionHolder. (EE)
|
||||
/// </remarks>
|
||||
private class LazySessionPerConversationHolder : SessionHolder
|
||||
{
|
||||
private readonly ILog log = LogManager.GetLogger(typeof(LazySessionPerConversationHolder));
|
||||
private SessionPerConversationScope owner;
|
||||
public SessionPerConversationScope Owner
|
||||
{
|
||||
get { return owner; }
|
||||
}
|
||||
IConversationState activeConversation;
|
||||
ICollection<IConversationState> allManagedConversation;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance.
|
||||
/// </summary>
|
||||
public LazySessionPerConversationHolder(SessionPerConversationScope owner, IConversationState activeConversation, ICollection<IConversationState> allManagedConversation)
|
||||
{
|
||||
if (log.IsDebugEnabled) log.Debug("Created LazyReconnectableSessionHolder");
|
||||
this.owner = owner;
|
||||
this.activeConversation = activeConversation;
|
||||
this.allManagedConversation = allManagedConversation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new session on demand
|
||||
/// </summary>
|
||||
protected override void EnsureInitialized()
|
||||
{
|
||||
if (this.activeConversation.RootSessionPerConversation == null || !this.activeConversation.RootSessionPerConversation.IsConnected)
|
||||
{
|
||||
if (log.IsDebugEnabled) log.Debug("EnsureInitialized: 'session-per-conversation' instance requested - opening new session");
|
||||
owner.DoOpenSession(this.activeConversation);
|
||||
AddSession(this.activeConversation.RootSessionPerConversation);
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseAll()
|
||||
{
|
||||
foreach (IConversationState conversation in this.allManagedConversation)
|
||||
{
|
||||
this.CloseConversation(conversation);
|
||||
}
|
||||
owner = null;
|
||||
this.activeConversation = null;
|
||||
this.allManagedConversation = null;
|
||||
|
||||
if (log.IsDebugEnabled) log.Debug("CloseAll LazySessionPerConversationHolder");
|
||||
}
|
||||
|
||||
private void CloseConversation(IConversationState conversation)
|
||||
{
|
||||
if (log.IsDebugEnabled) log.Debug(String.Format("CloseConversation: Id='{0}'", conversation.Id));
|
||||
if (conversation.RootSessionPerConversation != null)
|
||||
{
|
||||
ISession tmpSession = conversation.RootSessionPerConversation;
|
||||
if (conversation.Ended)
|
||||
{
|
||||
SessionFactoryUtils.CloseSession(tmpSession);
|
||||
conversation.RootSessionPerConversation = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tmpSession.IsConnected)
|
||||
tmpSession.Disconnect();
|
||||
}
|
||||
RemoveSession(tmpSession);
|
||||
}
|
||||
if (log.IsDebugEnabled) log.Debug("Closed LazySessionPerConversationHolder");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NHibernate;
|
||||
using Spring.Util;
|
||||
|
||||
namespace Spring.Data.NHibernate.Support
|
||||
{
|
||||
/// <summary>
|
||||
/// Setting for <see cref="SessionPerConversationScope"/>
|
||||
/// </summary>
|
||||
/// <author>Hailton de Castro</author>
|
||||
public class SessionPerConversationScopeSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Default value for <see cref="DefaultFlushMode"/> property.
|
||||
/// </summary>
|
||||
public static readonly FlushMode FLUSHMODE_DEFAULT = FlushMode.Never;
|
||||
|
||||
private IInterceptor entityInterceptor;
|
||||
private FlushMode defaultFlushMode;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of <see cref="SessionScopeSettings"/> with default values.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Calling this constructor from your derived class leaves <see cref="EntityInterceptor"/>
|
||||
/// uninitialized. See <see cref="ResolveEntityInterceptor"/> for more.
|
||||
/// </remarks>
|
||||
public SessionPerConversationScopeSettings()
|
||||
{
|
||||
this.entityInterceptor = null;
|
||||
this.defaultFlushMode = FLUSHMODE_DEFAULT;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of <see cref="SessionPerConversationScopeSettings"/> with the given values and references.
|
||||
/// </summary>
|
||||
/// <param name="entityInterceptor">
|
||||
/// Specify the <see cref="IInterceptor"/> to be set on each session provided by the <see cref="SessionPerConversationScope"/> instance.
|
||||
/// </param>
|
||||
/// <param name="defaultFlushMode">
|
||||
/// Specify the flushmode to be applied on each session provided by the <see cref="SessionScope"/> instance.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// Calling this constructor marks all properties initialized.
|
||||
/// </remarks>
|
||||
public SessionPerConversationScopeSettings(IInterceptor entityInterceptor, FlushMode defaultFlushMode)
|
||||
{
|
||||
this.entityInterceptor = entityInterceptor;
|
||||
this.defaultFlushMode = defaultFlushMode;
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configured <see cref="IInterceptor"/> instance to be used.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public virtual IInterceptor EntityInterceptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return entityInterceptor;
|
||||
}
|
||||
set
|
||||
{
|
||||
entityInterceptor = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the flushmode to be applied on each newly created session.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property defaults to <see cref="FlushMode.Never"/> to ensure that modifying objects outside the boundaries
|
||||
/// of a transaction will not be persisted. It is recommended to not change this value but wrap any modifying operation
|
||||
/// within a transaction.
|
||||
/// </remarks>
|
||||
public FlushMode DefaultFlushMode
|
||||
{
|
||||
get { return defaultFlushMode; }
|
||||
set { defaultFlushMode = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to resolve an <see cref="IInterceptor"/> instance according to your chosen strategy.
|
||||
/// </summary>
|
||||
protected virtual IInterceptor ResolveEntityInterceptor()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{64400FF8-2E9F-4809-B5F4-0C7EB8ABFF87}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Spring</RootNamespace>
|
||||
<AssemblyName>Spring.ConversationWA.NH32</AssemblyName>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<OldToolsVersion>2.0</OldToolsVersion>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<TargetFrameworkSubset>
|
||||
</TargetFrameworkSubset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\..\build\VS.Net.2008\Spring.ConversationWA.NH32\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Debug\Spring.ConversationWA.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Common.Logging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\Net\2.0\Common.Logging.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Iesi.Collections, Version=1.0.1.0, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\NHibernate32\net\3.5\Iesi.Collections.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NHibernate, Version=3.2.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\NHibernate32\net\3.5\NHibernate.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\CommonAssemblyInfo.cs">
|
||||
<Link>CommonAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
<Compile Include="ConversationWA\HttpModule\ConversationModule.cs" />
|
||||
<Compile Include="ConversationWA\IConversationManager.cs" />
|
||||
<Compile Include="ConversationWA\IConversationState.cs" />
|
||||
<Compile Include="ConversationWA\Imple\InnerConversationList.cs" />
|
||||
<Compile Include="ConversationWA\Imple\WebConversationManager.cs" />
|
||||
<Compile Include="ConversationWA\Imple\WebConversationSpringState.cs" />
|
||||
<Compile Include="Data\NHibernate\Support\SessionPerConversationScope.cs" />
|
||||
<Compile Include="Data\NHibernate\Support\SessionPerConversationSettings.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\..\build-support\tools\nant\schema\nant.xsd">
|
||||
<Link>nant.xsd</Link>
|
||||
</None>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Spring.Core\Spring.Core.2008.csproj">
|
||||
<Project>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</Project>
|
||||
<Name>Spring.Core.2008</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Spring.Data.NHibernate32\Spring.Data.NHibernate32.2008.csproj">
|
||||
<Project>{1C8E0481-A70D-445E-AB4D-4A963CF7DC83}</Project>
|
||||
<Name>Spring.Data.NHibernate32.2008</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Spring.Data\Spring.Data.2008.csproj">
|
||||
<Project>{AE00E5AB-C39A-436F-86D2-33BFE33E2E40}</Project>
|
||||
<Name>Spring.Data.2008</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{64400FF8-2E9F-4809-B5F4-0C7EB8ABFF87}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Spring</RootNamespace>
|
||||
<AssemblyName>Spring.ConversationWA.NH32</AssemblyName>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<OldToolsVersion>3.5</OldToolsVersion>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\..\build\VS.Net.2010\Spring.ConversationWA.NH32\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Debug\Spring.ConversationWA.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Common.Logging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\Net\2.0\Common.Logging.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Iesi.Collections, Version=1.0.1.0, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\NHibernate32\net\3.5\Iesi.Collections.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NHibernate, Version=3.2.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\NHibernate32\net\3.5\NHibernate.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\CommonAssemblyInfo.cs">
|
||||
<Link>CommonAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
<Compile Include="ConversationWA\HttpModule\ConversationModule.cs" />
|
||||
<Compile Include="ConversationWA\IConversationManager.cs" />
|
||||
<Compile Include="ConversationWA\IConversationState.cs" />
|
||||
<Compile Include="ConversationWA\Imple\InnerConversationList.cs" />
|
||||
<Compile Include="ConversationWA\Imple\WebConversationManager.cs" />
|
||||
<Compile Include="ConversationWA\Imple\WebConversationSpringState.cs" />
|
||||
<Compile Include="Data\NHibernate\Support\SessionPerConversationScope.cs" />
|
||||
<Compile Include="Data\NHibernate\Support\SessionPerConversationSettings.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\..\build-support\tools\nant\schema\nant.xsd">
|
||||
<Link>nant.xsd</Link>
|
||||
</None>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Spring.Core\Spring.Core.2010.csproj">
|
||||
<Project>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</Project>
|
||||
<Name>Spring.Core.2010</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Spring.Data.NHibernate32\Spring.Data.NHibernate32.2010.csproj">
|
||||
<Project>{1C8E0481-A70D-445E-AB4D-4A963CF7DC83}</Project>
|
||||
<Name>Spring.Data.NHibernate32.2010</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Spring.Data\Spring.Data.2010.csproj">
|
||||
<Project>{AE00E5AB-C39A-436F-86D2-33BFE33E2E40}</Project>
|
||||
<Name>Spring.Data.2010</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" ?>
|
||||
<project name="Spring.ConversationWA.NH32" default="build" xmlns="http://nant.sf.net/release/0.91-alpha2/nant.xsd">
|
||||
<!--
|
||||
Required properties:
|
||||
* current.bin.dir - (path) root level to build to
|
||||
* current.build.debug - (true|false) debug build?
|
||||
* current.build.defines.csc - framework-specific build defines for C# compiler
|
||||
-->
|
||||
<target name="build">
|
||||
|
||||
<!-- copy nh libs -->
|
||||
<echo message="NH Libs: ${nh32.lib.dir}" />
|
||||
<copy todir="${current.bin.dir}" overwrite="true">
|
||||
<fileset basedir="${nh32.lib.dir}">
|
||||
<include name="**/*.dll" />
|
||||
</fileset>
|
||||
</copy>
|
||||
|
||||
<!-- build Spring.Data.NHibernate -->
|
||||
<csc target="library" define="${current.build.defines.csc}"
|
||||
warnaserror="true"
|
||||
optimize="${build.optimize}"
|
||||
debug="${current.build.debug}"
|
||||
output="${current.bin.dir}/${project::get-name()}.dll"
|
||||
doc="${current.bin.dir}/${project::get-name()}.xml">
|
||||
<arg line="${compiler.args}"/>
|
||||
<nowarn>
|
||||
<warning number="${nowarn.numbers}" />
|
||||
</nowarn>
|
||||
<sources failonempty="true">
|
||||
<include name="**/*.cs" />
|
||||
<include name="../GenCommonAssemblyInfo.cs" />
|
||||
</sources>
|
||||
<references basedir="${current.bin.dir}">
|
||||
<include name="System.Data.dll" />
|
||||
<include name="System.Web.dll" />
|
||||
<include name="System.EnterpriseServices.dll" />
|
||||
<include name="*.dll" />
|
||||
<exclude name="${project::get-name()}.dll" />
|
||||
<exclude name="Spring.ConversationWA.NH*.dll" />
|
||||
<exclude name="Spring.Data.NHibernate33.dll" />
|
||||
</references>
|
||||
</csc>
|
||||
</target>
|
||||
</project>
|
||||
3
src/Spring/Spring.ConversationWA.NH32/app.config
Normal file
3
src/Spring/Spring.ConversationWA.NH32/app.config
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<startup/></configuration>
|
||||
5
src/Spring/Spring.ConversationWA.NH33/AssemblyInfo.cs
Normal file
5
src/Spring/Spring.ConversationWA.NH33/AssemblyInfo.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyTitle("Spring.ConversationWA.NH33. NHibernate 3.3 support.")]
|
||||
[assembly: AssemblyDescription("Interfaces and classes that provide 'Conversation Workaround' support in Spring.Net")]
|
||||
@@ -0,0 +1 @@
|
||||
sources are linked here from Spring.ConversationWA.NH32
|
||||
@@ -0,0 +1 @@
|
||||
sources are linked here from Spring.ConversationWA.NH32
|
||||
@@ -0,0 +1 @@
|
||||
sources are linked here from Spring.ConversationWA.NH32
|
||||
@@ -0,0 +1 @@
|
||||
sources are linked here from Spring.ConversationWA.NH32
|
||||
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{CF375928-B6D5-485C-B04D-2BC41D9DBF1E}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Spring</RootNamespace>
|
||||
<AssemblyName>Spring.ConversationWA.NH33</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkSubset>
|
||||
</TargetFrameworkSubset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\..\build\VS.Net.2008\Spring.ConversationWA.NH33\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Debug\Spring.ConversationWA.NH33.XML</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Common.Logging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\Net\2.0\Common.Logging.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Iesi.Collections, Version=1.0.1.0, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\NHibernate33\net\3.5\Iesi.Collections.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NHibernate, Version=3.3.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\NHibernate33\net\3.5\NHibernate.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\CommonAssemblyInfo.cs">
|
||||
<Link>CommonAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\ConversationWA\HttpModule\ConversationModule.cs">
|
||||
<Link>Conversation\HttpModule\ConversationModule.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\ConversationWA\IConversationManager.cs">
|
||||
<Link>Conversation\IConversationManager.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\ConversationWA\IConversationState.cs">
|
||||
<Link>Conversation\IConversationState.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\ConversationWA\Imple\InnerConversationList.cs">
|
||||
<Link>Conversation\Imple\InnerConversationList.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\ConversationWA\Imple\WebConversationManager.cs">
|
||||
<Link>Conversation\Imple\WebConversationManager.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\ConversationWA\Imple\WebConversationSpringState.cs">
|
||||
<Link>Conversation\Imple\WebConversationSpringState.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\Data\NHibernate\Support\SessionPerConversationScope.cs">
|
||||
<Link>Data\NHibernate\Support\SessionPerConversationScope.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\Data\NHibernate\Support\SessionPerConversationSettings.cs">
|
||||
<Link>Data\NHibernate\Support\SessionPerConversationSettings.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Spring.Core\Spring.Core.2008.csproj">
|
||||
<Project>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</Project>
|
||||
<Name>Spring.Core.2008</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Spring.Data.NHibernate33\Spring.Data.NHibernate33.2008.csproj">
|
||||
<Project>{67EA5988-C54E-4348-BFFB-E4A61F26143C}</Project>
|
||||
<Name>Spring.Data.NHibernate33.2008</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Spring.Data\Spring.Data.2008.csproj">
|
||||
<Project>{AE00E5AB-C39A-436F-86D2-33BFE33E2E40}</Project>
|
||||
<Name>Spring.Data.2008</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{CF375928-B6D5-485C-B04D-2BC41D9DBF1E}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Spring</RootNamespace>
|
||||
<AssemblyName>Spring.ConversationWA.NH33</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<OldToolsVersion>3.5</OldToolsVersion>
|
||||
<UpgradeBackupLocation />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\..\build\VS.Net.2010\Spring.ConversationWA.NH33\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Debug\Spring.ConversationWA.NH33.XML</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Common.Logging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\Net\2.0\Common.Logging.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Iesi.Collections, Version=1.0.1.0, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\NHibernate33\net\3.5\Iesi.Collections.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NHibernate, Version=3.3.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\NHibernate33\net\3.5\NHibernate.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\CommonAssemblyInfo.cs">
|
||||
<Link>CommonAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\ConversationWA\HttpModule\ConversationModule.cs">
|
||||
<Link>Conversation\HttpModule\ConversationModule.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\ConversationWA\IConversationManager.cs">
|
||||
<Link>Conversation\IConversationManager.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\ConversationWA\IConversationState.cs">
|
||||
<Link>Conversation\IConversationState.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\ConversationWA\Imple\InnerConversationList.cs">
|
||||
<Link>Conversation\Imple\InnerConversationList.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\ConversationWA\Imple\WebConversationManager.cs">
|
||||
<Link>Conversation\Imple\WebConversationManager.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\ConversationWA\Imple\WebConversationSpringState.cs">
|
||||
<Link>Conversation\Imple\WebConversationSpringState.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\Data\NHibernate\Support\SessionPerConversationScope.cs">
|
||||
<Link>Data\NHibernate\Support\SessionPerConversationScope.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Spring.ConversationWA.NH32\Data\NHibernate\Support\SessionPerConversationSettings.cs">
|
||||
<Link>Data\NHibernate\Support\SessionPerConversationSettings.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Spring.Core\Spring.Core.2010.csproj">
|
||||
<Project>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</Project>
|
||||
<Name>Spring.Core.2010</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Spring.Data.NHibernate33\Spring.Data.NHibernate33.2010.csproj">
|
||||
<Project>{67EA5988-C54E-4348-BFFB-E4A61F26143C}</Project>
|
||||
<Name>Spring.Data.NHibernate33.2010</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Spring.Data\Spring.Data.2010.csproj">
|
||||
<Project>{AE00E5AB-C39A-436F-86D2-33BFE33E2E40}</Project>
|
||||
<Name>Spring.Data.2010</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" ?>
|
||||
<project name="Spring.ConversationWA.NH33" default="build" xmlns="http://nant.sf.net/release/0.91-alpha2/nant.xsd">
|
||||
<!--
|
||||
Required properties:
|
||||
* current.bin.dir - (path) root level to build to
|
||||
* current.build.debug - (true|false) debug build?
|
||||
* current.build.defines.csc - framework-specific build defines for C# compiler
|
||||
-->
|
||||
<target name="build">
|
||||
|
||||
<!-- copy nh libs -->
|
||||
<echo message="NH Libs: ${nh33.lib.dir}" />
|
||||
<copy todir="${current.bin.dir}" overwrite="true">
|
||||
<fileset basedir="${nh33.lib.dir}">
|
||||
<include name="**/*.dll" />
|
||||
</fileset>
|
||||
</copy>
|
||||
|
||||
<!-- build Spring.Data.NHibernate -->
|
||||
<csc target="library" define="${current.build.defines.csc}"
|
||||
warnaserror="true"
|
||||
optimize="${build.optimize}"
|
||||
debug="${current.build.debug}"
|
||||
output="${current.bin.dir}/${project::get-name()}.dll"
|
||||
doc="${current.bin.dir}/${project::get-name()}.xml">
|
||||
<arg line="${compiler.args}"/>
|
||||
<nowarn>
|
||||
<warning number="${nowarn.numbers}" />
|
||||
</nowarn>
|
||||
<sources failonempty="true">
|
||||
<include name="**/*.cs" />
|
||||
<include name="../Spring.ConversationWA.NH32/**/*.cs" />
|
||||
<include name="../GenCommonAssemblyInfo.cs" />
|
||||
<exclude name="../Spring.ConversationWA.NH32/AssemblyInfo.cs"/>
|
||||
</sources>
|
||||
<references basedir="${current.bin.dir}">
|
||||
<include name="System.Data.dll" />
|
||||
<include name="System.Web.dll" />
|
||||
<include name="System.EnterpriseServices.dll" />
|
||||
<include name="*.dll" />
|
||||
<exclude name="${project::get-name()}.dll" />
|
||||
<exclude name="Spring.ConversationWA.NH*.dll" />
|
||||
<exclude name="Spring.Data.NHibernate32.dll" />
|
||||
</references>
|
||||
</csc>
|
||||
</target>
|
||||
</project>
|
||||
3
src/Spring/Spring.ConversationWA.NH33/app.config
Normal file
3
src/Spring/Spring.ConversationWA.NH33/app.config
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<startup/></configuration>
|
||||
Reference in New Issue
Block a user