Conversation Workaround (Core Implementation)

This commit is contained in:
Hailton de Castro
2012-06-08 22:52:18 -03:00
parent fe115c2c33
commit 83856555c9
22 changed files with 2729 additions and 0 deletions

View 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")]

View File

@@ -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
}
}

View File

@@ -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; }
}
}

View File

@@ -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();
}
}

View File

@@ -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
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0"?>
<configuration>
<startup/></configuration>

View 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")]

View File

@@ -0,0 +1 @@
sources are linked here from Spring.ConversationWA.NH32

View File

@@ -0,0 +1 @@
sources are linked here from Spring.ConversationWA.NH32

View File

@@ -0,0 +1 @@
sources are linked here from Spring.ConversationWA.NH32

View File

@@ -0,0 +1 @@
sources are linked here from Spring.ConversationWA.NH32

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0"?>
<configuration>
<startup/></configuration>