update product version numbers and dates in readme.txt etc

update docs
update to ActiveMQ NMS svn revision 704303
Add missing code xml docs.
This commit is contained in:
markpollack
2008-10-14 05:26:44 +00:00
parent 54e330a21c
commit 22724a7122
21 changed files with 1865 additions and 1391 deletions

View File

@@ -14,10 +14,10 @@
<!-- Include spring helpers -->
<include buildfile="${spring.basedir}/Spring.include"/>
<property name="package.version" value="1.1.2" unless="${property::exists('project.version')}"/>
<property name="package.version" value="1.2.0" unless="${property::exists('project.version')}"/>
<property name="using-clover" value="false"/>
<!-- change this to sync the sdk doc version generated -->
<property name="package.version.docs" value="1.1.0" unless="${property::exists('project.version')}"/>
<property name="package.version.docs" value="1.2.0" unless="${property::exists('project.version')}"/>
<!-- make system environment variables available as NAnt properties -->
<sysinfo/>

View File

@@ -2,6 +2,61 @@ SPRING.NET FRAMEWORK CHANGELOG
==============================
http://www.springframework.net
Release Version 1.2.0 RC1, October 15, 2008
Bug
[SPRNET-899] - HttpContext.CurrenHandler returns Spring's internal PageHandler instead of real Page
[SPRNET-923] - Spring.Services generated proxies should not use explicitly ContextRegistry.GetContext or WebApplicationContext.Current direclty
[SPRNET-967] - SaoFactoryObject should cast TransparentProxies to the interface it implements before to return
[SPRNET-1008] - Add check for [Serializable] in SimpleMessageConverter
[SPRNET-1017] - Apache.NMS.ActiveMQ.ConnectionClosedException when using CachingConnectionFactory
[SPRNET-1020] - Consumer caching was not taking place for queues.
[SPRNET-1023] - Problems performing DI with Telerik RAD controls
[SPRNET-1027] - Spring Page Handler Factory does not respect EnableSessionState="ReadOnly", page setting cached until app pool reload
[SPRNET-1028] - DefaultListableObjectFactory.IsAutowireCandidate(string , DependencyDescriptor) does not search parent container
[SPRNET-1032] - <name-values> elements to not append values for same key value
[SPRNET-1047] - ObjectPostProcessors order isn't respected when mixing <object order="x"> and ObjectFactory.AddObjectPostProcessor() registrations
Improvement
[SPRNET-547] - Add Unsubscribe functionality to IEventRegistry
[SPRNET-567] - Allow parameters to be passed to Spring.Web.UI.Page.SetResult()
[SPRNET-784] - WebServiceProxyFactory support for SoapHeaderAttribute with Direction=SoapHeaderDirection.In
[SPRNET-795] - Change TransactionStatus Rollback setter property to Rollback() method so the contract of setting it to true is more explicit
[SPRNET-840] - Reduce PageHandlerFactory complexity
[SPRNET-958] - Result mapping doesn't support expressions for TargetPage
[SPRNET-960] - Allow for selection of logging level when using exception handling advice for logging
[SPRNET-995] - IsValid state of AbstractBinding should not be stored in LogicalThreadContext
[SPRNET-1016] - Apply local transaction to DataSet operations in AdoTemplate.
[SPRNET-1019] - Messaging namespace parser should support use of PropertyPlaceholderConfigurer
[SPRNET-1021] - Avoid antlr exception thrown for internal flow control when selecting handler method in MessageListenerAdapter
[SPRNET-1024] - Add AutoStartup as an attribute of the <listener-container> element
[SPRNET-1025] - Make GetObject virtual in MethodInvokingJobDetailFactoryObject and provide protected access to JobDetail object.
[SPRNET-1038] - Eliminate storing binding state to TLS in Spring.DataBinding.AbstractBinding
[SPRNET-1056] - Provide easier configuration of exception handlers in ExceptionTranslationAdvice.
New Feature
[SPRNET-711] - Add generic DI support for IHttpHandlers
[SPRNET-713] - Add support for custom .ashx IHttpHandler implementations
[SPRNET-815] - Add Sql Server Compact Edition 3.5 database provider
[SPRNET-883] - Add support for translation of exceptions using PersistenceExceptionTranslationInterceptor.
[SPRNET-936] - Add message converter to/from XML (NMS)
[SPRNET-1039] - Add support for Sybase ADO.NET 2.0 provider
[SPRNET-1040] - Support encrypting url parameters when using Page.SetResult()
[SPRNET-1046] - Move hardcoded, Spring.Web specific ISharedStateAware handling to generic SharedStateAwareObjectPostProcessor
[SPRNET-1055] - Provide ability to easily register to MessageQueueFactoryObject definitions with the container at runtime
[SPRNET-1057] - Add new 'execute' action for ExceptionTranslationAdvice
[SPRNET-1058] - Provide WCF Service export akin to those for Remoting and .asmx WebServices
Refactoring
[SPRNET-1052] - Extract "SetResult" related code into a reusable ResultNavigator component
Task
[SPRNET-1045] - Colorize code listings (XML/C#) in reference documentation
[SPRNET-1048] - Drop IProcess support from Spring.Web
[SPRNET-1050] - Provide sample configuration for using MultiDelegatingDbProvider
Release 1.2.0 M1, August 15, 2008
@@ -40,6 +95,7 @@ Improvement
[SPRNET-999] - Add DefaultDbProvider property to MultiDelegatingDbProvider.
New Feature
[SPRNET-753] - Add ILifecycle interface
[SPRNET-896] - Allow registering custom ICollectionProcessors in SpEL MethodNode
[SPRNET-922] - NHibernate 2.0 RC1 Support
[SPRNET-953] - Allow for circular dependencies of web-scoped singletons

View File

@@ -16,7 +16,13 @@
* limitations under the License.
*/
-->
<chapter xml:id="aop-aspect-library" xmlns="http://docbook.org/ns/docbook" version="5">
<chapter version="5" xml:id="aop-aspect-library"
xmlns="http://docbook.org/ns/docbook"
xmlns:ns6="http://www.w3.org/1999/xlink"
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
xmlns:ns4="http://www.w3.org/1999/xhtml"
xmlns:ns3="http://www.w3.org/2000/svg"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Aspect Library</title>
<sect1 xml:id="aop-library-introduction">
@@ -54,8 +60,8 @@
implement is <literal>Spring.Caching.ICache</literal> located in
<literal>Spring.Core</literal>. Two implementations are provided,
<literal>Spring.Caching.AspNetCache</literal> located in
<literal>Spring.Web</literal> which stores cache entries within an
ASP.NET cache and a simple implementation,
<literal>Spring.Web</literal> which stores cache entries within an ASP.NET
cache and a simple implementation,
<literal>Spring.Caching.NonExpiringCache</literal> that stores cache
entries in memory and never expires these entries. Custom implementations
based on 3rd party implementations, such as Oracle Coherence, or
@@ -81,8 +87,8 @@
</listitem>
<listitem>
<para><literal>CacheResultItems</literal> - used when returning a collection as
a return value</para>
<para><literal>CacheResultItems</literal> - used when returning a
collection as a return value</para>
</listitem>
<listitem>
@@ -97,9 +103,8 @@
</itemizedlist>
<para>Each <literal>CacheResult</literal>,
<literal>CacheResultItems</literal>, and
<literal>CacheParameter</literal> attributes define the following
properties.</para>
<literal>CacheResultItems</literal>, and <literal>CacheParameter</literal>
attributes define the following properties.</para>
<itemizedlist>
<listitem>
@@ -125,14 +130,14 @@
</listitem>
</itemizedlist>
<para>The <literal>InvalidateCache</literal> attribute has properties
for the CacheName, the Key as well as the Condition, with the same
meanings as listed previously.</para>
<para>The <literal>InvalidateCache</literal> attribute has properties for
the CacheName, the Key as well as the Condition, with the same meanings as
listed previously.</para>
<para>Each <literal>ICache</literal> implementation will have
properties that are specific to a caching technology. In the case of
<literal>AspNetCache</literal>, the two important properties to
configure are:</para>
<para>Each <literal>ICache</literal> implementation will have properties
that are specific to a caching technology. In the case of
<literal>AspNetCache</literal>, the two important properties to configure
are:</para>
<itemizedlist>
<listitem>
@@ -202,32 +207,32 @@ public <classname>Airport</classname> GetAirport(long id)
<para>The configuration to enable the caching aspect is shown below</para>
<programlisting language="myxml"><![CDATA[<object" id="CacheAspect" type="Spring.Aspects.Cache.CacheAspect, Spring.Aop"/>
<programlisting language="myxml">&lt;object" id="CacheAspect" type="Spring.Aspects.Cache.CacheAspect, Spring.Aop"/&gt;
<object id="AspNetCache" type="Spring.Caching.AspNetCache, Spring.Web">
<property name="SlidingExpiration" value="true"/>
<property name="Priority" value="Low"/>
<property name="TimeToLive" value="00:02:00"/>
</object>
&lt;object id="AspNetCache" type="Spring.Caching.AspNetCache, Spring.Web"&gt;
&lt;property name="SlidingExpiration" value="true"/&gt;
&lt;property name="Priority" value="Low"/&gt;
&lt;property name="TimeToLive" value="00:02:00"/&gt;
&lt;/object&gt;
<!-- Apply aspects to DAOs -->
<object type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
<property name="ObjectNames">
<list>
<value>*Dao</value>
</list>
</property>
<property name="InterceptorNames">
<list>
<value>CacheAspect</value>
</list>
</property>
</object>]]></programlisting>
&lt;!-- Apply aspects to DAOs --&gt;
&lt;object type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop"&gt;
&lt;property name="ObjectNames"&gt;
&lt;list&gt;
&lt;value&gt;*Dao&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="InterceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;CacheAspect&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>in this example an <literal>ObjectNameAutoProxyCreator</literal>
was used to apply the cache aspect to objects that have Dao in their name.
The AspNetCache setting for TimeToLive will override the TimeToLive value
set at the method level via the attribute.</para>
<para>in this example an <literal>ObjectNameAutoProxyCreator</literal> was
used to apply the cache aspect to objects that have Dao in their name. The
AspNetCache setting for TimeToLive will override the TimeToLive value set
at the method level via the attribute.</para>
</sect1>
<sect1 xml:id="exception-aspect">
@@ -251,6 +256,11 @@ public <classname>Airport</classname> GetAirport(long id)
<listitem>
<para>swallow - the exception is ignored.</para>
</listitem>
<listitem>
<para>execute - Execute an abritrary Spring Expression Language (SpEL
expression)</para>
</listitem>
</itemizedlist>
<para>The applicability of general exception handling advice depends
@@ -268,13 +278,13 @@ public <classname>Airport</classname> GetAirport(long id)
could be referred to as a Domain Specific Language (DSL). Here is a simple
example, which should hopefully be self explanatory.</para>
<para><programlisting language="myxml"><![CDATA[<object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop">
<property name="exceptionHandlers">
<list>
<value>on exception name ArithmeticException wrap System.InvalidOperationException</value>
</list>
</property>
</object>]]></programlisting>What this is instructing the advice to do is
<para><programlisting language="myxml">&lt;object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop"&gt;
&lt;property name="exceptionHandlers"&gt;
&lt;list&gt;
&lt;value&gt;on exception name ArithmeticException wrap System.InvalidOperationException&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>What this is instructing the advice to do is
the following bit of code when an ArithmeticException is thrown, throw new
System.InvalidOperationException("Wrapped ArithmeticException", e), where
e is the original ArithmeticException. The default message, "Wrapped
@@ -356,19 +366,19 @@ on exception name ArithmeticException return 12</programlisting>
action, i.e. log(Debug,"LoggerName").</para>
<para>Multiple exception handling statements can be specified within the
list shown above. The processing flow is on exception, the name of
the exception listed in the statement is compared to the thrown exception
to see if there is a match. A comma separated list of exceptions can be
used to group together the same action taken for different exception
names. If the action to take is logging, then the logging action is
performed and the search for other matching exception names continues. For
all other actions, namely translate, wrap, replace, swallow, return, once
an exception handler is matched, those in the chain are no longer
evaluated. Note, do not confuse this handler chain with the general advice
AOP advice chain. For translate, wrap, and replace actions a SpEL
expression is created and used to instantiate a new exception (in addition
to any other processing that may occur when evaluating the expression)
which is then thrown.</para>
list shown above. The processing flow is on exception, the name of the
exception listed in the statement is compared to the thrown exception to
see if there is a match. A comma separated list of exceptions can be used
to group together the same action taken for different exception names. If
the action to take is logging, then the logging action is performed and
the search for other matching exception names continues. For all other
actions, namely translate, wrap, replace, swallow, return, once an
exception handler is matched, those in the chain are no longer evaluated.
Note, do not confuse this handler chain with the general advice AOP advice
chain. For translate, wrap, and replace actions a SpEL expression is
created and used to instantiate a new exception (in addition to any other
processing that may occur when evaluating the expression) which is then
thrown.</para>
<para>The exception handling DSL also supports the ability to provide a
SpEL boolean expression to determine if the advice will apply instead of
@@ -393,23 +403,55 @@ on exception name ArithmeticException return 12</programlisting>
likely be better off creating your own custom aspect class instead of a
scripting approach.</para>
<para>You can also configure ExceptionHandlerAdvice to use an instance of
IExceptionHandler, for example to access more sophisticated logging
functionality than is available with the current log action in the DSL,
for example setting the logging level and pass the exception into the
logging subsystem</para>
<para>You can also configure the each of the Handlers individually based
on the action keyword. For example, to configure the logging properties on
the LogExceptionHandler.</para>
<programlisting language="myxml"><![CDATA[<object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop">
<property name="exceptionHandlers">
<list>
<object type="Spring.Aspects.Exceptions.LogExceptionHandler">
<property name="LogName" value="Cms.Session.ExceptionHandler" />
<property name="ConstraintExpressionText" value="#e is T(System.Threading.ThreadAbortException)" />
<property name="ActionExpressionText" value="#log.Fatal('Request Timeout occured', #e)" />
</object>
</list>
</property>
</object>]]></programlisting>
<programlisting language="csharp">&lt;object name="logExceptionHandler" type="Spring.Aspects.Exceptions.LogExceptionHandler, Spring.Aop"&gt;
&lt;property name="LogName" value="Cms.Session.ExceptionHandler" /&gt;
&lt;property name="LogLevel" value="Debug"/&gt;
&lt;property name="LogMessageOnly" value="true"/&gt;
&lt;/object&gt;
&lt;object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop"&gt;
&lt;property name="ExceptionHandlerDictionary"&gt;
&lt;dictionary&gt;
&lt;entry key="log" ref="logExceptionHandler"/&gt;
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;property name="ExceptionHandlers"&gt;
&lt;list&gt;
&lt;value&gt;on exception name ArithmeticException,ArgumentException log 'My Message, Method Name ' + #method.Name&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
</programlisting>
<para>You can also configure <literal>ExceptionHandlerAdvice</literal> to
use an instance of <literal>IExceptionHandler</literal> by specifing it as
an entry in the ExceptionHandlers list. This gives you complete control
over all properties of the handler but you must set
ConstraintExpressionText and ActionExpressionText which are normally
parsed for you from the string. To use the case of configuring the
LogExceptionHandler, this approach also lets you specify advanced logging
functionality, but at a cost of some additional complexity. For example
setting the logging level and pass the exception into the logging
subsystem</para>
<programlisting language="myxml">&lt;object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop"&gt;
&lt;property name="exceptionHandlers"&gt;
&lt;list&gt;
&lt;object type="Spring.Aspects.Exceptions.LogExceptionHandler"&gt;
&lt;property name="LogName" value="Cms.Session.ExceptionHandler" /&gt;
&lt;property name="ConstraintExpressionText" value="#e is T(System.Threading.ThreadAbortException)" /&gt;
&lt;property name="ActionExpressionText" value="#log.Fatal('Request Timeout occured', #e)" /&gt;
&lt;/object&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para></para>
<para>The configuration of the logger name, level, and weather or not to
pass the thrown exception as the second argument to the log method will be
@@ -455,6 +497,10 @@ on exception name ArithmeticException return 12</programlisting>
<listitem>
<para>swallow</para>
</listitem>
<listitem>
<para>execute</para>
</listitem>
</itemizedlist>
<para>The form of the expression depends on the action. For logging, the
@@ -476,9 +522,10 @@ on exception name ArithmeticException return 12</programlisting>
<para>The logging advice lets you log the information on method entry,
exit and thrown exception (if any). The implementation is based on the
logging library, <ulink url="http://netcommon.sourceforge.net/">Common.Logging</ulink>, that provides
portability across different logging libraries. There are a number of
configuration options available, listed below</para>
logging library, <ulink
url="http://netcommon.sourceforge.net/">Common.Logging</ulink>, that
provides portability across different logging libraries. There are a
number of configuration options available, listed below</para>
<itemizedlist>
<listitem>
@@ -510,19 +557,19 @@ on exception name ArithmeticException return 12</programlisting>
XML fragment. Alternatively, you can use the class
<literal>SimpleLoggingAdvice</literal> programatically.</para>
<programlisting language="myxml"><![CDATA[<object name="loggingAdvice" type="Spring.Aspects.Logging.SimpleLoggingAdvice, Spring.Aop">
<property name="logUniqueIdentifier" value="true"/>
<property name="logExecutionTime" value="true"/>
<property name="logMethodArguments" value="true"/>
<property name="LogReturnValue" value="true"/>
<programlisting language="myxml">&lt;object name="loggingAdvice" type="Spring.Aspects.Logging.SimpleLoggingAdvice, Spring.Aop"&gt;
&lt;property name="logUniqueIdentifier" value="true"/&gt;
&lt;property name="logExecutionTime" value="true"/&gt;
&lt;property name="logMethodArguments" value="true"/&gt;
&lt;property name="LogReturnValue" value="true"/&gt;
<property name="Separator" value=";"/>
<property name="LogLevel" value="Info"/>
&lt;property name="Separator" value=";"/&gt;
&lt;property name="LogLevel" value="Info"/&gt;
<property name="HideProxyTypeNames" value="true"/>
<property name="UseDynamicLogger" value="true"/>
</object>]]></programlisting>
&lt;property name="HideProxyTypeNames" value="true"/&gt;
&lt;property name="UseDynamicLogger" value="true"/&gt;
&lt;/object&gt;</programlisting>
<para>The default values for LogUniqueIdentifier, LogExecutionTime,
LogMethodArguments and LogReturnValue are false. The default separator
@@ -568,8 +615,8 @@ on exception name ArithmeticException return 12</programlisting>
use a GUID. You can alter this behavior by overriding the method
<literal>string CreateUniqueIdentifier()</literal>. The
<literal>SimpleLoggingAdvice</literal> class inherits from
<literal>AbstractLoggingAdvice</literal>, which has the abstract
method <literal>object InvokeUnderLog(IMethodInvocation invocation, ILog
<literal>AbstractLoggingAdvice</literal>, which has the abstract method
<literal>object InvokeUnderLog(IMethodInvocation invocation, ILog
log)</literal> and you can also override the method <literal>ILog
GetLoggerForInvocation(IMethodInvocation invocation)</literal> to
customize the logger instance used for logging. Refer to the SDK
@@ -675,9 +722,9 @@ on exception (#e is T(System.ArithmeticException)) retry 3x rate (1*#n + 0.5)</p
following XML fragment. Alternatively, you can use the
<literal>RetryAdvice</literal> class programatically.</para>
<programlisting language="myxml"><![CDATA[<object name="exceptionHandlingAdvice" type="Spring.Aspects.RetryAdvice, Spring.Aop">
<property name="retryExpression" value="on exception name ArithmeticException retry 3x delay 1s"/>
</object>]]></programlisting>
<programlisting language="myxml">&lt;object name="exceptionHandlingAdvice" type="Spring.Aspects.RetryAdvice, Spring.Aop"&gt;
&lt;property name="retryExpression" value="on exception name ArithmeticException retry 3x delay 1s"/&gt;
&lt;/object&gt;</programlisting>
<sect2>
<title>Language Reference</title>
@@ -721,13 +768,13 @@ on exception (#e is T(System.ArithmeticException)) retry 3x rate (1*#n + 0.5)</p
<para>To address some of the common needs for validation on the server
side, Spring provides parameter validation advice so that applies Spring's
validation rules to the method parameters. The class
<literal>ParameterValidationAdvice</literal> is used in conjunction
with the <literal>Validated</literal> attribute to specify which
validation rules are applied to method parameters. For example, to apply
parameter validation to the method SuggestFlights in the BookingAgent
class used in the <link linkend="springair">SpringAir sample
application</link>, you would apply the <literal>Validated</literal>
attribute to the method parameters as shown below.</para>
<literal>ParameterValidationAdvice</literal> is used in conjunction with
the <literal>Validated</literal> attribute to specify which validation
rules are applied to method parameters. For example, to apply parameter
validation to the method SuggestFlights in the BookingAgent class used in
the <link linkend="springair">SpringAir sample application</link>, you
would apply the <literal>Validated</literal> attribute to the method
parameters as shown below.</para>
<programlisting language="csharp">public <classname>FlightSuggestions</classname> SuggestFlights( [<classname>Validated</classname>("tripValidator")] <classname>Trip</classname> trip)
{
@@ -736,30 +783,29 @@ on exception (#e is T(System.ArithmeticException)) retry 3x rate (1*#n + 0.5)</p
<para>The <literal>Validated</literal> attribute takes a string name that
specifies the name of the validation rule, i.e. the name of the IValidator
object in the Spring application context. The
<literal>Validated</literal> attribute is located in the namespace
<literal>Spring.Validation</literal> of the <literal>Spring.Core</literal>
assembly.</para>
object in the Spring application context. The <literal>Validated</literal>
attribute is located in the namespace <literal>Spring.Validation</literal>
of the <literal>Spring.Core</literal> assembly.</para>
<para>The configuration of the advice is to simply define the an instance
of the <literal>ParameterValidationAdvice</literal> class and apply the
advice, for example based on object names using an
<literal>ObjectNameAutoProxyCreator</literal>, as shown below,</para>
<programlisting language="myxml"><![CDATA[<object id="validationAdvice" type="Spring.Aspects.Validation.ParameterValidationAdvice, Spring.Aop"/>
<programlisting language="myxml">&lt;object id="validationAdvice" type="Spring.Aspects.Validation.ParameterValidationAdvice, Spring.Aop"/&gt;
<object type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
<property name="ObjectNames">
<list>
<value>bookingAgent</value>
</list>
</property>
<property name="InterceptorNames">
<list>
<value>validationAdvice</value>
</list>
</property>
</object>]]></programlisting>
&lt;object type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop"&gt;
&lt;property name="ObjectNames"&gt;
&lt;list&gt;
&lt;value&gt;bookingAgent&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="InterceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;validationAdvice&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>When the advised method is invoked first the validation of each
method parameter is performed. If all validation succeeds, then the method
@@ -768,4 +814,4 @@ on exception (#e is T(System.ArithmeticException)) retry 3x rate (1*#n + 0.5)</p
errors information from its property <literal>ValidationErrors</literal>.
See the SDK documentation for details.</para>
</sect1>
</chapter>
</chapter>

View File

@@ -60,8 +60,8 @@
<info>
<title>The Spring.NET Framework</title>
<subtitle>Reference Documentation</subtitle>
<releaseinfo>Version 1.2.0 M1</releaseinfo>
<pubdate>Last Updated August 15,2008</pubdate>
<releaseinfo>Version 1.2.0 RC1</releaseinfo>
<pubdate>Last Updated October 15,2008</pubdate>
<authorgroup>
<author>
<firstname>Mark</firstname>

View File

@@ -16,8 +16,13 @@
* limitations under the License.
*/
-->
<chapter xml:id="messaging" xmlns="http://docbook.org/ns/docbook" version="5">
<title>Message Oriented Middleware</title>
<chapter version="5" xml:id="messaging" xmlns="http://docbook.org/ns/docbook"
xmlns:ns6="http://www.w3.org/1999/xlink"
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
xmlns:ns4="http://www.w3.org/1999/xhtml"
xmlns:ns3="http://www.w3.org/2000/svg"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Message Oriented Middleware - Apache ActiveMQ</title>
<section>
<title>Introduction</title>
@@ -50,25 +55,27 @@
<orderedlist>
<listitem>
<para>Apache NMS in namespace <literal>Spring.Messaging.Nms</literal>.
'Nms' is sometimes used as the class prefix</para>
<para>Apache ActiveMQ (NMS) in namespace
<literal>Spring.Messaging.Nms</literal>. 'Nms' is sometimes used as
the class prefix</para>
</listitem>
<listitem>
<para>TIBCO EMS in namespace <literal>Spring.Messaging.Ems</literal>.
'Ems' is sometimes used as the class prefix</para>
'Ems' is sometimes used as the class prefix (to becommerically
available )</para>
</listitem>
<listitem>
<para>SonicMQ in namespace <literal>Spring.Messaging.Sonic</literal>,
'Jms' is sometimes used as the class prefix. (available 1.2
RC1)</para>
'Jms' is sometimes used as the class prefix. (to be commercially
available)</para>
</listitem>
<listitem>
<para>Websphere MQ in namespace
<literal>Spring.Messaging.Xms</literal>, 'Xms' is sometimes used as
the class prefix (available 1.2 RC1)</para>
the class prefix (to be commerically available)</para>
</listitem>
</orderedlist>
@@ -78,11 +85,11 @@
<literal>NmsTemplate</literal>, <literal> EmsTemplate</literal> (etc.) is
used. Asynchronous message consumption is performed though a
multi-threaded message listener container,
<literal>SimpleMessageListenerContainer</literal>. This message
listener container is used to create Message-Driven PONOs (MDPs) which
refer to a messaging callback class that consists of just 'plain .NET
object's and is devoid of any specific messaging types or other artifacts.
The <literal>IMessageConverter</literal> interface is used by both the
<literal>SimpleMessageListenerContainer</literal>. This message listener
container is used to create Message-Driven PONOs (MDPs) which refer to a
messaging callback class that consists of just 'plain .NET object's and is
devoid of any specific messaging types or other artifacts. The
<literal>IMessageConverter</literal> interface is used by both the
template class and the message listener container to convert between
provider message types and PONOs.</para>
@@ -211,7 +218,12 @@
even with the option of using WCF, many people prefer to sit 'closer to
the metal' when using messaging middleware, to access specific features
and functionality not available in WCF, or simply because they are more
comfortable with that programming model.</para>
comfortable with that programming model. </para>
<para>A WCF binding for Apache NMS is being developed as a separate
project under the <link
ns6:href="http://www.springframework.org/extensions/faq">Spring
Extensions</link> umbrella project. Stay tuned for details.</para>
</section>
</section>
@@ -222,16 +234,16 @@
<title>Messaging Template overview</title>
<para>Code that uses the messaging template classes
(<literal>NmsTemplate</literal>, <literal>EmsTemplate</literal>,
etc) only needs to implement callback interfaces giving them a clearly
(<literal>NmsTemplate</literal>, <literal>EmsTemplate</literal>, etc)
only needs to implement callback interfaces giving them a clearly
defined contract. The <literal>IMessageCreator</literal> callback
interface creates a message given a Session provided by the calling code
in <literal>NmsTemplate</literal>. In order to allow for more complex
usage of the provider messaging API, the callback
<literal>ISessionCallback</literal> provides the user with the
provider specific messaging Session and the callback
<literal>IProducerCallback</literal> exposes a provider specific
Session and MessageProducer pair.</para>
<literal>ISessionCallback</literal> provides the user with the provider
specific messaging Session and the callback
<literal>IProducerCallback</literal> exposes a provider specific Session
and MessageProducer pair.</para>
<para>Provider messaging APIs typically expose two types of send
methods, one that takes delivery mode, priority, and time-to-live as
@@ -250,8 +262,8 @@
<literal>NmsTemplate</literal> and then safely inject this shared
reference into multiple collaborators. To be clear, the
<literal>NmsTemplate</literal> is stateful, in that it maintains a
reference to a <literal>ConnectionFactory</literal>, but this
state is not conversational state.</para>
reference to a <literal>ConnectionFactory</literal>, but this state is
not conversational state.</para>
</note>
</section>
@@ -321,11 +333,10 @@
session and also take into account the unique properties of the
producers and consumers when caching.</para>
<para><literal>MessageProducers</literal> are cached based on
their destination. <literal>MessageConsumers</literal> are cached
based on a key composed of the destination, selector, noLocal delivery
flag, and the durable subscription name (if creating durable
consumers).</para>
<para><literal>MessageProducers</literal> are cached based on their
destination. <literal>MessageConsumers</literal> are cached based on a
key composed of the destination, selector, noLocal delivery flag, and
the durable subscription name (if creating durable consumers).</para>
</section>
</section>
@@ -349,13 +360,12 @@
unique to the messaging provider. Examples of such advanced destination
management would be the creation of dynamic destinations or support for
a hierarchical namespace of destinations. The
<literal>NmsTemplate</literal> delegates the resolution of a
destination name to a destination object by delegating to an
implementation of the interface
<literal>IDestinationResolver</literal>.
<literal>NmsTemplate</literal> delegates the resolution of a destination
name to a destination object by delegating to an implementation of the
interface <literal>IDestinationResolver</literal>.
<literal>DynamicDestinationResolver</literal> is the default
implementation used by <literal>NmsTemplate</literal> and
accommodates resolving dynamic destinations.</para>
implementation used by <literal>NmsTemplate</literal> and accommodates
resolving dynamic destinations.</para>
<para>Quite often the destinations used in a messaging application are
only known at runtime and therefore cannot be administratively created
@@ -412,17 +422,15 @@
possibly responding to it), and delegates boilerplate messaging
infrastructure concerns to the framework.</para>
<para>A subclass of
<literal>AbstractMessageListenerContainer</literal> is used to
receive messages from JMS and drive the Message-Driven PONOs (MDPs) that
are injected into it. There are one subclasses of
<literal>AbstractMessageListenerContainer</literal> packaged with
Spring - <literal>SimpleMessageListenerContainer</literal>.
Additional subclasses, in particular to participate in distributed
transactions (if the provider supports it), will be provided in future
releases. SimpleMessageListenerContainer creates a fixed number of JMS
sessions at startup and uses them throughout the lifespan of the
container.</para>
<para>A subclass of <literal>AbstractMessageListenerContainer</literal>
is used to receive messages from JMS and drive the Message-Driven PONOs
(MDPs) that are injected into it. There are one subclasses of
<literal>AbstractMessageListenerContainer</literal> packaged with Spring
- <literal>SimpleMessageListenerContainer</literal>. Additional
subclasses, in particular to participate in distributed transactions (if
the provider supports it), will be provided in future releases.
SimpleMessageListenerContainer creates a fixed number of JMS sessions at
startup and uses them throughout the lifespan of the container.</para>
</section>
<section>
@@ -471,14 +479,14 @@
the JMS Destination object is specified directly. The second case
specifies the destination using a string that is then resolved to a
messaging <literal>Destination</literal> object using the
<literal>IDestinationResolver</literal> associated with the template.
The last method sends the message to the destination specified by
<literal>NmsTemplate</literal>''s
<literal>DefaultDestination</literal> property.</para>
<literal>IDestinationResolver</literal> associated with the template. The
last method sends the message to the destination specified by
<literal>NmsTemplate</literal>''s <literal>DefaultDestination</literal>
property.</para>
<para>All methods take as an argument an instance of
<literal>IMessageCreator</literal> which defines the API contract for
you to create the JMS message. The interface is show below</para>
<literal>IMessageCreator</literal> which defines the API contract for you
to create the JMS message. The interface is show below</para>
<para><programlisting language="csharp">public interface IMessageCreator {
IMessage CreateMessage(ISession session);
@@ -554,23 +562,21 @@
<title>Using MessageConverters</title>
<para>In order to facilitate the sending of domain model objects, the
<literal>NmsTemplate</literal> has various send methods that take a
.NET object as an argument for a message's data content. The overloaded
<literal>NmsTemplate</literal> has various send methods that take a .NET
object as an argument for a message's data content. The overloaded
methods <literal>ConvertAndSend</literal> and
<literal>ReceiveAndConvert</literal> in
<literal>NmsTemplate</literal> delegate the conversion process to an
instance of the <literal>IMessageConverter</literal>
interface. This interface defines a simple contract to convert between
.NET objects and JMS messages. The default implementation
<literal>SimpleMessageConverter</literal> supports conversion
between String and TextMessage, byte[] and BytesMesssage, and
System.Collections.IDictionary and MapMessage. By using the converter,
you and your application code can focus on the business object that is
being sent or received via messaging and not be concerned with the
details of how it is represented as a JMS message.</para>
<para>The sample applications contains a XmlMessageConverter that
converts objects to an XML string and vice-versa for sending via a
<literal>ReceiveAndConvert</literal> in <literal>NmsTemplate</literal>
delegate the conversion process to an instance of the
<literal>IMessageConverter</literal> interface. This interface defines a
simple contract to convert between .NET objects and JMS messages. The
default implementation <literal>SimpleMessageConverter</literal>
supports conversion between String and TextMessage, byte[] and
BytesMesssage, and System.Collections.IDictionary and MapMessage. By
using the converter, you and your application code can focus on the
business object that is being sent or received via messaging and not be
concerned with the details of how it is represented as a JMS message.
There is also an <literal>XmlMessageConverter</literal> that converts
objects to an XML string and vice-versa for sending via a
TextMessage.</para>
<para>The family of <literal>ConvertAndSend</literal> messages are
@@ -619,13 +625,13 @@
template.ConvertAndSend("APP.STOCK.MARKETDATA", marketData);
}</programlisting>To accommodate the setting of message's properties, headers,
and body that can not be generally encapsulated inside a converter
class, the <literal>IMessageConverterPostProcessor</literal>
interface gives you access to the message after it has been converted
but before it is sent. The example below demonstrates how to modify a
message header and a property after a Hashtable is converted to a
message using the IMessagePostProcessor. The methods
<literal>ConvertAndSendUsingDelegate</literal> allow for the use of
a delegate to perform message post processing. This family of methods is
class, the <literal>IMessageConverterPostProcessor</literal> interface
gives you access to the message after it has been converted but before
it is sent. The example below demonstrates how to modify a message
header and a property after a Hashtable is converted to a message using
the IMessagePostProcessor. The methods
<literal>ConvertAndSendUsingDelegate</literal> allow for the use of a
delegate to perform message post processing. This family of methods is
listed below</para>
<itemizedlist>
@@ -734,8 +740,8 @@ public delegate object ProducerDelegate(ISession session, IMessageProducer produ
available. This can be a dangerous operation since the calling thread
can potentially be blocked indefinitely. The property
<code><property>ReceiveTimeout</property></code> on
<literal>NmsTemplate</literal> specifies how long the receiver
should wait before giving up waiting for a message.</para>
<literal>NmsTemplate</literal> specifies how long the receiver should
wait before giving up waiting for a message.</para>
<para>The <methodname>Receive</methodname> methods are listed
below</para>
@@ -774,8 +780,8 @@ public delegate object ProducerDelegate(ISession session, IMessageProducer produ
<para>The <methodname>Receive</methodname> method without arguments will
use the <property>DefaultDestination</property>. The
<methodname>ReceiveSelected</methodname> methods apply the provided
message selector string to the <literal>MessageConsumer</literal>
that is created.</para>
message selector string to the <literal>MessageConsumer</literal> that
is created.</para>
<para>The <methodname>ReceiveAndConvert</methodname> methods apply the
template's message converter when receiving a message. The message
@@ -832,10 +838,9 @@ public delegate object ProducerDelegate(ISession session, IMessageProducer produ
<para>Other vendors may provide a delegate based version of this
callback or even both a delegate and interface options. Apache ActiveMQ
supports only the use of delegates for message reception callbacks. As a
programming convenience in
<literal>Spring.Messaging.Nms.Core</literal> is an interface
<literal>IMessageListener</literal> that can be used with
NMS.</para>
programming convenience in <literal>Spring.Messaging.Nms.Core</literal>
is an interface <literal>IMessageListener</literal> that can be used
with NMS.</para>
<para>Below is a simple implementation of the IMessageListener interface
that processing a message.</para>
@@ -879,11 +884,10 @@ namespace MyApp
specifies various messaging configuration parameters, such as the
ConnectionFactory, and the number of concurrent consumers to create.
There is an abstract base class for message listener containers,
<literal>AbstractMessageListenerContainer</literal>, and one
concrete implementation,
<literal>SimpleMessageListenerContainer</literal>.
<literal>SimpleMessageListenerContainer</literal> creates a fixed
number of JMS Sessions/MessageConsumer pairs as set by the property
<literal>AbstractMessageListenerContainer</literal>, and one concrete
implementation, <literal>SimpleMessageListenerContainer</literal>.
<literal>SimpleMessageListenerContainer</literal> creates a fixed number
of JMS Sessions/MessageConsumer pairs as set by the property
<property>ConcurrentConsumers</property>. Here is a sample
configuration</para>
@@ -933,13 +937,12 @@ namespace MyApp
to an implementation of <literal>IExceptionHandler</literal> and
registered with the container via the property
<literal>ExceptionListener</literal>. The registered
<literal>IExceptionHandler</literal> will be invoked if the
exception is of the type <literal>NMSException</literal> (or the
equivalent root exception type for other providers). The
SimpleMessageListenerContainer will logs the exception at error level
and not propagate the exception to the provider. All handling of
acknowledgement and/or transactions is done by the listener container.
You can override the method
<literal>IExceptionHandler</literal> will be invoked if the exception is
of the type <literal>NMSException</literal> (or the equivalent root
exception type for other providers). The SimpleMessageListenerContainer
will logs the exception at error level and not propagate the exception
to the provider. All handling of acknowledgement and/or transactions is
done by the listener container. You can override the method
<literal>HandleListenerException</literal> to change this
behavior.</para>
@@ -951,12 +954,11 @@ namespace MyApp
<section>
<title>The ISessionAwareMessageListener interface</title>
<para>The <literal>ISessionAwareMessageListener</literal> interface
is a Spring-specific interface that provides a similar contract to the
messaging provider's <literal>IMessageListener</literal> interface
or Listener delegate/event, but also provides the message handling
method with access to the Session from which the Message was
received.</para>
<para>The <literal>ISessionAwareMessageListener</literal> interface is a
Spring-specific interface that provides a similar contract to the
messaging provider's <literal>IMessageListener</literal> interface or
Listener delegate/event, but also provides the message handling method
with access to the Session from which the Message was received.</para>
<programlisting language="csharp">public interface ISessionAwareMessageListener
{
@@ -976,9 +978,8 @@ namespace MyApp
of course some constraints).</para>
<para>Consider the following interface definition. Notice that although
the interface extends neither the
<literal>IMessageListener</literal> nor
<literal>ISessionAwareMessageListener</literal> interfaces, it can
the interface extends neither the <literal>IMessageListener</literal>
nor <literal>ISessionAwareMessageListener</literal> interfaces, it can
still be used as a Message-Driven PONOs (MDP) via the use of the
<literal>MessageListenerAdapter</literal> class. Notice also how the
various message handling methods are strongly typed according to the
@@ -1042,8 +1043,7 @@ namespace MyApp
}</programlisting>
<para>The configuration of the attendant
<literal>MessageListenerAdapter</literal> would look like
this</para>
<literal>MessageListenerAdapter</literal> would look like this</para>
<programlisting language="myxml">&lt;object id="MessagleHandler" type="MyApp.DefaultMessageHandler, MyApp"/&gt;
@@ -1058,12 +1058,11 @@ namespace MyApp
<para>Please note that if the above 'MessageListener' receives a Message
of a type other than ITextMessage, a
<literal>ListenerExecutionFailedException</literal> will be thrown
(and subsequently handled by the container by logging the
exception).</para>
<literal>ListenerExecutionFailedException</literal> will be thrown (and
subsequently handled by the container by logging the exception).</para>
<para>If your <literal>IMessageConverter</literal> implementation
will return multiple object types, overloading the handler method is
<para>If your <literal>IMessageConverter</literal> implementation will
return multiple object types, overloading the handler method is
perfectly acceptable, the most specific matching method will be used. A
method with an object signature would be consider a 'catch-all' method
of last resort. For example, you can have an handler interface as shown
@@ -1173,72 +1172,83 @@ namespace MyApp
<row>
<entry>id</entry>
<entry><para>A object name for the hosting listener container.
If not specified, a object name will be automatically
generated.</para></entry>
<entry>
<para>A object name for the hosting listener container. If not
specified, a object name will be automatically
generated.</para>
</entry>
</row>
<row>
<entry>destination <emphasis
role="bold">(required)</emphasis></entry>
<entry><para>The destination name for this listener, resolved
through the <literal>IDestinationResolver</literal>
strategy.</para></entry>
<entry>
<para>The destination name for this listener, resolved through
the <literal>IDestinationResolver</literal> strategy.</para>
</entry>
</row>
<row>
<entry>ref <emphasis role="bold">(required)</emphasis></entry>
<entry><para>The object name of the handler
object.</para></entry>
<entry>
<para>The object name of the handler object.</para>
</entry>
</row>
<row>
<entry>method</entry>
<entry><para>The name of the handler method to invoke. If the
<literal>ref</literal> points to a
<literal>IMessageListener</literal> or Spring
<literal>ISessionAwareMessageListener</literal>,
this attribute may be omitted.</para></entry>
<entry>
<para>The name of the handler method to invoke. If the
<literal>ref</literal> points to a
<literal>IMessageListener</literal> or Spring
<literal>ISessionAwareMessageListener</literal>, this
attribute may be omitted.</para>
</entry>
</row>
<row>
<entry>response-destination</entry>
<entry><para>The name of the default response destination to
send response messages to. This will be applied in case of a
request message that does not carry a "NMSReplyTo" field. The
type of this destination will be determined by the
listener-container's "destination-type" attribute. Note: This
only applies to a listener method with a return value, for which
each result object will be converted into a response
message.</para></entry>
<entry>
<para>The name of the default response destination to send
response messages to. This will be applied in case of a
request message that does not carry a "NMSReplyTo" field. The
type of this destination will be determined by the
listener-container's "destination-type" attribute. Note: This
only applies to a listener method with a return value, for
which each result object will be converted into a response
message.</para>
</entry>
</row>
<row>
<entry>subscription</entry>
<entry><para>The name of the durable subscription, if
any.</para></entry>
<entry>
<para>The name of the durable subscription, if any.</para>
</entry>
</row>
<row>
<entry>selector</entry>
<entry><para>An optional message selector for this
listener.</para></entry>
<entry>
<para>An optional message selector for this listener.</para>
</entry>
</row>
<row>
<entry>pubsub-domain</entry>
<entry><para>An optional boolean value. Set to true for the
publish-subscribe domain (Topics) or false (the default) for
point-to-point domain (Queues). This is useful when using the
default implementation for destination resolvers.
</para></entry>
<entry>
<para>An optional boolean value. Set to true for the
publish-subscribe domain (Topics) or false (the default) for
point-to-point domain (Queues). This is useful when using the
default implementation for destination resolvers.</para>
</entry>
</row>
</tbody>
</tgroup>
@@ -1264,8 +1274,8 @@ namespace MyApp
<para>The following table describes all available attributes. Consult
the class-level SDK documentation of the
<literal>AbstractMessageListenerContainer</literal> and its subclass
<literal>SimpleMessageListenerContainer</literal> for more detail on
the individual properties.</para>
<literal>SimpleMessageListenerContainer</literal> for more detail on the
individual properties.</para>
<table id="nms-namespace-listener-container-tbl">
<title>Attributes of the NMS
@@ -1288,86 +1298,97 @@ namespace MyApp
<row>
<entry>connection-factory</entry>
<entry><para>A reference to the NMS
<literal>ConnectionFactory</literal> object (the
default object name is
<literal>'ConnectionFactory'</literal>).</para></entry>
<entry>
<para>A reference to the NMS
<literal>ConnectionFactory</literal> object (the default
object name is <literal>'ConnectionFactory'</literal>).</para>
</entry>
</row>
<row>
<entry>destination-resolver</entry>
<entry><para>A reference to the
<literal>IDestinationResolver</literal> strategy for
resolving JMS
<literal>Destinations</literal>.</para></entry>
<entry>
<para>A reference to the
<literal>IDestinationResolver</literal> strategy for resolving
JMS <literal>Destinations</literal>.</para>
</entry>
</row>
<row>
<entry>message-converter</entry>
<entry><para>A reference to the
<literal>IMessageConverter</literal> strategy for
converting NMS Messages to listener method arguments. Default is
a <literal>SimpleMessageConverter</literal>.</para></entry>
<entry>
<para>A reference to the <literal>IMessageConverter</literal>
strategy for converting NMS Messages to listener method
arguments. Default is a
<literal>SimpleMessageConverter</literal>.</para>
</entry>
</row>
<row>
<entry>destination-type</entry>
<entry><para>The NMS destination type for this listener:
<literal>queue</literal>, <literal>topic</literal> or
<literal>durableTopic</literal>. The default is
<literal>queue</literal>.</para></entry>
<entry>
<para>The NMS destination type for this listener:
<literal>queue</literal>, <literal>topic</literal> or
<literal>durableTopic</literal>. The default is
<literal>queue</literal>.</para>
</entry>
</row>
<row>
<entry>client-id</entry>
<entry><para>The NMS client id for this listener container.
Needs to be specified when using durable
subscriptions.</para></entry>
<entry>
<para>The NMS client id for this listener container. Needs to
be specified when using durable subscriptions.</para>
</entry>
</row>
<row>
<entry>acknowledge</entry>
<entry><para>The native NMS acknowledge mode:
<literal>auto</literal>, <literal>client</literal>,
<literal>dups-ok</literal> or <literal>transacted</literal>. A
value of <literal>transacted</literal> activates a locally
transacted <literal>Session</literal>. As an
alternative, specify the <literal>transaction-manager</literal>
attribute described below. Default is
<literal>auto</literal>.</para></entry>
<entry>
<para>The native NMS acknowledge mode:
<literal>auto</literal>, <literal>client</literal>,
<literal>dups-ok</literal> or <literal>transacted</literal>. A
value of <literal>transacted</literal> activates a locally
transacted <literal>Session</literal>. As an alternative,
specify the <literal>transaction-manager</literal> attribute
described below. Default is <literal>auto</literal>.</para>
</entry>
</row>
<row>
<entry>concurrency</entry>
<entry><para>The number of concurrent sessions/consumers to
start for each listener. Default is 1; keep concurrency limited
to 1 in case of a topic listener or if queue ordering is
important; consider raising it for general
queues.</para></entry>
<entry>
<para>The number of concurrent sessions/consumers to start for
each listener. Default is 1; keep concurrency limited to 1 in
case of a topic listener or if queue ordering is important;
consider raising it for general queues.</para>
</entry>
</row>
<row>
<entry>recovery-interval</entry>
<entry><para>The time interval between connection recovery
attempts. The default is 5 seconds. Specify as a TimeSpan value
using Spring's TimeSpanConverter (e.g. 10s, 10m, 3h, etc)
</para></entry>
<entry>
<para>The time interval between connection recovery attempts.
The default is 5 seconds. Specify as a TimeSpan value using
Spring's TimeSpanConverter (e.g. 10s, 10m, 3h, etc)</para>
</entry>
</row>
<row>
<entry>max-recovery-time</entry>
<entry><para>The maximum time try reconnection attempts. The
default is 10 minutes. Specify as a TimeSpan value using
Spring's TimeSpanConverter (e.g. 10s, 10m, 3h, etc)
</para></entry>
<entry>
<para>The maximum time try reconnection attempts. The default
is 10 minutes. Specify as a TimeSpan value using Spring's
TimeSpanConverter (e.g. 10s, 10m, 3h, etc)</para>
</entry>
</row>
<row>
@@ -1425,4 +1446,4 @@ namespace MyApp
EmsTemplate</para>
</section>
</section>
</chapter>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,12 @@
* limitations under the License.
*/
-->
<chapter xml:id="orm" xmlns="http://docbook.org/ns/docbook" version="5">
<chapter version="5" xml:id="orm" xmlns="http://docbook.org/ns/docbook"
xmlns:ns6="http://www.w3.org/1999/xlink"
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
xmlns:ns4="http://www.w3.org/1999/xhtml"
xmlns:ns3="http://www.w3.org/2000/svg"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Object Relational Mapping (ORM) data access</title>
<section xml:id="orm-introduction">
@@ -48,11 +53,11 @@
<listitem>
<para><emphasis>Ease of testing.</emphasis> Spring's IoC approach
makes it easy to swap the implementations and config locations of
Hibernate <literal>SessionFactory</literal> instances,
ADO.NET <literal>DbProvider</literal> instances,
transaction managers, and mapper object implementations (if needed).
This makes it much easier to isolate and test each piece of
persistence-related code in isolation.</para>
Hibernate <literal>SessionFactory</literal> instances, ADO.NET
<literal>DbProvider</literal> instances, transaction managers, and
mapper object implementations (if needed). This makes it much easier
to isolate and test each piece of persistence-related code in
isolation.</para>
</listitem>
<listitem>
@@ -69,21 +74,20 @@
<listitem>
<para><emphasis>General resource management.</emphasis> Spring
application contexts can handle the location and configuration of
Hibernate <literal>ISessionFactory</literal> instances,
ADO.NET <literal>DbProvider</literal> instances and other
related resources. This makes these values easy to manage and change.
Spring offers efficient, easy and safe handling of persistence
resources. For example: related code using NHibernate generally needs
to use the same NHibernate <literal>Session</literal> for
efficiency and proper transaction handling. Spring makes it easy to
transparently create and bind a <literal>Session</literal>
to the current thread, either by using an explicit 'template' wrapper
class at the code level or by exposing a current
<literal>Session</literal> through the Hibernate
<literal>SessionFactory</literal> (for DAOs based on plain
Hibernate 1.2 API). Thus Spring solves many of the issues that
repeatedly arise from typical NHibernate usage, for any transaction
environment (local or distributed).</para>
Hibernate <literal>ISessionFactory</literal> instances, ADO.NET
<literal>DbProvider</literal> instances and other related resources.
This makes these values easy to manage and change. Spring offers
efficient, easy and safe handling of persistence resources. For
example: related code using NHibernate generally needs to use the same
NHibernate <literal>Session</literal> for efficiency and proper
transaction handling. Spring makes it easy to transparently create and
bind a <literal>Session</literal> to the current thread, either by
using an explicit 'template' wrapper class at the code level or by
exposing a current <literal>Session</literal> through the Hibernate
<literal>SessionFactory</literal> (for DAOs based on plain Hibernate
1.2 API). Thus Spring solves many of the issues that repeatedly arise
from typical NHibernate usage, for any transaction environment (local
or distributed).</para>
</listitem>
<listitem>
@@ -157,13 +161,13 @@
for appropriate conversion of specific API exceptions to a common
infrastructure exception hierarchy. Spring introduces a DAO exception
hierarchy, applicable to any data access strategy. For direct ADO.NET,
the <literal>AdoTemplate</literal> class mentioned in a previous
section cares for connection handling, and for proper conversion of
ADO.NET data access exceptions (not even singly rooted in .NET 1.1) to
Spring's <literal>DataAccessException</literal> hierarchy, including
translation of database-specific SQL error codes to meaningful exception
classes. It supports both distributed and local transactions, via
respective Spring transaction managers.</para>
the <literal>AdoTemplate</literal> class mentioned in a previous section
cares for connection handling, and for proper conversion of ADO.NET data
access exceptions (not even singly rooted in .NET 1.1) to Spring's
<literal>DataAccessException</literal> hierarchy, including translation
of database-specific SQL error codes to meaningful exception classes. It
supports both distributed and local transactions, via respective Spring
transaction managers.</para>
<para>Spring also offers Hibernate support, consisting of a
<literal>HibernateTemplate</literal> analogous to
@@ -200,23 +204,22 @@
the other the .NET 2.0 TransactionScope API.</para>
<para>The first strategy is encapsulated in the class
<literal>Spring.Data.NHibernate.HibernateTransactionManager
</literal>in both the <literal>Spring.Data.NHibernate
</literal>namespace. This strategy is preferred when you are using a
single database. ADO.NET operations can also participate in the same
transaction, either by using AdoTemplate or by retrieving the ADO.NET
connection/transaction object pair stored in thread local storage when
the transaction begins. Refer to the documentation of Spring's ADO.NET
framework for more information on retrieving and using the
connection/transaction pair without using AdoTemplate. You can use the
HibernateTransactionManager and associated classes such as
SessionFactory, HibernateTemplate directly as you would any third party
API, however they are most commonly used through Spring's XML
configuration file to gain the benefits of easy configuration for a
particular runtime environment and as the basis for the configuration of
a data access layer also configured using XML. An XML fragment showing
the declaration of <literal>HibernateTransactionManager</literal> is
shown below.</para>
<literal>Spring.Data.NHibernate.HibernateTransactionManager </literal>in
both the <literal>Spring.Data.NHibernate </literal>namespace. This
strategy is preferred when you are using a single database. ADO.NET
operations can also participate in the same transaction, either by using
AdoTemplate or by retrieving the ADO.NET connection/transaction object
pair stored in thread local storage when the transaction begins. Refer
to the documentation of Spring's ADO.NET framework for more information
on retrieving and using the connection/transaction pair without using
AdoTemplate. You can use the HibernateTransactionManager and associated
classes such as SessionFactory, HibernateTemplate directly as you would
any third party API, however they are most commonly used through
Spring's XML configuration file to gain the benefits of easy
configuration for a particular runtime environment and as the basis for
the configuration of a data access layer also configured using XML. An
XML fragment showing the declaration of
<literal>HibernateTransactionManager</literal> is shown below.</para>
<programlisting language="myxml"> &lt;object id="HibernateTransactionManager"
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate"&gt;
@@ -227,17 +230,17 @@
&lt;/object&gt;</programlisting>
<para>The important property of
<literal>HibernateTransactionManager</literal> are the references to
the DbProvider and the Hibernate ISessionFactory. For more information
on the DbProvider, refer to the chapter <link
<literal>HibernateTransactionManager</literal> are the references to the
DbProvider and the Hibernate ISessionFactory. For more information on
the DbProvider, refer to the chapter <link
linkend="dbprovider">DbProvider</link> and the following section on
SessionFactory set up.</para>
<para>The second strategy is to use the class
<literal>Sping.Data.TxScopeTransactionManager</literal> that uses
.NET 2.0 System.Transaction namespace and its corresponding
TransactionScope API. This is preferred when you are using multiple
transactional resources, such as multiple databases.</para>
<literal>Sping.Data.TxScopeTransactionManager</literal> that uses .NET
2.0 System.Transaction namespace and its corresponding TransactionScope
API. This is preferred when you are using multiple transactional
resources, such as multiple databases.</para>
<para>Both strategies associate one Hibernate Session for the scope of
the transaction (scope in the general demarcation sense, not
@@ -264,13 +267,13 @@
<para>To avoid tying application objects to hard-coded resource lookups,
Spring allows you to define resources like a
<literal>DbProvider</literal> or a Hibernate
<literal>SessionFactory</literal> as objects in an
application context. Application objects that need to access resources
just receive references to such pre-defined instances via object
references (the DAO definition in the next section illustrates this).
The following excerpt from an XML application context definition shows
how to set up Spring's ADO.NET DbProvider and a Hibernate
<literal>SessionFactory</literal> on top of it:</para>
<literal>SessionFactory</literal> as objects in an application context.
Application objects that need to access resources just receive
references to such pre-defined instances via object references (the DAO
definition in the next section illustrates this). The following excerpt
from an XML application context definition shows how to set up Spring's
ADO.NET DbProvider and a Hibernate <literal>SessionFactory</literal> on
top of it:</para>
<programlisting language="myxml">&lt;objects xmlns="http://www.springframework.net"
xmlns:db="http://www.springframework.net/database"&gt;
@@ -352,9 +355,9 @@
</listitem>
<listitem>
<para>Delegate to the <literal>DbProvider</literal> itself as
the NHibernate connection provider instead of listing it via
property hibernate.connection.provider via
<para>Delegate to the <literal>DbProvider</literal> itself as the
NHibernate connection provider instead of listing it via property
hibernate.connection.provider via
<literal>HibernateProperties</literal>.</para>
</listitem>
</itemizedlist>
@@ -364,8 +367,8 @@
hibernate.connection.provider is used and a warning level message is
logged. If you use Spring's <literal>DbProvider</literal> as the
NHibernate connection provider then you can take advantage of
<literal>IDbProvider</literal> implementations that will let you
change the connection string at runtime such as <link lang=""
<literal>IDbProvider</literal> implementations that will let you change
the connection string at runtime such as <link lang=""
linkend="dbprovider-usercredentials">UserCredentialsDbProvider</link>
and <link
linkend="dbprovider-multidelegating">MultiDelegatingDbProvider</link>.</para>
@@ -377,20 +380,19 @@
linkend="dbprovider-multidelegating">MultiDelegatingDbProvider</link>
only change the connection string at runtime based on values in thread
local storage and do not clear out the Hibernate cache that is unique
to each <literal>ISessionFactory</literal> instance. As such, they
are only useful for selecting at runtime a single database instance.
to each <literal>ISessionFactory</literal> instance. As such, they are
only useful for selecting at runtime a single database instance.
Cleaning up an existing session factory when switching to a new
database is left to user code. Creating a new session factory per
connection string (assuming the same mapping files can be used across
all databases connections) is not currently supported. To support this
functionality, you can subclass
<literal>LocalSessionFactoryObject</literal> and override the
method <literal>ISessionFactory NewSessionFactory(Configuration
<literal>LocalSessionFactoryObject</literal> and override the method
<literal>ISessionFactory NewSessionFactory(Configuration
config)</literal> so that it returns an implementation of
<literal>ISessionFactory</literal> that selects among multiple
instances based on values in thread local storage, much like the
implementation of
<literal>MultiDelegatingDbProvider</literal>.</para>
implementation of <literal>MultiDelegatingDbProvider</literal>.</para>
</note>
</section>
@@ -401,13 +403,12 @@
methods that can be part of any custom data access object or business
service. There are no restrictions on the implementation of the
surrounding object at all, it just needs to provide a Hibernate
<literal>SessionFactory</literal>. It can get the latter
from anywhere, but preferably as an object reference from a Spring IoC
container - via a simple <methodname>SessionFactory</methodname>
property setter. The following snippets show a DAO definition in a
Spring container, referencing the above defined
<literal>SessionFactory</literal>, and an example for a DAO
method implementation.</para>
<literal>SessionFactory</literal>. It can get the latter from anywhere,
but preferably as an object reference from a Spring IoC container - via
a simple <methodname>SessionFactory</methodname> property setter. The
following snippets show a DAO definition in a Spring container,
referencing the above defined <literal>SessionFactory</literal>, and an
example for a DAO method implementation.</para>
<programlisting language="myxml">&lt;objects&gt;
@@ -437,11 +438,11 @@
<para>The <literal>HibernateTemplate</literal> class provides many
methods that mirror the methods exposed on the Hibernate
<literal>Session</literal> interface, in addition to a
number of convenience methods such as the one shown above. If you need
access to the <literal>Session</literal> to invoke methods
that are not exposed on the <literal>HibernateTemplate</literal>,
you can always drop down to a callback-based approach like so.</para>
<literal>Session</literal> interface, in addition to a number of
convenience methods such as the one shown above. If you need access to
the <literal>Session</literal> to invoke methods that are not exposed on
the <literal>HibernateTemplate</literal>, you can always drop down to a
callback-based approach like so.</para>
<programlisting language="csharp">public class HibernateCustomerDao : ICustomerDao {
@@ -483,20 +484,20 @@
inside the anonymous delegate implementation.</para>
<para>A callback implementation effectively can be used for any
Hibernate data access. <literal>HibernateTemplate</literal> will
ensure that <literal>Session</literal> instances are
properly opened and closed, and automatically participate in
transactions. The template instances are thread-safe and reusable, they
can thus be kept as instance variables of the surrounding class. For
simple single step actions like a single Find, Load, SaveOrUpdate, or
Delete call, <literal>HibernateTemplate</literal> offers alternative
convenience methods that can replace such one line callback
implementations. Furthermore, Spring provides a convenient
Hibernate data access. <literal>HibernateTemplate</literal> will ensure
that <literal>Session</literal> instances are properly opened and
closed, and automatically participate in transactions. The template
instances are thread-safe and reusable, they can thus be kept as
instance variables of the surrounding class. For simple single step
actions like a single Find, Load, SaveOrUpdate, or Delete call,
<literal>HibernateTemplate</literal> offers alternative convenience
methods that can replace such one line callback implementations.
Furthermore, Spring provides a convenient
<literal>HibernateDaoSupport</literal> base class that provides a
<methodname>SessionFactory</methodname> property for receiving a
<literal>SessionFactory</literal> and for use by subclasses.
In combination, this allows for very simple DAO implementations for
typical requirements:</para>
<literal>SessionFactory</literal> and for use by subclasses. In
combination, this allows for very simple DAO implementations for typical
requirements:</para>
<programlisting language="csharp">public class HibernateCustomerDao : HibernateDaoSupport, ICustomerDao
{
@@ -512,22 +513,21 @@
<title>Implementing Spring-based DAOs without callbacks</title>
<para>As an alternative to using Spring's
<literal>HibernateTemplate</literal> to implement DAOs, data access
code can also be written in a more traditional fashion, without wrapping
the Hibernate access code in a callback, while still respecting and
participating in Spring's generic
<literal>DataAccessException</literal> hierarchy. The
<literal>HibernateDaoSupport</literal> base class offers methods to
access the current transactional <literal>Session</literal>
<literal>HibernateTemplate</literal> to implement DAOs, data access code
can also be written in a more traditional fashion, without wrapping the
Hibernate access code in a callback, while still respecting and
participating in Spring's generic <literal>DataAccessException</literal>
hierarchy. The <literal>HibernateDaoSupport</literal> base class offers
methods to access the current transactional <literal>Session</literal>
and to convert exceptions in such a scenario; similar methods are also
available as static helpers on the
<literal>SessionFactoryUtils</literal> class. Note that such code
will usually pass '<literal>false</literal>' as the value of the
<literal>SessionFactoryUtils</literal> class. Note that such code will
usually pass '<literal>false</literal>' as the value of the
<methodname>DoGetSession(..)</methodname> method's
'<literal>allowCreate</literal>' argument, to enforce running within a
transaction (which avoids the need to close the returned
<literal>Session</literal>, as its lifecycle is managed by
the transaction). Asking for the</para>
<literal>Session</literal>, as its lifecycle is managed by the
transaction). Asking for the</para>
<programlisting language="csharp">public class HibernateProductDao extends HibernateDaoSupport implements ProductDao {
@@ -540,20 +540,19 @@
}
}</programlisting>
<para>This code will not translate the Hibernate exception to a generic
DataAccessException.</para>
<para>This code will <emphasis>not</emphasis> translate the Hibernate
exception to a generic <literal>DataAccessException</literal>.</para>
</section>
<section xml:id="orm-hibernate-straight">
<title>Implementing DAOs based on plain Hibernate 1.2 API</title>
<title>Implementing DAOs based on plain Hibernate 1.2/2.0 API</title>
<para>Hibernate 1.2 introduced a feature called "contextual Sessions",
where Hibernate itself manages one current
<literal>ISession</literal> per transaction. This is roughly
equivalent to Spring's synchronization of one Hibernate
<literal>Session</literal> per transaction. A corresponding
DAO implementation looks like as follows, based on the plain Hibernate
API:</para>
where Hibernate itself manages one current <literal>ISession</literal>
per transaction. This is roughly equivalent to Spring's synchronization
of one Hibernate <literal>Session</literal> per transaction. A
corresponding DAO implementation looks like as follows, based on the
plain Hibernate API:</para>
<programlisting language="csharp">public class ProductDaoImpl implements IProductDao {
@@ -592,8 +591,8 @@ public class HibernateCustomerDao : ICustomerDao {
<para>The above DAO follows the Dependency Injection pattern: it fits
nicely into a Spring IoC container, just like it would if coded against
Spring's <literal>HibernateTemplate</literal>. Of course, such a DAO
can also be set up in plain C# (for example, in unit tests): simply
Spring's <literal>HibernateTemplate</literal>. Of course, such a DAO can
also be set up in plain C# (for example, in unit tests): simply
instantiate it and call <methodname>SessionFactory</methodname> property
with the desired factory reference. As a Spring object definition, it
would look as follows:</para>
@@ -651,26 +650,72 @@ public class HibernateCustomerDao : ICustomerDao {
of course appealing from a non-invasiveness perspective, and will no
doubt feel more natural to Hibernate developers.</para>
<para>However, the DAO throws plain
<literal>HibernateException</literal> which means that callers can
only treat exceptions as generally fatal - unless they want to depend on
Hibernate's own exception hierarchy. Catching specific causes such as an
optimistic locking failure is not possible without tying the caller to
the implementation strategy. This trade off might be acceptable to
applications that are strongly Hibernate-based and/or do not need any
special exception treatment.</para>
<section xml:id="orm-hibernate-straight-ex">
<title>Exception Translation</title>
<para>Fortunately, Spring's
<literal>LocalSessionFactoryObject</literal> supports Hibernate's
<methodname>SessionFactory.GetCurrentSession()</methodname> method for
any Spring transaction strategy, returning the current Spring-managed
transactional <literal>Session</literal> even with
<literal>HibernateTransactionManager</literal>.</para>
<para>However, the DAO implemenation as shown throws plain
<literal>HibernateException</literal> which means that callers can
only treat exceptions as generally fatal - unless they want to depend
on Hibernate's own exception hierarchy. Catching specific causes such
as an optimistic locking failure is not possible without tying the
caller to the implementation strategy. This trade off might be
acceptable to applications that are strongly Hibernate-based and/or do
not need any special exception treatment. As an alternative you can
use Spring's exception translation advice to convert the NHibernate
exception to Spring's DataAccessException hierarchy.</para>
<para>In summary: DAOs can be implemented based on the plain Hibernate
1.2 API, while still being able to participate in Spring-managed
transactions. In this approach there is one session associated with the
transaction.</para>
<para>Spring offers a solution allowing exception translation to be
applied transparently through the <literal>[Repository]</literal>
attribute:</para>
<programlisting language="csharp">[Repository]
public class HibernateCustomerDao : ICustomerDao {
// class body here
}</programlisting>
<para>and register an exception translation post processor.</para>
<programlisting>&lt;objects&gt;
&lt;!-- configure session factory (omittied for brevity) --&gt;
&lt;!-- Exception translation object post processor --&gt;
&lt;object type="Spring.Dao.Attributes.PersistenceExceptionTranslationPostProcessor, Spring.Data"/&gt;
&lt;!-- Same DAO configuration as before --&gt;
&lt;object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerDao, Spring.Northwind.Dao.NHibernate"&gt;
&lt;property name="sessionFactory" ref="MySessionFactory"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<para>The postprocessor will automatically look for all exception
translators (implementations of the
<literal>IPersistenceExceptionTranslator</literal> interface) and
advise all object marked with the <literal>[Repository]</literal>
attribute so that the discovered translators can intercept and apply
the appropriate translation on the thrown exceptions. Spring's
<literal>LocalSessionFactory</literal> object implements the
<literal>IPersistenceExceptionTranslator</literal> interface and
performs the same exception translation as was done when using
<literal>HibernateTemplate</literal>.</para>
<para>The <literal>[Repository</literal>] attribute is definedin the
Spring.Data assembly, however it is used as a 'marker' attribute, and
you can provide your own if you would like to avoid coupling your DAO
implementation to a Spring attribute. This is done by setting
<literal>PersistenceExceptionTranslationPostProcessor's</literal>
property <literal>RepositoryAttributeType</literal> to your own
attribute type.</para>
<para><note>
<para>In summary: DAOs can be implemented based on the plain
Hibernate 1.2/2.0 API, while still being able to participate in
Spring-managed transactions and exception translation.</para>
</note></para>
</section>
</section>
<section xml:id="orm-hibernate-tx-programmatic">
@@ -680,10 +725,10 @@ public class HibernateCustomerDao : ICustomerDao {
application, on top of such lower-level data access services spanning
any number of operations. There are no restrictions on the
implementation of the surrounding business service here as well, it just
needs a Spring <literal>PlatformTransactionManager</literal>. Again,
the latter can come from anywhere, but preferably as an object reference
via a <methodname>TransactionManager</methodname> property - just like
the <literal>productDAO</literal> should be set via a
needs a Spring <literal>PlatformTransactionManager</literal>. Again, the
latter can come from anywhere, but preferably as an object reference via
a <methodname>TransactionManager</methodname> property - just like the
<literal>productDAO</literal> should be set via a
<methodname>setProductDao(..)</methodname> method. The following
snippets show a transaction manager and a business service definition in
a Spring application context, and an example for a business method
@@ -691,7 +736,23 @@ public class HibernateCustomerDao : ICustomerDao {
<programlisting language="myxml">&lt;objects&gt;
TO BE DONE
&lt;object id="HibernateTransactionManager"
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate"&gt;
&lt;property name="DbProvider" ref="DbProvider"/&gt;
&lt;property name="SessionFactory" ref="MySessionFactory"/&gt;
&lt;/object&gt;
&lt;!-- DAO definition not listed, see above for an example. --&gt;
&lt;object id="FulfillmentService" type="Spring.Northwind.Service.FulfillmentService, Spring.Northwind.Service"&gt;
&lt;property name="CustomerDao" ref="CustomerDao"/&gt;
&lt;property name="OrderDao" ref="OrderDao"/&gt;
&lt;property name="ShippingService" ref="ShippingService"/&gt;
&lt;property name="TransactionManager" ref="HibernateTransactionManager"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
@@ -707,9 +768,35 @@ TO BE DONE
private IShippingService shippingService;
public TransactionManager TransactionManager
{
set { transactionTemplate = new TransactionTemplate(value);
}
public void ProcessCustomer(string customerId)
{
TO BE DONE
tt.Execute(delegate(ITransactionStatus status)
{
//Find all orders for customer
Customer customer = CustomerDao.FindById(customerId);
foreach (Order order in customer.Orders)
{
//Validate Order
Validate(order);
//Ship with external shipping service
ShippingService.ShipOrder(order);
//Update shipping date
order.ShippedDate = DateTime.Now;
//Update shipment date
OrderDao.SaveOrUpdate(order);
//Other operations...Decrease product quantity... etc
}
return null;
});
}
}</programlisting>
</section>
@@ -846,19 +933,17 @@ TO BE DONE
<title>Transaction management strategies</title>
<para>Both <literal>TransactionTemplate</literal> and
<literal>TransactionInterceptor</literal> (not yet seen explicitly
in above configuration, TransactionProxyFactoryObject uses a
<literal>TransactionInterceptor</literal> (not yet seen explicitly in
above configuration, TransactionProxyFactoryObject uses a
TransactionInterceptor, you would have to specify it explicitly if you
were using an ordinary ProxyFactoryObject.) delegate the actual
transaction handling to a
<literal>PlatformTransactionManager</literal> instance, which can be
a <literal>HibernateTransactionManager</literal> (for a single
Hibernate <literal>SessionFactory</literal>, using a
<literal>ThreadLocal</literal>
<literal>Session</literal> under the hood) or a
<literal>TxScopeTransactionManager</literal> (delegating to MS-DTC
for distributed transaction) for Hibernate applications. You could even
use a custom <literal>PlatformTransactionManager</literal>
transaction handling to a <literal>PlatformTransactionManager</literal>
instance, which can be a <literal>HibernateTransactionManager</literal>
(for a single Hibernate <literal>SessionFactory</literal>, using a
<literal>ThreadLocal</literal> <literal>Session</literal> under the
hood) or a <literal>TxScopeTransactionManager</literal> (delegating to
MS-DTC for distributed transaction) for Hibernate applications. You
could even use a custom <literal>PlatformTransactionManager</literal>
implementation. So switching from native Hibernate transaction
management to TxScopeTransactionManager, such as when facing distributed
transaction requirements for certain deployments of your application, is
@@ -868,22 +953,20 @@ TO BE DONE
as they just use the generic transaction management APIs.</para>
<para>For distributed transactions across multiple Hibernate session
factories, simply combine
<literal>TxScopeTransactionManager</literal> as a transaction
strategy with multiple <literal>LocalSessionFactoryObject</literal>
definitions. Each of your DAOs then gets one specific
<literal>SessionFactory</literal> reference passed into it's
respective object property.</para>
factories, simply combine <literal>TxScopeTransactionManager</literal>
as a transaction strategy with multiple
<literal>LocalSessionFactoryObject</literal> definitions. Each of your
DAOs then gets one specific <literal>SessionFactory</literal> reference
passed into it's respective object property.</para>
<programlisting>TO BE DONE
</programlisting>
<para><literal>HibernateTransactionManager</literal> can export the
ADO.NET <literal>Transaction</literal> used by Hibernate to
plain ADO.NET access code, for a specific
<literal>DbProvider</literal>. (matching connection string).
This allows for high-level transaction demarcation with mixed
Hibernate/ADO.NET data access!</para>
ADO.NET <literal>Transaction</literal> used by Hibernate to plain
ADO.NET access code, for a specific <literal>DbProvider</literal>.
(matching connection string). This allows for high-level transaction
demarcation with mixed Hibernate/ADO.NET data access!</para>
</section>
<section xml:id="orm-hibernate-web">
@@ -962,4 +1045,4 @@ TO BE DONE
this chapter for further information on those usage scenarios.</para>
</section>
</section>
</chapter>
</chapter>

View File

@@ -16,7 +16,12 @@
* limitations under the License.
*/
-->
<chapter xml:id="wcf" xmlns="http://docbook.org/ns/docbook" version="5">
<chapter version="5" xml:id="wcf" xmlns="http://docbook.org/ns/docbook"
xmlns:ns6="http://www.w3.org/1999/xlink"
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
xmlns:ns4="http://www.w3.org/1999/xhtml"
xmlns:ns3="http://www.w3.org/2000/svg"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Windows Communication Foundation (WCF)</title>
<section xml:id="wcf-introduction">
@@ -26,19 +31,6 @@
dependency injection and add additional behavior to them using
Aspect-Oriented programming (AOP).</para>
<para>There are two approaches in the 1.2 M1 release for configuring your
services with DI which are discussed in the following sections. One
approach creates an implementation of your service interface (a dynamic
proxy) that retrieves a configured instance of your service type from the
Spring container. This dynamic proxy is then the final service type that
is hosted. The second approach uses the extension points in WCF itself to
delegate to the Spring container to create and configure your service
type. Both approaches are discussed below.</para>
<para>As it is desirable to have just one approach, we would greatly
appreciate your input regarding these two approaches as well as any other
suggestions regarding WCF support.</para>
<para>For those who would like to get their feet wet right way, check out
the WcfQuickStart application in the examples directory.</para>
</section>
@@ -117,18 +109,17 @@
<para>To host this service type in a standalone application define an
instance of a
<literal>Spring.ServiceModel.Activation.ServiceHostFactoryObject</literal>
and set is property <literal>TargetName</literal> to the id value of
the previously defined service type.
and set is property <literal>TargetName</literal> to the id value of the
previously defined service type.
<literal>ServiceHostFactoryObject</literal> is a Spring
<literal>IFactoryObject</literal> implementation. (See <link
linkend="objects-factory-lifecycle-factoryobject">here</link> for more
information on <literal>IFactoryObjects</literal> and their
interaction with the container.) The
<literal>ServiceHostFactoryObject</literal> will create an instance
of
<literal>Spring.ServiceModel.Activation.SpringServiceHost</literal>
that will be the ServiceHost instance associated with your service type.
This configuration for this step is shown below.</para>
information on <literal>IFactoryObjects</literal> and their interaction
with the container.) The <literal>ServiceHostFactoryObject</literal>
will create an instance of
<literal>Spring.ServiceModel.Activation.SpringServiceHost</literal> that
will be the ServiceHost instance associated with your service type. This
configuration for this step is shown below.</para>
<programlisting language="myxml"> &lt;object id="calculatorServiceHost" type="Spring.ServiceModel.Activation.ServiceHostFactoryObject, Spring.Services"&gt;
&lt;property name="TargetName" value="calculator" /&gt;
@@ -155,21 +146,21 @@
</note>
<para><literal>Spring.ServiceModel.Activation.SpringServiceHost
</literal>is where the dynamic proxy for your service type is
generated. This dynamic proxy will implement a single 'WCF' interface,
the same on that your service type implements. The implementation of the
service interface methods on the proxy will delegate to a wrapped
'target' object which is the object instance retrieved by name from the
Spring container using the Spring API,
<literal>ApplicationContext.GetObject(name)</literal>. Since the
object retrieved in this manner is fully configured, your WCF service is
as well.</para>
</literal>is where the dynamic proxy for your service type is generated.
This dynamic proxy will implement a single 'WCF' interface, the same on
that your service type implements. The implementation of the service
interface methods on the proxy will delegate to a wrapped 'target'
object which is the object instance retrieved by name from the Spring
container using the Spring API,
<literal>ApplicationContext.GetObject(name)</literal>. Since the object
retrieved in this manner is fully configured, your WCF service is as
well.</para>
<para>Outside of a standalone application you can also use the class
<literal>Spring.ServiceModel.Activation.ServiceHostFactory</literal>
(which inherits from
<literal>System.ServiceModel.Activation.ServiceHostFactory</literal>)
to host your services so that they can be configured via dependency
<literal>System.ServiceModel.Activation.ServiceHostFactory</literal>) to
host your services so that they can be configured via dependency
injection. To use the dynamic proxy approached described here you should
still refer to the name of the service as the name of the object
definition used to configure the service type in the Spring
@@ -179,75 +170,10 @@
need to specify the service name as the name of the object definition in
the Spring container and to ensure that singleton=false is used in the
object definition. You can also use
<literal>Spring.ServiceModel.Activation.ServiceHostFactory</literal>
to host your service inside IIS but should still refer to the service by
<literal>Spring.ServiceModel.Activation.ServiceHostFactory</literal> to
host your service inside IIS but should still refer to the service by
the name of the object in the Spring container.</para>
</section>
<section xml:id="wcf-di-extension-points">
<title>Dependency Injection using WCF extensibility points.</title>
<para>The second approach uses the extensibility points in WCF itself to
delegate to Spring to create and configure your WCF service. This
approach was first taken (afaik) by Oran Dennison on his <ulink
url="http://orand.blogspot.com/2006/10/wcf-service-dependency-injection.html">blog</ulink>
and several other folks on the web since then. In this approach Spring
specific implementations of the WCF interfaces
<literal>System.ServiceModel.Dispatcher.IInstanceProvider</literal>
and
<literal>System.ServiceModel.Description.IServiceBehavior</literal>
are used to integrate Spring directly into the instancing of WCF
services.</para>
<para>Spring's implementation of
<literal>IInstanceProvider</literal> is
<literal>Spring.ServiceModel.Support.SpringInstanceProvider</literal>.
This implementation will look for an object by type in the Spring
container and retrieve an instance configured using DI. If there is more
than one object of the type registered with the container than an
exception will be thrown. The
<literal>SpringInstanceProvider</literal> is used by a custom
service behavior class,
<literal>Spring.ServiceModel.Support.SpringServiceBehavior</literal>
where it is applied to all the service endpoints. This behavior is then
added to the custom service host
<literal>Spring.ServiceModel.Activation.SpringServiceHost</literal></para>
<para>The service type is used to locate the object in the container. In
your .svc file you specify the custom service host type and also the
type of the service. Here is an example taken from the WcfQuickStart
application that shows the use of this approach inside IIS.</para>
<programlisting language="myxml">&lt;%@ ServiceHost Language="C#" Debug="true" Service="Spring.WcfQuickStart.CalculatorService"
Factory="Spring.ServiceModel.Activation.ServiceHostFactory" %&gt;
</programlisting>
<para>The Spring configuration for the object is shown below.</para>
<programlisting language="myxml"> &lt;object id="calculator" type="Spring.WcfQuickStart.CalculatorService, App_Code" <emphasis
role="bold">singleton="false"</emphasis>&gt;
&lt;property name="SleepInSeconds" value="1"/&gt;
&lt;/object&gt;</programlisting>
<note>
<para>The object must be declared as a 'prototype' object, i.e. not a
singleton, in order to interact correctly with WCF instancing.</para>
</note>
<para>While integrating 'natively' with WCF does seem to be the most
natural approach there is one 'gotya' that needs to be investigated
further to see if there is an acceptable workaround in order for this
approach to be viable. The issue is that if the service is configured to
be a singleton, for example using
<literal>[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]</literal>
then the invocation of the <literal>IInstanceProvider</literal> is
short-circuited. See the notes on the MSDN class documentation <ulink
url="http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.iinstanceprovider.aspx">here</ulink>.
One workaround, which is not very appealing, is to use the PerCall
instancing mode but set the singleton attribute in the Spring
configuration to true, this way the same instance is always
returned.</para>
</section>
</section>
<section xml:id="wcf-aop">
@@ -257,8 +183,8 @@
additional AOP advice to your WCF services in the same way as you have
always done in Spring. The following configuration shows how to apply some
simple performance monitoring advice to all services in the
<literal>Spring.WcfQuickStart</literal> namespace and is taken from
the QuickStart example.</para>
<literal>Spring.WcfQuickStart</literal> namespace and is taken from the
QuickStart example.</para>
<programlisting language="myxml"> &lt;object id="serviceOperation" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop"&gt;
&lt;property name="pattern" value="Spring.WcfQuickStart.*"/&gt;
@@ -299,7 +225,7 @@
&lt;/object&gt;</programlisting>
<note>
<para>This will be shortened using a custom namespce in the 1.2 RC1
<para>This will be shortened using a custom namespce in the 1.2 final
release</para>
</note>
@@ -307,4 +233,44 @@
enpoints in the &lt;client&gt; section of the standard WCF configuration
inside of App.config.</para>
</section>
</chapter>
<section>
<title>Exporting PONOs as WCF Services</title>
<para>Much like the approach taken for .asmx web services Spring provides
an exporter that will add <literal>[ServiceContract] </literal>and
<literal>[OperationContract]</literal> attributes by default to all public
interface methods on a given (PONO) class. The exporter class is
<literal>Spring.ServiceModel.ServiceExporter</literal> and has various
options to fine-tune what interfaces are exported and the specific
attributes that get applied to each method and on that class. Here is a
simple example</para>
<programlisting>&lt;object id="HelloWorldExporter" type="Spring.ServiceModel.ServiceExporter, Spring.Services"&gt;
&lt;property name="TargetName" value="HelloWorld"/&gt;
&lt;property name="MemberAttributes"&gt;
&lt;dictionary&gt;
&lt;entry key="SayHelloWorld"&gt;
&lt;object type="System.ServiceModel.OperationContractAttribute, System.ServiceModel"&gt;
&lt;property name="IsOneWay" value="false"/&gt;
&lt;!-- configure any other OperationContractAttribute properties here --&gt;
&lt;/object&gt;
&lt;/entry&gt;
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>Spring does not provide any means to add<literal>
[DataContract]</literal> or <literal>[DataMember]</literal> attributes to
method arguments of your service operations. As such, either you will do
that yourself or you may choose to use a serializer other than
DataContractSerializer, for example one that relies on method arguments
that implement the <literal>ISerializable</literal> interface, having the
<literal>[Serializable]</literal> attribute, or are serializable via the
XmlSerializer. Use the latter serializers is a good way to migrate from an
existing RCP based approach, such as using .NET remoting, to WCF in order
to take advantage of the WCF runtime and avoid editing much existing code.
You can then incrementally refactor and/or create new operations that use
<literal>DataContractSerializer</literal>. </para>
</section>
</chapter>

View File

@@ -16,12 +16,135 @@
* limitations under the License.
*/
-->
<chapter xml:id="web" xmlns="http://docbook.org/ns/docbook" version="5">
<chapter version="5" xml:id="web" xmlns="http://docbook.org/ns/docbook"
xmlns:ns6="http://www.w3.org/1999/xlink"
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
xmlns:ns4="http://www.w3.org/1999/xhtml"
xmlns:ns3="http://www.w3.org/2000/svg"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Spring.NET Web Framework</title>
<sect1 xml:id="web-introduction">
<title>Introduction</title>
<para>Spring.NET's web application framework aims to increase your
productivity writing ASP.NET WebForms applications. It offers a unique
value proposition to creating ASP.NET applications not found in other .NET
web framework. </para>
<para>The goal of the framework is to make it easy to write 'thin and
clean' web applications. By thin, what is meant is that the WebForm's
responsibility is to act as adapter between the HTML based world of the
web and the oo world of your application. The application layer your web
form communicates with is where the business logic resides, not in the web
tier. By 'clean' what is meant that the web framework should have a good
separation of concerns, leading ideally to an event-handler that does not
contain any reference to UI elements. This makes it possible to test your
event handler code in integration style tests. Last but not least,
Spring's web framework reduces the incidental complexity of common tasks
in the web tier, for example the conversion of HTML control data to object
and then vice-versa after the request has been processed by the
application layer.</para>
<para>Highlights of Spring's Web framework are</para>
<itemizedlist>
<listitem>
<para><link linkend="web-di">Dependency Injection</link> for all
ASP.NET artifacts. This includes pages and user controls but also
modules, providers and HTTP handlers. Your pages, controls, etc., do
not have any Spring dependencies in order to be configured via
dependency injection.</para>
</listitem>
<listitem>
<para><link linkend="web-databinding">Bi-directional data
binding</link>. This allows you to declaratively define the data that
will be marshaled out of your html/user controls and into a data model
that in turn is generally submitted to the application layer. After
the data model is updated in the application layer, those changes are
automatically reflected in the html/user controls on post back. This
removes large amounts of tedious, error prone boilerplate code.</para>
</listitem>
<listitem>
<para><link linkend="web-objectscope">Web object scopes</link>. Object
definitions can be defined at the application, session or request
scope. This makes it easy to inject, say a session scoped shopping
cart, into your page without having to do any lower level
programming</para>
</listitem>
<listitem>
<para><link linkend="web-databinding">Data Model Management</link>.
While ASP.NET managed the view state of your form, it does not offer
facilities to manage the data model that you build up to submit to the
application layer. Spring provides a mechanism similar to view state
to help manage your data model.</para>
</listitem>
<listitem>
<para><link linkend="web-validation-controls">UI agnostic validation
framework</link>. Declaratively define complex validation rules, for
example that take into account complex relationships in your data
model. Error controls are provided to easily render validation
failure. This allows you to centralize your validation logic and also
reuse it on the server side, for example using parameter validation
advice described in the aspect library chapter</para>
</listitem>
<listitem>
<para><link linkend="web-resultmapping">Externalized page navigation
through 'result mapping'</link>. Instead of hard coding urls and data
to direct where a page should go next, result mappings are externally
defined and configured that associate logical names and a URL (+
data). This also allows to encryption of values that are sent via
Response.Redirect.</para>
</listitem>
<listitem>
<para>Improved <link linkend="web-localization">localization</link>
and master page support - Advanced localization features (including
image localization) as well as declarative configuration of what mater
page to apply to different parts of your web application are easy to
perform.</para>
<para> </para>
</listitem>
</itemizedlist>
<para>All you know about ASP.NET development still applies, Spring's
approach is to 'embrace and extend' the basic ASP.NET programming model so
you can be as productive as possible. </para>
<note>
<para>Support for ASP.NET MVC is planned for Spring.NET 2.0 and previews
of our integration with the MVC framework will be mode available when
the final MVC framework ships.</para>
</note>
<para>What follows is a more detailed background and motivation of
features and most importantly the detailed reference manual for using
Spring's web framework. One of the great things about the framework is
that it is not an all or nothing solution. If you choose to use only
dependency injection and bi-directional data binding, that is just fine.
You can incrementally adopt the web framework, addressing problems areas
in your current web application with a specific feature. There is no need
to go 'whole hog' into using all parts of the framework everywhere in your
application.</para>
<para>The Spring.NET distribution ships with a number of Web QuickStarts
and a complete reference application, SpringAir. Web QuickStarts are the
best way to learn each Spring.Web feature by following simple examples,
and the SpringAir reference application has a Spring.Web-enabled frontend
which uses many best practices for Spring.NET web applications, so please
do refer to it as you are reading this (reference) material (see <xref
linkend="springair" />).</para>
</sect1>
<sect1>
<title>Background</title>
<para>One of the objections many developers have to the ASP.NET
programming model is that it is not a "true MVC" (Model-View-Controller)
implementation, because controller-type logic within the page is too
@@ -60,13 +183,11 @@
transliterated to help developers be more productive when using the
upcoming ASP.NET MVC support.</para>
<para></para>
<para>Spring.Web also adds support for applying the dependency injection
principle to one's ASP.NET <literal>Pages</literal> and
<literal>Controls</literal> as well as http modules and custom
provider modules. This means that application developers can easily inject
service dependencies into web controllers by leveraging the power of the
<literal>Controls</literal> as well as http modules and custom provider
modules. This means that application developers can easily inject service
dependencies into web controllers by leveraging the power of the
Spring.NET IoC container. See <link linkend="web-di">Dependency Injection
for ASP.NET Pages</link> for more information.</para>
@@ -74,41 +195,40 @@
should not have to deal with ASP.NET UI controls directly. Such event
handlers should rather work with the presentation model of the page,
represented either as a hierarchy of domain objects or an ADO.NET
<literal>DataSet</literal>. It is for that reason that the Spring.NET
team implemented bidirectional data binding framework to handle the
mapping of values to and from the controls on a page to the underlying
data model. The data binding framework also transparently takes care of
data type conversion and formatting, enabling application developers to
work with fully typed data (domain) objects in the event handlers of
code-behind files. See <link linkend="web-databinding">Bidirectional Data
Binding and Model Management</link> for more information.</para>
<literal>DataSet</literal>. It is for that reason that the Spring.NET team
implemented bidirectional data binding framework to handle the mapping of
values to and from the controls on a page to the underlying data model.
The data binding framework also transparently takes care of data type
conversion and formatting, enabling application developers to work with
fully typed data (domain) objects in the event handlers of code-behind
files. See <link linkend="web-databinding">Bidirectional Data Binding and
Model Management</link> for more information.</para>
<para>The flow of control through an application is another area of
concern that is addressed by Spring.NET Web Framework. Typical ASP.NET
applications will use <literal>Response.Redirect</literal> or
<literal>Server.Transfer</literal> calls within
<literal>Page</literal> logic to navigate to an appropriate page after
an action is executed. This typically leads to hard-coded target URLs in
the <literal>Page</literal>, which is never a good thing. Result
mapping solves this problem by allowing application developers to specify
aliases for action results that map to target URLs based on information in
an external configuration file that can easily be edited. Under
consideration for future releases of Spring.NET is a process management
framework, which will take this approach to another level, allowing you to
control complex page flows in a very simple way. See <link
linkend="web-resultmapping">Result Mapping</link> for more
information.</para>
<literal>Server.Transfer</literal> calls within <literal>Page</literal>
logic to navigate to an appropriate page after an action is executed. This
typically leads to hard-coded target URLs in the <literal>Page</literal>,
which is never a good thing. Result mapping solves this problem by
allowing application developers to specify aliases for action results that
map to target URLs based on information in an external configuration file
that can easily be edited. Under consideration for future releases of
Spring.NET is a process management framework, which will take this
approach to another level, allowing you to control complex page flows in a
very simple way. See <link linkend="web-resultmapping">Result
Mapping</link> for more information.</para>
<para>Standard localization support is also limited in versions of ASP.NET
prior to ASP.NET 2.0. Even though Visual Studio 2003 generates a local
resource file for each ASP.NET <literal>Page</literal> and user
control, those resources are never used by the ASP.NET infrastructure.
This means that application developers have to deal directly with resource
managers whenever they need access to localized resources, which in the
opinion of the Spring.NET team should not be the case. Spring.NET's Web
Framework (hereafter referred to as Spring.Web) adds comprehensive support
for localization using both local resource files and global resources that
are configured within and for a Spring.NET container. See <link
resource file for each ASP.NET <literal>Page</literal> and user control,
those resources are never used by the ASP.NET infrastructure. This means
that application developers have to deal directly with resource managers
whenever they need access to localized resources, which in the opinion of
the Spring.NET team should not be the case. Spring.NET's Web Framework
(hereafter referred to as Spring.Web) adds comprehensive support for
localization using both local resource files and global resources that are
configured within and for a Spring.NET container. See <link
linkend="web-localization">Localization and Message Sources</link> for
more information.</para>
@@ -124,27 +244,18 @@
<para>In order to implement some of the above mentioned features the
Spring.NET team had to extend (as in the object-oriented sense) the
standard ASP.NET <literal>Page</literal> and
<literal>UserControl</literal> classes. This means that in order to
take advantage of the <emphasis>full</emphasis> feature stack of
Spring.Web (most notably bidirectional data binding, localization and
result mapping), your code-behind classes will have to extend Spring.Web
specific base classes such as <literal>Spring.Web.UI.Page</literal>;
however, some very powerful features such as dependency injection for
ASP.NET Pages, Controls, and providers can be leveraged without having to
extend Spring.Web-specific base classes. It is worth stating that by
taking advantage of <emphasis>some</emphasis> of the more useful features
offered by Spring.Web you will be coupling the presentation tier of your
<literal>UserControl</literal> classes. This means that in order to take
advantage of the <emphasis>full</emphasis> feature stack of Spring.Web
(most notably bidirectional data binding, localization and result
mapping), your code-behind classes will have to extend Spring.Web specific
base classes such as <literal>Spring.Web.UI.Page</literal>; however, some
very powerful features such as dependency injection for ASP.NET Pages,
Controls, and providers can be leveraged without having to extend
Spring.Web-specific base classes. It is worth stating that by taking
advantage of <emphasis>some</emphasis> of the more useful features offered
by Spring.Web you will be coupling the presentation tier of your
application(s) to Spring.Web. The choice of whether or not this is
appropriate is, of course, left to you.</para>
<para>Finally, please be aware that the standard Spring.NET distribution
(as of v1.1) ships with a number of Web QuickStarts and a complete
reference application, SpringAir. Web QuickStarts are the best way to
learn each Spring.Web feature by following simple examples, and the
SpringAir reference application has a Spring.Web-enabled frontend which
uses many best practices for Spring.NET web applications, so please do
refer to it as you are reading this (reference) material (see <xref
linkend="springair" />).</para>
</sect1>
<sect1 xml:id="web-contexts">
@@ -159,13 +270,12 @@
means that all of the controllers (ASP.NET <literal>Page</literal>s)
that make up a typical Spring.Web enabled application will be configured
using the same standard Spring.NET XML configuration syntax. Spring.Web
uses a custom <literal>PageHandlerFactory</literal> implementation
to load and configure a Spring.NET IoC container, which is in turn used
to locate an appropriate <literal>Page</literal> to handle a HTTP
request. The <literal>WebSupportModule</literal> configures
miscellaneous Spring infrastructure classes for use in a web
environment, for example setting the storage strategy of
<literal>LogicalThreadContext</literal> to be
uses a custom <literal>PageHandlerFactory</literal> implementation to
load and configure a Spring.NET IoC container, which is in turn used to
locate an appropriate <literal>Page</literal> to handle a HTTP request.
The <literal>WebSupportModule</literal> configures miscellaneous Spring
infrastructure classes for use in a web environment, for example setting
the storage strategy of <literal>LogicalThreadContext</literal> to be
<literal>HybridContextStorage</literal>.</para>
<para>The instantiation and configuration of the Spring.NET IoC
@@ -202,8 +312,8 @@
infrastructure to use Spring.NET's page factory, which will in turn
create instances of the appropriate <literal>.aspx</literal>
<literal>Page</literal>, (possibly) inject dependencies into said
<literal>Page</literal> (as required), and then forward the handling
of the request to said <literal>Page</literal>.</para>
<literal>Page</literal> (as required), and then forward the handling of
the request to said <literal>Page</literal>.</para>
<para>After the Spring.Web page factory is configured, you will also
need to define a root application context by adding a Spring.NET
@@ -265,8 +375,8 @@
<listitem>
<para>The custom configuration section handler is of the type
<literal>Spring.Context.Support.WebContextHandler</literal>
which will in turn instantiate an IoC container of the type
<literal>Spring.Context.Support.WebContextHandler</literal> which
will in turn instantiate an IoC container of the type
<literal>Spring.Context.Support.WebApplicationContext</literal>.
This will ensure that all of the features provided by Spring.Web are
handled properly (such as request and session-scoped object
@@ -410,8 +520,8 @@
<para>Spring.Web builds on top of the feature set and capabilities of
ASP.NET; one example of this can be seen the way that Spring.Web has used
the code-behind class of the <literal>Page</literal> mechanism to
satisfy the <literal>Controller</literal> portion of the MVC architectural
the code-behind class of the <literal>Page</literal> mechanism to satisfy
the <literal>Controller</literal> portion of the MVC architectural
pattern. In MVC-based (web) applications, the
<literal>Controller</literal> is typically a thin wrapper around one or
more service objects. In the specific case of Spring.Web, the Spring.NET
@@ -419,12 +529,11 @@
be easily injected into <literal>Page</literal>
<literal>Controller</literal>s. Accordingly, Spring.Web provides first
class support for dependency injection in ASP.NET
<literal>Page</literal>s. This allows application developers to inject
any required service object dependencies (and indeed any other
dependencies) into their <literal>Page</literal>s using standard
Spring.NET configuration instead of having to rely on custom service
locators or manual object lookups in a Spring.NET application
context.</para>
<literal>Page</literal>s. This allows application developers to inject any
required service object dependencies (and indeed any other dependencies)
into their <literal>Page</literal>s using standard Spring.NET
configuration instead of having to rely on custom service locators or
manual object lookups in a Spring.NET application context.</para>
<para>Once an application developer has <link
linkend="web-configuration">configured</link> the Spring.NET web
@@ -476,15 +585,14 @@
pages from the configuration of other .NET classes is in the value passed
to the <literal>type</literal> attribute. As can be seen in the above
configuration snippet the <literal>type</literal> name is actually the
path to the <literal>.aspx</literal> file for the
<literal>Page</literal>, relative to the directory context it is
defined in. In the case of the above example, those definitions are in the
root context so <literal>Login.aspx</literal> and
<literal>Default.aspx</literal> also must be in the root of the web
application's virtual directory. The master page is defined using an
absolute path because it could conceivably be referenced from child
contexts that are defined within subdirectories of the web
application.</para>
path to the <literal>.aspx</literal> file for the <literal>Page</literal>,
relative to the directory context it is defined in. In the case of the
above example, those definitions are in the root context so
<literal>Login.aspx</literal> and <literal>Default.aspx</literal> also
must be in the root of the web application's virtual directory. The master
page is defined using an absolute path because it could conceivably be
referenced from child contexts that are defined within subdirectories of
the web application.</para>
<para>The astute reader may have noticed that the definitions for the
<literal>Login</literal> and <literal>Default</literal> pages don't
@@ -493,13 +601,13 @@
Spring.NET, where the <literal>id</literal> or <literal>name</literal>
attributes are typically mandatory (although not always, as in the case of
inner object definitions). This is actually intentional, because in the
case of Spring.Web <literal>Page</literal>
<literal>Controller</literal> instances one typically wants to use the
name of the <literal>.aspx</literal> file name as the identifier. If an
case of Spring.Web <literal>Page</literal> <literal>Controller</literal>
instances one typically wants to use the name of the
<literal>.aspx</literal> file name as the identifier. If an
<literal>id</literal> is not specified, the Spring.Web infrastructure will
simply use the name of the <literal>.aspx</literal> file as the object
identifier (minus any leading path information, and minus the file
extension too). </para>
extension too).</para>
<para>Nothing prevents an application developer from specifying an
<literal>id</literal> or <literal>name</literal> value explicitly; one use
@@ -536,10 +644,10 @@
<para>You can perform dependency injection on custom HTTP modules
through the use of the class
<literal>Spring.Context.Support.HttpApplicationConfigurer</literal>.
You register your custom HTTP module as you would normally, for example
a module of the type <literal>HtmlCommentAppenderModule</literal>,
taken from the Web Quickstart, appends additional comments into the http
<literal>Spring.Context.Support.HttpApplicationConfigurer</literal>. You
register your custom HTTP module as you would normally, for example a
module of the type <literal>HtmlCommentAppenderModule</literal>, taken
from the Web Quickstart, appends additional comments into the http
response. It is registered as shown below</para>
<programlisting language="myxml">&lt;httpModules&gt;
@@ -706,7 +814,7 @@ class MyControl : Control, ISupportsWebDependencyInjection
</sect1>
<sect1 xml:id="web-objectscope">
<title>Object Scope</title>
<title>Web object scopes</title>
<para>Spring.NET web applications support an additional attribute within
object definition elements that allows you to control the scope of an
@@ -822,8 +930,8 @@ class MyControl : Control, ISupportsWebDependencyInjection
using the default content supplied in the master page.</para>
<para>Both the <literal>ContentPlaceHolder</literal> and
<literal>Content</literal> controls can contain any valid ASP.NET
markup: HTML, standard ASP.NET controls, user controls, etc.</para>
<literal>Content</literal> controls can contain any valid ASP.NET markup:
HTML, standard ASP.NET controls, user controls, etc.</para>
<tip>
<title>VS.NET 2003 issue</title>
@@ -840,9 +948,9 @@ class MyControl : Control, ISupportsWebDependencyInjection
<sect2 xml:id="web-childpage-linking">
<title>Linking child pages to their master</title>
<para>The <literal>Spring.Web.UI.Page</literal> class exposes a
property called <literal>MasterPageFile</literal>, which can be used to
specify the master page.</para>
<para>The <literal>Spring.Web.UI.Page</literal> class exposes a property
called <literal>MasterPageFile</literal>, which can be used to specify
the master page.</para>
<para>The recommended way to do this is by leveraging the Spring.NET IoC
container and creating definitions similar to the following:</para>
@@ -905,7 +1013,8 @@ class MyControl : Control, ISupportsWebDependencyInjection
management methods: <literal>InitializeModel</literal>,
<literal>LoadModel</literal> and <literal>SaveModel</literal>. This is
perhaps best illustrated by an example from the SpringAir reference
application. First, let's take a look at the page markup:<programlisting language="myxml">&lt;%@ Page Language="c#" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %&gt;
application. First, let's take a look at the page markup:<programlisting
language="myxml">&lt;%@ Page Language="c#" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %&gt;
&lt;asp:Content ID="body" ContentPlaceHolderID="body" runat="server"&gt;
&lt;div style="text-align: center"&gt;
@@ -1053,8 +1162,8 @@ class MyControl : Control, ISupportsWebDependencyInjection
<literal>TripPoint</literal> class to represent departure and return,
which are exposed as <literal>StartingFrom</literal> and
<literal>ReturningFrom</literal> properties. It also uses
<literal>TripMode</literal> enumeration to specify whether the trip is
one way or return trip, which is exposed as <literal>Mode</literal>
<literal>TripMode</literal> enumeration to specify whether the trip is one
way or return trip, which is exposed as <literal>Mode</literal>
property.</para>
<para>Finally, let's see the code-behind class that ties everything
@@ -1266,9 +1375,9 @@ class MyControl : Control, ISupportsWebDependencyInjection
commonly used binding type, <literal>SimpleExpressionBinding</literal>.
The <literal>SimpleExpressionBinding</literal> is what we used in the
example at the beginning of this section to bind our web form to a
<literal>Trip</literal> instance. It uses Spring.NET Expression
Language to extract and to set values within source and target objects.
We discussed <literal>sourceExpression</literal> and
<literal>Trip</literal> instance. It uses Spring.NET Expression Language
to extract and to set values within source and target objects. We
discussed <literal>sourceExpression</literal> and
<literal>targetExpression</literal> arguments earlier, so let's focus on
the remaining ones.</para>
@@ -1290,8 +1399,8 @@ class MyControl : Control, ISupportsWebDependencyInjection
form doesn't have a simple one-to-one mapping to presentation model.
In our earlier trip form example, the presentation model was
intentionally designed to allow for simple one-to-one mappings. For
the sake of discussion, let's add the <literal>Airport</literal>
class and modify our <literal>TripPoint</literal> class like
the sake of discussion, let's add the <literal>Airport</literal> class
and modify our <literal>TripPoint</literal> class like
this:<programlisting language="csharp">namespace SpringAir.Domain
{
[Serializable]
@@ -1346,14 +1455,14 @@ class MyControl : Control, ISupportsWebDependencyInjection
}
}
}</programlisting>Instead of the string property
<literal>AirportCode</literal>, our <literal>TripPoint</literal>
class now exposes an <literal>Airport</literal> property of type
<literal>AirportCode</literal>, our <literal>TripPoint</literal> class
now exposes an <literal>Airport</literal> property of type
<literal>Airport</literal>, which is defined above. Now we have a
problem: what used to be a simple string to string binding, with the
airport code selected in a dropdown being copied directly into the
TripPoint.AirportCode property and vice versa, now becomes a not so
simple string to <literal>Airport</literal> binding, so let's see
how we can solve this mismatch problem.</para>
simple string to <literal>Airport</literal> binding, so let's see how
we can solve this mismatch problem.</para>
<para>First of all, binding from the model to the control is still
very straight forward. We just need to set up one-way bindings from
@@ -1369,17 +1478,16 @@ class MyControl : Control, ISupportsWebDependencyInjection
might be able to set <literal>Code</literal> property of the
<literal>Airport</literal> object, but that will likely make the
<literal>Airport.Name</literal> property invalid. What we really want
do is find an instance of the <literal>Airport</literal> class
based on the airport code and set the
<literal>TripPoint.Airport</literal> property to it. Fortunately, this
is very simple to do with Spring.NET data binding, especially because
we already have <literal>airportDao</literal> object defined in the
Spring context, which has <literal>GetAirport(string
airportCode)</literal> finder method. All we need to do is set up data
bindings from source to target that will invoke this finder method
when evaluating the source expression. Our complete set of bindings
for these two drop down lists will then look like
this:<programlisting language="csharp">protected override void InitializeDataBindings()
do is find an instance of the <literal>Airport</literal> class based
on the airport code and set the <literal>TripPoint.Airport</literal>
property to it. Fortunately, this is very simple to do with Spring.NET
data binding, especially because we already have
<literal>airportDao</literal> object defined in the Spring context,
which has <literal>GetAirport(string airportCode)</literal> finder
method. All we need to do is set up data bindings from source to
target that will invoke this finder method when evaluating the source
expression. Our complete set of bindings for these two drop down lists
will then look like this:<programlisting language="csharp">protected override void InitializeDataBindings()
{
BindingManager.AddBinding("@(airportDao).GetAirport(leavingFromAirportCode.SelectedValue)", "Trip.StartingFrom.Airport", BindingDirection.SourceToTarget);
BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.Airport.Code", BindingDirection.TargetToSource);
@@ -1407,7 +1515,8 @@ class MyControl : Control, ISupportsWebDependencyInjection
Spring.Globalization.Formatters namespace, but if you have
requirements that cannot be satisfied by one of the standard
formatters it is easy enough to write your own -- all you need to do
is implement a very simple IFormatter interface:<programlisting language="csharp">public interface IFormatter
is implement a very simple IFormatter interface:<programlisting
language="csharp">public interface IFormatter
{
string Format(object value);
object Parse(string value);
@@ -1439,9 +1548,8 @@ class MyControl : Control, ISupportsWebDependencyInjection
<sect3 xml:id="web-binding-events">
<title>Data Binding Events</title>
<para>Spring.Web's base <literal>Page</literal> class adds two
events to the standard .NET page lifecycle -
<literal>DataBound</literal> and
<para>Spring.Web's base <literal>Page</literal> class adds two events
to the standard .NET page lifecycle - <literal>DataBound</literal> and
<literal>DataUnbound</literal>.</para>
<para>The <literal>DataUnbound</literal> event is fired after the data
@@ -1829,8 +1937,8 @@ protected override void InitializeDataBindings()
</important>
<para>The global resources are cached within the Spring.NET
<literal>IApplicationContext</literal> and are accessible through
the Spring.NET <literal>IMessageSource</literal> interface.</para>
<literal>IApplicationContext</literal> and are accessible through the
Spring.NET <literal>IMessageSource</literal> interface.</para>
<para>The Spring.Web <literal>Page</literal> and
<literal>UserControl</literal> classes have a reference to their owning
@@ -1857,11 +1965,11 @@ protected override void InitializeDataBindings()
<para>A localizer is simply an object that implements the
<literal>Spring.Globalization.ILocalizer</literal> interface.
<literal>Spring.Globalization.AbstractLocalizer</literal> is
provided as a convenient base class for localization: this class has one
abstract method, <literal>LoadResources</literal>. This method must load
and return a list of all the resources that must be automatically
applied from the resource store.</para>
<literal>Spring.Globalization.AbstractLocalizer</literal> is provided as
a convenient base class for localization: this class has one abstract
method, <literal>LoadResources</literal>. This method must load and
return a list of all the resources that must be automatically applied
from the resource store.</para>
<para>Spring.NET ships with one concrete implementation of a localizer,
<literal>Spring.Globalization.Localizers.ResourceSetLocalizer</literal>,
@@ -1872,8 +1980,8 @@ protected override void InitializeDataBindings()
store resources within the files in a web application instead of as
embedded resources in an assembly. Of course, if an application
developer would rather store such resources in a database, he or she can
write their own <literal>ILocalizer</literal> implementation that
will load a list of resources to apply from a database.</para>
write their own <literal>ILocalizer</literal> implementation that will
load a list of resources to apply from a database.</para>
<para>As mentioned previously, one would typically configure the
localizer to be used within an abstract base definition for those pages
@@ -1896,9 +2004,9 @@ protected override void InitializeDataBindings()
automatically one can completely omit the localizer definition.</para>
<para>One last thing to note is that Spring.NET
<literal>UserControl</literal> instances will (by default) inherit
the localizer and other localization settings from the page that they
are contained within, but one can similarly also override that behavior
<literal>UserControl</literal> instances will (by default) inherit the
localizer and other localization settings from the page that they are
contained within, but one can similarly also override that behavior
using explicit dependency injection.</para>
</sect2>
@@ -2011,17 +2119,16 @@ protected override void InitializeDataBindings()
<para>In addition to global and local resource management, Spring.Web
also adds support for user culture management by exposing the current
<literal>CultureInfo</literal> through the
<literal>UserCulture</literal> property on the
<literal>Page</literal> and <literal>UserControl</literal>
classes.</para>
<literal>UserCulture</literal> property on the <literal>Page</literal>
and <literal>UserControl</literal> classes.</para>
<para>The <literal>UserCulture</literal> property will simply delegate
culture resolution to an implementation of
<literal>Spring.Globalization.ICultureResolver</literal> interface.
One can specify exactly which culture resolver to use by configuring the
<literal>Spring.Globalization.ICultureResolver</literal> interface. One
can specify exactly which culture resolver to use by configuring the
<literal>CultureResolver</literal> property of the
<literal>Page</literal> class in the relevant object definition as
shown below.</para>
<literal>Page</literal> class in the relevant object definition as shown
below.</para>
<programlisting language="myxml">&lt;object name="BasePage" abstract="true"&gt;
&lt;property name="CultureResolver"&gt;
@@ -2030,43 +2137,42 @@ protected override void InitializeDataBindings()
&lt;/object&gt;</programlisting>
<para>Several useful implementations of
<literal>ICultureResolver</literal> ship as part of Spring.Web, so
it is unlikely that application developers will have to implement their
own culture resolver. However, if one does have such a requirement, the
<literal>ICultureResolver</literal> ship as part of Spring.Web, so it is
unlikely that application developers will have to implement their own
culture resolver. However, if one does have such a requirement, the
resulting implementation should be fairly straightforward as there are
only two methods that one need implement. The following sections discuss
each available implementation of the
<literal>ICultureResolver</literal> interface.</para>
each available implementation of the <literal>ICultureResolver</literal>
interface.</para>
<sect3>
<title>DefaultWebCultureResolver</title>
<para>This is default culture resolver implementation. It will be used
if one does not specify a culture resolver for a page, or if one
explicitly injects a <literal>DefaultWebCultureResolver</literal>
into a page definition explicitly. The latter case (explicit
injection) is sometimes useful because it allows one to specify a
culture that should always be used by providing a value to the
explicitly injects a <literal>DefaultWebCultureResolver</literal> into
a page definition explicitly. The latter case (explicit injection) is
sometimes useful because it allows one to specify a culture that
should always be used by providing a value to the
<literal>DefaultCulture</literal> property on the resolver.</para>
<para>The <literal>DefaultWebCultureResolver</literal> will first
look at the <literal>DefaultCulture</literal> property and return its
value if said property value is not null. If it is null, the
<literal>DefaultWebCultureResolver</literal> will fall back to
request header inspection, and finally, if no
<literal>'Accept-Lang'</literal> request headers are present it will
return the UI culture of the currently executing thread.</para>
<para>The <literal>DefaultWebCultureResolver</literal> will first look
at the <literal>DefaultCulture</literal> property and return its value
if said property value is not null. If it is null, the
<literal>DefaultWebCultureResolver</literal> will fall back to request
header inspection, and finally, if no <literal>'Accept-Lang'</literal>
request headers are present it will return the UI culture of the
currently executing thread.</para>
</sect3>
<sect3>
<title>RequestCultureResolver</title>
<para>This resolver works in a similar way to the
<literal>DefaultWebCultureResolver</literal> with the exception
that it always checks request headers <emphasis>first</emphasis>, and
only then falls back to the value of the
<literal>DefaultCulture</literal> property or the culture code of the
current thread.</para>
<literal>DefaultWebCultureResolver</literal> with the exception that
it always checks request headers <emphasis>first</emphasis>, and only
then falls back to the value of the <literal>DefaultCulture</literal>
property or the culture code of the current thread.</para>
</sect3>
<sect3>
@@ -2086,16 +2192,16 @@ protected override void InitializeDataBindings()
of the <literal>DefaultWebCultureResolver</literal>.</para>
<warning>
<para><literal>CookieCultureResolver</literal> will not work if
your application uses <literal>localhost</literal> as the server
URL, which is a typical setting in a development environment.</para>
<para><literal>CookieCultureResolver</literal> will not work if your
application uses <literal>localhost</literal> as the server URL,
which is a typical setting in a development environment.</para>
<para>In order to work around this limitation you should use
<literal>SessionCultureResolver</literal> during development and
switch to <literal>CookieCultureResolver</literal> before you
deploy the application in a production. This is easily accomplished
in Spring.Web (simply change the config file) but is something that
you should be aware of.</para>
switch to <literal>CookieCultureResolver</literal> before you deploy
the application in a production. This is easily accomplished in
Spring.Web (simply change the config file) but is something that you
should be aware of.</para>
</warning>
</sect3>
</sect2>
@@ -2106,14 +2212,14 @@ protected override void InitializeDataBindings()
<para>In order to be able to change the culture application developers
will need to use one of the culture resolvers that support culture
changes, such as <literal>SessionCultureResolver</literal> or
<literal>CookieCultureResolver</literal>. One could also write a
custom <literal>ICultureResolver</literal> that will persist culture
<literal>CookieCultureResolver</literal>. One could also write a custom
<literal>ICultureResolver</literal> that will persist culture
information in a database, as part of a user's profile.</para>
<para>Once that requirement is satisfied, all that one need do is to set
the <literal>UserCulture</literal> property to a new
<literal>CultureInfo</literal> object before the page is rendered.
In the following <literal>.aspx</literal> example, there are two link
<literal>CultureInfo</literal> object before the page is rendered. In
the following <literal>.aspx</literal> example, there are two link
buttons that can be used to change the user's culture. In the
code-behind, this is all one need do to set the new culture. A code
snippet for the code-behind file
@@ -2159,8 +2265,8 @@ private void SetLanguage(object sender, CommandEventArgs e)
flow.</para>
<para>In Spring.Web, a logical result is encapsulated and defined by the
<literal>Result</literal> class; because of this one can configure
results just like any other object:</para>
<literal>Result</literal> class; because of this one can configure results
just like any other object:</para>
<programlisting language="myxml">
&lt;objects xmlns="http://www.springframework.net"&gt;
@@ -2242,10 +2348,10 @@ private void SetLanguage(object sender, CommandEventArgs e)
<para>The above example shows independent result object definitions, which
are useful for global results such as a home- and login- page.
<literal>Result</literal> definitions that are only going to be used
by one page should be simply embedded within the definition of a page,
either as inner object definitions or using a special shortcut notation
for defining a result definition:</para>
<literal>Result</literal> definitions that are only going to be used by
one page should be simply embedded within the definition of a page, either
as inner object definitions or using a special shortcut notation for
defining a result definition:</para>
<programlisting language="myxml">
&lt;object type="~/UI/Forms/UserRegistration.aspx" parent="basePage"&gt;
@@ -2441,7 +2547,7 @@ protected override void OnInit(EventArgs e)
additional information. There are a few other controls not documented
here, please check the SDK docs for their descriptions.</para>
<sect2>
<sect2 xml:id="web-validation-controls">
<title>Validation Controls</title>
<para>The location in the web page where validation errors are to be
@@ -2464,8 +2570,8 @@ protected override void OnInit(EventArgs e)
<para>Some standard controls are not easy to use with Spring's
databinding support. Examples are check boxes and ratio button groups.
In this case you should use the <literal>CheckBoxList</literal> and
<literal>RadioButtonGroup</literal> controls. Databinding itself can
be done using the <link
<literal>RadioButtonGroup</literal> controls. Databinding itself can be
done using the <link
linkend="web-databindingpanel">DataBindingPanel</link> instead of the
using the BindingManager API within the code behind page.</para>
</sect2>
@@ -2488,4 +2594,4 @@ protected override void OnInit(EventArgs e)
injection</link> for more information.</para>
</sect2>
</sect1>
</chapter>
</chapter>

View File

@@ -16,10 +16,16 @@
* limitations under the License.
*/
-->
<chapter xml:id="webservices" xmlns="http://docbook.org/ns/docbook" version="5">
<chapter version="5" xml:id="webservices"
xmlns="http://docbook.org/ns/docbook"
xmlns:ns6="http://www.w3.org/1999/xlink"
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
xmlns:ns4="http://www.w3.org/1999/xhtml"
xmlns:ns3="http://www.w3.org/2000/svg"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Web Services</title>
<sect1>
<sect1 xml:id="web-services-introduction">
<title>Introduction</title>
<para>While the out-of-the-box support for web services in .NET is
@@ -66,7 +72,7 @@
example named 'calculator'. More information can be found here '<link
linkend="websvc-example">Web Services example</link>'.</para>
<sect2>
<sect2 xml:id="web-services-noasmx">
<title>Removing the need for .asmx files</title>
<para>Unlike web pages, which use <literal>.aspx</literal> files to
@@ -107,8 +113,8 @@ namespace MyComany.MyApp.Services
is:</para>
<para><emphasis> 1. Register the
<literal>Spring.Web.Services.WebServiceFactoryHandler</literal> as
the HTTP handler for <literal>*.asmx</literal> requests within one's
<literal>Spring.Web.Services.WebServiceFactoryHandler</literal> as the
HTTP handler for <literal>*.asmx</literal> requests within one's
<literal>web.config</literal> file. </emphasis></para>
<programlisting language="myxml">
@@ -156,7 +162,7 @@ namespace MyComany.MyApp.Services
<programlisting>http://localhost/MyWebApp/HelloWorld.asmx</programlisting>
</sect2>
<sect2>
<sect2 xml:id="web-services-di">
<title>Injecting dependencies into web services</title>
<para>For arguments sake, let's say that we want to change the
@@ -228,8 +234,8 @@ namespace MyApp.Services
&lt;/object&gt;
</programlisting>
<para>The <literal>WebServiceExporter</literal> copies the existing
web service and method attribute values to the proxy implementation (if
<para>The <literal>WebServiceExporter</literal> copies the existing web
service and method attribute values to the proxy implementation (if
indeed any are defined). Please note however that existing values can be
overridden by setting properties on the
<literal>WebServiceExporter</literal>.</para>
@@ -248,7 +254,7 @@ namespace MyApp.Services
</tip>
</sect2>
<sect2>
<sect2 xml:id="web-services-exporter">
<title>Exposing PONOs as Web Services</title>
<para>Now that we are generating a server-side proxy for the service,
@@ -260,12 +266,12 @@ namespace MyApp.Services
target service's methods.</para>
<para>This means that we can safely remove the
<literal>WebService</literal> and <literal>WebMethod</literal>
attribute declarations from the service implementation, and what we are
left with is a plain old .NET object (a PONO). The example above would
still work, because the proxy generator will automatically add
<literal>WebMethod</literal> attributes to all methods of the
exported interfaces.</para>
<literal>WebService</literal> and <literal>WebMethod</literal> attribute
declarations from the service implementation, and what we are left with
is a plain old .NET object (a PONO). The example above would still work,
because the proxy generator will automatically add
<literal>WebMethod</literal> attributes to all methods of the exported
interfaces.</para>
<para>However, that is still not the ideal solution. You would lose
information that the optional <literal>WebService</literal> and
@@ -424,11 +430,10 @@ namespace MyApp.Services
<para>Spring.NET provides a simple <literal>IFactoryObject</literal>
implementation that will generate a <emphasis>"proxy for
proxy"</emphasis> (however obtuse that may sound). Basically, the
<literal>Spring.Web.Services.WebServiceProxyFactory</literal> class
will create a proxy for the VS.NET- / WSDL-generated proxy that
implements a specified service interface (thus solving the problem with
the web-service proxy classes mentioned in the preceding
paragraph).</para>
<literal>Spring.Web.Services.WebServiceProxyFactory</literal> class will
create a proxy for the VS.NET- / WSDL-generated proxy that implements a
specified service interface (thus solving the problem with the
web-service proxy classes mentioned in the preceding paragraph).</para>
<para>At this point, an example may well be more illustrative in
conveying what is happening; consider the following interface definition
@@ -457,26 +462,25 @@ namespace MyCompany.Services
<para>What is important to notice is that the underlying implementation
class for the web service does not have to implement the same
<literal>IHelloWorld</literal> service interface... so long as
matching methods with compliant signatures exist (a kind of duck
typing), Spring.NET will be able to create a proxy and delegate method
calls appropriately. If a matching method cannot be found, the
Spring.NET infrastructure code will throw an exception.</para>
<literal>IHelloWorld</literal> service interface... so long as matching
methods with compliant signatures exist (a kind of duck typing),
Spring.NET will be able to create a proxy and delegate method calls
appropriately. If a matching method cannot be found, the Spring.NET
infrastructure code will throw an exception.</para>
<para>That said, if you control both the client and the server it is
probably a good idea to make sure that the web service class on the
server implements the service interface, especially if you plan on
exporting it using Spring.NET's
<literal>WebServiceExporter</literal>, which requires an interface
in order to work.</para>
exporting it using Spring.NET's <literal>WebServiceExporter</literal>,
which requires an interface in order to work.</para>
</sect2>
<sect2>
<title>Generating proxies dynamically</title>
<para>The <literal>WebServiceProxyFactory</literal> can also
dynamically generate a web-service proxy. The XML object definition for
this factory object is shown below</para>
<para>The <literal>WebServiceProxyFactory</literal> can also dynamically
generate a web-service proxy. The XML object definition for this factory
object is shown below</para>
<programlisting language="myxml">
&lt;object id="calculatorService" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services"&gt;
@@ -507,8 +511,8 @@ namespace MyCompany.Services
<sect2>
<title>Configuring the proxy instance</title>
<para>The <literal>WebServiceProxyFactory</literal> also implements
the interface,
<para>The <literal>WebServiceProxyFactory</literal> also implements the
interface,
<literal>Spring.Objects.Factory.IConfigurableFactoryObject</literal>,
allowing to specify configuration for the product that the
<literal>WebServiceProxyFactory</literal> creates. This is done by
@@ -550,4 +554,4 @@ namespace MyCompany.Services
on our wiki.</para>
</sect2>
</sect1>
</chapter>
</chapter>

View File

@@ -1,4 +1,4 @@
THE SPRING.NET FRAMEWORK, Release 1.2.0 M1 (August 14, 2008)
THE SPRING.NET FRAMEWORK, Release 1.2.0 RC1 (October 15, 2008)
--------------------------------------------------------------------
http://www.springframework.net/

View File

@@ -14,7 +14,7 @@ using System.Reflection;
[assembly: AssemblyConfiguration("net-2.0.win32; Release")]
#endif
[assembly: AssemblyCompany("http://www.springframework.net")]
[assembly: AssemblyProduct("Spring.NET Framework 1.2")]
[assembly: AssemblyProduct("Spring.NET Framework 1.2.0 RC1")]
[assembly: AssemblyCopyright("Copyright 2002-2007 Spring.NET Framework Team.")]
[assembly: AssemblyTrademark("Apache License, Version 2.0")]
[assembly: AssemblyCulture("")]

View File

@@ -394,6 +394,7 @@
<Compile Include="Aspects\Cache\InvalidateCacheAdvisor.cs" />
<Compile Include="Aspects\Cache\InvalidateCacheAdvice.cs" />
<Compile Include="Aspects\Exceptions\ExceptionHandlerAdvice.cs" />
<Compile Include="Aspects\Exceptions\ExecuteSpelExceptionHandler.cs" />
<Compile Include="Aspects\Exceptions\LogExceptionHandler.cs" />
<Compile Include="Aspects\Exceptions\ReturnValueExceptionHandler.cs" />
<Compile Include="Aspects\Exceptions\SwallowExceptionHandler.cs" />

View File

@@ -976,6 +976,7 @@
<Compile Include="Util\AssertUtils.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Util\AttributeUtils.cs" />
<Compile Include="Util\CollectionUtils.cs">
<SubType>Code</SubType>
</Compile>

View File

@@ -590,6 +590,16 @@ namespace Spring.Messaging.Nms.Connections
target.Rollback();
}
/// <summary>
/// Gets or sets the request timeout.
/// </summary>
/// <value>The request timeout.</value>
public TimeSpan RequestTimeout
{
get { return target.RequestTimeout; }
set { target.RequestTimeout = value; }
}
/// <summary>
/// Gets a value indicating whether this <see cref="CachedSession"/> is transacted.
/// </summary>

View File

@@ -474,6 +474,12 @@ namespace Spring.Messaging.Nms.Connections
// don't pass the call to the target.
}
public TimeSpan RequestTimeout
{
get { return target.RequestTimeout; }
set { target.RequestTimeout = value; }
}
public void Start()
{
// Handle start method: track started state.
@@ -505,16 +511,6 @@ namespace Spring.Messaging.Nms.Connections
}
public ISession CreateSession(AcknowledgementMode acknowledgementMode, TimeSpan requestTimeout)
{
ISession session = singleConnectionFactory.GetSession(target, acknowledgementMode, requestTimeout);
if (session != null)
{
return session;
}
return target.CreateSession(acknowledgementMode, requestTimeout);
}
#region Pass through implementations to the target connection

View File

@@ -30,13 +30,27 @@ namespace Spring.Messaging.Nms.Support.Converter
/// <author>Mark Pollack</author>
public interface ITypeMapper
{
/// <summary>
/// Gets the name of the field in the message that has type information..
/// </summary>
/// <value>The name of the type id field.</value>
string TypeIdFieldName
{
get;
}
/// <summary>
/// Convert from a type to a string.
/// </summary>
/// <param name="typeOfObjectToConvert">The type of object to convert.</param>
/// <returns></returns>
string FromType(Type typeOfObjectToConvert);
/// <summary>
/// Convert from a string to a type
/// </summary>
/// <param name="typeId">The type id.</param>
/// <returns></returns>
Type ToType(string typeId);
}
}

View File

@@ -1,3 +1,22 @@
#region License
/*
* Copyright <20> 2002-2006 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
using System;
@@ -6,6 +25,9 @@ using Spring.Core.TypeResolution;
namespace Spring.Messaging.Nms.Support.Converter
{
/// <summary>
/// Provides a layer of indirection when adding the 'type' of the object as a message property.
/// </summary>
public class TypeMapper : ITypeMapper
{
private string defaultNamespace;
@@ -15,12 +37,13 @@ namespace Spring.Messaging.Nms.Support.Converter
private IDictionary idTypeMapping;
private IDictionary typeIdMapping;
//TODO generalize?
private string defaultHashtableTypeId = "Hashtable";
private Type defaultHashtableClass = typeof(Hashtable);
/// <summary>
/// Initializes a new instance of the <see cref="TypeMapper"/> [ERROR: invalid expression DeclaringTypeKind].
/// </summary>
public TypeMapper()
{
idTypeMapping = new Hashtable();
@@ -28,23 +51,40 @@ namespace Spring.Messaging.Nms.Support.Converter
}
/// <summary>
/// Gets or sets the id type mapping.
/// </summary>
/// <value>The id type mapping.</value>
public IDictionary IdTypeMapping
{
get { return idTypeMapping; }
set { idTypeMapping = value; }
}
/// <summary>
/// Gets the name of the field in the message that has type information..
/// </summary>
/// <value>The name of the type id field.</value>
public string TypeIdFieldName
{
get { return "__TypeId__"; }
}
/// <summary>
/// Sets the default hashtable class.
/// </summary>
/// <value>The default hashtable class.</value>
public Type DefaultHashtableClass
{
set { defaultHashtableClass = value; }
}
/// <summary>
/// Convert from a type to a string.
/// </summary>
/// <param name="typeOfObjectToConvert">The type of object to convert.</param>
/// <returns></returns>
public string FromType(Type typeOfObjectToConvert)
{
@@ -62,6 +102,11 @@ namespace Spring.Messaging.Nms.Support.Converter
}
}
/// <summary>
/// Convert from a string to a type
/// </summary>
/// <param name="typeId">The type id.</param>
/// <returns></returns>
public Type ToType(string typeId)
{
if (idTypeMapping.Contains(typeId))
@@ -82,6 +127,10 @@ namespace Spring.Messaging.Nms.Support.Converter
}
/// <summary>
/// Gets or sets the default namespace.
/// </summary>
/// <value>The default namespace.</value>
public string DefaultNamespace
{
get
@@ -95,6 +144,10 @@ namespace Spring.Messaging.Nms.Support.Converter
}
/// <summary>
/// Gets or sets the default name of the assembly.
/// </summary>
/// <value>The default name of the assembly.</value>
public string DefaultAssemblyName
{
get
@@ -107,6 +160,9 @@ namespace Spring.Messaging.Nms.Support.Converter
}
}
/// <summary>
/// Afters the properties set.
/// </summary>
public void AfterPropertiesSet()
{
ValidateIdTypeMapping();

View File

@@ -9,6 +9,10 @@ using Spring.Messaging.Nms.Support.Converter;
namespace Spring.Messaging.Nms.Support.Converter
{
/// <summary>
/// Convert an object via XML serialization for sending via an ITextMessage
/// </summary>
/// <author>Mark Pollack</author>
public class XmlMessageConverter : IMessageConverter
{
private IMessageConverter defaultMessageConverter = new SimpleMessageConverter();
@@ -17,11 +21,24 @@ namespace Spring.Messaging.Nms.Support.Converter
private ITypeMapper typeMapper;
/// <summary>
/// Sets the type mapper.
/// </summary>
/// <value>The type mapper.</value>
public ITypeMapper TypeMapper
{
set { typeMapper = value; }
}
/// <summary>
/// Convert a .NET object to a NMS Message using the supplied session
/// to create the message object.
/// </summary>
/// <param name="objectToConvert">the object to convert</param>
/// <param name="session">the Session to use for creating a NMS Message</param>
/// <returns>the NMS Message</returns>
/// <throws>NMSException if thrown by NMS API methods </throws>
/// <throws>MessageConversionException in case of conversion failure </throws>
public IMessage ToMessage(object objectToConvert, ISession session)
{
if (objectToConvert == null)
@@ -49,7 +66,12 @@ namespace Spring.Messaging.Nms.Support.Converter
}
}
private string GetXmlString(object objectToConvert)
/// <summary>
/// Gets the XML string for an object
/// </summary>
/// <param name="objectToConvert">The object to convert.</param>
/// <returns>XML string</returns>
protected virtual string GetXmlString(object objectToConvert)
{
string xmlString;
XmlTextWriter xmlTextWriter = null;
@@ -72,6 +94,12 @@ namespace Spring.Messaging.Nms.Support.Converter
return xmlString;
}
/// <summary>
/// Convert from a NMS Message to a .NET object.
/// </summary>
/// <param name="messageToConvert">the message to convert</param>
/// <returns>the converted .NET object</returns>
/// <throws>MessageConversionException in case of conversion failure </throws>
public object FromMessage(IMessage messageToConvert)
{
if (messageToConvert == null)
@@ -108,13 +136,23 @@ namespace Spring.Messaging.Nms.Support.Converter
}
}
private Type GetTargetType(ITextMessage message)
/// <summary>
/// Gets the type of the target given the message.
/// </summary>
/// <param name="message">The message.</param>
/// <returns>Type of the target</returns>
protected virtual Type GetTargetType(ITextMessage message)
{
return typeMapper.ToType(message.Properties.GetString(typeMapper.TypeIdFieldName));
}
private String UTF8ByteArrayToString(Byte[] characters)
/// <summary>
/// Converts a byte array to a UTF8 string.
/// </summary>
/// <param name="characters">The characters.</param>
/// <returns>UTF8 string</returns>
protected virtual String UTF8ByteArrayToString(Byte[] characters)
{
UTF8Encoding encoding = new UTF8Encoding();
@@ -124,11 +162,16 @@ namespace Spring.Messaging.Nms.Support.Converter
}
private Byte[] StringToUTF8ByteArray(String pXmlString)
/// <summary>
/// Converts a UTF8 string to a byte array
/// </summary>
/// <param name="xmlString">The p XML string.</param>
/// <returns></returns>
protected virtual Byte[] StringToUTF8ByteArray(String xmlString)
{
UTF8Encoding encoding = new UTF8Encoding();
Byte[] byteArray = encoding.GetBytes(pXmlString);
Byte[] byteArray = encoding.GetBytes(xmlString);
return byteArray;
}

View File

@@ -83,8 +83,11 @@
<Compile Include="Messaging\Nms\Listener\RecoveryTimeExceededException.cs" />
<Compile Include="Messaging\Nms\Listener\SimpleMessageListenerContainer.cs" />
<Compile Include="Messaging\Nms\Support\Converter\IMessageConverter.cs" />
<Compile Include="Messaging\Nms\Support\Converter\ITypeMapper.cs" />
<Compile Include="Messaging\Nms\Support\Converter\MessageConversionException.cs" />
<Compile Include="Messaging\Nms\Support\Converter\SimpleMessageConverter.cs" />
<Compile Include="Messaging\Nms\Support\Converter\TypeMapper.cs" />
<Compile Include="Messaging\Nms\Support\Converter\XmlMessageConverter.cs" />
<Compile Include="Messaging\Nms\Support\Destinations\DynamicDestinationResolver.cs" />
<Compile Include="Messaging\Nms\Support\Destinations\IDestinationResolver.cs" />
<Compile Include="Messaging\Nms\Support\Destinations\NmsDestinationAccessor.cs" />

View File

@@ -73,7 +73,6 @@
<Compile Include="Messaging\Listener\TransactionAction.cs" />
<Compile Include="Messaging\Listener\IExceptionHandler.cs" />
<Compile Include="Messaging\Listener\IMessageListener.cs" />
<Compile Include="Messaging\Listener\SimpleMessageListenerContainer.cs" />
<Compile Include="Messaging\MessagingException.cs" />
<Compile Include="Messaging\Support\Converters\ActiveXMessageConverter.cs" />
<Compile Include="Messaging\Support\Converters\BinaryMessageConverter.cs" />