Initial import!

This commit is contained in:
markpollack
2008-05-30 22:55:02 +00:00
commit c478a783c0
2978 changed files with 510966 additions and 0 deletions

2307
doc/reference/src/ado.xml Normal file

File diff suppressed because it is too large Load Diff

154
doc/reference/src/ajax.xml Normal file
View File

@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<chapter id="ajax">
<title>ASP.NET AJAX</title>
<sect1 id="introduction-ajax">
<title>Introduction</title>
<para>Spring's ASP.NET AJAX integration allows for a plain .NET object
(PONO), that is one that doesn't have any attributes or special base
classes, to be exported as a web service, configured via dependency
injection, 'decorated' by applying AOP, and then exposed to client side
JavaScript.</para>
</sect1>
<sect1 id="webServices">
<title>Web Services</title>
<para>Spring.NET, and particularly Spring.Web, improved <ulink
url="http://www.springframework.net/doc-latest/reference/html/webservices.html">support
for web services</ulink> in .NET with the
<classname>WebServiceExporter</classname>. Exporting of an ordinary plain
.NET object as a web service is achieved by registering a custom
implementation of the <classname>WebServiceHandlerFactory</classname>
class as the HTTP handler for <literal>*.asmx</literal> requests.</para>
<para><ulink
url="http://www.springframework.net/doc-latest/reference/html/webservices.html">Microsoft
ASP.NET AJAX</ulink> introduced a new HTTP handler
<classname>System.Web.Script.Services.ScriptHandlerFactory</classname> to
allow a Web Service to be invoked from the browser by using
JavaScript.</para>
<para>Spring's integration allows for both Spring.Web and ASP.NET AJAX
functionality to be used together by creating a new HTTP handler.</para>
<sect2 id="exposingWebServices">
<title>Exposing Web Services</title>
<para>The <classname>WebServiceExporter</classname> combined with the
new HTTP handler exposes PONOs as Web Services in your ASP.NET AJAX
application.</para>
<para>In order for a Web service to be accessed from script, the
<classname>WebServiceExporter</classname> should decorate the Web
Service class with the <classname>ScriptServiceAttribute</classname>.
The code below is taken from the sample application
Spring.Web.Extensions.Sample, aka the 'AJAX' shortcut in the
installation. : <programlisting>
&lt;object id="ContactWebService" type="Spring.Web.Services.WebServiceExporter, Spring.Web"&gt;
&lt;property name="TargetName" value="ContactService"/&gt;
&lt;property name="Namespace" value="http://Spring.Examples.Atlas/ContactService"/&gt;
&lt;property name="Description" value="Contact Web Services"/&gt;
&lt;property name="TypeAttributes"&gt;
&lt;list&gt;
&lt;object type="System.Web.Script.Services.ScriptServiceAttribute, System.Web.Extensions"/&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
</programlisting></para>
<para>All that one needs to do in order to use the
<classname>WebServiceExporter</classname> is:</para>
<para><emphasis> 1. Configure the Web.config file of your ASP.NET AJAX
application as a Spring.Web application. </emphasis> <programlisting>
&lt;sectionGroup name="spring"&gt;
&lt;section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/&gt;
&lt;/sectionGroup&gt;
</programlisting> <programlisting>
&lt;spring&gt;
&lt;context&gt;
&lt;resource uri="~/Spring.config"/&gt;
&lt;/context&gt;
&lt;/spring&gt;
</programlisting></para>
<para><emphasis> 2. Register the HTTP handler and the Spring HttpModule
under the <literal>system.web</literal> section. </emphasis>
<programlisting>
&lt;httpHandlers&gt;
&lt;remove verb="*" path="*.asmx"/&gt;
&lt;add verb="*" path="*.asmx" validate="false" type="Spring.Web.Script.Services.ScriptHandlerFactory, Spring.Web.Extensions"/&gt;
&lt;add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/&gt;
&lt;add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/&gt;
&lt;/httpHandlers&gt;
&lt;httpModules&gt;
&lt;add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/&gt;
&lt;add name="SpringModule" type="Spring.Context.Support.WebSupportModule, Spring.Web"/&gt;
&lt;/httpModules&gt;
</programlisting></para>
<para><emphasis> 3. Register the HTTP handler and the Spring HttpModule
under <literal>system.webServer</literal> section. </emphasis>
<programlisting>
&lt;modules&gt;
&lt;add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/&gt;
&lt;add name="SpringModule" type="Spring.Context.Support.WebSupportModule, Spring.Web"/&gt;
&lt;/modules&gt;
&lt;handlers&gt;
&lt;remove name="WebServiceHandlerFactory-Integrated" /&gt;
&lt;add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode"
type="Spring.Web.Script.Services.ScriptHandlerFactory, Spring.Web.Extensions"/&gt;
&lt;add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode"
type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/&gt;
&lt;add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /&gt;
&lt;/handlers&gt;
</programlisting></para>
<para>You can find a full Web.config file in the example that comes with
this integration.</para>
</sect2>
<sect2 id="callingWebServices">
<title>Calling Web Services by using JavaScript</title>
<para>A proxy class is generated for each Web Service. Calls to Web
Services methods are made by using this proxy class. When using the
<classname>WebServiceExporter</classname>, the name of the proxy class
is equal to the <classname>WebServiceExporter</classname>'s id.
<programlisting>
// This function calls the Contact Web service method
// passing simple type parameters and the callback function
function GetEmails(prefix, count)
{
ContactWebService.GetEmails(prefix, count, GetEmailsOnSucceeded);
}
</programlisting></para>
</sect2>
</sect1>
</chapter>

View File

@@ -0,0 +1,733 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="aop-aspect-library">
<title>Aspect Library</title>
<sect1 id="aop-library-introduction">
<title>Introduction</title>
<para>Spring provides several aspects in the distribution. The most
popular of which is transactional advice, located in the Spring.Data
module. However, the aspects that are documented in this section are those
contained within the Spring.Aop module itself. The aspects in within
Spring.Aop.dll are Caching, Exception Handling, Logging, Retry, and
Parameter Validation. Other traditional advice types such as validation,
security, and thread management, will be included in a future
release.</para>
</sect1>
<sect1 id="caching-aspect">
<title>Caching</title>
<para>Caching the return value of a method or the value of a method
parameter is a common approach to increase application performance.
Application performance is increased with effective use of caching since
layers in the application that are closer to the user can return
information within their own layer as compared to making more expensive
calls to retrieve that information from a lower, and more slow, layer such
as a database or a web service. Caching also can help in terms of
application scalability, which is generally the more important
concern.</para>
<para>The caching support in Spring.NET consists of base cache interfaces
that can be used to specify a specific storage implementation of the cache
and also an aspect that determines where to apply the caching
functionality and its configuration.</para>
<para>The base cache interface that any cache implementation should
implement is <classname>Spring.Caching.ICache</classname> located in
<classname>Spring.Core.</classname> Two implementations are provided,
<classname>Spring.Caching.AspNetCache </classname>located in
<classname>Spring.Web</classname> which stores cache entries within an
ASP.NET cache and a simple implementation,
<classname>Spring.Caching.NonExpiringCache</classname> that stores cache
entries in memory and never expires these entries. Custom implementations
based on 3rd party implementations, such as Oracle Coherence, or
memcached, can be used by implementing the <literal>ICache</literal>
interface.</para>
<para>The cache aspect is
<literal>Spring.Aspects.Cache.CacheAspect</literal> located in
<literal>Spring.Aop</literal>. It consists of three pieces of
functionality, the ability to cache return values, method parameters, and
explicit eviction of an item from the cache. The aspect currently relies
on using attributes to specify the pointcut as well as the behavior, much
like the transactional aspect. Future versions will allow for external
configuration of the behavior so you can apply caching to a code base
without needing to use attributes in the code.</para>
<para>The following attributes are available</para>
<itemizedlist>
<listitem>
<para><literal>CacheResult - used to cache the return
value</literal></para>
</listitem>
<listitem>
<para><literal>CacheResultItems - used when returning a collection as
a return value </literal></para>
</listitem>
<listitem>
<para><literal>CacheParameter - used to cache a method
parameter</literal></para>
</listitem>
<listitem>
<para><literal>InvalidateCache</literal> - used to indicate one or
more cache items should be invalidated.</para>
</listitem>
</itemizedlist>
<para>Each <classname>CacheResult</classname>,
<classname>CacheResultItems</classname>, and
<classname>CacheParameter</classname> attributes define the following
properties.</para>
<itemizedlist>
<listitem>
<para><literal>CacheName</literal> - the name of the cache
implementation to use</para>
</listitem>
<listitem>
<para><literal>Key</literal> - a string representing a Spring
Expression Language (SpEL) expression used as the key in the
cache.</para>
</listitem>
<listitem>
<para><literal>Condition</literal> - a SpEL expression that should be
evaluated in order to determine whether the item should be
cached.</para>
</listitem>
<listitem>
<para><literal>TimeToLive</literal> - The amount of time an object
should remain in the cache (in seconds).</para>
</listitem>
</itemizedlist>
<para>The <classname>InvalidateCache</classname> attribute has properties
for the CacheName, the Key as well as the Condition, with the same
meanings as listed previously.</para>
<para>Each <classname>ICache</classname> implementation will have
properties that are specific to a caching technology. In the case of
<classname>AspNetCache</classname>, the two important properties to
configure are:</para>
<itemizedlist>
<listitem>
<para><literal>SlidingExperation</literal> - If this property value is
set to true, every time the marked object is accessed it's TimeToLive
value is reset to its original value</para>
</listitem>
<listitem>
<para><literal>Priority</literal> - the cache item priority
controlling how likely an object is to be removed from an associated
cache when the cache is being purged.</para>
</listitem>
<listitem>
<para><literal>TimeToLive</literal> - The amount of time an object
should remain in the cache (in seconds).</para>
</listitem>
</itemizedlist>
<para>The values of the Priority enumeration are</para>
<itemizedlist>
<listitem>
<para><literal>Low</literal> - low likelihood of deletion when cache
is purged.</para>
</listitem>
<listitem>
<para><literal>Normal</literal> - default priority for deletion when
cache is purged.</para>
</listitem>
<listitem>
<para><literal>High</literal> - high likelihood of deletion when cache
is purged.</para>
</listitem>
<listitem>
<para><literal>NotRemovable</literal> - cache item not deleted when
cache is purged.</para>
</listitem>
</itemizedlist>
<para>An important element of the applying these attributes is the use of
the expression language that allows for calling context information to
drive the caching actions. Here is an example taken from the Spring Air
sample application of the AirportDao implementation that implements an
interface with the method GetAirport(long id).</para>
<programlisting> [CacheResult("AspNetCache", "'Airport.Id=' + #id", TimeToLive = "0:1:0")]
public Airport GetAirport(long id)
{
// implementation not shown...
}
</programlisting>
<para>The first parameter is the cache name. The second string parameter
is the cache key and is a string expression that incorporates the argument
passed into the method, the id. The method parameter names are exposed as
variables to the key expression. If you do not specify a key, then all the
parameter values will be used to cache the returned value. The expression
may also call out to other objects in the Spring container allowing for a
more complex key algorithm to be encapsulated. The end result is that the
Airport object is cached by id for 60 seconds in a cache named
AspNetCache. The TimetoLive property could also have been specified on the
configuration of the AspNetCache object.</para>
<para>The configuration to enable the caching aspect is shown below</para>
<programlisting> &lt;object id="CacheAspect" type="Spring.Aspects.Cache.CacheAspect, Spring.Aop"/&gt;
&lt;object id="AspNetCache" type="Spring.Caching.AspNetCache, Spring.Web"&gt;
&lt;property name="SlidingExpiration" value="true"/&gt;
&lt;property name="Priority" value="CachePriority.Low"/&gt;
&lt;property name="TimeToLive" value="00:02:00"/&gt;
&lt;/object&gt;
&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 <classname>ObjectNameAutoProxyCreator</classname>
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 id="exception-aspect">
<title>Exception Handling</title>
<para>In some cases existing code can be easily adopted to a simple error
handling strategy that can perform one of the following actions</para>
<itemizedlist>
<listitem>
<para>translations - either wrap the thrown exception inside a new one
or replace it with a new exception type (no inner exception is
set).</para>
</listitem>
<listitem>
<para>return value - the exception is ignored and a return value for
the method is provided instead</para>
</listitem>
<listitem>
<para>swallow - the exception is ignored.</para>
</listitem>
</itemizedlist>
<para>The applicability of general exception handling advice depends
greatly on how tangled the code is regarding access to local variables
that may form part of the exception. Once you get familiar with the
feature set of Spring declarative exception handling advice you should
evaluate where it may be effectively applied in your code base. It is
worth noting that you can still chain together multiple pieces of
exception handling advice allowing you to mix the declarative approach
shown in this section with the traditional inheritance based approach,
i.e. implementing IThrowsAdvice or IMethodInterceptor.</para>
<para>Declarative exception handling is expressed in the form of a
mini-language relevant to the domain at hand, exception handling. This
could be referred to as a Domain Specific Language (DSL). Here is a simple
example, which should hopefully be self explanatory.</para>
<para><programlisting>&lt;object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop"&gt;
&lt;property name="exceptionHandlers"&gt;
&lt;list&gt;
&lt;value&gt;<emphasis role="bold">on exception name ArithmeticException wrap System.InvalidOperationException</emphasis>&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
ArithmethicException" is automatically appended. You may however specify
the message used in the newly thrown exception as shown below</para>
<programlisting>on exception name ArithmeticException wrap System.InvalidOperationException 'My Message'</programlisting>
<para>Similarly, if you would rather replace the exception, that is do not
nest one inside the other, you can use the following syntax</para>
<programlisting>on exception name ArithmeticException replace System.InvalidOperationException
or
on exception name ArithmeticException replace System.InvalidOperationException 'My Message'</programlisting>
<para>Both wrap and replace are special cases of the more general
translate action. An example of a translate expression is shown
below</para>
<para><programlisting>on exception name ArithmeticException translate new System.InvalidOperationException('My Message, Method Name ' + #method.Name, #e)</programlisting>What
we see here after the translate keyword is text that will be passed into
Spring's expression language (SpEL) for evaluation. Refer to the chapter
on the <link linkend="expression">expression language</link> for more
details. One important feature of the expression evaluation is the
availability of variables relating to the calling context when the
exception was thrown. These are</para>
<itemizedlist>
<listitem>
<para>method - the MethodInfo object corresponding to the method that
threw the exception</para>
</listitem>
<listitem>
<para>args - the argument array to the method that threw the
exception, signature is object[]</para>
</listitem>
<listitem>
<para>target - the AOP target object instance.</para>
</listitem>
<listitem>
<para>e - the thrown exception</para>
</listitem>
</itemizedlist>
<para>You can invoke methods on these variables, prefixed by a '#' in the
expression. This gives you the flexibility to call special purpose
constructors that can have any piece of information accessible via the
above variables, or even other external data through the use of SpEL's
ability to reference objects within the Spring container.</para>
<para>You may also choose to 'swallow' the exception or to return a
specific return value, for example</para>
<programlisting>on exception name ArithmeticException swallow
or
on exception name ArithmeticException return 12</programlisting>
<para>You may also simply log the exception</para>
<programlisting>on exception name ArithmeticException,ArgumentException log 'My Message, Method Name ' + #method.Name</programlisting>
<para>Here we see that a comma delimited list of exception names can be
specified.</para>
<para>The logging is performed using the Commons.Logging library that
provides an abstraction over the underlying logging implementation.
Logging is currently at the debug level with a logger name of
"LogExceptionHandler" The ability to specify these values will be a future
enhancement and likely via a syntax resembling a constructor for the
action, i.e. log(Debug,"LoggerName").</para>
<para>Multiple exception handling statements can be specified within the
&lt;list&gt; 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
just filtering by the expression name. For example, the following is the
equivalent to the first example based on exception names but compares the
specific type of the exception thrown</para>
<programlisting><emphasis role="bold">on exception (#e is T(System.ArithmeticException))</emphasis> wrap System.InvalidOperationException</programlisting>
<para>The syntax use is 'on exception (SpEL boolean expression)' and
inside the expression you have access to the variables of the calling
context listed before, i.e. method, args, target, and e. This can be
useful to implement a small amount of conditional logic, such as checking
for a specific error number in an exception, i.e. <literal>(#e is
T(System.Data.SqlException) &amp;&amp; #e.Errors[0].Number in
{156,170,207,208})</literal>, to catch and translate bad grammar codes in
a SqlException.</para>
<para>While the examples given above are toy examples, they could just as
easily be changed to convert your application specific exceptions. If you
find yourself pushing the limits of using SpEL expressions, you will
likely be better off creating your own custom aspect class instead of a
scripting approach.</para>
<sect2>
<title>Language Reference</title>
<para>The general syntax of the language is</para>
<para><literal>on exception name [ExceptionName1,ExceptionName2,...]
[action] [SpEL expression]</literal></para>
<para>or</para>
<para><literal>on exception (SpEL boolean expression) [action] [SpEL
expression]</literal></para>
<para>The exception names are required as well as the action. The valid
actions are</para>
<itemizedlist>
<listitem>
<para>log</para>
</listitem>
<listitem>
<para>translate</para>
</listitem>
<listitem>
<para>wrap</para>
</listitem>
<listitem>
<para>replace</para>
</listitem>
<listitem>
<para>return</para>
</listitem>
<listitem>
<para>swallow</para>
</listitem>
</itemizedlist>
<para>The form of the expression depends on the action. For logging, the
entire string is taken as the SpEL expression to log. Translate expects
an exception to be returned from evaluation the SpEL expression. Wrap
and replace are shorthand for the translate action. For wrap and replace
you specify the exception name and the message to pass into the standard
exception constructors (string, exception) and (string). The exception
name can be a partial or fully qualified name. Spring will attempt to
resolve the typename across all referenced assemblies. You may also
register type aliases for use with SpEL in the standard manner with
Spring.NET and those will be accessible from within the exception
handling expression.</para>
</sect2>
</sect1>
<sect1 id="logging-aspect">
<title>Logging</title>
<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, <link linkend="???">Common.Logging</link>, that provides
portability across different logging libraries. There are a number of
configuration options available, listed below</para>
<itemizedlist>
<listitem>
<para>LogUniqueIdentifier</para>
</listitem>
<listitem>
<para>LogExecutionTime</para>
</listitem>
<listitem>
<para>LogMethodArguments</para>
</listitem>
<listitem>
<para>LogReturnValue</para>
</listitem>
<listitem>
<para>Separator</para>
</listitem>
<listitem>
<para>LogLevel</para>
</listitem>
</itemizedlist>
<para>You declare the logging advice in IoC container with the following
XML fragment. Alternatively, you can use the class
<classname>SimpleLoggingAdvice</classname> programatically.</para>
<programlisting>&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;
&lt;property name="Separator" value=";"/&gt;
&lt;property name="LogLevel" value="Info"/&gt;
&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 value is ", " and the
default log level is Common.Logging's LogLevel.Trace.</para>
<para>You can set the name of the logger with the property
<property>LoggerName</property>, for example "DataAccessLayer" for a
logging advice that would be applied across the all the classes in the
data access layer. That works well when using a 'category' style of
logging. If you do not set the <property>LoggerName</property> property,
then the type name of the logging advice is used as the logging name.
Another approach to logging is to log based on the type of the object
being called, the target type. Since often this is a proxy class with a
relatively meaningless name, the property
<property>HideProxyTypeNames</property> can be set to true to show the
true target type and not the proxy type.</para>
<para>To further extend the functionality of the
<classname>SimpleLoggingAdvice</classname> you can subclass
<classname>SimpleLoggingAdvice</classname> and override the methods</para>
<itemizedlist>
<listitem>
<para><literal>string GetEntryMessage(IMethodInvocation invocation,
string idString)</literal></para>
</listitem>
<listitem>
<para><literal>string GetExceptionMessage(IMethodInvocation
invocation, Exception e, TimeSpan executionTimeSpan, string
idString)</literal></para>
</listitem>
<listitem>
<para><literal>string GetExitMessage(IMethodInvocation invocation,
object returnValue, TimeSpan executionTimeSpan, string
idString)</literal></para>
</listitem>
</itemizedlist>
<para>The default implementation to calculate a unique identifier is to
use a GUID. You can alter this behavior by overriding the method
<literal>string CreateUniqueIdentifier()</literal>. The
<classname>SimpleLoggingAdvice</classname> class inherits from
<classname>AbstractLoggingAdvice</classname>, 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
documentation for more details on subclassing
<classname>AbstractLoggingAdvice</classname>.</para>
<para>As an example of the Logging advice's output, adding the advice to
the method</para>
<programlisting>public int Bark(string message, int[] luckyNumbers)
{
return 4;
}</programlisting>
<para>And calling Bark("hello", new int[]{1, 2, 3} ), results in the
following output</para>
<programlisting>Entering Bark, 5d2bad47-62cd-435b-8de7-91f12b7f433e, message=hello; luckyNumbers=System.Int32[]
Exiting Bark, 5d2bad47-62cd-435b-8de7-91f12b7f433e, 30453.125 ms, return=4</programlisting>
<para>The method parameters values are obtained using the ToString()
method. If you would like to have an alternate implementation, say to view
some values in an array, override the method string
GetMethodArgumentAsString(IMethodInvocation invocation).</para>
<para>The Spring 1.2 release will have an additional logging advice
implementation that leverages the Spring Expression Language to further
customize the content of the logging messages via simple configuration
using similar syntax to the retry and exception handling advice.</para>
</sect1>
<sect1 id="retry-aspect">
<title>Retry</title>
<para>When making a distributed call it is often a common requirement to
be able to retry the method invocation if there was an exception.
Typically the exception will be due to a communication issue that is
intermittent and retrying over a period of time will likely result in a
successful invocation. When applying retry advice it is important to know
if making two calls to the remote service will cause side effects.
Generally speaking, the method being invoked should be <ulink
url="http://en.wikipedia.org/wiki/Idempotent#Computer_Science">idempotent</ulink>,
that is, it is safe to call multiple times.</para>
<para>The retry advice is specified using a little language, i.e a DSL. A
simple example is shown below</para>
<programlisting>on exception name ArithmeticException retry 3x delay 1s</programlisting>
<para>The meaning is: when an exception that has 'ArithmeticException' in
its type name is thrown, retry the invocation up to 3 times and delay for
1 second between each retry event.</para>
<para>You can also provide a SpEL (Spring Expression Language) expression
that calculates the time interval to sleep between each retry event. The
syntax for this is shown below</para>
<programlisting>on exception name ArithmeticException retry 3x rate (1*#n + 0.5)</programlisting>
<para>As with the exception handling advice, you may also specify a
boolean SpEL that must evaluate to true in order for the advice to apply.
For example</para>
<programlisting>on exception (#e is T(System.ArithmeticException)) retry 3x delay 1s
on exception (#e is T(System.ArithmeticException)) retry 3x rate (1*#n + 0.5)</programlisting>
<para>The time specified after the delay keyword is converted to a
TimeSpan object using Spring's TimeSpanConverter. This supports setting
the time as an integer + time unit. Time units are (d, h, m, s, ms)
representing (days, hours, minutes, seconds, and milliseconds). For
example; 1d = 1day, 5h = 5 hours etc. You can not specify a string such as
'1d 5h'. The value that is calculated from the expression after the rate
keyword is interpreted as a number of seconds. The power of using SpEL for
the rate expression is that you can easily specify some exponential retry
rate (a bigger delay for each retry attempt) or call out to a custom
function developed for this purpose.</para>
<para>When using a SpEL expression for the filter condition or for the
rate expression, the following variable are available</para>
<para><itemizedlist>
<listitem>
<para>method - the MethodInfo object corresponding to the method
that threw the exception</para>
</listitem>
<listitem>
<para>args - the argument array to the method that threw the
exception, signature is object[]</para>
</listitem>
<listitem>
<para>target - the AOP target object instance.</para>
</listitem>
<listitem>
<para>e - the thrown exception</para>
</listitem>
</itemizedlist>You declare the advice in IoC container with the
following XML fragment. Alternatively, you can use the
<classname>RetryAdvice</classname> class programatically.</para>
<programlisting>&lt;object name="exceptionHandlingAdvice" type="Spring.Aspects.RetryAdvice, Spring.Aop"&gt;
&lt;property name="retryExpression" value="<emphasis role="bold">on exception name ArithmeticException retry 3x delay 1s</emphasis>"/&gt;
&lt;/object&gt;</programlisting>
<sect2>
<title>Language Reference</title>
<para>The general syntax of the language is</para>
<para><literal>on exception name [ExceptionName1,ExceptionName2,...]
retry [number of times]x [delay|rate] [delay time|SpEL rate
expression]</literal></para>
<para>or</para>
<para><literal>on exception (SpEL boolean expression) retry [number of
times]x [delay|rate] [delay time|SpELrate expression]</literal></para>
</sect2>
</sect1>
<sect1 id="tx-aspect">
<title>Transactions</title>
<para>The transaction aspect is more fully described in the section on
<link linkend="transaction">transaction management</link>.</para>
</sect1>
<sect1 id="parameter-validation">
<title>Parameter Validation</title>
<para>Spring provides a UI-agnostic <link linkend="validation">validation
framework</link> in which you can declare validation rules, both
progammatically and declaratively, and have those rules evaluated against
an arbitrary .NET object. Spring provides additional support for rendering
of validation errors within Spring's ASP.NET framework. (See the section
on <link linkend="validation-aspnet-usage" os="">ASP.NET usage tips</link>
for more information.) However, validation is not confined to the UI tier.
It is a common task that occurs across most, if not all, applications
layers. Validation that is performed in the UI layer is often repeated in
the service layer, in order to be proactive in case non UI-based clients
invoke the service layer. Validation rules completely different from those
used in the UI layer may be used on the server side.</para>
<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
<classname>ParameterValidationAdvice</classname> is used in conjunction
with the <classname>Validated</classname> 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 <classname>Validated</classname>
attribute to the method parameters as shown below.</para>
<programlisting>public FlightSuggestions SuggestFlights( [Validated("tripValidator")] Trip trip)
{
// unmodified implementation goes here
}</programlisting>
<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
<classname>Validated</classname> 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
<classname>ObjectNameAutoProxyCreator</classname>, as shown below,</para>
<programlisting> &lt;object id="<emphasis role="bold">validationAdvice</emphasis>" type="Spring.Aspects.Validation.ParameterValidationAdvice, Spring.Aop"/&gt;
&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;<emphasis role="bold">validationAdvice</emphasis>&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
body is executed. If validation fails an exception of the type
<classname>ValidationException</classname> is thrown and you can retrieve
errors information from its property <literal>ValidationErrors</literal>.
See the SDK documentation for details.</para>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

2470
doc/reference/src/aop.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="background">
<title>Background information</title>
<sect1 id="background-ioc">
<title>Inversion of Control</title>
<para>In early 2004, Martin Fowler asked the readers of his site: when
talking about Inversion of Control: <emphasis>"the question, is what
aspect of control are they inverting?"</emphasis>. After talking about the
term Inversion of Control Martin suggests renaming the pattern, or at
least giving it a more self-explanatory name, and starts to use the term
<emphasis>Dependency Injection</emphasis>. His <ulink
url="http://martinfowler.com/articles/injection.html">article</ulink>
continues to explain some of the ideas behind this important software
engineering principle.</para>
<para>Other references you may find useful are</para>
<itemizedlist>
<listitem>
<para>Wikipedia Article - <ulink
url="http://en.wikipedia.org/wiki/Dependency_injection">Dependency
Injection</ulink></para>
</listitem>
<listitem>
<para>CodeProject article - <ulink
url="http://www.codeproject.com/cs/design/DependencyInjection.asp">Dependency
Injection for Loose Coupling</ulink></para>
</listitem>
</itemizedlist>
</sect1>
</chapter>

325
doc/reference/src/dao.xml Normal file
View File

@@ -0,0 +1,325 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="dao">
<title>DAO support</title>
<section id="dao-introduction">
<title>Introduction</title>
<para>Spring promotes the use of data access interfaces in your
application architecture. These interfaces encapsulate the storage and
retrieval of data and objects specific to your business domain without
reference to a specific persistence API. Within a layered architecture,
the service layer is typically responsible for coordinating responses to a
particular business request and it delegates any persistence related
activities to objects that implement these data access interfaces. These
objects are commonly referred to as DAOs (Data Access Objects) and the
architectural layer as a DAL (Data Access Layer).</para>
<para>The benefits of using DAOs in your application are increased
portability across persistence technology and ease of testing. Testing is
more easily facilitated because a mock or stub implementation of the data
access interface can be easily created in a NUnit test so that service
layer functionality can be tested without any dependency on the database.
This is beneficial because tests that rely on the database are usually
hard to set up and tear down and also are impractical for testing
exceptional behavior.</para>
<para>The Data Access Object (DAO) support in Spring is aimed at making it
easy to work with data access technologies like ADO.NET and NHibernate in
a standardized way. Spring provides two central pieces of functionality to
meet this goal. The first is providing a common exception hierarchy across
providers and the second is providing base DAOs classes that raise the
level of abstraction when performing common ADO.NET operations. This
allows one to switch between the aforementioned persistence technologies
fairly easily and it also allows one to code without worrying about
catching exceptions that are specific to each technology.</para>
</section>
<section id="dao-exceptions">
<title>Consistent exception hierarchy</title>
<para>Database exceptions in the ADO.NET API are not consistent across
providers. The .NET 1.1 BCL did not provide a common base class for
ADO.NET exceptions. As such you were required to handle exceptions
specific to each provider such as
<classname>System.Data.SqlClient.SqlException</classname> or
<classname>System.Data.OracleClient.OracleException</classname>. The .NET
2.0 BCL improved in this regard by introducing a common base class for
exceptions, <classname>System.Data.Common.DbException</classname>. However
the common DbException is not very portable either as it provides a vendor
specific error code as the underlying piece of information as to what went
wrong. This error code is different across providers for the same
conceptual error, such as a violation of data integrity or providing bad
SQL grammar.</para>
<para>To promote writing portable and descriptive exception handling code
Spring provides a convenient translation from technology specific
exceptions like <classname>System.Data.SqlClient.SqlException</classname>
or <classname>System.Data.OracleClient.OracleException</classname> to its
own exception hierarchy with the
<classname>Spring.Dao.DataAccessException</classname> as the root
exception. These exceptions wrap the original exception so there is never
any risk that one might lose any information as to what might have gone
wrong.</para>
<para>In addition to exceptions from ADO.NET providers, Spring can also
wrap NHibernate-specific exceptions.. This allows one to handle most
persistence exceptions, which are non-recoverable, only in the appropriate
layers, without boilerplate using or catch and throw blocks, and exception
declarations. As mentioned above, ADO.NET exceptions (including
database-specific dialects) are also converted to the same hierarchy,
meaning that one can perform some operations with ADO.NET within a
consistent programming model. The above holds true for the various
template-based versions of the ORM access framework.</para>
<para>The exception hierarchy that Spring uses is outlined in the
following image:</para>
<mediaobject>
<imageobject>
<imagedata fileref="images/DataAccessException.gif" />
</imageobject>
</mediaobject>
<para>(Please note that the class hierarchy detailed in the above image
shows only a subset of the whole, rich,
<classname>DataAccessException</classname> hierarchy.)</para>
<para>The exception translation functionality is in the namespace
Spring.Data.Support and is based on the interface
<literal>IAdoExceptionTranslator</literal> shown below.</para>
<programlisting>public interface IAdoExceptionTranslator
{
DataAccessException Translate( string task, string sql, Exception exception );
}</programlisting>
<para>The arguments to the translator are a task string providing a
description of the task being attempted, the SQL query or update that
caused the problem, and the 'raw' exception thrown by the ADO.NET data
provider. The additional task and SQL arguments allow for very readable
and clear error messages to be created when an exception occurs.</para>
<para>A default implementation,
<literal>ErrorCodeExceptionTranslator</literal>, is provided that uses the
error codes defined for each data provider in the file dbproviders.xml.
Refer to this file, an embedded resource in the Spring.Data assembly, for
the exact mappings of error codes to Spring DataAccessExceptions.</para>
<para>A common need is to modify the error codes that are map onto the
exception hierarchy. There are several ways to accomplish this
task.</para>
<para>One approach is to override the error codes that are defined in
<code>assembly://Spring.Data/Spring.Data.Common/dbproviders.xml</code>. By
default, the <link
linkend="dbprovider-dbprovider">DbProviderFactory</link> will look for
additional metadata for the IoC container it uses internally to define and
manage the DbProviders in a file named <literal>dbProviders.xml</literal>
located in the root runtime directory. (You can change this location, see
the documentation on <link lang=""
linkend="dbprovider-dbprovider">DbProvider</link> for more information.)
This is a standard Spring application context so all features, such as
<link
linkend="objects-factory-customizing-factory-postprocessors">ObjectFactoryPostProcessors</link>
are available and will be automatically applied. Defining a <link
linkend="objects-factory-overrideconfigurer">PropertyOverrideConfigurer</link>
in this additional configuration file will allow for you to override
specific property values defined in the embedded resource file. As an
example, the additional <literal>dbProviders.xml</literal> file shown
below will add the error code <literal>2601</literal> to the list of error
codes that map to a
<literal>DataIntegrityViolationException</literal>.</para>
<para><programlisting>&lt;objects xmlns='http://www.springframework.net'&gt;
&lt;alias name='SqlServer-2.0' alias='SqlServer2005'/&gt;
&lt;object name="appConfigPropertyOverride" type="Spring.Objects.Factory.Config.PropertyOverrideConfigurer, Spring.Core"&gt;
&lt;property name="Properties"&gt;
&lt;name-values&gt;
&lt;add key="SqlServer2005.DbMetadata.ErrorCodes.DataIntegrityViolationCodes"
value="544,2601,2627,8114,8115"/&gt;
&lt;/name-values&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>The reason to define the alias is that <link
linkend="objects-factory-overrideconfigurer">PropertyOverrideConfigurer</link>
assumes a period <literal>(.)</literal> as the separator to pick out the
object name but the names of the objects in
<literal>dbProviders.xml</literal> have periods in them (i.e.
SqlServer-2.0 or System.Data.SqlClient). Creating an alias that has no
periods in the name is a workaround.</para>
<para>Another way to customize the mappings of error codes to exceptions
is to subclass <classname>ErrorCodeExceptionTranslator</classname> and
override the method, <literal>DataAccessException
TranslateException(string task, string sql, string errorCode, Exception
exception)</literal>. This will be called before referencing the metadata
to perform exception translation. The vendor specific error code provided
as a method argument has already been parsed out of the raw ADO.NET
exception. If you create your own specific subclass, then you should set
the property <literal>ExceptionTranslator</literal> on
<literal>AdoTemplate</literal> and
<literal>HibernateTemplate/HibernateTransactionManager</literal> to refer
to your custom implementation (unless you are using autowiring).</para>
<para>The third way is to write an implementation of
<literal>IAdoExceptionTranslator</literal> and set the property
<literal>FallbackTranslator</literal>'on
<literal>ErrorCodeExceptionTranslator</literal>. In this case you are
responsible for parsing our the vendor specific error code from the raw
ADO.NET exception. As with the case of subclassing
ErrorCodeExceptionTranslator, you will need to refer to this custom
exception translator when using <literal>AdoTemplate</literal> or
<literal>HibernateTemplate/HibernateTransactionManager</literal>.</para>
<para>The ordering of the exception translation processing is as follows.
The method TranslateException is called first, then the standard exception
translation logic, then the FallbackTranslator.</para>
<para>Note that you can use this API directly in your own Spring
independent data layer. If you are using Spring's ADO.NET abstraction
class, <literal>AdoTemplate</literal>, or
<literal>HibernateTemplate</literal>, the converted exceptions will be
thrown automatically. Somewhere in between these two cases is using
Spring's declarative transaction management features in .NET 2.0 with the
raw ADO.NET APIs and using <literal>IAdoExceptionTranslator</literal> in
your exception handling layer (which might be implemented in AOP using
Spring's exception translation aspect).</para>
<para>Some of the more common data access exceptions are described here.
Please refer to the API documentation for more details.</para>
<table>
<title>Common DataAccessExceptions</title>
<tgroup cols="2">
<colspec align="left" />
<thead>
<row>
<entry>Exception</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>BadSqlGrammarException</entry>
<entry>Exception thrown when SQL specified is invalid.</entry>
</row>
<row>
<entry>DataIntegrityViolationException</entry>
<entry>Exception thrown when an attempt to insert or update data
results in violation of an integrity constraint. For example,
inserting a duplicate key.</entry>
</row>
<row>
<entry>PermissionDeniedDataAccessException</entry>
<entry>Exception thrown when the underling resource denied a
permission to access a specific element, such as a specific
database table.</entry>
</row>
<row>
<entry>DataAccessResourceFailureException</entry>
<entry>Exception thrown when a resource fails completely, for
example, if we can't connect to a database.</entry>
</row>
<row>
<entry>ConcurrentyFailureException</entry>
<entry>Exception thrown when a concurrency error occurs.
OptimisticLockingFailureException and
PessimisticLockingFailureException are subclasses. This is a
useful exception to catch and to retry the transaction again. See
Spring's <link linkend="retry-aspect">Retry Aspect</link> for an
AOP based solution.</entry>
</row>
<row>
<entry>OptimisticLockingFailureException</entry>
<entry>Exception thrown when there an optimistic locking failure
occurs. The subclass ObjectOptimisticLockingFailureException can
be used to examine the Type and the IDof the object that failed
the optimistic locking.</entry>
</row>
<row>
<entry>PessimisticLockingFailure</entry>
<entry>Exception thrown when a pessimistic locking failure
occures. Subclasses of this exception are
CannotAcquireLockException, CannotSerializeTransactionException,
and DeadlockLoserDataAccessException.</entry>
</row>
<row>
<entry>CannotAcquireLockException</entry>
<entry>Exception thrown when a lock can not be acquired, for
example during an update, i..e a select for update</entry>
</row>
<row>
<entry>CannotSerializeTransactionException</entry>
<entry>Exception thrown when a transaction can not be
serialized.</entry>
</row>
</tbody>
</tgroup>
</table>
</section>
<section>
<title>Consistent abstract classes for DAO support</title>
<para>To make it easier to work with a variety of data access technologies
such as ADO.NET, NHibernate, and iBatis.NET in a consistent way, Spring
provides a set of abstract DAO classes that one can extend. These abstract
classes have methods for providing the data source and any other
configuration settings that are specific to the technology one is
currently using.</para>
<para>DAO support classes:</para>
<itemizedlist>
<listitem>
<para><classname>AdoDaoSupport</classname> - super class for ADO.NET
data access objects. Requires a
<interfacename>DbProvider</interfacename> to be provided; in turn,
this class provides a <classname>AdoTemplate</classname> instance
initialized from the supplied
<interfacename>DbProvider</interfacename> to subclasses. See the
documentation for <literal>AdoTemplate</literal> for more
information.</para>
</listitem>
<listitem>
<para><classname>HibernateDaoSupport</classname> - super class for
NHibernate data access objects. Requires a
<interfacename>ISessionFactory</interfacename> to be provided; in
turn, this class provides a <classname>HibernateTemplate</classname>
instance initialized from the supplied
<interfacename>SessionFactory</interfacename> to subclasses. Can
alternatively be initialized directly via a
<classname>HibernateTemplate</classname>, to reuse the latter's
settings like <interfacename>SessionFactory</interfacename>, flush
mode, exception translator, etc. This is contained in a download
separate from the main Spring.NET distribution.</para>
</listitem>
</itemizedlist>
</section>
</chapter>

View File

@@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="data-quickstart">
<title>Data Access QuickStart</title>
<section>
<title>Introduction</title>
<para>The data access quick start demonstrates the API usage of
AdoTemplate (both generic and non-generic versions) as well as the use of
the object based data access classes contained in Spring.Data.Objects. It
uses the Northwind database and is located under the directory
examples/DataAccessQuickStart.</para>
<para>The quick start contains pseudo DAO objects and a collection of
NUnit tests to exercise them rather than a full blown application. To run
the tests from within VS.NET install <ulink
url="http://www.testdriven.net/"><link
linkend="???">TestDriven.NET</link></ulink>, <ulink
url="http://www.jetbrains.com/resharper/">ReSharper</ulink>, or an
equivalent . The listing of DAO classes and the parts of Spring.Data that
they demonstrate is shown below.</para>
<itemizedlist>
<listitem>
<para><classname>CommandCallbackDao</classname> - Use of the
ICommandCallback and CommandCallbackDelegate</para>
</listitem>
<listitem>
<para><classname>ResultSetExtractorDao</classname> - Use of
IResultSetExtractor and ResultSetExtractorDelegate</para>
</listitem>
<listitem>
<para><classname>RowCallbackDao</classname> - Use of IRowCallback and
RowCallbackDelegate</para>
</listitem>
<listitem>
<para><classname>RowMapperDao</classname> - Use of IRowMapper and
RowMapperDelegate</para>
</listitem>
<listitem>
<para><classname>QueryForObject</classname> - Use of QueryForObject
method.</para>
</listitem>
<listitem>
<para><classname>StoredProcDao</classname> - Use of
Spring.Data.Objects.StoredProcedure</para>
</listitem>
</itemizedlist>
<para>The are simple domain objects in the Spring.DataQuickStart.Domain
namespace, collections of which are generally returned from the DAO
methods.</para>
<section>
<title>Database configuration</title>
<para>To get started running the 'unit test' you should configure the
database connection string. The listing in
DataQuickStart.GenericTemplate.ExampleTests.xml is shown below</para>
<programlisting>&lt;objects xmlns="http://www.springframework.net"
xmlns:db="http://www.springframework.net/database"&gt;
&lt;db:provider id="dbProvider"
provider="SqlServer-1.1"
connectionString="Data Source=(local);Database=Northwind;User ID=springqa;Password=springqa;Trusted_Connection=False"/&gt;
&lt;! -- other definitions not shown
&lt;/objects&gt;</programlisting>
<para>You should change the value of the provider element to correspond
to you database and the connection string as appropriate. Please refer
to the documentation on the <link linkend="dbprovider">DbProvider</link>
abstraction for details particular to your database configuration. You
should also install the Northwind database, which is available for
SqlServer 2005 from this <ulink
url="http://www.microsoft.com/downloads/details.aspx?FamilyID=06616212-0356-46a0-8da2-eebc53a68034&amp;DisplayLang=en">download
location</ulink>. The minimal schema to support other database providers
may be supported in the future.</para>
<section>
<title>AdoTemplate Configuration</title>
<para>The various DAO objects refer to an instance of AdoTemplate
which is responsible for performing data access operations. This is
declared in ExampleTest.xml as shown below</para>
<programlisting> &lt;object id="adoTemplate" type="Spring.Data.Generic.AdoTemplate, Spring.Data"&gt;
&lt;property name="DbProvider" ref="dbProvider"/&gt;
&lt;property name="DataReaderWrapperType" value="Spring.Data.Support.NullMappingDataReader, Spring.Data"/&gt;
&lt;/object&gt;
</programlisting>
<para>The property DbProvider refers to the database configuration you
previously defined. Also the property DataReaderWrapper is set to the
NullMappingDataReader that ships with Spring. This provides convenient
default values for null values returned from the database. To read
more about AdoTemplate, refer to the chapter, <link linkend="???">Data
access using ADO.NET</link>.</para>
</section>
</section>
<section>
<title>CommandCallback</title>
<para>The code that exercises the use of a CommandCallback is shown
below</para>
<programlisting> [Test]
public void CallbackDaoTest()
{
CommandCallbackDao commandCallbackDao = ctx["commandCallbackDao"] as CommandCallbackDao;
int count = commandCallbackDao.FindCountWithPostalCode("1010");
Assert.AreEqual(3, count);
}</programlisting>
<para>The configuration of the CommandCallbackDao is shown below</para>
<programlisting> &lt;object id="commandCallbackDao" type="Spring.DataQuickStart.Dao.GenericTemplate.CommandCallbackDao, Spring.DataQuickStart"&gt;
&lt;property name="AdoTemplate" ref="adoTemplate"/&gt;
&lt;/object&gt;</programlisting>
<para>This the minimal configuration required for a DAO object,
typically DAO objects in your application will include other
configuraiton information, for example properties to specify the maximum
size of the result set returned etc. The implementation of the
FindCountWithPostalCode is shown below</para>
<programlisting> public virtual int FindCountWithPostalCodeWithDelegate(string postalCode)
{
// Using anonymous delegates allows you to easily reference the
// surrounding parameters for use with the DbCommand processing.
return AdoTemplate.Execute&lt;int&gt;(delegate(DbCommand command)
{
// Do whatever you like with the DbCommand... downcast to get
// provider specific funtionality if necesary.
command.CommandText = cmdText;
DbParameter p = command.CreateParameter();
p.ParameterName = "@PostalCode";
p.Value = postalCode;
command.Parameters.Add(p);
return (int)command.ExecuteScalar();
});
}</programlisting>
<para>Anonymous delegates are used to specify the implementation of the
callback function that passes in a DbCommand object. You can then use
the DbCommand object as you see fit to access the database. If you are
using Spring's delcarative transaction management features then this
DbCommand would have its transaction and connection properties based on
the context of the surrounding transaction. All resource management for
the DbCommand are handled for you by the framework, as well as error
reporting on error etc. If you execute the test, it will pass, assuming
you haven't modified any data in the Northwind database from its raw
installation.</para>
</section>
</section>
</chapter>

View File

@@ -0,0 +1,464 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="dbprovider">
<title>DbProvider</title>
<section id="dbprovider-introduction">
<title>Introduction</title>
<para>Spring provides a generic factory for creating ADO.NET API artifacts
such as <code><classname>IDbConnection</classname></code> and
<code><classname>IDbCommand</classname></code>. The factory API is very
similar to the one introduced in .NET 2.0 but adds extra metadata needed
by Spring to support features provided by its DAO/ADO.NET framework such
as error code translation to a DAO exception hierarchy. The factory itself
is configured by using a standard Spring XML based configuration file
though it is unlikely you will need to modify those settings yourself, you
only need be concerned with using the factory. Out of the box several
popular databases are supported and an extension mechanism is available
for defining new database providers or modifying existing ones. A custom
database namespace for configuration aids in making terse XML based
declarations of Spring's database objects you wish to use.</para>
<para>The downside of Spring's factory as compared to the one in .NET 2.0
is that the types returned are lower level interfaces and not the abstract
base classes in System.Data.Common. However, there are still 'holes' in
the current .NET 2.0 provider classes that are 'plugged' with Spring's
provider implementation. One of the most prominent is the that the top
level DbException exposes the HRESULT of the remote procedure call, which
is not what you are commonly looking for when things go wrong. As such
Spring's provider factory exposes the vendor sql error code and also maps
that error code onto a consistent data access exception hierarchy. This
makes writing portable exception handlers much easier. In addition, the
DbParameter class doesn't provide the most common convenient methods you
would expect as when using say the SqlServer provider. If you need to
access the BCL provider abstraction, you still can through Spring's
provider class. Furthermore, a small wrapper around the standard BCL
provider abstraction allows for integration with Spring's transaction
management facilities, allowing you to create a DbCommand with its
connection and transaction properties already set based on the transaction
calling context.</para>
</section>
<section id="dbprovider-dbprovider">
<title>IDbProvider and DbProviderFactory</title>
<para>The <code><interfacename>IDbProvider</interfacename></code> API is
shown below and should look familiar to anyone using .NET 2.0 data
providers. Note that Spring's DbProvider abstraction can be used on .NET
1.1 in addition to .NET 2.0</para>
<programlisting> public interface IDbProvider
{
IDbCommand CreateCommand();
object CreateCommandBuilder();
IDbConnection CreateConnection();
IDbDataAdapter CreateDataAdapter();
IDbDataParameter CreateParameter();
string CreateParameterName(string name);
string CreateParameterNameForCollection(string name);
IDbMetadata DbMetadata
{
get;
}
string ConnectionString
{
set;
get;
}
string ExtractError(Exception e);
bool IsDataAccessException(Exception e);
}</programlisting>
<para>ExtractError is used to return an error string for translation into
a DAO exception. On .NET 1.1 the method IsDataAccessException is used to
determine if the thrown exception is related to data access since in .NET
1.1 there isn't a common base class for database exceptions.
CreateParameterName is used to create the string for parameters used in a
CommandText object while CreateParameterNameForCollection is used to
create the string for a IDataParameter.ParameterName, typically contained
inside a IDataParameterCollection.</para>
<para>The class <classname>DbProviderFactory</classname> creates
IDbProvider instances given a provider name. The connection string
property will be used to set the IDbConnection returned by the factory if
present. The provider names, and corresponding database, currently
configured are listed below.</para>
<itemizedlist>
<listitem>
<para><code>SqlServer-1.1</code> - Microsoft SQL Server, provider
V1.0.5000.0 in framework .NET V1.1</para>
</listitem>
<listitem>
<para><code>SqlServer-2.0</code> (aliased to
<code>System.Data.SqlClient</code>) - Microsoft SQL Server, provider
V2.0.0.0 in framework .NET V2.0</para>
</listitem>
<listitem>
<para><literal>SqlServerCe-3.1</literal> (aliased to
<literal>System.Data.SqlServerCe</literal>) - Microsoft SQL Server
Compact Edition, provider V9.0.242.0</para>
</listitem>
<listitem>
<para><code>OleDb-1.1</code> - OleDb, provider V1.0.5000.0 in
framework .NET V1.1</para>
</listitem>
<listitem>
<para><code>OleDb-2.0</code> (aliased to
<code>System.Data.OleDb</code>) - OleDb, provider V2.0.0.0 in
framework .NET V2.0</para>
</listitem>
<listitem>
<para><code>OracleClient-2.0</code> (aliased to
<code>System.Data.OracleClient</code>) - Oracle, Microsoft provider
V2.0.0.0</para>
</listitem>
<listitem>
<para><code>OracleODP-2.0</code> (aliased to
<code>System.DataAccess.Client</code>) - Oracle, Oracle provider
V2.102.2.20</para>
</listitem>
<listitem>
<para><code>MySql</code> - MySQL, MySQL provider 1.0.10.1</para>
</listitem>
<listitem>
<para><literal>MySql-1.0.9</literal> - MySQL, MySQL provider
1.0.9</para>
</listitem>
<listitem>
<para><literal>MySql-5.0</literal> - MySQL, MySQL provider
5.0.7.0</para>
</listitem>
<listitem>
<para><literal>MySql-5.0.8.1</literal> - MySQL, MySQL provider
5.0.8.1</para>
</listitem>
<listitem>
<para><literal>MySql-5.1 </literal>- MySQL, MySQL provider
5.1.2.2</para>
</listitem>
<listitem>
<para><literal>MySql-5.1.4</literal> - (aliased to
<literal>MySql.Data.MySqlClient</literal>) MySQL, MySQL provider
5.1.2.2</para>
</listitem>
<listitem>
<para><literal>Npgsql-1.0</literal> - Postgresql provider 1.0.0.0 (and
1.0.0.1 - were build with same version info)</para>
</listitem>
<listitem>
<para><literal>Npgsql-2.0-beta1</literal> - Postgresql provider
1.98.1.0 beta 1</para>
</listitem>
<listitem>
<para><literal>DB2-9.0.0-1.1</literal> - IBM DB2 Data Provider 9.0.0
for .NET Framework 1.1</para>
</listitem>
<listitem>
<para><literal>DB2-9.0.0-2.0 </literal>- (aliased to
<literal>IBM.Data.DB2</literal>) - IBM DB2 Data Provider 9.0.0 for
.NET Framework 2.0</para>
</listitem>
<listitem>
<para><literal>DB2-9.1.0-1.1</literal> - IBM DB2 Data Provider 9.1.0
for .NET Framework 1.1</para>
</listitem>
<listitem>
<para><literal>DB2-9.1.0.2</literal> - (aliased to
<literal>IBM.Data.DB2.9.1.0</literal>) - IBM DB2 Data Provider 9.1.0
for .NET Framework 2.0</para>
</listitem>
<listitem>
<para><literal>SQLite-1.0.43 </literal>SQLite provider 1.0.43 for .NET
Framework 2.0</para>
</listitem>
<listitem>
<para><literal>SQLite-1.0.47 </literal>- (aliased to
System.Data.SQLite) - SQLite provider 1.0.43 for .NET Framework
2.0</para>
</listitem>
<listitem>
<para><literal>SybaseAse-12</literal> - Sybase ASE provider for ASE
12.x</para>
</listitem>
<listitem>
<para><literal>SybaseAse-15</literal> - Sybase ASE provider for ASE
15.x</para>
</listitem>
<listitem>
<para><literal>Odbc-1.1</literal> - ODBC provider V1.0.5000.0 in
framework .NET V1.1</para>
</listitem>
<listitem>
<para><literal>Odbc-2.0</literal> - ODBC provider V2.0.0.0 in
framework .NET V2</para>
</listitem>
</itemizedlist>
<para>An example using DbProviderFactory is shown below</para>
<programlisting>IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient");</programlisting>
<para>The default definitions of the providers are contained in the
assembly resource
<code>assembly://Spring.Data/Spring.Data.Common/dbproviders.xml</code>.
Future additions to round out the database coverage are forthcoming. The
current crude mechanism to add additional providers, or to apply any
standard Spring <interfacename>IApplicationContext</interfacename>
functionality, such as applying AOP advice, is to set the public static
property DBPROVIDER_ADDITIONAL_RESOURCE_NAME in
<classname>DbProviderFactory</classname> to a Spring resource location.
The default value is <code>file://dbProviders.xml</code>. (That isn't a
typo, there is a difference in case with the name of the embedded
resource). This crude mechanism will eventually be replaced with one based
on a custom configuration section in App.config/Web.config.</para>
<para>It may happen that the version number of an assembly you have
downloaded is different than the one listed above. If it is a point
release, i.e. the API hasn't changed in anyway that is material to your
application, you should add an assembly redirect of the form shown
below.</para>
<programlisting>&lt;dependentAssembly&gt;
&lt;assemblyIdentity name="MySql.Data"
publicKeyToken="c5687fc88969c44d"
culture="neutral"/&gt;
&lt;bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535"
newVersion="1.0.10.1"/&gt;
&lt;/dependentAssembly&gt;</programlisting>
<para>This redirects any reference to an older version of the assembly
MySql.Data to the version 1.0.10.1.</para>
</section>
<section>
<title>XML based configuration</title>
<para>Creating a DbProvider in Spring's XML configuration file is shown
below in the typical case of using it to specify the DbProvider property
on an AdoTemplate.</para>
<programlisting>&lt;objects xmlns='http://www.springframework.net'
xmlns:db="http://www.springframework.net/database"&gt;
&lt;db:provider id="DbProvider"
provider="System.Data.SqlClient"
connectionString="Data Source=(local);Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"/&gt;
&lt;object id="adoTemplate" type="Spring.Data.AdoTemplate, Spring.Data"&gt;
&lt;property name="DbProvider" ref="DbProvider"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<para>A custom namespace should be registered in the main application
configuration file to use this syntax. This configuration, only for the
parsers, is shown below. Additional section handlers are needed to specify
the rest of the Spring configuration locations as described in previous
chapters.</para>
<programlisting>&lt;configuration&gt;
&lt;configSections&gt;
&lt;sectionGroup name="spring"&gt;
&lt;section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" /&gt;
&lt;/sectionGroup&gt;
&lt;/configSections&gt;
&lt;spring&gt;
&lt;parsers&gt;
&lt;parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" /&gt;
&lt;/parsers&gt;
&lt;/spring&gt;
&lt;/configuration&gt;</programlisting>
</section>
<section>
<title>Connection String management</title>
<para>There are a few options available to help manage your connection
strings.</para>
<para>The first option is to leverage the Spring property replacement
functionality, as described in <xref
linkend="objects-factory-placeholderconfigurer" />. This lets you insert
variable names as placeholders for values in a Spring configuration file.
In the following example specific parts of a connection string have been
parameterized but you can also use a variable to set the entire connection
string.</para>
<para>An example of such a setting is shown below</para>
<programlisting>&lt;configuration&gt;
&lt;configSections&gt;
&lt;sectionGroup name="spring"&gt;
&lt;section name='context' type='Spring.Context.Support.ContextHandler, Spring.Core'/&gt;
&lt;/sectionGroup&gt;
&lt;section name="databaseSettings" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /&gt;
&lt;/configSections&gt;
&lt;spring&gt;
&lt;context&gt;
&lt;resource uri="Aspects.xml" /&gt;
&lt;resource uri="Services.xml" /&gt;
&lt;resource uri="Dao.xml" /&gt;
&lt;/context&gt;
&lt;/spring&gt;
&lt;!-- These properties are referenced in Dao.xml --&gt;
&lt;databaseSettings&gt;
&lt;add key="db.datasource" value="(local)" /&gt;
&lt;add key="db.user" value="springqa" /&gt;
&lt;add key="db.password" value="springqa" /&gt;
&lt;add key="db.database" value="Northwind" /&gt;
&lt;/databaseSettings&gt;
&lt;/configuration&gt;</programlisting>
<para>Where <literal>Dao.xml</literal> has a connection string as shown
below</para>
<programlisting>&lt;objects xmlns='http://www.springframework.net'
xmlns:db="http://www.springframework.net/database"&gt;
&lt;db:provider id="DbProvider"
provider="System.Data.SqlClient"
connectionString="${db.datasource};Database=${db.database};User ID=${db.user};Password=${db.password};Trusted_Connection=False"/&gt;
&lt;object id="adoTemplate" type="Spring.Data.AdoTemplate, Spring.Data"&gt;
&lt;property name="DbProvider" ref="DbProvider"/&gt;
&lt;/object&gt;
&lt;!-- configuration of what values to substitute for ${ } variables listed above --&gt;
&lt;object name="appConfigPropertyHolder"
type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core"&gt;
&lt;property name="configSections" value="DatabaseConfiguration"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<para>Please refer to the Section <xref
linkend="objects-factory-placeholderconfigurer" /> for more
information.</para>
</section>
<section id="dbprovider-additional">
<title>Additional IDbProvider implementations</title>
<para>Spring provides some convenient implementations of the IDbProvider
interface that add addtional behavior on top of the standard
implementation.</para>
<section id="dbprovider-usercredentials">
<title>UserCredentialsDbProvider</title>
<para>This <classname>UserCredentialsDbProvider</classname> will allow
you to change the username and password of a database connection at
runtime. The API contains the properties <literal>Username</literal> and
<literal>Password</literal> which are used as the default strings
representing the user and password in the connection string. You can
then change the value of these properties in the connection string by
calling the method <literal>SetCredentialsForCurrentThread</literal> and
fall back to the default values by calling the method
<literal>RemoveCredentialsFromCurrentThread</literal>. You call the
<literal>SetCredentialsForCurrentThread</literal> method at runtime,
before any data access occurs, to determine which database user should
be used for the current user-case. Which user to select is up to you.
You may retrieve the user information from an HTTP session for example.
Example configuration and usage is shown below</para>
<programlisting>&lt;object id="DbProvider" type="Spring.Data.Common.UserCredentialsDbProvider, Spring.Data"&gt;
&lt;property name="TargetDbProvider" ref="targetDbProvider"/&gt;
&lt;property name="Username" value="User ID=defaultName"/&gt;
&lt;property name="Password" value="Password=defaultPass"/&gt;
&lt;/object&gt;
&lt;db:provider id="targetDbProvider" provider="SqlServer-2.0"
connectionString="Data Source=MARKT60\SQL2005;Database=Spring;Trusted_Connection=False"/&gt;</programlisting>
<para>If you use dependency injection to configure a class with a
property of the type <literal>IDbProvider</literal>, you will need to
downcast to the subtype or you can change your class to have a property
of the type <literal>UserCredentialsDbProvider</literal> instead of
<literal>IDbProvider</literal>.</para>
<programlisting>userCredentialsDbProvider.SetCredentialsForCurrentThread("User ID=springqa", "Password=springqa");</programlisting>
<para><literal>UserCredentialsDbProvider's</literal> has a base class,
<literal>DelegatingDbProvider</literal>, and is intended for you to use
in your own implementations that delegate calls to a target
<literal>IDbProvider</literal> instance. This class in meant to be
subclassed with subclasses overriding only those methods, such as
<literal>CreateConnection()</literal>, that should not simply delegate
to the target <literal>IDbProvider</literal>.</para>
</section>
<section id="dbprovider-multidelegating">
<title>MultiDelegatingDbProvider</title>
<para>There are use-cases in which there will need to be a runtime
selection of the database to connect to among many possible candidates.
This is often the case where the same schema is installed in separate
databases for different clients. The
<classname>MultiDelegatingDbProvider</classname> implements the
<classname>IDbProvider</classname> interface and provides an abstraction
to the multiple databases and can be used in DAO layer such that the DAO
layer is unaware of the switching between databases.
<classname>MultiDelegatingDbProvider</classname> does its job by looking
into thread local storage under the key dbProviderName. This storage
location stores the name of the dbProvider that is to be used for
processing the request. <classname>MultiDelegatingDbProvider</classname>
is configured using the dictionary property
<literal>TargetDbProviders</literal>. The key of this dictionary
contains the name of a dbProvider and its value is a dbProvider object.
(You can also provide this dictionary as a constructor argument.) During
request processing, once you have determined which target dbProvider
should be use, in this example database1ProviderName, you should execute
the following code
<literal>LogicalThreadContext.SetData("dbProviderName",
"database1ProviderName")</literal> and then call the data access
layer.</para>
<para></para>
</section>
</section>
</chapter>

View File

@@ -0,0 +1,990 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="expressions">
<title>Expression Evaluation</title>
<sect1 id="expressions-introduction">
<title>Introduction</title>
<para>The Spring.Expressions namespace provides a powerful expression
language for querying and manipulating an object graph at runtime. The
language supports setting and getting of property values, property
assignment, method invocation, accessing the context of arrays,
collections and indexers, logical and arithmetic operators, named
variables, and retrieval of objects by name from Spring's IoC container.
It also supports list projection and selection, as well as common list
aggregators.</para>
<para>The functionality provided in this namespace serves as the
foundation for a variety of other features in Spring.NET such as enhanced
property evaluation in the XML based configuration of the IoC container, a
Data Validation framework, and a Data Binding framework for ASP.NET. You
will likely find other cool uses for this library in your own work where
run-time evaluation of criteria based on an object's state is required.
For those with a Java background, the Spring.Expressions namespace
provides functionality similar to the Java based Object Graph Navigation
Language, <ulink url="http://www.ognl.org/">OGNL</ulink>.</para>
<para>This chapter covers the features of the expression language using an
Inventor and Inventor's Society class as the target objects for expression
evaluation. The class declarations and the data used to populate them are
listed at the end of the chapter in section <xref
linkend="expressions-classes" />. These classes are blatantly taken from
the NUnit tests for the Expressions namespace which you can refer to for
additional example usage.</para>
</sect1>
<sect1 id="expressions-evaluating">
<title>Evaluating Expressions</title>
<para>The simplest, but not the most efficient way to perform expression
evaluation is by using one of the static convenience methods of the
<classname>ExpressionEvaluator</classname> class:<programlisting>public static object GetValue(object root, string expression);
public static object GetValue(object root, string expression, IDictionary variables)
public static void SetValue(object root, string expression, object newValue)
public static void SetValue(object root, string expression, IDictionary variables, object newValue)</programlisting>
The first argument is the 'root' object that the expression string (2nd argument) will be
evaluated against. The third argument is used to support variables in the expression
and will be discussed later.
Simple usage to get the value of an object property is shown below using the
<classname>Inventor</classname> class.
You can find the class listing in section <xref linkend="expressions-classes"/>.
<programlisting>Inventor tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian");
tesla.PlaceOfBirth.City = "Smiljan";
string evaluatedName = (string) ExpressionEvaluator.GetValue(tesla, "Name");
string evaluatedCity = (string) ExpressionEvaluator.GetValue(tesla, "PlaceOfBirth.City"));</programlisting>
The value of 'evaluatedName' is 'Nikola Tesla' and that of 'evaluatedCity'
is 'Smiljan'. A period is used to navigate the nested properties of the
object. Similarly to set the property of an object, say we want to rewrite
history and change Tesla's city of birth, we would simply add the
following line <programlisting>ExpressionEvaluator.SetValue(tesla, "PlaceOfBirth.City", "Novi Sad");</programlisting></para>
<para>A much better way to evaluate expressions is to parse them once and
then evaluate as many times as you want
using<classname>Expression</classname>class. Unlike
<classname>ExpressionEvaluator</classname>, which parses expression every
time you invoke one of its methods, <classname>Expression</classname>
class will cache the parsed expression for increased performance. The
methods of this class are listed below: <programlisting>public static IExpression Parse(string expression)
public override object Get(object context, IDictionary variables)
public override void Set(object context, IDictionary variables, object newValue)</programlisting>
The retrieval of the Name property in the previous example using the
Expression class is shown below <programlisting>IExpression exp = Expression.Parse("Name");
string evaluatedName = (string) exp.GetValue(tesla, null);</programlisting></para>
<para>The difference in performance between the two approaches, when
evaluating the same expression many times, is several orders of magnitude,
so you should only use convenience methods of the
<classname>ExpressionEvaluator</classname> class when you are doing
one-off expression evaluations. In all other cases you should parse the
expression first and then evaluate it as many times as you need.</para>
<para>There are a few exception classes to be aware of when using the
<classname>ExpressionEvaluator</classname>. These are
<classname>InvalidPropertyException</classname>, when you refer to a
property that doesn't exist,
<classname>NullValueInNestedPathException</classname>, when a null value
is encountered when traversing through the nested property list, and
<classname>ArgumentException</classname> and
<classname>NotSupportedException</classname> when you pass in values that
are in error in some other manner.</para>
<para>The expression language is based on a grammar and uses <ulink
url="http://www.antlr.org/">ANTLR</ulink> to construct the lexer and
parser. Errors relating to bad syntax of the language will be caught at
this level of the language implementation. For those interested in the
digging deeper into the implementation, the grammar file is named
Expression.g and is located in the src directory of the namespace. As a
side note, the release version of the ANTLR DLL included with Spring.NET
was signed with the Spring.NET key, which means that you should always use
the included version of <literal>antlr.runtime.dll</literal> within your
application. Upcoming releases of ANTLR will provide strongly signed
assemblies, which will remove this requirement.</para>
</sect1>
<sect1 id="expressions-language-ref">
<title>Language Reference</title>
<sect2 id="expressions-literals">
<title>Literal expressions</title>
<para>The types of literal expressions supported are strings, dates,
numeric values (int, real, and hex), boolean and null. String are
delimited by single quotes. To put a single quote itself in a string use
the backslash character. The following listing shows simple usage of
literals. Typically they would not be used in isolation like this, but
as part of a more complex expression, for example using a literal on one
side of a logical comparison operator. <programlisting>string helloWorld = (string) ExpressionEvaluator.GetValue(null, "'Hello World'"); // evals to "Hello World"
string tonyPizza = (string) ExpressionEvaluator.GetValue(null, "'Tony\\'s Pizza'"); // evals to "Tony's Pizza"
double avogadrosNumber = (double) ExpressionEvaluator.GetValue(null, "6.0221415E+23");
int maxValue = (int) ExpressionEvaluator.GetValue(null, "0x7FFFFFFF"); // evals to 2147483647
DateTime birthday = (DateTime) ExpressionEvaluator.GetValue(null, "date('1974/08/24')");
DateTime exactBirthday =
(DateTime) ExpressionEvaluator.GetValue(null, " date('19740824T131030', 'yyyyMMddTHHmmss')");
bool trueValue = (bool) ExpressionEvaluator.GetValue(null, "true");
object nullValue = ExpressionEvaluator.GetValue(null, "null");</programlisting>
Note that the extra backslash character in Tony's Pizza is to satisfy C#
escape syntax. Numbers support the use of the negative sign, exponential
notation, and decimal points. By default real numbers are parsed using
<classname>Double.Parse</classname> unless the format character "M" or
"F" is supplied, in which case <classname>Decimal.Parse</classname> and
<classname>Single.Parse</classname> would be used respectfully. As shown
above, if two arguments are given to the date literal then
<classname>DateTime.ParseExact</classname> will be used. Note that all
parse methods of classes that are used internally reference the
<classname>CultureInfo.InvariantCulture</classname>.</para>
</sect2>
<!-- PROPERTIES -->
<sect2 id="expressions-properties">
<title>Properties, Arrays, Lists, Dictionaries, Indexers</title>
<para>As shown in the previous example in <xref
linkend="expressions-evaluating" />, navigating through properties is
easy, just use a period to indicate a nested property value. The
instances of <classname>Inventor</classname> class,
<emphasis>pupin</emphasis> and <emphasis>tesla</emphasis>, were
populated with data listed in section <xref
linkend="expressions-classes" />. To navigate "down" and get Tesla's
year of birth and Pupin's city of birth the following expressions are
used <programlisting>int year = (int) ExpressionEvaluator.GetValue(tesla, "DOB.Year")); // 1856
string city = (string) ExpressionEvaluator.GetValue(pupin, "PlaCeOfBirTh.CiTy"); // "Idvor"</programlisting>
For the sharp-eyed, that isn't a typo in the property name for place of
birth. The expression uses mixed cases to demonstrate that the
evaluation is case insensitive.</para>
<para>The contents of arrays and lists are obtained using square bracket
notation. <programlisting>// Inventions Array
string invention = (string) ExpressionEvaluator.GetValue(tesla, "Inventions[3]"); // "Induction motor"
// Members List
string name = (string) ExpressionEvaluator.GetValue(ieee, "Members[0].Name"); // "Nikola Tesla"
// List and Array navigation
string invention = (string) ExpressionEvaluator.GetValue(ieee, "Members[0].Inventions[6]") // "Wireless communication"</programlisting></para>
<para>The contents of dictionaries are obtained by specifying the
literal key value within the brackets. In this case, because keys for
the <emphasis>Officers</emphasis> dictionary are strings, we can specify
string literal.<programlisting>// Officer's Dictionary
Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers['president']";
string city = (string) ExpressionEvaluator.GetValue(ieee, "Officers['president'].PlaceOfBirth.City"); // "Idvor"
ExpressionEvaluator.SetValue(ieee, "Officers['advisors'][0].PlaceOfBirth.Country", "Croatia");</programlisting></para>
<para>You may also specify non literal values in place of the quoted
literal values by using another expression inside the square brackets
such as variable names or static properties/methods on other types.
These features are discussed in other sections.</para>
<para>Indexers are similarly referenced using square brackets. The
following is a small example that shows the use of indexers.
Multidimensional indexers are also supported. <programlisting>public class Bar
{
private int[] numbers = new int[] {1, 2, 3};
public int this[int index]
{
get { return numbers[index];}
set { numbers[index] = value; }
}
}
Bar b = new Bar();
int val = (int) ExpressionEvaluator.GetValue(bar, "[1]") // evaluated to 2
ExpressionEvaluator.SetValue(bar, "[1]", 3); // set value to 3</programlisting></para>
<sect3>
<title>Defining Arrays, Lists and Dictionaries Inline</title>
<para>In addition to accessing arrays, lists and dictionaries by
navigating the graph for the context object, Spring.NET Expression
Language allows you to define them inline, within the expression.
Inline lists are defined by simply enclosing a comma separated list of
items with curly brackets:<programlisting>{1, 2, 3, 4, 5}
{'abc', 'xyz'}</programlisting> If you want to ensure that a strongly typed
array is initialized instead of a weakly typed list, you can use array
initializer instead: <programlisting>new int[] {1, 2, 3, 4, 5}
new string[] {'abc', 'xyz'}</programlisting></para>
<para>Dictionary definition syntax is a bit different: you need to use
a # prefix to tell expression parser to expect key/value pairs within
the brackets and to specify a comma separated list of key/value pairs
within the brackets:<programlisting>#{'key1' : 'Value 1', 'today' : DateTime.Today}
#{1 : 'January', 2 : 'February', 3 : 'March', ...}</programlisting></para>
<para>Arrays, lists and dictionaries created this way can be used
anywhere where arrays, lists and dictionaries obtained from the object
graph can be used, which we will see later in the examples.</para>
<para>Keep in mind that even though examples above use literals as
array/list elements and dictionary keys and values, that's only to
simplify the examples -- you can use any valid expression wherever
literals are used.</para>
</sect3>
</sect2>
<sect2 id="expressions-methods">
<title>Methods</title>
<para>Methods are invoked using typical C# programming syntax. You may
also invoke methods on literals.</para>
<programlisting>//string literal
char[] chars = (char[]) ExpressionEvaluator.GetValue(null, "'test'.ToCharArray(1, 2)")) // 't','e'
//date literal
int year = (int) ExpressionEvaluator.GetValue(null, "date('1974/08/24').AddYears(31).Year") // 2005
// object usage, calculate age of tesla navigating from the IEEE society.
ExpressionEvaluator.GetValue(ieee, "Members[0].GetAge(date('2005-01-01')") // 149 (eww..a big anniversary is coming up ;)</programlisting>
</sect2>
<sect2 id="expressions-operators">
<title>Operators</title>
<sect3 id="expressions-relational">
<title>Relational operators</title>
<para>The relational operators; equal, not equal, less than, less than
or equal, greater than, and greater than or equal are supported using
standard operator notation. These operators take into account if the
object implements the <classname>IComparable</classname> interface.
Enumerations are also supported but you will need to register the
enumeration type, as described in Section <xref
linkend="expressions-typeregistration" />, in order to use an
enumeration value in an expression if it is not contained in the
mscorlib.</para>
<programlisting>ExpressionEvaluator.GetValue(null, "2 == 2") // true
ExpressionEvaluator.GetValue(null, "date('1974-08-24') != DateTime.Today") // true
ExpressionEvaluator.GetValue(null, "2 &lt; -5.0") // false
ExpressionEvaluator.GetValue(null, "DateTime.Today &lt;= date('1974-08-24')") // false
ExpressionEvaluator.GetValue(null, "'Test' &gt;= 'test'") // true</programlisting>
<para>Enumerations can be evaluated as shown below <programlisting>FooColor fColor = new FooColor();
ExpressionEvaluator.SetValue(fColor, "Color", KnownColor.Blue);
bool trueValue = (bool) ExpressionEvaluator.GetValue(fColor, "Color == KnownColor.Blue"); //true</programlisting>
Where FooColor is the following class. <programlisting>public class FooColor
{
private KnownColor knownColor;
public KnownColor Color
{
get { return knownColor;}
set { knownColor = value; }
}
}</programlisting></para>
<para>In addition to standard relational operators, Spring.NET
Expression Language supports some additional, very useful operators
that were "borrowed" from SQL, such as <emphasis>in</emphasis>,
<emphasis>like</emphasis> and <emphasis>between</emphasis>, as well as
<emphasis>is</emphasis> and <emphasis>matches</emphasis> operators,
which allow you to test if object is of a specific type or if the
value matches a regular expression.<programlisting>ExpressionEvaluator.GetValue(null, "3 in {1, 2, 3, 4, 5}") // true
ExpressionEvaluator.GetValue(null, "'Abc' like '[A-Z]b*'") // true
ExpressionEvaluator.GetValue(null, "'Abc' like '?'") // false
ExpressionEvaluator.GetValue(null, "1 between {1, 5}") // true
ExpressionEvaluator.GetValue(null, "'efg' between {'abc', 'xyz'}") // true
ExpressionEvaluator.GetValue(null, "'xyz' is int") // false
ExpressionEvaluator.GetValue(null, "{1, 2, 3, 4, 5} is IList") // true
ExpressionEvaluator.GetValue(null, "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'")) // false
ExpressionEvaluator.GetValue(null, @"'5.00' matches '^-?\d+(\.\d{2})?$'") // true</programlisting>Note
that the Visual Basic and not SQL syntax is used for the
<emphasis>like</emphasis> operator pattern string.
</para>
</sect3>
<sect3 id="expressions-logical">
<title>Logical operators</title>
<para>The logical operators that are supported are
<emphasis>and</emphasis>, <emphasis>or</emphasis>, and
<emphasis>not</emphasis>. Their use is demonstrated
below<programlisting>// AND
bool falseValue = (bool) ExpressionEvaluator.GetValue(null, "true and false"); //false
string expression = @"IsMember('Nikola Tesla') and IsMember('Mihajlo Pupin')";
bool trueValue = (bool) ExpressionEvaluator.GetValue(ieee, expression); //true
// OR
bool trueValue = (bool) ExpressionEvaluator.GetValue(null, "true or false"); //true
string expression = @"IsMember('Nikola Tesla') or IsMember('Albert Einstien')";
bool trueValue = (bool) ExpressionEvaluator.GetValue(ieee, expression); // true
// NOT
bool falseValue = (bool) ExpressionEvaluator.GetValue(null, "!true");
// AND and NOT
string expression = @"IsMember('Nikola Tesla') and !IsMember('Mihajlo Pupin')";
bool falseValue = (bool) ExpressionEvaluator.GetValue(ieee, expression);</programlisting></para>
</sect3>
<sect3 id="expressions-math">
<title>Mathematical operators</title>
<para>The addition operator can be used on numbers, strings and dates.
Subtraction can be used on numbers and dates. Multiplication and
division can be used only on numbers. Other mathematical operators
supported are modulus (%) and exponential power (^). Standard operator
precedence is enforced. These operators are demonstrated below
<programlisting>// Addition
int two = (int)ExpressionEvaluator.GetValue(null, "1 + 1"); // 2
String testString = (String)ExpressionEvaluator.GetValue(null, "'test' + ' ' + 'string'"); //'test string'
DateTime dt = (DateTime)ExpressionEvaluator.GetValue(null, "date('1974-08-24') + 5"); // 8/29/1974
// Subtraction
int four = (int) ExpressionEvaluator.GetValue(null, "1 - -3"); //4
Decimal dec = (Decimal) ExpressionEvaluator.GetValue(null, "1000.00m - 1e4"); // 9000.00
TimeSpan ts = (TimeSpan) ExpressionEvaluator.GetValue(null, "date('2004-08-14') - date('1974-08-24')"); //10948.00:00:00
// Multiplication
int six = (int) ExpressionEvaluator.GetValue(null, "-2 * -3"); // 6
int twentyFour = (int) ExpressionEvaluator.GetValue(null, "2.0 * 3e0 * 4"); // 24
// Division
int minusTwo = (int) ExpressionEvaluator.GetValue(null, "6 / -3"); // -2
int one = (int) ExpressionEvaluator.GetValue(null, "8.0 / 4e0 / 2"); // 1
// Modulus
int three = (int) ExpressionEvaluator.GetValue(null, "7 % 4"); // 3
int one = (int) ExpressionEvaluator.GetValue(null, "8.0 % 5e0 % 2"); // 1
// Exponent
int sixteen = (int) ExpressionEvaluator.GetValue(null, "-2 ^ 4"); // 16
// Operator precedence
int minusFortyFive = (int) ExpressionEvaluator.GetValue(null, "1+2-3*8^2/2/2"); // -45
</programlisting></para>
</sect3>
</sect2>
<sect2 id="expressions-assignment">
<title>Assignment</title>
<para>Setting of a property is done by using the assignment operator.
This would typically be done within a call to
<literal>GetValue</literal> since in the simple case
<literal>SetValue</literal> offers the same functionality. Assignment in
this manner is useful when combining multiple operators in an expression
list, discussed in the next section. Some examples of assignment are
shown below <programlisting>Inventor inventor = new Inventor();
String aleks = (String) ExpressionEvaluator.GetValue(inventor, "Name = 'Aleksandar Seovic'");
DateTime dt = (DateTime) ExpressionEvaluator.GetValue(inventor, "DOB = date('1974-08-24')");
//Set the vice president of the society
Inventor tesla = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers['vp'] = Members[0]");</programlisting></para>
</sect2>
<sect2 id="expressions-explist">
<title>Expression lists</title>
<para>Multiple expressions can be evaluated against the same context
object by separating them with a semicolon and enclosing the entire
expression within parentheses. The value returned is the value of the
last expression in the list. Examples of this are shown below
<programlisting>//Perform property assignments and then return Name property.
String pupin = (String) ExpressionEvaluator.GetValue(ieee.Members,
"( [1].PlaceOfBirth.City = 'Beograd'; [1].PlaceOfBirth.Country = 'Serbia'; [1].Name )"));
// pupin = "Mihajlo Pupin"</programlisting></para>
</sect2>
<sect2 id="expressions-types">
<title>Types</title>
<para>In many cases, you can reference types by simply specifying type
name:<programlisting>ExpressionEvaluator.GetValue(null, "1 is int")
ExpressionEvaluator.GetValue(null, "DateTime.Today")
ExpressionEvaluator.GetValue(null, "new string[] {'abc', 'efg'}")</programlisting></para>
<para>This is possible for all standard types from
<literal>mscorlib</literal>, as well as for any other type that is
registered with the <literal>TypeRegistry</literal> as described in the
next section.</para>
<para>For all other types, you need to use special
<literal>T(typeName)</literal> expression:<programlisting>Type dateType = (Type) ExpressionEvaluator.GetValue(null, "T(System.DateTime)")
Type evalType = (Type) ExpressionEvaluator.GetValue(null, "T(Spring.Expressions.ExpressionEvaluator, Spring.Core)")
bool trueValue = (bool) ExpressionEvaluator.GetValue(tesla, "T(System.DateTime) == DOB.GetType()")</programlisting></para>
<note>
<para>The implementation delegates to Spring's
<classname>ObjectUtils.ResolveType</classname> method for the actual
type resolution, which means that the types used within expressions
are resolved in the exactly the same way as the types specified in
Spring configuration files.</para>
</note>
</sect2>
<sect2 id="expressions-typeregistration">
<title>Type Registration</title>
<para>To refer to a type within an expression that is not in the
mscorlib you need to register it with the
<literal>TypeRegistry</literal>. This will allow you to refer to a
shorthand name of the type within your expressions. This is commonly
used in expression that use the new operator or refer to a static
properties of an object. Example usage is shown below.</para>
<programlisting>TypeRegistry.RegisterType("Society", typeof(Society));
Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers[Society.President]");</programlisting>
<para>Alternatively, you can register types using
<literal>typeAliases</literal> configuration section.</para>
</sect2>
<sect2 id="expressions-ctor">
<title>Constructors</title>
<para>Constructors can be invoked using the new operator. For classes
outside mscorlib you will need to register your types so they can be
resolved. Examples of using constructors are shown below:
<programlisting>// simple ctor
DateTime dt = (DateTime) ExpressionEvaluator.GetValue(null, "new DateTime(1974, 8, 24)");
// Register Inventor type then create new inventor instance within Add method inside an expression list.
// Then return the new count of the Members collection.
TypeRegistry.RegisterType(typeof(Inventor));
int three = (int) ExpressionEvaluator.GetValue(ieee.Members, "{ Add(new Inventor('Aleksandar Seovic', date('1974-08-24'), 'Serbian')); Count}"));
</programlisting></para>
<para>As a convenience, Spring.NET also allows you to define named
constructor arguments, which are used to set object's properties after
instantiation, similar to the way standard .NET attributes work. For
example, you could create an instance of the <literal>Inventor</literal>
class and set its <literal>Inventions</literal> property in a single
statement:<programlisting>
Inventor aleks = (Inventor) ExpressionEvaluator.GetValue(null, "new Inventor('Aleksandar Seovic', date('1974-08-24'), 'Serbian', Inventions = {'SPELL'})");
</programlisting>The only rule you have to follow is that named arguments
should be specified <emphasis>after</emphasis> standard constructor
arguments, just like in the .NET attributes.</para>
<para>While we are on the subject, Spring.NET Expression Language also
provides a convenient syntax for .NET attribute instance creation.
Instead of using standard constructor syntax, you can use a somewhat
shorter and more familiar syntax to create an instance of a .NET
attribute class:<programlisting>
WebMethodAttribute webMethod = (WebMethodAttribute) ExpressionEvaluator.GetValue(null, "@[WebMethod(true, CacheDuration = 60, Description = 'My Web Method')]");
</programlisting>As you can see, with the exception of the
<literal>@</literal> prefix, syntax is exactly the same as in C#.</para>
<para>Slightly different syntax is not the only thing that
differentiates an attribute expression from a standard constructor
invocation expression. In addition to that, attribute expression uses
slightly different type resolution mechanism and will attempt to load
both the specified type name and the specified type name with an
<literal>Attribute</literal> suffix, just like the C# compiler.</para>
</sect2>
<sect2 id="expressions-variables">
<title>Variables</title>
<para>Variables can referenced in the expression using the syntax
<literal>#</literal><emphasis>variableName</emphasis>. The variables are
passed in and out of the expression using the dictionary parameter in
<classname>ExpressionEvaluator</classname>'s <literal>GetValue</literal>
or <literal>SetValue</literal> methods. <programlisting>public static object GetValue(object root, string expression, IDictionary variables)
public static void SetValue(object root, string expression, IDictionary variables, object newValue)</programlisting>
The variable name is the key value of the dictionary. Example usage is
shown below; <programlisting>IDictionary vars = new Hashtable();
vars["newName"] = "Mike Tesla";
ExpressionEvaluator.GetValue(tesla, "Name = #newName", vars));</programlisting>
You can also use the dictionary as a place to store values of the object
as they are evaluated inside the expression. For example to change
Tesla's first name back again and keep the old value; <programlisting>ExpressionEvaluator.GetValue(tesla, "{ #oldName = Name; Name = 'Nikola Tesla' }", vars);
String oldName = (String)vars["oldName"]; // Mike Tesla</programlisting>
Variable names can also be used inside indexers or maps instead of
literal values. For example; <programlisting>vars["prez"] = "president";
Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers[#prez]", vars);</programlisting></para>
<sect3 id="expressions-this">
<title>The '#this' and '#root' variables</title>
<para>There are two special variables that are always defined and can
be references within the expression: <literal>#this</literal> and
<literal>#root</literal>.</para>
<para>The <literal>#this</literal> variable can be used to explicitly
refer to the context for the node that is currently being
evaluated:<programlisting>// sets the name of the president and returns its instance
ExpressionEvaluator.GetValue(ieee, "Officers['president'].( #this.Name = 'Nikola Tesla'; #this )")</programlisting></para>
<para>Similarly, the <literal>#root</literal> variable allows you to
refer to the root context for the expression:<programlisting>// removes president from the Officers dictionary and returns removed instance
ExpressionEvaluator.GetValue(ieee, "Officers['president'].( #root.Officers.Remove('president'); #this )")</programlisting></para>
</sect3>
</sect2>
<sect2 id="expressions-ternary">
<title>Ternary Operator (If-Then-Else)</title>
<para>You can use the ternary operator for performing if-then-else
conditional logic inside the expression. A minimal example is;
<programlisting>String aTrueString = (String) ExpressionEvaluator.GetValue(null, "false ? 'trueExp' : 'falseExp'") // trueExp
</programlisting> In this case, the boolean false results in returning the
string value 'trueExp'. A less artificial example is shown below
<programlisting>ExpressionEvaluator.SetValue(ieee, "Name", "IEEE");
IDictionary vars = new Hashtable();
vars["queryName"] = "Nikola Tesla";
string expression = @"IsMember(#queryName)
? #queryName + ' is a member of the ' + Name + ' Society'
: #queryName + ' is not a member of the ' + Name + ' Society'";
String queryResultString = (String) ExpressionEvaluator.GetValue(ieee, expression, vars));
// queryResultString = "Nikola Tesla is a member of the IEEE Society"</programlisting></para>
</sect2>
<sect2>
<title>List Projection and Selection</title>
<para>List projection and selection are very powerful expression
language features that allow you to transform the source list into
another list by either <emphasis>projecting</emphasis> across its
"columns", or <emphasis>selecting</emphasis> from its "rows". In other
words, projection can be thought of as a column selector in a SQL SELECT
statement, while selection would be comparable to the WHERE
clause.</para>
<para>For example, let's say that we need a list of the cities where our
inventors were born. This could be easily obtained by projecting on the
<literal>PlaceOfBirth.City</literal> property: <programlisting>IList placesOfBirth = (IList) ExpressionEvaluator.GetValue(ieee, "Members.!{PlaceOfBirth.City}") // { 'Smiljan', 'Idvor' }
</programlisting>Or we can get the list of officers' names:<programlisting>IList officersNames = (IList) ExpressionEvaluator.GetValue(ieee, "Officers.Values.!{Name}") // { 'Nikola Tesla', 'Mihajlo Pupin' }
</programlisting></para>
<para>As you can see from the examples, projection uses
<literal>!{</literal><emphasis>projectionExpression</emphasis><literal>}</literal>
syntax and will return a new list of the same length as the original
list but typically with the elements of a different type.</para>
<para>On the other hand, selection, which uses
<literal>?{</literal><emphasis>projectionExpression</emphasis><literal>}</literal>
syntax, will filter the list and return a new list containing a subset
of the original element list. For example, selection would allow us to
easily get a list of Serbian inventors:<programlisting>IList serbianInventors = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{Nationality == 'Serbian'}") // { tesla, pupin }
</programlisting>Or to get a list of inventors that invented
sonar:<programlisting>IList sonarInventors = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{'Sonar' in Inventions}") // { pupin }
</programlisting>Or we can combine selection and projection to get a list of
sonar inventors' names:<programlisting>IList sonarInventorsNames = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{'Sonar' in Inventions}.!{Name}") // { 'Mihajlo Pupin' }
</programlisting></para>
<para>As a convenience, Spring.NET Expression Language also supports a
special syntax for selecting the first or last match. Unlike regular
selection, which will return an empty list if no matches are found,
first or last match selection expression will either return an instance
of the matched element, or <literal>null</literal> if no matching
elements were found. In order to return a first match you should prefix
your selection expression with <literal>^{</literal> instead of
<literal>?{</literal>, and to return last match you should use
<literal>${</literal> prefix:<programlisting>ExpressionEvaluator.GetValue(ieee, "Members.^{Nationality == 'Serbian'}.Name") // 'Nikola Tesla'
ExpressionEvaluator.GetValue(ieee, "Members.${Nationality == 'Serbian'}.Name") // 'Mihajlo Pupin'
</programlisting>Notice that we access the <literal>Name</literal> property
directly on the selection result, because an actual matched instance is
returned by the first and last match expression instead of a filtered
list.</para>
</sect2>
<sect2>
<title>Collection Processors and Aggregators</title>
<para>In addition to list projection and selection, Spring.NET
Expression Language also supports several collection processors, such as
<literal>distinct</literal>, <literal>nonNull</literal> and
<literal>sort</literal>, as well as a number of commonly used
aggregators, such as <literal>max</literal>, <literal>min</literal>,
<literal>count</literal>, <literal>sum</literal> and
<literal>average</literal>.</para>
<para>The difference between processors and aggregators is that
processors return a new or transformed collection, while aggregators
return a single value. Other than that, they are very similar -- both
processors and aggregators are invoked on a collection node using
standard method invocation expression syntax, which makes them very
simple to use and allows easy chaining of multiple processors.</para>
<sect3>
<title>Count Aggregator</title>
<para>The count aggregator is a safe way to obtain a number of items
in a collection. It can be applied to a collection of any type,
including arrays, which helps eliminate the decision on whether to use
<literal>Count</literal> or <literal>Length</literal> property
depending on the context. Unlike its standard .NET counterparts, count
aggregator can also be invoked on the <literal>null</literal> context
without throwing a <classname>NullReferenceException</classname>. It
will simply return zero in this case, which makes it much safer than
standard .NET properties within larger expression.<programlisting>ExpressionEvaluator.GetValue(null, "{1, 5, -3}.count()") // 3
ExpressionEvaluator.GetValue(null, "count()") // 0
</programlisting></para>
</sect3>
<sect3>
<title>Sum Aggregator</title>
<para>The sum aggregator can be used to calculate a total for the list
of numeric values. If numbers within the list are not of the same type
or precision, it will automatically perform necessary conversion and
the result will be the highest precision type. If any of the
collection elements is not a number, this aggregator will throw an
<classname>InvalidArgumentException</classname>.<programlisting>ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.sum()") // 13 (int)
ExpressionEvaluator.GetValue(null, "{5, 5.8, 12.2, 1}.sum()") // 24.0 (double)
</programlisting></para>
</sect3>
<sect3>
<title>Average Aggregator</title>
<para>The average aggregator will return the average for the
collection of numbers. It will use the same type coercion rules, as
the sum aggregator in order to be as precise as possible. Just like
the sum aggregator, if any of the collection elements is not a number,
it will throw an
<classname>InvalidArgumentException</classname>.<programlisting>ExpressionEvaluator.GetValue(null, "{1, 5, -4, 10}.average()") // 3
ExpressionEvaluator.GetValue(null, "{1, 5, -2, 10}.average()") // 3.5
</programlisting></para>
</sect3>
<sect3>
<title>Minimum Aggregator</title>
<para>The minimum aggregator will return the smallest item in the
list. In order to determine what "the smallest" actually means, this
aggregator relies on the assumption that the collection items are of
the uniform type and that they implement the
<classname>IComparable</classname> interface. If that is not the case,
this aggregator will throw an
<classname>InvalidArgumentException</classname>.<programlisting>ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.min()") // -3
ExpressionEvaluator.GetValue(null, "{'abc', 'efg', 'xyz'}.min()") // 'abc'
</programlisting></para>
</sect3>
<sect3>
<title>Maximum Aggregator</title>
<para>The maximum aggregator will return the largest item in the list.
In order to determine what "the largest" actually means, this
aggregator relies on the assumption that the collection items are of
the uniform type and that they implement
<classname>IComparable</classname> interface. If that is not the case,
this aggregator will throw an
<classname>InvalidArgumentException</classname>.<programlisting>ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.max()") // 10
ExpressionEvaluator.GetValue(null, "{'abc', 'efg', 'xyz'}.max()") // 'xyz'
</programlisting></para>
</sect3>
<sect3>
<title>Non-null Processor</title>
<para>A non-null processor is a very simple collection processor that
eliminates all <literal>null</literal> values from the
collection.<programlisting>ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', null, 'abc', 'def', null}.nonNull()") // { 'abc', 'xyz', 'abc', 'def' }
ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', null, 'abc', 'def', null}.nonNull().distinct().sort()") // { 'abc', 'def', 'xyz' }
</programlisting></para>
</sect3>
<sect3>
<title>Distinct Processor</title>
<para>A distinct processor is very useful when you want to ensure that
you don't have duplicate items in the collection. It can also accept
an optional <literal>Boolean</literal> argument that will determine
whether <literal>null</literal> values should be included in the
results. The default is <literal>false</literal>, which means that
they will not be included. <programlisting>ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', 'abc', 'def', null, 'def' }.distinct(true).sort()") // { null, 'abc', 'def', 'xyz' }
ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', 'abc', 'def', null, 'def' }.distinct(false).sort()") // { 'abc', 'def', 'xyz' }
</programlisting></para>
</sect3>
<sect3>
<title>Sort Processor</title>
<para>The sort processor can be used to sort uniform collections of
elements that implement <classname>IComparable</classname>.</para>
<programlisting>ExpressionEvaluator.GetValue(null, "{1.2, 5.5, -3.3}.sort()") // { -3.3, 1.2, 5.5 }
ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', 'abc', 'def', null, 'def' }.sort()") // { null, 'abc', 'abc', 'def', 'def', 'xyz' }
</programlisting>
</sect3>
</sect2>
<sect2>
<title>Spring Object References</title>
<para>Expressions can refer to objects that are declared in Spring's
application context using the syntax
<literal>@(</literal><emphasis>contextName</emphasis><literal>:</literal><emphasis>objectName</emphasis><literal>)</literal>.
If no contextName is specified the default root context name
(<literal>Spring.RootContext</literal>) is used. Using the application
context defined in the MovieFinder example from <xref
linkend="quickstarts" />, the following expression returns the number of
movies directed by Roberto Benigni. <programlisting>public static void Main()
{
. . .
// Retrieve context defined in the spring/context section of
// the standard .NET configuration file.
IApplicationContext ctx = ContextRegistry.GetContext();
int numMovies = (int) ExpressionEvaluator.GetValue(null,
"@(MyMovieLister).MoviesDirectedBy('Roberto Benigni').Length");
. . .
}</programlisting> The variable numMovies is evaluated to 2 in this
example.</para>
</sect2>
<sect2>
<title>Lambda Expressions</title>
<para>A somewhat advanced, but a very powerful feature of Spring.NET
Expression Language are lambda expressions. Lambda expressions allow you
to define inline functions, which can then be used within your
expressions just like any other function or method.</para>
<para>The syntax for defining lambda expressions is:</para>
<para><literal>#</literal><emphasis>functionName</emphasis><literal> =
{|</literal><emphasis>argList</emphasis><literal>|
</literal><emphasis>functionBody</emphasis><literal> }</literal></para>
<para>For example, you could define a <literal>max</literal> function
and call it like this:<programlisting>ExpressionEvaluator.GetValue(null, "(#max = {|x,y| $x &gt; $y ? $x : $y }; #max(5,25))", new Hashtable()) // 25</programlisting></para>
<para>As you can see, any arguments defined for the expression can be
referenced within the function body using a <emphasis>local
variable</emphasis> syntax,
<literal>$</literal><emphasis>varName</emphasis>. Invocation of the
function defined using lambda expression is as simple as specifying the
comma-separated list of function arguments in parentheses, after the
function name.</para>
<para>Lambda expressions can be recursive, which means that you can
invoke the function within its own body:<programlisting>ExpressionEvaluator.GetValue(null, "(#fact = {|n| $n &lt;= 1 ? 1 : $n * #fact($n-1) }; #fact(5))", new Hashtable()) // 120</programlisting></para>
<para>Notice that in both examples above we had to specify a
<literal>variables</literal> parameter for the
<literal>GetValue</literal> method. This is because lambda expressions
are actually nothing more than parameterized variables and we need
variables dictionary in order to store them. If you don't specify a
valid <literal>IDictionary</literal> instance for the
<literal>variables</literal> parameter, you will get a runtime
exception.</para>
<para>Also, in both examples above we used an expression list in order
to define and invoke a function in a single expression. However, more
likely than not, you will want to define your functions once and then
use them within as many expressions as you need. Spring.NET provides an
easy way to pre-register your lambda expressions by exposing a static
<literal>Expression.RegisterFunction</literal> method, which takes
function name, lambda expression and variables dictionary to register
function in as parameters:<programlisting>IDictionary vars = new Hashtable();
Expression.RegisterFunction("sqrt", "{|n| Math.Sqrt($n)}", vars);
Expression.RegisterFunction("fact", "{|n| $n &lt;= 1 ? 1 : $n * #fact($n-1)}", vars);</programlisting>Once
the function registration is done, you can simply evaluate an expression
that uses these functions, making sure that the <literal>vars</literal>
dictionary is passed as a parameter to expression evaluation
engine:<programlisting>ExpressionEvaluator.GetValue(null, "#fact(5)", vars) // 120
ExpressionEvaluator.GetValue(null, "#sqrt(9)", vars) // 3</programlisting></para>
<para>Finally, because lambda expressions are treated as variables, they
can be assigned to other variables or passed as parameters to other
lambda expressions. In the following example we are defining a delegate
function that accepts function <literal>f</literal> as the first
argument and parameter <literal>n</literal> that will be passed to
function <literal>f</literal> as the second. Then we invoke the
functions registered in the previous example, as well as the lambda
expression defined inline, through our delegate:<programlisting>Expression.RegisterFunction("delegate", "{|f, n| $f($n) }", vars);
ExpressionEvaluator.GetValue(null, "#delegate(#sqrt, 4)", vars) // 2
ExpressionEvaluator.GetValue(null, "#delegate(#fact, 5)", vars) // 120
ExpressionEvaluator.GetValue(null, "#delegate({|n| $n ^ 2 }, 5)", vars) // 25</programlisting>While
this particular example is not particularly useful, it does demonstrate
that lambda expressions are indeed treated as nothing more than
parameterized variables, which is important to remember.</para>
</sect2>
<sect2>
<title>Null Context</title>
<para>If you do not specify a root object, i.e. pass in null, then the
expressions evaluated either have to be literal values, i.e.
ExpressionEvaluator.GetValue(null, "2 + 3.14"), refer to classes that
have static methods or properties, i.e.
ExpressionEvaluator.GetValue(null, "DateTime.Today"), create new
instances of objects, i.e. ExpressionEvaluator.GetValue(null, "new
DateTime(2004, 8, 14)") or refer to other objects such as those in the
variable dictionary or in the IoC container. The latter two usages will
be discussed later.</para>
</sect2>
</sect1>
<!-- SAMPLE CLASSES AND DATA -->
<sect1 id="expressions-classes">
<title>Classes used in the examples</title>
<para>The following simple classes are used to demonstrate the
functionality of the expression language.</para>
<programlisting>public class Inventor
{
public string Name;
public string Nationality;
public string[] Inventions;
private DateTime dob;
private Place pob;
public Inventor() : this(null, DateTime.MinValue, null)
{}
public Inventor(string name, DateTime dateOfBirth, string nationality)
{
this.Name = name;
this.dob = dateOfBirth;
this.Nationality = nationality;
this.pob = new Place();
}
public DateTime DOB
{
get { return dob; }
set { dob = value; }
}
public Place PlaceOfBirth
{
get { return pob; }
}
public int GetAge(DateTime on)
{
// not very accurate, but it will do the job ;-)
return on.Year - dob.Year;
}
}
public class Place
{
public string City;
public string Country;
}
public class Society
{
public string Name;
public static string Advisors = "advisors";
public static string President = "president";
private IList members = new ArrayList();
private IDictionary officers = new Hashtable();
public IList Members
{
get { return members; }
}
public IDictionary Officers
{
get { return officers; }
}
public bool IsMember(string name)
{
bool found = false;
foreach (Inventor inventor in members)
{
if (inventor.Name == name)
{
found = true;
break;
}
}
return found;
}
}</programlisting>
<para>The code listings in this chapter use instances of the data
populated with the following information.</para>
<programlisting>Inventor tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian");
tesla.Inventions = new string[]
{
"Telephone repeater", "Rotating magnetic field principle",
"Polyphase alternating-current system", "Induction motor",
"Alternating-current power transmission", "Tesla coil transformer",
"Wireless communication", "Radio", "Fluorescent lights"
};
tesla.PlaceOfBirth.City = "Smiljan";
Inventor pupin = new Inventor("Mihajlo Pupin", new DateTime(1854, 10, 9), "Serbian");
pupin.Inventions = new string[] {"Long distance telephony &amp; telegraphy", "Secondary X-Ray radiation", "Sonar"};
pupin.PlaceOfBirth.City = "Idvor";
pupin.PlaceOfBirth.Country = "Serbia";
Society ieee = new Society();
ieee.Members.Add(tesla);
ieee.Members.Add(pupin);
ieee.Officers["president"] = pupin;
ieee.Officers["advisors"] = new Inventor[] {tesla, pupin};</programlisting>
</sect1>
</chapter>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

383
doc/reference/src/index.xml Normal file
View File

@@ -0,0 +1,383 @@
<?xml version='1.0' encoding="iso-8859-1"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
"../../reference/lib/docbook-dtd/docbookx.dtd"
[
<!ENTITY aop SYSTEM "aop.xml">
<!ENTITY aop-aspect-library SYSTEM "aop-aspect-library.xml">
<!ENTITY background SYSTEM "background.xml">
<!ENTITY objects SYSTEM "objects.xml">
<!ENTITY resources SYSTEM "resources.xml">
<!ENTITY objects-misc SYSTEM "objects-misc.xml">
<!ENTITY expressions SYSTEM "expressions.xml">
<!ENTITY validation SYSTEM "validation.xml">
<!ENTITY logging SYSTEM "logging.xml">
<!ENTITY testing SYSTEM "testing.xml">
<!ENTITY overview SYSTEM "overview.xml">
<!ENTITY psa-intro SYSTEM "psa-intro.xml">
<!ENTITY remoting SYSTEM "remoting.xml">
<!ENTITY web SYSTEM "web.xml">
<!ENTITY ajax SYSTEM "ajax.xml">
<!ENTITY services SYSTEM "services.xml">
<!ENTITY webservices SYSTEM "webservices.xml">
<!ENTITY threading SYSTEM "threading.xml">
<!ENTITY pool SYSTEM "pool.xml">
<!ENTITY preface SYSTEM "preface.xml">
<!ENTITY transaction SYSTEM "transaction.xml">
<!ENTITY dbprovider SYSTEM "dbprovider.xml">
<!ENTITY dao SYSTEM "dao.xml">
<!ENTITY ado SYSTEM "ado.xml">
<!ENTITY orm SYSTEM "orm.xml">
<!ENTITY vsnet SYSTEM "vsnet.xml">
<!ENTITY migration SYSTEM "migration.xml">
<!ENTITY quickstarts SYSTEM "quickstarts.xml">
<!ENTITY aop-quickstart SYSTEM "aop-quickstart.xml">
<!ENTITY remoting-quickstart SYSTEM "remoting-quickstart.xml">
<!ENTITY springair SYSTEM "springair.xml">
<!ENTITY web-quickstart SYSTEM "web-quickstart.xml">
<!ENTITY data-quickstart SYSTEM "data-quickstart.xml">
<!ENTITY tx-quickstart SYSTEM "tx-quickstart.xml">
<!ENTITY javadevelopers SYSTEM "javadevelopers.xml">
<!ENTITY misc SYSTEM "misc.xml">
<!ENTITY pooling-example SYSTEM "pooling-example.xml">
<!ENTITY xsd-configuration SYSTEM "xsd-configuration.xml">
<!ENTITY xml-custom SYSTEM "xml-custom.xml">
<!ENTITY xsd SYSTEM "xsd.xml">
]>
<book>
<bookinfo>
<title>The Spring.NET Framework</title>
<subtitle>Reference Documentation</subtitle>
<releaseinfo>Version 1.1.2</releaseinfo>
<pubdate>Last Updated May 6, 2008</pubdate>
<authorgroup>
<author>
<firstname>Mark</firstname>
<surname>Pollack</surname>
</author>
<author>
<firstname>Rick</firstname>
<surname>Evans</surname>
</author>
<author>
<firstname>Aleksandar</firstname>
<surname>Seovic</surname>
</author>
<author>
<firstname>Bruno</firstname>
<surname>Baia</surname>
</author>
<author>
<firstname>Federico</firstname>
<surname>Spinazzi</surname>
</author>
<author>
<firstname>Rob</firstname>
<surname>Harrop</surname>
</author>
<author>
<firstname>Griffin</firstname>
<surname>Caprio</surname>
</author>
<author>
<firstname>Ruben</firstname>
<surname>Bartelink</surname>
</author>
<author>
<firstname>Choy</firstname>
<surname>Rim</surname>
</author>
<author>
<firstname>The Spring</firstname>
<surname>Java Team</surname>
</author>
</authorgroup>
<legalnotice>
<para>
Copies of this document may be made for your own use and for
distribution to others, provided that you do not charge any fee for such
copies and further provided that each copy contains this Copyright
Notice, whether distributed in print or electronically.
</para>
</legalnotice>
</bookinfo>
<toc/>
&preface;
&overview;
&background;
&migration;
<part id="index-core">
<title>Core Technologies</title>
<partintro>
<para>
This initial part of the reference documentation covers
all of those technologies that are absolutely integral
to the Spring Framework.
</para>
<para>
Foremost amongst these is the Spring Framework's
Inversion of Control (IoC) container. A thorough treatment
of the Spring Framework's IoC container is closely followed
by comprehensive coverage of Spring's Aspect-Oriented
Programming (AOP) technologies. The Spring Framework has
its own AOP framework, which is conceptually easy to understand,
and which successfully addresses the 80% sweet spot of AOP
requirements in enterprise programming.
</para>
<para>
The core functionality also includes an expression language
for lightweight scripting and a ui-agnostic validation framework.
</para>
<para>
Finally, the adoption of the test-driven-development (TDD)
approach to software development is certainly advocated by
the Spring team, and so coverage of Spring's support for
integration testing is covered (alongside best practices for
unit testing). The Spring team have found that the correct
use of IoC certainly does make both unit and integration
testing easier (in that the presence of properties and
appropriate constructors on classes makes them
easier to wire together on a test without having to set up
service locator registries and suchlike)... the chapter
dedicated solely to testing will hopefully convince you of
this as well.
</para>
<itemizedlist>
<listitem>
<xref linkend="objects" />
</listitem>
<listitem>
<xref linkend="objects-misc" />
</listitem>
<listitem>
<xref linkend="resources" />
</listitem>
<listitem>
<xref linkend="threading" />
</listitem>
<listitem>
<xref linkend="pool" />
</listitem>
<listitem>
<xref linkend="expressions" />
</listitem>
<listitem>
<xref linkend="misc" />
</listitem>
<listitem>
<xref linkend="validation" />
</listitem>
<listitem>
<xref linkend="aop" />
</listitem>
<listitem>
<xref linkend="aop-aspect-library" />
</listitem>
<listitem>
<xref linkend="logging" />
</listitem>
<listitem>
<xref linkend="testing" />
</listitem>
</itemizedlist>
</partintro>
&objects;
&objects-misc;
&resources;
&threading;
&pool;
&misc;
&expressions;
&validation;
<!-- &util; -->
&aop;
&aop-aspect-library;
&logging;
&testing;
</part>
<part id="index-middle-tier">
<title>Middle Tier Data Access</title>
<partintro id="index-middle-tier-intro">
<para>
This part of the reference documentation is concerned
with the middle tier, and specifically the data access
responsibilities of said tier.
</para>
<para>
Spring's comprehensive transaction management support is
covered in some detail, followed by thorough coverage of
the various middle tier data access frameworks and
technologies that the Spring Framework integrates with.
</para>
<itemizedlist>
<listitem>
<xref linkend="transaction" />
</listitem>
<listitem>
<xref linkend="dao" />
</listitem>
<listitem>
<xref linkend="dbprovider" />
</listitem>
<listitem>
<xref linkend="ado" />
</listitem>
<listitem>
<xref linkend="orm" />
</listitem>
</itemizedlist>
</partintro>
&transaction;
&dao;
&dbprovider;
&ado;
&orm;
</part>
<part>
<title>The Web</title>
<partintro>
<para>
This part of the reference documentation covers the
Spring Framework's support for the presentation tier,
specifically web-based presentation tiers.
</para>
<itemizedlist>
<listitem>
<xref linkend="web" />
</listitem>
<listitem>
<xref linkend="ajax" />
</listitem>
</itemizedlist>
</partintro>
&web;
&ajax;
</part>
<part id="index-services">
<title>Services</title>
<partintro>
<para>
This part of the reference documentation covers
the Spring Framework's integration with .NET distributed
technologies such as .NET Remoting, Enterprise Services,
Web Services. Integration with WCF Services is forthcoming.
Please refer to the introduction chapter for more details.
</para>
<itemizedlist>
<listitem>
<xref linkend="psa-intro" />
</listitem>
<listitem>
<xref linkend="remoting" />
</listitem>
<listitem>
<xref linkend="services" />
</listitem>
<listitem>
<xref linkend="webservices" />
</listitem>
</itemizedlist>
</partintro>
&psa-intro;
&remoting;
&services;
&webservices;
</part>
<part id="index-vsnet">
<title>VS.NET Integration</title>
<partintro>
<para>
This part of the reference documentation covers
the Spring Framework's integration with VS.NET
</para>
<itemizedlist>
<listitem>
<xref linkend="vsnet" />
</listitem>
</itemizedlist>
</partintro>
&vsnet;
</part>
<part id="index-quickstarts">
<title>Quickstart applications</title>
<partintro>
<para>
This part of the reference documentation covers
the quickstart applications included with
Spring that demonstrate features in a code centric
manner.
</para>
<itemizedlist>
<listitem>
<xref linkend="quickstarts" />
</listitem>
<listitem>
<xref linkend="aop-quickstart" />
</listitem>
<listitem>
<xref linkend="remoting-quickstart" />
</listitem>
<listitem>
<xref linkend="web-quickstart" />
</listitem>
<listitem>
<xref linkend="springair" />
</listitem>
<listitem>
<xref linkend="data-quickstart" />
</listitem>
<listitem>
<xref linkend="tx-quickstart" />
</listitem>
</itemizedlist>
</partintro>
&quickstarts;
&aop-quickstart;
&remoting-quickstart;
&web-quickstart;
&springair;
&data-quickstart;
&tx-quickstart;
</part>
<part id="index-javadevelopers">
<title>Spring.NET for Java developers</title>
<partintro>
<para>
This part of the reference documentation
is for Java developers who would like a quick
orientation to what is different between
the Java and .NET versions of the framework.
</para>
<itemizedlist>
<listitem>
<xref linkend="javadevelopers" />
</listitem>
</itemizedlist>
</partintro>
&javadevelopers;
</part>
<!-- back matter -->
&xsd-configuration;
&xml-custom;
&xsd;
</book>

View File

@@ -0,0 +1,234 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="javadevelopers">
<title>Spring.NET for Java Developers</title>
<sect1 id="jd-introduction">
<title>Introduction</title>
<para>This chapter is to help Java developers get their sea legs using
Spring.NET. It is not intended to be a comprehensive comparison between
.NET and Java. Rather, it highlights the day-to-day differences you will
experience when you start to use Spring.NET.</para>
</sect1>
<sect1 id="jd-beans-objects">
<title>Beans to Objects</title>
<para>There are some simple name changes, basically everywhere you saw the
word 'bean' you will now see the word 'object'. A comparison of a simple
Spring configuration file highlights these small name changes. Here is the
application.xml file for the sample MovieFinder application in Spring.Java
<programlisting>&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"&gt;
&lt;beans&gt;
&lt;bean id="MyMovieLister" class="MovieFinder.MovieLister"&gt;
&lt;property name="finder" ref="MyMovieFinder"/&gt;
&lt;/bean&gt;
&lt;bean id="MyMovieFinder" class="MovieFinder.SimpleMovieFinder"/&gt;
&lt;/beans&gt;</programlisting> Here is the corresponding file in Spring.NET
<programlisting>&lt;objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects-1.1.xsd"&gt;
&lt;object name="MyMovieLister"
type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder"&gt;
&lt;property name="movieFinder" ref="MyMovieFinder"/&gt;
&lt;/object&gt;
&lt;object name="MyMovieFinder"
type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/&gt;
&lt;/objects&gt;</programlisting> As you can easily see the &lt;beans&gt; and
&lt;bean&gt; elements are replaced by &lt;objects&gt; and &lt;object&gt;
elements. The class definition in Spring.Java contains the fully qualified
class name. The Spring.NET version also contains the fully qualified
classname but in addition specifies the name of the assembly where that
type is located. This is necessary since .NET does not have a 'classpath'
concept. Assembly names in .NET can have up to four parts to describe the
exact version.</para>
<para>The other XML Schema elements in Spring.NET are the same as in
Spring.Java's DTD except for specifying string based key value pairs. In
Java this is represented by the java.util.Properties class and the xml
element is name &lt;props&gt; as shown below <programlisting
format="linespecific" xml:space="preserve">
&lt;property name="people"&gt;
&lt;props&gt;
&lt;prop key="PennAndTeller"&gt;The magic property&lt;/prop&gt;
&lt;prop key="GeorgeCarlin"&gt;The funny property&lt;/prop&gt;
&lt;/props&gt;
&lt;/property&gt;</programlisting> In .NET the analogous class is
System.Collections.Specialized.NameValueCollection and is represented by
the xml element &lt;name-values&gt;. The listing of the elements also
follows the .NET convention of application configuration files using the
&lt;add&gt; element with 'key' and 'value' attributes. This is show below
<programlisting format="linespecific" xml:space="preserve">
&lt;property name="people"&gt;
&lt;name-values&gt;
&lt;add key="PennAndTeller" value="The magic property"/&gt;
&lt;add key="GeorgeCarlin" value="The funny property"/&gt;
&lt;/name-values&gt;
&lt;/property&gt;</programlisting></para>
</sect1>
<sect1 id="jd-propertyeditor-typeconverter">
<title>PropertyEditors to TypeConverters</title>
<para>PropertyEditors from the java.beans package provide the ability to
convert from a string to an instance of a Java class and vice-versa. For
example, to set a string array property, a comma delimited string can be
used. The Java class that provides this functionality is the appropriately
named StringArrayPropertyEditor. In .NET, TypeConverters from the
System.ComponentModel namespace provide the same functionality. The type
conversion functionality in .NET also allows for TypeConverters to be
explicitly registered with a data type. This allows for transparent
setting of complex object properties. However, some classes in the .NET
framework do not support the style of conversion we are used to from
Spring.Java, such as setting of a string[] with a comma delimited string.
The type converter, StringArrayConverter in the
Spring.Objects.TypeConverters namespace is therefore explicitly registered
with Spring.NET in order to provide this functionality. As in the case of
Spring.Java, Spring.NET allows user defined type converters to be
registered. However, if you are creating a custom type in .NET, using the
standard .NET mechanisms for type conversion is the preferred
approach.</para>
</sect1>
<sect1 id="jd-ResourceBundle-ResourceManager">
<title>ResourceBundle-ResourceManager</title>
</sect1>
<sect1 id="jd-exceptions">
<title>Exceptions</title>
<para>Exceptions in Java can either be checked or unchecked. .NET supports
only unchecked exceptions. Spring.Java prefers the use of unchecked
exceptions, frequently making conversions from checked to unchecked
exceptions. In this respect Spring.Java is similar to the default behavior
of .NET</para>
</sect1>
<sect1 id="jd-app-config">
<title>Application Configuration</title>
<para>In Spring.Java it is very common to create an ObjectFactory or
ApplicationContext from an external XML configuration file This
functionality is also provided in Spring.NET. However, in .NET the
System.Configuration namespace provides support for managing application
configuration information. The functionality in this namespace depends on
the availability of specially named files: Web.config for ASP.NET
applications and &lt;MyExe&gt;.exe.config for WinForms and console
applications. &lt;MyExe&gt; is the name of your executable. As part of the
compilation process, if you have a file name App.config in the root of
your project, the compiler will rename the file to
&lt;MyExe&gt;.exe.config and place it into the runtime executable
folder.</para>
<para>These application configuration files are XML based and contain
configuration sections that can be referenced by name to retrieve custom
configuration objects. In order to inform the .NET configuration system
how to create a custom configuration object from one of these sections, an
implementation of the interface, IConfigurationSectionHandler, needs to be
registered. Spring.NET provides two implementations, one to create an
IApplicationContext from a <literal>&lt;context&gt;</literal> section and
another to configure the context with object definitions contained in an
<literal>&lt;objects&gt;</literal> section. The
<literal>&lt;context&gt;</literal> section is very powerful and
expressive. It provides full support for locating all
<literal>IResource</literal> via Uri syntax and hierarchical contexts
without coding or using more verbose XML as would be required in the
current version of Spring.Java</para>
<programlisting>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;configuration&gt;
&lt;configSections&gt;
&lt;sectionGroup name="spring"&gt;
&lt;section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/&gt;
&lt;section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /&gt;
&lt;/sectionGroup&gt;
&lt;/configSections&gt;
&lt;spring&gt;
&lt;context&gt;
&lt;resource uri="config://spring/objects"/&gt;
&lt;/context&gt;
&lt;objects&gt;
&lt;description&gt;An example that demonstrates simple IoC features.&lt;/description&gt;
&lt;object name="MyMovieLister" type="Spring.Examples.MovieFinder.MovieLister, MovieFinder"&gt;
&lt;property name="movieFinder" ref="AnotherMovieFinder"/&gt;
&lt;/object&gt;
&lt;object name="MyMovieFinder" type="Spring.Examples.MovieFinder.SimpleMovieFinder, MovieFinder"/&gt;
&lt;!--
An IMovieFinder implementation that uses a text file as it's movie source...
--&gt;
&lt;object name="AnotherMovieFinder" type="Spring.Examples.MovieFinder.ColonDelimitedMovieFinder, MovieFinder"&gt;
&lt;constructor-arg index="0" value="movies.txt"/&gt;
&lt;/object&gt;
&lt;/objects&gt;
&lt;/spring&gt;
&lt;/configuration&gt;</programlisting>
<para>The &lt;configSections&gt; and &lt;section&gt; elements are a
standard part of the .NET application configuration file. These elements
are used to register an instance of IConfigurationSectionHandler and
associate it with another xml element in the file, in this case the
&lt;context&gt; and &lt;objects&gt; elements.</para>
<para>The following code segment is used to retrieve the
IApplicationContext from the .NET application configuration file.
<programlisting>IApplicationContext ctx
= ConfigurationUtils.GetSection("spring/context") as IApplicationContext;</programlisting></para>
<para>In order to enforce the usage of the named configuration section
<literal>spring/context</literal> the preferred instantiation mechanism is
via the use of the registry class ContextRegistry as shown below
<programlisting>IApplicationContext ctx = ContextRegistry.GetContext();</programlisting></para>
</sect1>
<sect1 id="jd-aop-framework">
<title>AOP Framework</title>
<sect2 id="NoTargetInInterceptorNames">
<title>Cannot specify target name at the end of interceptorNames for
ProxyFactoryObject</title>
<para>When configuring the list of interceptor names on a
<classname>ProxyFactoryObject</classname> instance (or object
definition), one <emphasis>cannot</emphasis> specify the name of the
target (i.e. the object being proxied) at the end of the list of
interceptor names. This shortcut <emphasis>is</emphasis> valid in Spring
Java, where the <classname>ProxyFactoryBean</classname> will
automatically detect this, and use the last name in the interceptor
names list as the target of the <classname>ProxyFactoryBean</classname>.
The following configuration, which would be valid in Spring Java
(barring the obvious element name changes), is <emphasis
role="bold">not</emphasis> valid in Spring.NET (so don't do it).</para>
<programlisting>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;object id="target" type="Spring.Objects.TestObject"&gt;
&lt;property name="name" value="Bingo"/&gt;
&lt;/object&gt;
&lt;object id="nopInterceptor" type="Spring.Aop.Interceptor.NopInterceptor"/&gt;
&lt;object id="prototypeTarget" type="Spring.Aop.Framework.ProxyFactoryObject"&gt;
&lt;property name="interceptorNames" value="nopInterceptor,target"/&gt; &lt;!-- not valid! --&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<para>In Spring.NET, the <literal>InterceptorNames</literal> property of
the <classname>ProxyFactoryObject</classname> can
<emphasis>only</emphasis> be used to specify the names of interceptors.
Use the <literal>TargetName</literal> property to specify the name of
the target object that is to be proxied.</para>
<para>The main reason for not supporting exactly the same style of
configuration as Spring Java is because this 'feature' is regarded as a
legacy holdover from Rod Johnson's initial Spring AOP implementation,
and is currently only kept as-is (in Spring Java) for reasons of
backward compatibility.</para>
</sect2>
</sect1>
</chapter>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="logging">
<title>Common Logging</title>
<section id="logging-abstract">
<title>Introduction</title>
<para>Spring uses a simple logging abstraction in order to provide a layer
of indirection between logging calls made by Spring and the specific
logging library used in your application (log4net, EntLib logging, NLog).
The library is available for .NET 1.0, 1.1, and 2.0 with both debug and
strongly signed assemblies. Since this need is not specific to Spring, the
logging library was moved out of the Spring project and into a more
general open source project called <ulink
url="http://netcommon.sourceforge.net/">Common Infrastructure Libraries
for .NET</ulink>. The logging abstraction within the project is known as
Common.Logging. Note that it is not the intention of this library to be a
replacement for the many fine logging libraries that are out there. The
API is incredibly minimal and will very likely stay that way. Please note
that this library is intended only for use where the paramount requirement
is portability and you will generally be better served by using a specific
logging implementation so that you can leverage its advanced features and
extended APIs to your advantage.</para>
<para>You can find online documentation on how to configure Common.Logging
is available in <ulink
url="http://netcommon.sourceforge.net/doc-latest/reference/html/index.html">HTML</ulink>
, <ulink
url="http://netcommon.sourceforge.net/doc-latest/reference/pdf/commong-logging-reference.pdf">PDF</ulink>,
and <ulink
url="http://netcommon.sourceforge.net/doc-latest/reference/htmlhelp/htmlhelp.chm">HTML
Help</ulink> formats.</para>
</section>
</chapter>

View File

@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="migration">
<title>Migrating from 1.1 M2</title>
<sect1 id="M2RC1-introduction">
<title>Introduction</title>
<para>Several API changes were made after 1.1 M2 (before 1.1 RC1)due
primarily by the need to refactor the code base to remove circular
dependency cycles, which are now all removed. Class and schema name
changes were also made to provide a more consistent naming convention
across the codebase. As a result of these changes, you can not simply drop
in the new .dlls as you may have done in previous release. This document
serves as a high level guide to the most likely areas where you will need
to make changes to either your configuration or your code.</para>
<para>The file, BreakingChanges-1.1.txt, in the root directory of the
distribution contains the full listing of breaking changes made for RC1
and higher</para>
</sect1>
<sect1 id="migration-changes">
<title>Important Changes</title>
<para>This section covers the common areas were you will need to make
changes in code/configuration when migration from M2 to RC1or
higher.</para>
<sect2>
<title>Namespaces</title>
<para>Note: If you previously installed Spring .xsd files to your VS.NET
installation directory, remove them manually, and copy over the new
ones, which have the -1.1.xsd suffix.</para>
<para>The names of the section handlers to register custom schemas has
changed, from ConfigParsersSectionHandler to
<classname>NamespaceParsersSectionHandler</classname>.</para>
<para>The target namespaces have changed, the 'directory' named /schema/
has been removed. For example, the target schema changed from
http://www.springframework.net/schema/tx to
<classname>http://www.springframework.net/tx.</classname></para>
<para>A typical declaration to use custom schemas within your
configuration file looks like this</para>
<programlisting>&lt;objects xmlns='http://www.springframework.net'
xmlns:db="http://www.springframework.net/database"
xmlns:tx="http://www.springframework.net/tx"
xmlns:aop="http://www.springframework.net/aop"&gt;</programlisting>
<para>The class <classname>XmlParserRegistry</classname> was renamed to
<classname>NamespaceParserRegistry</classname>.</para>
<para>Renamed
<classname>Spring.Validation.ValidationConfigParser</classname> to
<classname>Spring.Validation.Config.ValidationNamespaceParser</classname></para>
<para>Renamed from <classname>DatabaseConfigParser</classname> to
<classname>DatabaseNamespaceParser</classname></para>
<para>Renamed/Moved <classname>Remoting.RemotingConfigParser</classname>
to
<classname>Remoting.Config.RemotingNamespaceParser</classname><parameter></parameter></para>
<para>A typical registration of custom parsers within your configuration
file looks like this</para>
<programlisting>&lt;configuration&gt;
&lt;configSections&gt;
&lt;sectionGroup name="spring"&gt;
&lt;section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/&gt;
&lt;/sectionGroup&gt;
&lt;/configSections&gt;
&lt;spring&gt;
&lt;parsers&gt;
&lt;parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" /&gt;
&lt;parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" /&gt;
&lt;parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" /&gt;
&lt;/parsers&gt;
&lt;/spring&gt;</programlisting>
<para>A manual registration would look like this</para>
<programlisting>NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser));
NamespaceParserRegistry.RegisterParser(typeof(DatabaseNamespaceParser));
NamespaceParserRegistry.RegisterParser(typeof(TxNamespaceParser));
</programlisting>
</sect2>
<sect2>
<title>Core</title>
<para>Moved Spring.Util.DynamicReflection to
Spring.Reflection.Dynamic</para>
<para>Moved TypeRegistry and related classes from Spring.Context.Support
to Spring.Core.TypeResolution</para>
<para>Moved Spring.Objects.TypeConverters to
Spring.Core.TypeConvesion</para>
</sect2>
<sect2>
<title>Web</title>
<para>Moved Spring.Web.Validation to Spring.Web.UI.Validation</para>
</sect2>
<sect2>
<title>Data</title>
<para>Changed schema to use 'provider' instead of 'dbProvider' element,
usage is now &lt;db:provider ... /&gt; and not &lt;db:dbProvider
.../&gt;</para>
<para>Moved TransactionTemplate, TransactionDelegate and
ITransactionCallback from Spring.Data to Spring.Data.Support</para>
<para>Moved AdoTemplate, AdoAccessor, AdoDaoSupport,
RowMapperResultSetExtractor from Spring.Data to Spring.Data.Core</para>
<para>Moved AdoPlatformTransactionManager,
ServiceDomainPlatformTransactionManager, and TxScopeTransactionManager
from Spring.Data to Spring.Data.Core</para>
</sect2>
</sect1>
</chapter>

186
doc/reference/src/misc.xml Normal file
View File

@@ -0,0 +1,186 @@
<chapter id="misc">
<title>Spring.NET miscellanea</title>
<sect1>
<title>Introduction</title>
<para>
This chapter contains miscellanea information on features, goodies, caveats
that does not belong to any paricular area.
</para>
</sect1>
<sect1>
<title>PathMatcher</title>
<para>
<emphasis>Note, Spring.Util.PathMatcher is
currently only available in CVS, not the RC3 release. If you want to use these feature
please get the code from CVS
<ulink url="http://opensource.atlassian.com/confluence/spring/display/NET/Project+Structure">(instructions)</ulink>
or from the download section of the
Spring.NET website that contains an .zip with the full CVS tree.
</emphasis>
</para>
<para>
<literal>Spring.Util.PathMatcher</literal> provides <literal>Ant/NAnt</literal>-like path name matching
features.
</para>
<para>To do the match, you use the method:
<programlisting format='linespecific' xml:space='preserve'>
tatic bool Match(string pattern, string path)</programlisting>
</para>
<para>If you want to decide if case is important or not use the method:
<programlisting format='linespecific' xml:space='preserve'>
tatic bool Match(string pattern, string path, bool ignoreCase)</programlisting>
</para>
<sect2>
<title>General rules</title>
<para>
To build your pattern, you use the <literal>*</literal>, <literal>?</literal>
and <literal>**</literal> building blocks:
<itemizedlist spacing="compact">
<listitem>
<para><literal>*</literal>: matches any number of non slash
characters;
</para>
</listitem>
<listitem>
<para><literal>?</literal>: matches exactly 1 (one) non slash/dot
character;
</para>
</listitem>
<listitem>
<para><literal>**</literal>: matches any subdirectory, without
taking care of the depth;
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title>Matching filenames</title>
<para>
A file name can be matched using the following
notation:
<programlisting format='linespecific' xml:space='preserve'>
foo?bar.*</programlisting>
matches:
<programlisting format='linespecific' xml:space='preserve'>
fooAbar.txt
foo1bar.txt
foo_bar.txt
foo-bar.txt</programlisting>
does not match:
<programlisting format='linespecific' xml:space='preserve'>
foo.bar.txt
foo/bar.txt
foo\bar.txt</programlisting>
</para>
<para>
The classical all files pattern:
<programlisting format='linespecific' xml:space='preserve'>
*.*</programlisting>
matches:
<programlisting format='linespecific' xml:space='preserve'>
foo.db
.db
foo
foo.bar.db
foo.db.db
db.db.db</programlisting>
does not match:
<programlisting format='linespecific' xml:space='preserve'>
c:/
c:/foo.db
c:/foo
c:/.db
c:/foo.foo.db
//server/foo</programlisting>
</para>
</sect2>
<sect2>
<title>Matching subdirectories</title>
<para>
A directory name can be matched at any depth level using the following
notation:
<programlisting format='linespecific' xml:space='preserve'>
**/db/**</programlisting>
That pattern matches the following paths:
<programlisting format='linespecific' xml:space='preserve'>
/db
//server/db
c:/db
c:/spring/app/db/foo.db
//Program Files/App/spaced dir/db/foo.db
/home/spring/spaced dir/db/v1/foo.db</programlisting>
but does not match these:
<programlisting format='linespecific' xml:space='preserve'>
c:/spring/app/db-v1/foo.db
/home/spring/spaced dir/db-v1/foo.db</programlisting>
</para>
<para>
You can compose subdirectories to match like this:
<programlisting format='linespecific' xml:space='preserve'>
**/bin/**/tmp/**</programlisting>
That pattern matches the following paths:
<programlisting format='linespecific' xml:space='preserve'>
c:/spring/foo/bin/bar/tmp/a
c:/spring/foo/bin/tmp/a/b.c</programlisting>
but does not match these:
<programlisting format='linespecific' xml:space='preserve'>
c:/spring/foo/bin/bar/temp/a
c:/tmp/foo/bin/bar/a/b.c</programlisting>
</para>
<para>
You can use more advanced patterns:
<programlisting format='linespecific' xml:space='preserve'>
**/.spring-assemblies*/**</programlisting>
matches:
<programlisting format='linespecific' xml:space='preserve'>
c:/.spring-assemblies
c:/.spring-assembliesabcd73xs
c:/app/.spring-assembliesabcd73xs
c:/app/.spring-assembliesabcd73xs/foo.dll
//server/app/.spring-assembliesabcd73xs</programlisting>
does not match:
<programlisting format='linespecific' xml:space='preserve'>
c:/app/.spring-assemblie</programlisting>
</para>
</sect2>
<sect2>
<title>Case does matter, slashes don't</title>
<para>
.NET is expected to be a cross-platform development ... platform. So,
<literal>PathMatcher</literal> will match taking care of the case of the pattern
and the case of the path. For example:
<programlisting format='linespecific' xml:space='preserve'>
**/db/**/*.DB</programlisting>
matches:
<programlisting format='linespecific' xml:space='preserve'>
c:/spring/service/deploy/app/db/foo.DB</programlisting>
but does not match:
<programlisting format='linespecific' xml:space='preserve'>
c:/spring/service/deploy/app/DB/foo.DB
c:spring/service/deploy/app/spaced dir/DB/foo.DB
//server/share/service/deploy/app/DB/backup/foo.db</programlisting>
</para>
<para>If you do not matter about case, you should explicitly tell the
<literal>Pathmatcher</literal>.</para>
<para>
Back and forward slashes, in the very same cross-platform spirit, are
not important:
<programlisting format='linespecific' xml:space='preserve'>
spring/foo.bar</programlisting>
matches all the following paths:
<programlisting format='linespecific' xml:space='preserve'>
c:\spring\foo.bar
c:/spring\foo.bar
c:/spring/foo.bar
/spring/foo.bar
\spring\foo.bar</programlisting>
</para>
</sect2>
</sect1>
</chapter>

View File

@@ -0,0 +1,75 @@
<chapter id="navigation">
<title>Object Navigation</title>
<sect1 id="navigation-introduction">
<title>Introduction</title>
<para><emphasis>(Available in 1.0)</emphasis></para>
<para>Spring provides an expression language that allows for the easy setting
or getting of an object's properties and the invoking of its methods.
The simple syntax of this expression language allows one to to easily express the
properties or methods to invoke
in order to retrieve or set a value on an object. For example to
get the name property of an object, the required expression to
evaluate this is simply "Name". The chaining of properties is also supported...
for example, if a person object contains a date of birth property (DOB)
that returns a <literal>DateTime</literal>, the year may be retrieved with
the expression "DOB.Year". This generic reflection-like functionality
has many uses but is often found as the foundation to support
a data binding language between GUI elements and model
objects. As such, the data binding support for ASP.NET web pages
contained in the <literal>Spring.Web</literal> library uses this expression language.
</para>
</sect1>
<sect1 id="navigation-simpleexprssions">
<title>Simple Expressions</title>
<para>
Consider the simple class shown below with the
public field <literal>Name</literal><programlisting>
public class Inventor
{
public string Name;
private DateTime dob;
public DateTime DOB
{
get { return dob; }
set { dob = value; }
}
}
</programlisting>
which may have been instantiated in code somewhere and had its Name
and DOB set to particular values<programlisting>Inventor inventor = new Inventor();
inventor.Name = "Nikola Tesla";
inventor.DOB = new DateTime(1854, 10, 9);
</programlisting>
The <classname>ObjectNavigator</classname> is the central
class used to set or retrieve the value of an object, and contains
the following static methods
<programlisting>
object GetValue(object root, string expression)
object GetValue(object root, NavigationExpression expression)
void SetValue(object root, string expression, object newValue)
void SetValue(object root, NavigationExpression expression, object newValue)
</programlisting>
To retrieve the name and year of birth we can use the following code<programlisting>
string name = (string) ObjectNavigator.GetValue(inventor, "Name");
int year = (int) ObjectNavigator.GetValue(inventor, "DOB.Year");
</programlisting>
The string "DOB.Year" is used to create a
<literal>NavigationExpression</literal> object that forms the basis
for the parsing of the string. If you need to
evaluate a complex expression frequently, creating a
<literal>NavigationExpression</literal>
and reusing it will increase performance. To set the property values of this
object instance to that of another famous inventor we would write<programlisting>
ObjectNavigator.SetValue(inventor, "Name", "Michael Pupin");
ObjectNavigator.SetValue(inventor, "DOB", new DateTime(1854, 10, 9));
</programlisting>
</para>
</sect1>
<sect1 id="navigation-collections">
<title>Navigating Collections</title>
<para>TODO. TestCase shows some example usage. Please check the Spring.NET <ulink url="http://www.springframework.net/doc/reference/navigation.html">website</ulink> for the latest updates to this document.
</para>
</sect1>
</chapter>

View File

@@ -0,0 +1,438 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="objects-misc">
<title>The IObjectWrapper and Type conversion</title>
<sect1 id="objects-misc-introduction">
<title>Introduction</title>
<para>The concepts encapsulated by the
<classname>IObjectWrapper</classname> interface are fundamental to the
workings of the core Spring.NET libraries The typical application
developer most probably will not ever have the need to use the
<classname>IObjectWrapper</classname> directly... because this is
reference documentation however, we felt that some explanation of this
core interface might be right. The <classname>IObjectWrapper</classname>
is explained in this chapter since if you were going to use it at all, you
would probably do that when trying to bind data to objects, which, nicely
enough, is precisely the area that the
<classname>IObjectWrapper</classname> addresses.</para>
</sect1>
<sect1 id="objects-objects">
<title>Manipulating objects using the IObjectWrapper</title>
<para>One quite important concept of the <literal>Spring.Objects</literal>
namespace is encapsulated in the definition
<classname>IObjectWrapper</classname> interface and its corresponding
implementation, the <classname>ObjectWrapper</classname> class. The
functionality offered by the <classname>IObjectWrapper</classname>
includes methods to set and get property values (either individually or in
bulk), get property descriptors (instances of the
<classname>System.Reflection.PropertyInfo</classname> class), and to query
the readability and writability of properties. The
<classname>IObjectWrapper</classname> also offers support for nested
properties, enabling the setting of properties on subproperties to an
unlimited depth. The <classname>IObjectWrapper</classname> usually isn't
used by application code directly, but by framework classes such as the
various <classname>IObjectFactory</classname> implementations.</para>
<para>The way the <classname>IObjectWrapper</classname> works is partly
indicated by its name: <emphasis>it wraps an object</emphasis> to perform
actions on a wrapped object instance... such actions would include the
setting and getting of properties exposed on the wrapped object.</para>
<para><emphasis>Note: the concepts explained in this section are not
important to you if you're not planning to work with the
<classname>IObjectWrapper</classname> directly.</emphasis></para>
<sect2 id="objects-objects-conventions">
<title>Setting and getting basic and nested properties</title>
<para>Setting and getting properties is done using the
<methodname>SetPropertyValue()</methodname> and
<methodname>GetPropertyValue()</methodname> methods, for which there are
a couple of overloaded variants. The details of the various overloads
(including return values and method parameters) are all described in the
extensive API documentation supplied as a part of the Spring.NET
distribution.</para>
<para>The aforementioned <methodname>SetPropertyValue()</methodname> and
<methodname>GetPropertyValue()</methodname> methods have a number of
conventions for indicating the path of a property. A property path is an
expression that implementations of the
<classname>IObjectWrapper</classname> interface can use to look up the
properties of the wrapped object; some examples of property paths
include...</para>
<para><table frame="all">
<title>Examples of property paths</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="2*" />
<colspec colname="c2" colwidth="4*" />
<thead>
<row>
<entry>Path</entry>
<entry>Explanation</entry>
</row>
</thead>
<tbody>
<row>
<entry>name</entry>
<entry>Indicates the <literal>name</literal> property of the
wrapped object.</entry>
</row>
<row>
<entry>account.name</entry>
<entry>Indicates the nested property <literal>name</literal>
of the <literal>account</literal> property of the wrapped
object.</entry>
</row>
<row>
<entry>account[2]</entry>
<entry>Indicates the <emphasis>third</emphasis> element of the
<literal>account</literal> property of the wrapped object.
Indexed properties are typically collections such as
<literal>lists</literal> and <literal>dictionaries</literal>,
but can be any class that exposes an indexer.</entry>
</row>
</tbody>
</tgroup>
</table></para>
<para>Below you'll find some examples of working with the
<classname>IObjectWrapper</classname> to get and set properties.
Consider the following two classes: <programlisting>[C#]
public class Company
{
private string name;
private Employee managingDirector;
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public Employee ManagingDirector
{
get { return this.managingDirector; }
set { this.managingDirector = value; }
}
}</programlisting> <programlisting>[C#]
public class Employee
{
private string name;
private float salary;
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public float Salary
{
get { return salary; }
set { this.salary = value; }
}
}</programlisting></para>
<para>The following code snippets show some examples of how to retrieve
and manipulate some of the properties of
<classname>IObjectWrapper</classname>-wrapped <literal>Company</literal>
and <literal>Employee</literal> instances. <programlisting>[C#]
Company c = new Company();
IObjectWrapper owComp = new ObjectWrapper(c);
// setting the company name...
owComp.SetPropertyValue("name", "Salina Inc.");
// can also be done like this...
PropertyValue v = new PropertyValue("name", "Salina Inc.");
owComp.SetPropertyValue(v);
// ok, let's create the director and bind it to the company...
Employee don = new Employee();
IObjectWrapper owDon = new ObjectWrapper(don);
owDon.SetPropertyValue("name", "Don Fabrizio");
owComp.SetPropertyValue("managingDirector", don);
// retrieving the salary of the ManagingDirector through the company
float salary = (float)owComp.GetPropertyValue("managingDirector.salary");</programlisting></para>
<para>Note that since the various Spring.NET libraries are compliant
with the Common Language Specification (CLS), the resolution of
arbitrary strings to properties, events, classes and such is performed
in a case-insensitive fashion. The previous examples were all written in
the C# language, which is a case-sensitive language, and yet the
<literal>Name</literal> property of the <literal>Employee</literal>
class was set using the all-lowercase <literal>'name'</literal> string
identifier. The following example (using the classes defined previously)
should serve to illustrate this...</para>
<programlisting>[C#]
// ok, let's create the director and bind it to the company...
Employee don = new Employee();
IObjectWrapper owDon = new ObjectWrapper(don);
owDon.SetPropertyValue("naMe", "Don Fabrizio");
owDon.GetPropertyValue("nAmE"); // gets "Don Fabrizio"
IObjectWrapper owComp = new ObjectWrapper(new Company());
owComp.SetPropertyValue("ManaGINGdirecToR", don);
owComp.SetPropertyValue("mANaGiNgdirector.salARY", 80000);
Console.WriteLine(don.Salary); // puts 80000</programlisting>
<para>The case-insensitivity of the various Spring.NET libraries
(dictated by the CLS) is not usually an issue... if you happen to have a
class that has a number of properties, events, or methods that differ
only by their case, then you might want to consider refactoring your
code, since this is generally regarded as poor programming
practice.</para>
</sect2>
<sect2 id="objects-objects-other">
<title>Other features worth mentioning</title>
<para>In addition to the features described in the preceding sections
there a number of features that might be interesting to you, though not
worth an entire section. <itemizedlist spacing="compact">
<listitem>
<para><emphasis>determining readability and
writability</emphasis>: using the <literal>IsReadable()</literal>
and <literal>IsWritable()</literal> methods, you can determine
whether or not a property is readable or writable.</para>
</listitem>
<listitem>
<para><emphasis>retrieving PropertyInfo instances</emphasis>:
using <literal>GetPropertyInfo(string)</literal> and
<literal>GetPropertyInfos()</literal> you can retrieve instances
of the <classname>System.Reflection.PropertyInfo</classname>
class, that might come in handy sometimes when you need access to
the property metadata specific to the object being wrapped.</para>
</listitem>
</itemizedlist></para>
</sect2>
</sect1>
<sect1 id="objects-objects-conversion">
<title>Type conversion</title>
<para>If you associate a <classname>TypeConverter</classname> with the
definition of a custom <classname>Type</classname> using the standard .NET
mechanism (see the example code below), Spring.NET will use the associated
<classname>TypeConverter</classname> to do the conversion.<programlisting>[C#]
[TypeConverter (typeof (FooTypeConverter))]
public class Foo
{
}</programlisting></para>
<para>The <classname>TypeConverter</classname> class from the
<literal>System.ComponentModel</literal> namespace of the .NET BCL is used
extensively by the various classes in the <literal>Spring.Core</literal>
library, as said class <quote>... provides a unified way of converting
types of values to other types, as well as for accessing standard values
and subproperties.</quote> <footnote>
<para>More information about creating custom
<literal>TypeConverter</literal> implementations can be found online
at Microsoft's MSDN website, by searching for <emphasis>Implementing a
Type Converter</emphasis>.</para>
</footnote></para>
<para>For example, a date can be represented in a human readable format
(such as <literal>30th August 1984</literal>), while we're still able to
convert the human readable form to the original date format or (even
better) to an instance of the <classname>System.DateTime</classname>
class. This behavior can be achieved by using the standard .NET idiom of
decorating a class with the <classname>TypeConverterAttribute</classname>.
Spring.NET also offers another means of associating a
<classname>TypeConverters</classname> with a class. You might want to do
this to achieve a conversion that is not possible using standard idiom...
for example, the <literal>Spring.Core</literal> library contains a custom
<classname>TypeConverter</classname> that converts comma-delimited strings
to String array instances. Registering custom converters on an
<classname>IObjectWrapper</classname> instance gives the wrapper the
knowledge of how to convert properties to the desired
<classname>Type</classname>.</para>
<para>An example of where property conversion is used in Spring.NET is the
setting of properties on objects, accomplished using the aforementioned
<literal>TypeConverters</literal>. When mentioning
<classname>System.String</classname> as the value of a property of some
object (declared in an XML file for instance), Spring.NET will (if the
type of the associated property is <classname>System.Type</classname>) use
the <classname>RuntimeTypeConverter</classname> class to try to resolve
the property value to a <classname>Type</classname> object. The example
below demonstrates this automatic conversion of the
<literal>Example.Xml.SAXParser</literal> (a string) into the corresponding
<classname>Type</classname> instance for use in this factory-style class.
<programlisting>&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;object id="parserFactory" type="Example.XmlParserFactory, ExamplesLibrary"
destroy-method="Close"&gt;
&lt;property name="ParserClass" value="Example.Xml.SAXParser, ExamplesLibrary"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting> <programlisting>[C#]
public class XmlParserFactory
{
private Type parserClass;
public Type ParserClass
{
get { return this.parserClass; }
set { this.parserClass = value; }
}
public XmlParser GetParser ()
{
return Activator.CreateInstance (ParserClass);
}
}</programlisting></para>
<sect2 id="objects-misc-enums">
<title>Type Conversion for Enumerations</title>
<para>The default type converter for enumerations is the
<classname>System.ComponentModel.EnumConverter</classname> class. To
specify the value for an enumerated property, simply use the name of the
property. For example the <classname>TestObject</classname> class has a
property of the enumerated type <classname>FileMode</classname>. One of
the values for this enumeration is named <literal>Create</literal>. The
following XML fragment shows how to configure this property</para>
<programlisting>&lt;object id="rod" type="Spring.Objects.TestObject, Spring.Core.Tests"&gt;
&lt;property name="name" value="Rod"/&gt;
&lt;property name="FileMode" value="Create"/&gt;
&lt;/object&gt;</programlisting>
</sect2>
</sect1>
<sect1 id="object-objects-builtin-converters">
<title>Built-in TypeConverters</title>
<para>Spring.NET has a number of built-in
<classname>TypeConverters</classname> to make life easy. Each of those is
listed below and they are all located in the
<literal>Spring.Objects.TypeConverters</literal> namespace of the
<literal>Spring.Core</literal> library.</para>
<para><table frame="all">
<title>Built-in <classname>TypeConverters</classname></title>
<tgroup cols="2">
<colspec colname="c1" colwidth="3*" />
<colspec colname="c2" colwidth="5*" />
<thead>
<row>
<entry>Type</entry>
<entry>Explanation</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>RuntimeTypeConverter</literal></entry>
<entry>Parses strings representing
<classname>System.Types</classname> to actual
<classname>System.Types</classname> and the other way
around.</entry>
</row>
<row>
<entry><literal>FileInfoConverter</literal></entry>
<entry>Capable of resolving strings to a
<classname>System.IO.FileInfo</classname> object.</entry>
</row>
<row>
<entry><literal>StringArrayConverter</literal></entry>
<entry>Capable of resolving a comma-delimited list of strings to
a string-array and vice versa.</entry>
</row>
<row>
<entry><literal>UriConverter</literal></entry>
<entry>Capable of resolving a string representation of a URI to
an actual <literal>Uri</literal>-object.</entry>
</row>
<row>
<entry><literal>FileInfoConverter</literal></entry>
<entry>Capable of resolving a string representation of a
FileInfo to an actual
<literal>FileInfo</literal>-object.</entry>
</row>
<row>
<entry><literal>StreamConverter</literal></entry>
<entry>Capable of resolving Spring IResource URI (string) to its
corresponding <literal>InputStream</literal>-object.</entry>
</row>
<row>
<entry><literal>ResourceConverter</literal></entry>
<entry>Capable of resolving Spring IResource URI (string) to an
<literal>IResource</literal> object.</entry>
</row>
<row>
<entry><literal>ResourceManagerConverter</literal></entry>
<entry>Capable of resolving a two part string (resource name,
assembly name) to a
<classname>System.Resources.ResourceManager</classname>
object.</entry>
</row>
<row>
<entry><literal>RgbColorConverter</literal></entry>
<entry>Capable of resolving a comma separated list of Red,
Green, Blue integer values to a
<classname>System.Drawing.Color</classname> structure.</entry>
</row>
<row>
<entry>RegexConverter</entry>
<entry>Converts string representation of regular expression into
an instance of System.Text.RegularExpressions.Regex</entry>
</row>
</tbody>
</tgroup>
</table></para>
<para>Spring.NET uses the standard .NET mechanisms for the resolution of
<classname>System.Types</classname>, including, but not limited to
checking any configuration files associated with your application,
checking the Global Assembly Cache (GAC), and assembly probing.</para>
<sect2>
<title>Custom type converters</title>
<para>You can register a custom type converter either Programatically
using the class TypeConverterRegistry or through configuration of
Spring's container and described in the section <link
linkend="context-type-converters">Registering Type
Converters</link>.</para>
<para></para>
</sect2>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

948
doc/reference/src/orm.xml Normal file
View File

@@ -0,0 +1,948 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="orm">
<title>Object Relational Mapping (ORM) data access</title>
<section id="orm-introduction">
<title>Introduction</title>
<para>The Spring Framework provides integration with <emphasis>NHibernate
</emphasis> in terms of resource management, DAO implementation support,
and transaction strategies. For example for NHibernate, there is
first-class support with lots of IoC convenience features, addressing many
typical NHibernate integration issues. All of these support packages for
O/R (Object Relational) mappers comply with Spring's generic transaction
and DAO exception hierarchies. There are usually two integration styles:
either using Spring's DAO 'templates' or coding DAOs against the 'plain'
NHibernate APIs. In both cases, DAOs can be configured through Dependency
Injection and participate in Spring's resource and transaction
management.</para>
<para>You can use Spring's support for NHibernate without needing to use
Spring IoC or transaction management functionality. The NHibernate support
classes can be used in typical 3rd party library style. However, usage
inside a Spring IoC container does provide additional benefits in terms of
ease of configuration and deployment; as such, most examples in this
section show configuration inside a Spring container.</para>
<para>Some of the benefits of using the Spring Framework to create your
ORM DAOs include:</para>
<itemizedlist>
<listitem>
<para><emphasis>Ease of testing.</emphasis> Spring's IoC approach
makes it easy to swap the implementations and config locations of
Hibernate <interfacename>SessionFactory</interfacename> instances,
ADO.NET <interfacename>DbProvider</interfacename> 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>
<para><emphasis>Common data access exceptions.</emphasis> Spring can
wrap exceptions from your O/R mapping tool of choice, converting them
from proprietary exceptions to a common runtime DataAccessException
hierarchy. You can still trap and handle exceptions anywhere you need
to. Remember that ADO.NET exceptions (including DB specific dialects)
are also converted to the same hierarchy, meaning that you can perform
some operations with ADO.NET within a consistent programming
model.</para>
</listitem>
<listitem>
<para><emphasis>General resource management.</emphasis> Spring
application contexts can handle the location and configuration of
Hibernate <interfacename>ISessionFactory</interfacename> instances,
ADO.NET <interfacename>DbProvider</interfacename> 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 <interfacename>Session</interfacename> for
efficiency and proper transaction handling. Spring makes it easy to
transparently create and bind a <interfacename>Session</interfacename>
to the current thread, either by using an explicit 'template' wrapper
class at the code level or by exposing a current
<interfacename>Session</interfacename> through the Hibernate
<interfacename>SessionFactory</interfacename> (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>
<para><emphasis>Integrated transaction management.</emphasis> Spring
allows you to wrap your O/R mapping code with either a declarative,
AOP style method interceptor, or an explicit 'template' wrapper class
at the code level. In either case, transaction semantics are handled
for you, and proper transaction handling (rollback, etc) in case of
exceptions is taken care of. As discussed below, you also get the
benefit of being able to use and swap various transaction managers,
without your Hibernate/ADO.NET related code being affected: for
example, between local transactions and distributed, with the same
full services (such as declarative transactions) available in both
scenarios. As an additional benefit, ADO.NET-related code can fully
integrate transactionally with the code you use to do O/R mapping.
This is useful for data access that's not suitable for O/R mapping
which still needs to share common transactions with ORM
operations.</para>
</listitem>
</itemizedlist>
<para>The NHibernate Northwind example in the Spring distribution shows a
NHibernate implementation of a persistence-technology agnostic DAO
interfaces. (In the upcoming RC1 release the SpringAir example will
demonstrate an ADO.NET and NHibernate based implementation of technology
agnostic DAO interfaces.) The NHibernate Northwind example serves as a
working sample application that illustrates the use of NHibernate in a
Spring web application. It also leverages declarative transaction
demarcation with different transaction strategies.</para>
<para>Both NHibernate 1.0 and NHibernate 1.2 are supported. Differences
relate to the use of generics and new features such as contextual
sessions. For information on the latter, refer to the section <link
linkend="orm-hibernate-straight">Implementing DAOs based on the plain
NHibernate API</link>. The NHibernate 1.0 support is in the assembly
Spring.Data.NHibernate and the 1.2 support is in the assembly
Spring.Data.NHibernate12</para>
<para>At the moment the only ORM supported in NHibernate, but others can
be integrated with Spring (in as much as makes sense) to offer the same
value proposition.</para>
</section>
<section id="orm-hibernate">
<title>NHibernate</title>
<para>We will start with a coverage of <ulink
url="http://www.hibernate.org/">NHibernate</ulink> in a Spring
environment, using it to demonstrate the approach that Spring takes
towards integrating O/R mappers. This section will cover many issues in
detail and show different variations of DAO implementations and
transaction demarcations. Most of these patterns can be directly
translated to all other supported O/R mapping tools.</para>
<para>The following discussion focuses on Hibernate 1.0.4, the major
differences with NHibernate 1.2 being the ability to participate in Spring
transaction/session management via the normal NHibernate API instead of
the 'template' approach. Spring supports both NHibernate 1.0 and
NHibernate 1.2 via separate .dlls with the same internal namespace.</para>
<section id="orm-resource-mngmnt">
<title>Resource management</title>
<para>Typical business applications are often cluttered with repetitive
resource management code. Many projects try to invent their own
solutions for this issue, sometimes sacrificing proper handling of
failures for programming convenience. Spring advocates strikingly simple
solutions for proper resource handling, namely IoC via templating; for
example infrastructure classes with callback interfaces, or applying AOP
interceptors. The infrastructure cares for proper resource handling, and
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 <classname>AdoTemplate</classname> 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 <classname>DataAccessException</classname> 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
<classname>HibernateTemplate</classname> analogous to
<classname>AdoTemplate</classname>, a
<classname>HibernateInterceptor</classname>, and a Hibernate transaction
manager. The major goal is to allow for clear application layering, with
any data access and transaction technology, and for loose coupling of
application objects. No more business service dependencies on the data
access or transaction strategy, no more hard-coded resource lookups, no
more hard-to-replace singletons, no more custom service registries. One
simple and consistent approach to wiring up application objects, keeping
them as reusable as possible. All the individual data access features
are usable on their own but integrate nicely with Spring's application
context concept, providing XML-based configuration and cross-referencing
of plain object instances that don't need to be Spring-aware. In a
typical Spring application, many important objects are plain .NET
objects: data access templates, data access objects (that use the
templates), transaction managers, business services (that use the data
access objects and transaction managers), ASP.NET web pages (that use
the business services),and so on.</para>
</section>
<section id="orm-tx-mgmt">
<title>Transaction Management</title>
<para>While NHibernate offers an API for transaction management you will
quite likely find the benefits of using Spring's generic transaction
management features to be more compelling to use, typically for use of a
declarative programming model for transaction demarcation and easily
mixing ADO.NET and NHibernate operations within a single transaction.
See the chapter on transaction management for more information on
Spring's transaction management features. There are two choices for
transaction management strategies, one based on the NHibernate API and
the other the .NET 2.0 TransactionScope API.</para>
<para>The first strategy is encapsulated in the class
<classname>Spring.Data.NHibernate.HibernateTransactionManager
</classname>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 <classname>HibernateTransactionManager</classname> is
shown below.</para>
<programlisting> &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;</programlisting>
<para>The important property of
<classname>HibernateTransactionManager</classname> 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
<classname>Sping.Data.TxScopeTransactionManager</classname> 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
System.Transaction sense). If there is no transaction then a new Session
will be opened for each operation. The exception to this rule is when
using the <classname>OpenSessionInViewModule</classname> in a web
application in single session mode (see <xref
linkend="orm-hibernate-web" />). In this case the session will be
created on the start of the web request and closed on the end of the
request. Note that the session's flush mode will be set to
<literal>FlushMode.NEVER</literal> at the start of the request. If a
non-readonly transaction is performed, then during the scope of that
transaction processing the flush mode will be changed to AUTO, and then
set back to NEVER at the end of the transaction scope so that any
changes to objects associated with the session during rendering will not
be persisted back to the database when the session is closed at the end
of the web request.</para>
</section>
<section id="orm-session-factory-setup">
<title><interfacename>SessionFactory</interfacename> set up in a Spring
container</title>
<para>To avoid tying application objects to hard-coded resource lookups,
Spring allows you to define resources like a
<interfacename>DbProvider</interfacename> or a Hibernate
<interfacename>SessionFactory</interfacename> 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
<interfacename>SessionFactory</interfacename> on top of it:</para>
<programlisting>&lt;objects xmlns="http://www.springframework.net"
xmlns:db="http://www.springframework.net/database"&gt;
&lt;!-- Property placeholder configurer for database settings --&gt;
&lt;object type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core"&gt;
&lt;property name="ConfigSections" value="databaseSettings"/&gt;
&lt;/object&gt;
&lt;!-- Database and NHibernate Configuration --&gt;
&lt;db:provider id="DbProvider"
provider="SqlServer-1.1"
connectionString="Integrated Security=false; Data Source=(local);Integrated Security=true;Database=Northwin;User ID=springqa;Password=springqa;"/&gt;
&lt;object id="MySessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate"&gt;
&lt;property name="DbProvider" ref="DbProvider"/&gt;
&lt;property name="MappingAssemblies"&gt;
&lt;list&gt;
&lt;value&gt;Spring.Northwind.Dao.NHibernate&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="HibernateProperties"&gt;
&lt;dictionary&gt;
&lt;entry key="hibernate.connection.provider"
value="NHibernate.Connection.DriverConnectionProvider"/&gt;
&lt;entry key="hibernate.dialect"
value="NHibernate.Dialect.MsSql2000Dialect"/&gt;
&lt;entry key="hibernate.connection.driver_class"
value="NHibernate.Driver.SqlClientDriver"/&gt;
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<para>Many of the properties on
<literal>LocalSessionFactoryObject</literal> are those you will commonly
configure, for example the property <literal>MappingAssemblies</literal>
specifies a list of assemblies to seach for hibernate mapping files. The
property <literal>HibernateProperies</literal> are the familiar
NHibernate properties used to set typical options such as dialect and
driver class. The location of NHibernate mapping information can also be
specified using Spring's <link linkend="resources">IResource
abstraction</link> via the property <literal>MappingResources</literal>.
The IResource abstraction supports opening an input stream from
assemblies, file system, and http(s) based on a Uri syntax. You can also
leverage the extensibility of IResource and thereby allow NHibernate to
obtain its configuration information from locations such as a database
or LDAP.For other properties you can configure them as you normal using
the file <literal>hibernate.cfg.xml</literal> and refer to it via the
property <literal>ConfigFileNames</literal>. This property is a string
array so multiple configuration files are supported.</para>
<para>There are other properties in
<classname>LocalSessionFactoryObject</classname> that relate to the
integration of Spring with NHibernate. The property
<literal>ExposeTransactionAwareSessionFactory</literal> is discussed
below and allows you to use Spring's declarative transaction demarcation
functionality with the standard NHibernate API (as compared to using
HibernateTemplate).</para>
<para>The property <literal>DbProvider</literal> is used to infer two
NHibernate configurations options.</para>
<itemizedlist>
<listitem>
<para>Infer the connection string, typically done via the hibernate
property "hibernate.connection.connection_string".</para>
</listitem>
<listitem>
<para>Delegate to the <classname>DbProvider</classname> itself as
the NHibernate connection provider instead of listing it via
property hibernate.connection.provider via
<literal>HibernateProperties</literal>.</para>
</listitem>
</itemizedlist>
<para>If you specify both the property hibernate.connection.provider and
DbProvider (as shown above) the configuration of the property
hibernate.connection.provider is used and a warning level message is
logged. If you use Spring's <classname>DbProvider</classname> as the
NHibernate connection provider then you can take advantage of
<classname>IDbProvider</classname> 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>
<note>
<para><link lang=""
linkend="dbprovider-usercredentials">UserCredentialsDbProvider</link>
and <link
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 <classname>ISessionFactory</classname> 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
<classname>LocalSessionFactoryObject</classname> and override the
method <literal>ISessionFactory NewSessionFactory(Configuration
config)</literal> so that it returns an implementation of
<classname>ISessionFactory</classname> that selects among multiple
instances based on values in thread local storage, much like the
implementation of
<classname>MultiDelegatingDbProvider</classname>.</para>
</note>
</section>
<section id="orm-hibernate-template">
<title>The <classname>HibernateTemplate</classname></title>
<para>The basic programming model for templating looks as follows for
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
<interfacename>SessionFactory</interfacename>. 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
<interfacename>SessionFactory</interfacename>, and an example for a DAO
method implementation.</para>
<programlisting>&lt;objects&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></para>
<programlisting>public class HibernateCustomerDao : ICustomerDao {
private HibernateTemplate hibernateTemplate;
public ISessionFactory SessionFactory
{
set { hibernateTemplate = new HibernateTemplate(value); }
}
public Customer SaveOrUpdate(Customer customer)
{
hibernateTemplate.SaveOrUpdate(customer);
return customer;
}
}</programlisting>
<para>The <classname>HibernateTemplate</classname> class provides many
methods that mirror the methods exposed on the Hibernate
<interfacename>Session</interfacename> interface, in addition to a
number of convenience methods such as the one shown above. If you need
access to the <interfacename>Session</interfacename> to invoke methods
that are not exposed on the <classname>HibernateTemplate</classname>,
you can always drop down to a callback-based approach like so.</para>
<programlisting>public class HibernateCustomerDao : ICustomerDao {
private HibernateTemplate hibernateTemplate;
public ISessionFactory SessionFactory
{
set { hibernateTemplate = new HibernateTemplate(value); }
}
public Customer SaveOrUpdate(Customer customer)
{
return HibernateTemplate.Execute(
delegate(ISession session)
{
// do whatever you want with the session....
session.SaveOrUpdate(customer);
return customer;
}) as Customer;
}
}</programlisting>
<para>Using the anonymous delegate is particularly convenient when you
would otherwise be passing various method parameter calls to the
interface based version of this callback. Furthermore, when using
generics, you can avoid the typecast and write code like the
following</para>
<programlisting>IList&lt;Supplier&gt; suppliers = HibernateTemplate.ExecuteFind&lt;Supplier&gt;(
delegate(ISession session)
{
return session.CreateQuery("from Supplier s were s.Code = ?")
.SetParameter(0, code)
.List&lt;Supplier&gt;();
});</programlisting>
<para>where code is a variable in the surrounding block, accessible
inside the anonymous delegate implementation.</para>
<para>A callback implementation effectively can be used for any
Hibernate data access. <classname>HibernateTemplate</classname> will
ensure that <interfacename>Session</interfacename> 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, <classname>HibernateTemplate</classname> offers alternative
convenience methods that can replace such one line callback
implementations. Furthermore, Spring provides a convenient
<classname>HibernateDaoSupport</classname> base class that provides a
<methodname>SessionFactory</methodname> property for receiving a
<interfacename>SessionFactory</interfacename> and for use by subclasses.
In combination, this allows for very simple DAO implementations for
typical requirements:</para>
<programlisting>public class HibernateCustomerDao : HibernateDaoSupport, ICustomerDao
{
public Customer SaveOrUpdate(Customer customer)
{
HibernateTemplate.SaveOrUpdate(customer);
return customer;
}
}</programlisting>
</section>
<section id="orm-hibernate-daos">
<title>Implementing Spring-based DAOs without callbacks</title>
<para>As an alternative to using Spring's
<classname>HibernateTemplate</classname> 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
<classname>DataAccessException</classname> hierarchy. The
<classname>HibernateDaoSupport</classname> base class offers methods to
access the current transactional <interfacename>Session</interfacename>
and to convert exceptions in such a scenario; similar methods are also
available as static helpers on the
<classname>SessionFactoryUtils</classname> 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
<interfacename>Session</interfacename>, as its lifecycle is managed by
the transaction). Asking for the</para>
<programlisting>public class HibernateProductDao extends HibernateDaoSupport implements ProductDao {
public Customer SaveOrUpdate(Customer customer)
{
ISession session = DoGetSession(false);
session.SaveOrUpdate(customer);
return customer;
}
}
}</programlisting>
<para>This code will not translate the Hibernate exception to a generic
DataAccessException.</para>
</section>
<section id="orm-hibernate-straight">
<title>Implementing DAOs based on plain Hibernate 1.2 API</title>
<para>Hibernate 1.2 introduced a feature called "contextual Sessions",
where Hibernate itself manages one current
<interfacename>ISession</interfacename> per transaction. This is roughly
equivalent to Spring's synchronization of one Hibernate
<interfacename>Session</interfacename> per transaction. A corresponding
DAO implementation looks like as follows, based on the plain Hibernate
API:</para>
<programlisting>public class ProductDaoImpl implements IProductDao {
private SessionFactory sessionFactory;
public ISessionFactory SessionFactory
{
get { return sessionFactory; }
set { sessionFactory = value; }
}
public IList&lt;Product&gt; LoadProductsByCategory(String category) {
return SessionFactory.GetCurrentSession()
.CreateQuery("from test.Product product where product.category=?")
.SetParameter(0, category)
.List&lt;Product&gt;();
}
}
public class HibernateCustomerDao : ICustomerDao {
private ISessionFactory sessionFactory;
public ISessionFactory SessionFactory
{
set { sessionFactory = value; }
}
public Customer SaveOrUpdate(Customer customer)
{
sessionFactory.GetCurrentSession().SaveOrUpdate(customer);
return customer;
}
}</programlisting>
<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 <classname>HibernateTemplate</classname>. 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>
<programlisting>
&lt;objects&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 SessionFactory configuration to support this programming model
can be done two ways, both via configuration of Spring's
LocalSessionFactoryObject. You can enable the use of Spring's
implementation of the NHibernate extension interface,
ICurrentSessionContext, by setting the property
'ExposeTransactionAwareSessionFactory' to true on
LocalSessionFactoryObject. This is just a short-cut for setting the
NHibernate property current_session_context_class with the name of the
implementation class to use.</para>
<para>The first way is shown below</para>
<programlisting>&lt;object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12"&gt;
<emphasis role="bold">&lt;property name="ExposeTransactionAwareSessionFactory" value="true" /&gt;</emphasis>
&lt;!-- other configuration settings omitted --&gt;
&lt;/object&gt;</programlisting>
<para>Which is simply a shortcut for the following configuration</para>
<programlisting>&lt;object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12"&gt;
&lt;!-- other configuration settings omitted --&gt;
&lt;property name="HibernateProperties"&gt;
&lt;dictionary&gt;
&lt;!-- other dictionary entries omitted --&gt;
<emphasis role="bold">&lt;entry key="hibernate.current_session_context_class"
value="Spring.Data.NHibernate.SpringSessionContext, Spring.Data.NHibernate12"/&gt;</emphasis>
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>The main advantage of this DAO style is that it depends on the
Hibernate API only; no import of any Spring class is required. This is
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
<classname>HibernateException</classname> 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>
<para>Fortunately, Spring's
<classname>LocalSessionFactoryObject</classname> supports Hibernate's
<methodname>SessionFactory.GetCurrentSession()</methodname> method for
any Spring transaction strategy, returning the current Spring-managed
transactional <interfacename>Session</interfacename> even with
<classname>HibernateTransactionManager</classname>.</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>
</section>
<section id="orm-hibernate-tx-programmatic">
<title>Programmatic transaction demarcation</title>
<para>Transactions can be demarcated in a higher level of the
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 <classname>PlatformTransactionManager</classname>. Again,
the latter can come from anywhere, but preferably as an object reference
via a <methodname>TransactionManager</methodname> property - just like
the <classname>productDAO</classname> 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
implementation.</para>
<programlisting>&lt;objects&gt;
TO BE DONE
&lt;/objects&gt;</programlisting>
<programlisting>public class FulfillmentService : IFulfillmentService
private TransactionTemplate transactionTemplate;
private IProductDao productDao;
private ICustomerDao customerDao;
private IOrderDao orderDao;
private IShippingService shippingService;
public void ProcessCustomer(string customerId)
{
TO BE DONE
}
}</programlisting>
</section>
<section id="orm-hibernate-tx-declarative">
<title>Declarative transaction demarcation</title>
<para>Alternatively, one can use Spring's declarative transaction
support, which essentially enables you to replace explicit transaction
demarcation API calls in your C# code with an AOP transaction
interceptor configured in a Spring container. You can either externalize
the transaction semantics (like propagation behavior and isolation level
) in a configuration file or use the Transaction attribute on the
service method to set the transaction semantics.</para>
<para>An example showing attribute driven transaction is shown
below</para>
<programlisting>&lt;objects&gt;
&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;/object&gt;
&lt;!-- Import 'standard xml' configuration for attribute driven declarative tx management --&gt;
&lt;import resource="DeclarativeServicesAttributeDriven.xml"/&gt;
&lt;/objects&gt;</programlisting>
<para>Note that with the new transaction namespace, you can replace the
importing of DeclarativeServicesAttributeDriven.xml with the following
single line, <code>&lt;tx:attribute-driven/&gt;</code> that more clearly
expresses the intent as compared to the contents of
DeclarativeServicesAttributeDriven.xml.</para>
<programlisting>&lt;objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.net/schema/tx"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/schema/objects/spring-objects.xsd
http://www.springframework.net/schema/tx http://www.springframework.net/schema/tx/spring-tx-1.1.xsd"&gt;
&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;/object&gt;
<emphasis>&lt;tx:attribute-driven/&gt;</emphasis>
&lt;/objects&gt;</programlisting>
<para>The placement of the transaction attribute in the service layer
method is shown below.</para>
<programlisting>public class FulfillmentService : IFulfillmentService
{
// fields and properties for dao object omitted, see above
[Transaction(ReadOnly=false)]
public void ProcessCustomer(string customerId)
{
//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
}
}
}</programlisting>
<para>If you prefer to not use attribute to demarcate your transaction
boundaries, you can import a configuration file with the following XML
instead of using &lt;tx:attribute-driven/&gt;</para>
<programlisting> &lt;object id="TxProxyConfigurationTemplate" abstract="true"
type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"&gt;
&lt;property name="PlatformTransactionManager" ref="HibernateTransactionManager"/&gt;
&lt;property name="TransactionAttributes"&gt;
&lt;name-values&gt;
&lt;!-- Add common methods across your services here --&gt;
&lt;add key="Process*" value="PROPAGATION_REQUIRED"/&gt;
&lt;/name-values&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>Refer to the documentation on Spring Transaction management for
configuration of other features, such as rollback rules.</para>
</section>
<section id="orm-hibernate-tx-strategies">
<title>Transaction management strategies</title>
<para>Both <classname>TransactionTemplate</classname> and
<classname>TransactionInterceptor</classname> (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
<classname>PlatformTransactionManager</classname> instance, which can be
a <classname>HibernateTransactionManager</classname> (for a single
Hibernate <interfacename>SessionFactory</interfacename>, using a
<classname>ThreadLocal</classname>
<interfacename>Session</interfacename> under the hood) or a
<classname>TxScopeTransactionManager</classname> (delegating to MS-DTC
for distributed transaction) for Hibernate applications. You could even
use a custom <classname>PlatformTransactionManager</classname>
implementation. So switching from native Hibernate transaction
management to TxScopeTransactionManager, such as when facing distributed
transaction requirements for certain deployments of your application, is
just a matter of configuration. Simply replace the Hibernate transaction
manager with Spring's TxScopeTransactionManager implementation. Both
transaction demarcation and data access code will work without changes,
as they just use the generic transaction management APIs.</para>
<para>For distributed transactions across multiple Hibernate session
factories, simply combine
<classname>TxScopeTransactionManager</classname> as a transaction
strategy with multiple <classname>LocalSessionFactoryObject</classname>
definitions. Each of your DAOs then gets one specific
<interfacename>SessionFactory</interfacename> reference passed into it's
respective object property.</para>
<programlisting>TO BE DONE
</programlisting>
<para><classname>HibernateTransactionManager</classname> can export the
ADO.NET <interfacename>Transaction</interfacename> used by Hibernate to
plain ADO.NET access code, for a specific
<interfacename>DbProvider</interfacename>. (matching connection string).
This allows for high-level transaction demarcation with mixed
Hibernate/ADO.NET data access!</para>
</section>
<section id="orm-hibernate-web">
<title>Web Session Management</title>
<para>The open session in view pattern keeps the hibernate session open
during page rendering so lazily loaded hibernate objects can be
displayed. You configure its use by adding an additional custom HTTP
module declaration as shown below</para>
<programlisting> &lt;system.web&gt;
&lt;httpModules&gt;
&lt;add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewModule, Spring.Data.NHibernate"/&gt;
&lt;/httpModules&gt;
...
&lt;/system.web&gt;</programlisting>
<para>You can configure which SessionFactory the OpenSessionInViewModule
will use by setting 'global' application key-value pairs as shown below.
(this will change in future releases)</para>
<programlisting> &lt;appSettings&gt;
&lt;add key="Spring.Data.NHibernate.Support.OpenSessionInViewModule.SessionFactoryObjectName" value="SessionFactory"/&gt;
&lt;/appSettings&gt;</programlisting>
<para>The default behavior of the module is that a single session is
currently used for the life of the request. Refer to the earlier section
on Transaction Management in this chapter for more information on how
sessions are managed in the OpenSessionInViewModule. You can also
configure in the application setting the EntityInterceptorObjectName
using the key
<literal>Spring.Data.NHibernate.Support.OpenSessionInViewModule.EntityInterceptorObjectName</literal>
and if SingleSession mode is used via the key
<literal>Spring.Data.NHibernate.Support.OpenSessionInViewModule.SingleSession</literal>.
If SingleSession is set to false, referred to as 'deferred close mode',
then each transaction scope will use a new Session and kept open until
the end of the web request. This has the drawback that the first level
cache is not reused across transactions and that objects are required to
be unique across all sessions. Problems can arise if the same object is
associated with more than one hibernate session.</para>
<important>
<para>By default, OSIV applies <literal>FlushMode.NEVER</literal> on
every session it creates. This is because if OSIV flushed pending
changes during "EndRequest" and an error occurs, all response has
already been sent to the client. There would be no way of telling the
client about the error.</para>
<para>By default this means you MUST explicitly demarcate transaction
boundaries around non-readonly statements when using OSIV. For
configuring transactions see <xref
linkend="orm-hibernate-tx-declarative" /> or the
<command>Spring.Data.NHibernate.Northwind</command> example
application.</para>
</important>
</section>
<section>
<title>Session Scope</title>
<para>The class Spring.Data.NHibernate.Support.SessionScope allows for
you to use a single NHibernate session across multiple transactions. The
usage is shown below</para>
<programlisting>using (new SessionScope())
{
... do multiple operations with a single session, possibly in multiple transactions.
}</programlisting>
<para>At the end of the using block the session is automatically closed.
All transactions within the scope use the same session, if you are using
Spring's HibernateTemplate or using Spring's implementation of
NHibernate 1.2's ICurrentSessionContext interface. See other sections in
this chapter for further information on those usage scenarios.</para>
</section>
</section>
</chapter>

View File

@@ -0,0 +1,307 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="introduction">
<title>Introduction</title>
<sect1 id="introduction-overview">
<title>Overview</title>
<para>Spring.NET is an application framework that provides comprehensive
infrastructural support for developing enterprise .NET applications. It
allows you to remove incidental complexity when using the base class
libraries makes best practices, such as test driven development, easy
practices. Spring.NET is created, supported and sustained by <ulink
url="http://www.springsource.com">SpringSource</ulink>.</para>
<para>The design of Spring.NET is based on the Java version of the Spring
Framework, which has shown real-world benefits and is used in thousands of
enterprise applications world wide. Spring .NET is not a quick port from
the Java version, but rather a 'spiritual port' based on following proven
architectural and design patterns in that are not tied to a particular
platform. The breadth of functionality in Spring .NET spans application
tiers which allows you to treat it as a one stop shop but that is not
required. Spring .NET is not an all-or-nothing solution. You can use the
functionality in its modules independently. These <link
linkend="intro-modules">modules</link> are described below.</para>
<para>Enterprise applications typically are composed of a number of a
variety of physical tiers and within each tier functionality is often
split into functional layers. The business service layer for example
typically uses a objects in the data access layer to fulfill a use-case.
No matter how your application is architected, at the end of the day there
are a variety of objects that collaborate with one another to form the
application proper. The objects in an application can thus be said to have
dependencies between themselves.</para>
<para>The .NET platform provides a wealth of functionality for
architecting and building applications, ranging all the way from the very
basic building blocks of primitive types and classes (and the means to
define new classes), to rich full-featured application servers and web
frameworks. One area that is decidedly conspicuous by its absence is any
means of taking the basic building blocks and composing them into a
coherent whole; this area has typically been left to the purvey of the
architects and developers tasked with building an application (or
applications). Now to be fair, there are a number of design patterns
devoted to the business of composing the various classes and object
instances that makeup an all-singing, all-dancing application. Design
patterns such as Factory, Abstract Factory, Builder, Decorator, and
Service Locator (to name but a few) have widespread recognition and
acceptance within the software development industry (presumably that is
why these patterns have been formalized as patterns in the first place).
This is all very well, but these patterns are just that: best practices
given a name, typically together with a description of what the pattern
does, where the pattern is typically best applied, the problems that the
application of the pattern addresses, and so forth. Notice that the last
paragraph used the phrase “... a description of what the pattern does...”;
pattern books and wikis are typically listings of such formalized best
practice that you can certainly take away, mull over, and then implement
yourself in your application.</para>
<para>The Spring Framework takes best practices that have been proven over
the years in numerous applications and formalized as design patterns, and
actually codifies these patterns as first class objects that you as an
architect and developer can take away and integrate into your own
application(s). This is a Very Good Thing Indeed as attested to by the
numerous organizations and institutions that have used the Spring
Framework to engineer robust, maintainable applications. For example, the
IoC component of the Spring Framework addresses the enterprise concern of
taking the classes, objects, and services that are to compose an
application, by providing a formalized means of composing these various
disparate components into a fully working application ready for use</para>
</sect1>
<sect1>
<title>Background</title>
<para>In early 2004, Martin Fowler asked the readers of his site: when
talking about Inversion of Control: “the question is, what aspect of
control are [they] inverting?”. Fowler then suggested renaming the
principle (or at least giving it a more self-explanatory name), and
started to use the term Dependency Injection. His article then continued
to explain the ideas underpinning the Inversion of Control (IoC) and
Dependency Injection (DI) principle. If you need a decent insight into IoC
and DI, please do refer to the article :
http://martinfowler.com/articles/injection.html.</para>
</sect1>
<sect1 id="intro-modules">
<title>Modules</title>
<para>The Spring Framework contains a lot of features, which are
well-organized into modules shown in the diagram below. The diagram below
shows the various core modules of Spring.NET.</para>
<para><mediaobject>
<imageobject>
<imagedata fileref="images/overview.gif" format="GIF" />
</imageobject>
</mediaobject></para>
<para>Click on the module name for more information.</para>
<para><link lang="" linkend="objects">Spring.Core</link> is the most
fundamental part of the framework allowing you to configure your
application using Dependency Injection. Other supporting functionality,
listed below, is located in Spring.Core</para>
<para><link linkend="aop">Spring.Aop</link> - Use this module to perform
Aspect-Oriented Programming (AOP). AOP centralizes common functionality
that can then be declaratively applied across your application in a
targeted manner. Spring's <link linkend="aop-aspect-library">aspect
library</link> provides predefined easy to use aspects for transactions,
logging, performance monitoring, caching, method retry, and exception
handling.</para>
<para><link linkend="index-middle-tier">Spring.Data</link> - Use this
module to achieve greater efficiency and consistency in writing data
access functionality in ADO.NET and to perform declarative transaction
management.</para>
<para><link linkend="orm">Spring.Data.NHibernate</link> - Use this module
to integrate NHibernate with Springs declarative transaction management
functionality allowing easy mixing of ADO.NET and NHibernate operations
within the same transaction. NHibernate 1.0 users will benefit from ease
of use APIs to perform data access operations.</para>
<para><link linkend="web">Spring.Web</link> - Use this module to raise the
level of abstraction when writing ASP.NET web applications allowing you to
effectively address common pain-points in ASP.NET such as data binding,
validation, and ASP.NET page/control/module/provider configuration.</para>
<para><link linkend="ajax">Spring.Web.Extensions</link> - Use this module
to raise the level of abstraction when writing ASP.NET web applications
allowing you to effectively address common pain-points in ASP.NET such as
data binding, validation, and ASP.NET page/control/module/provider
configuration.</para>
<para><link linkend="index-services">Spring.Services</link> - Use this
module to adapt plain .NET objects so they can be used with a specific
distributed communication technology, such as .NET Remoting, Enterprise
Services, and ASMX Web Services. These services can be configured via
dependency injection and decorated by applying AOP.</para>
<para><link linkend="testing">Spring.Testing.NUnit</link> - Use this
module to perform integration testing with NUnit.</para>
<para>The Spring.Core module also includes the following additional
features</para>
<itemizedlist>
<listitem>
<para><link linkend="expressions">Expression Language</link> -
provides efficient querying and manipulation of an object graphs at
runtime.</para>
</listitem>
<listitem>
<para><link linkend="validation">Validation Framework</link> - a
robust UI agnostic framework for creating complex validation rules for
business objects either programatically or declaratively.</para>
</listitem>
<listitem>
<para>Data binding Framework - a UI agnostic framework for performing
data binding.</para>
</listitem>
<listitem>
<para>Dynamic Reflection - provides a high performance reflection
API</para>
</listitem>
<listitem>
<para><link linkend="threading">Threading</link> - provides additional
concurrency abstractions such as Latch, Semaphore and Thread Local
Storage.</para>
</listitem>
<listitem>
<para><link lang="" linkend="resources">Resource abstraction</link> -
provides a common interface to treat the InputStream from a file and
from a URL in a polymorphic and protocol-independent manner.</para>
</listitem>
</itemizedlist>
</sect1>
<sect1>
<title>Usage Scenarios</title>
<para>With the building blocks described above you can use Spring in all
sorts of scenarios, from simple stand alone console applications to
fully-fledged enterprise applications using Spring's transaction
management functionality and web framework integration. </para>
<para>It is important to note that the Spring Framework <emphasis>does
not</emphasis> force you to use everything within it; it is not an
<emphasis>all-or-nothing</emphasis> solution. Existing front-ends built
using standard ASP.NET can be integrated perfectly well with a
Spring-based middle-tier, allowing you to use the transaction and/or data
access features that Spring offers. The only things you need to do is wire
up your business logic using Spring's IoC container and integrate it into
your web layer using WebApplicationContext to locate middle tier services
and/or configure your standard ASP.NET pages with depdenency injection.
</para>
<para>While the Spring framework does not force any particular application
architecure it encourages the use of a well layered application
architecture with distinct tiers for the presentation, service, data
access, and database. </para>
</sect1>
<sect1>
<title>Quickstart applications</title>
<para>There are several sample applications that showcase individual
features. If you are already familiar with the concepts of dependency
injection, AOP, or have experience using the Java version of the Spring
framework you may find jumping into the examples a better way to bootstrap
the learning processing process. The following quickstart applications are
available and can be found in the examples directory in the distribution.
Click on the links for additional information.</para>
<itemizedlist>
<listitem>
<para><link linkend="qs-moviefinder">Movie Finder</link> - A simple
demonstration of Dependency Injection (DI) techniques using Spring's
Inversion of Control (IoC) container.</para>
</listitem>
<listitem>
<para><link linkend="qs-appcontext-messagesource">Application
Context</link> - Demonstrates IoC container features such as
localization, accessing of ResourceSet objects, and applying resources
to object properties.</para>
</listitem>
<listitem>
<para><link linkend="aop-quickstart">Aspect Oriented
Programming</link> - Demonstrates use of the AOP framework to add
additional behavior to your existing objects. Examples of programmatic
and declarative AOP configuration are shown.</para>
</listitem>
<listitem>
<para><link linkend="remoting-quickstart">Distributed Computing</link>
- A calculator demonstrating remote service abstractions that let you
'export' a plain .NET object (PONO) via .NET Remoting, Web Services,
or an EnterpriseService ServiceComponent. Corresponding client side
proxies are also demonstrated. .</para>
</listitem>
<listitem>
<para><link linkend="springair">Web Application - SpringAir</link> -A
ticket booking application that demonstrates the ASP.NET framework
showing features such as DI for ASP.NET pages, data binding,
validation, and localization.</para>
</listitem>
<listitem>
<para>Web Development - Introductory examples showing use of
dependency injection and Spring's bi-directional data binding in
ASP.NET.</para>
</listitem>
<listitem>
<para><link linkend="data-quickstart">Data Access</link> -
Demonstrates the ADO.NET framework showing how to simplify developing
ADO.NET based data access layers.</para>
</listitem>
<listitem>
<para><link linkend="tx-quickstart">Transaction Management</link> :
Demonstrates the use of declarative transaction management for both
local and distributed transaction in both .NET 1.1 and 2.0.</para>
</listitem>
<listitem>
<para>AJAX : Demonstrates how to access a plain .NET object as a
webservice in client side JavaScript</para>
</listitem>
<listitem>
<para>NHibernate Northwind: Demonstrates use of Spring's NHibernate
integration to simplify the use of NHibernate. Web tier is also
included showing how to use the Open-Session In View approach to
session management in the web tier.</para>
</listitem>
</itemizedlist>
</sect1>
<sect1>
<title>License Information</title>
<para>Spring.NET is licensed according to the terms of the Apache License,
Version 2.0. The full text of this license are available online at <ulink
url="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</ulink>
. You can also view the full text of the license in the license.txt file
located in the root installation directory.</para>
</sect1>
<sect1>
<title>Support</title>
<para>Training and support are available through <ulink
url="http://www.springsource.com">SpringSource</ulink> in addition to the
mailing lists and forums you can find on the main <ulink
url="http://www.springframework.net">Spring.NET</ulink> website.</para>
</sect1>
</chapter>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="pool">
<title>Object Pooling</title>
<sect1 id="pool-introduction">
<title>Introduction</title>
<para>The Spring.Pool namespace contains a generic API for implementing
pools of objects. Object pooling is a well known technique to minimize the
creation of objects that can take a significant amount of time. Common
examples are to create a pool of database connections such that each
request to the database can reuse an existing connection instead of
creating one per client request. Threads are also another common candidate
for pooling in order to increase responsiveness of an application to
multiple concurrent client requests.</para>
<para>.NET contains support for object pooling in these common scenarios.
Support for database connection pools is directly supported by ADO.NET
data providers as a configuration option. Similarly, thread pooling is
supported via the System.ThreadPool class. Support for pooling of other
objects can be done using the CLR managed API to COM+ found in the
System.EnterpriseServices namespace.</para>
<para>Despite this built-in support there are scenarios where you would
like to use alternative pool implementations. This may be because the
default implementations, such as System.ThreadPool, do not meet your
requirements. (For a discussion on advanced ThreadPool usage see <ulink
url="http://www.codeproject.com/csharp/SmartThreadPool.asp"> Smart Thread
Pool</ulink> by Ami Bar.) Alternatively, you may want to pool classes that
do not inherit from
<literal>System.EnterpriseServices.ServicedComponent</literal>. Instead of
making changes to the object model to meet this inheritance requirement,
Spring .NET provides similar support for pooling, but for any object, by
using AOP proxies and a generic pool API for managing object
instances.</para>
<para>Note, that if you are concerned only with applying pooling to an
existing object, the pooling APIs discussed here are not very important.
Instead the use and configuration of
<classname>Spring.Aop.Target.SimplePoolTargetSource</classname> is more
relevant. Pooling of objects can either be done Programatically or through
the XML configuration of the Spring .NET container. Attribute support for
pooling, similar to the ServicedComponent approach, will be available in a
future release of Spring.NET.</para>
<para><xref linkend="quickstarts" /> contains an example that shows the
use of the pooling API independent of AOP functionality.</para>
</sect1>
<sect1 id="pool-api">
<title>Interfaces and Implementations</title>
<para>The <literal>Spring.Pool</literal> namespace provides two simple
interfaces to manage pools of objects. The first interface,
<classname>IObjectPool</classname> describes how to take and put back an
object from the pool. The second interface
<classname>IPoolableObjectFactory</classname> is meant to be used in
conjunction with implementations of the <classname>IObjectPool</classname>
to provide guidance in calling various lifecycle events on the objects
managed by the pool. These interfaces are based on the Jakarta Commons
Pool API. <classname>Spring.Pool.Support.SimplePool</classname> is a
default implementation of <classname>IObjectPool</classname> and
<classname>Spring.Aop.Target.SimplePoolTargetSource</classname> is the
implementation of <classname>IPoolableObjectFactory</classname> for use
with AOP. The current goal of the Spring.Pool namespace is not to provide
a one-for-one replacement of the Jakarta Commons Pool API, but rather to
support basic object pooling needs for common AOP scenarios. Consequently,
other interfaces and base classes available in the Jakarta package are not
available.</para>
</sect1>
</chapter>

View File

@@ -0,0 +1,279 @@
<sect1>
<title>Pooling example</title>
<para>
The idea is to build an executor backed by a pool of
<literal>QueuedExecutor</literal>: this will show how Spring.NET
provides some useful low-level/high-quality reusable threading and
pooling abstractions.
This executor will provide parallel executions (in our case
<literal>grep</literal>-like file scans). <emphasis>Note: This example
is not in the 1.0.0 release to its use of classes in the Spring.Threading
namespace scheduled for release in Spring 1.1. To access ths example
please get the code from CVS <ulink url="http://opensource.atlassian.com/confluence/spring/display/NET/Project+Structure">(instructions)</ulink> or from the download section of the
Spring.NET website that contains an .zip with the full CVS tree.</emphasis>
</para>
<para>
Some information on <literal>QueuedExecutor</literal> is helpful to
better understand the implementation and to possibly disagree with it.
Keep in mind that the point is to show how to develop your own
object-pool.
</para>
<para>
A <literal>QueuedExecutor</literal> is an executor where
<literal>IRunnable</literal> instances are run serialy by a worker
thread. When you <literal>Execute</literal> with a
<literal>QueuedExecutor</literal>, your request is queued; at some
point in the future your request will be taken and executed by the
worker thread: in case of error the thread is terminated.
However
this executor recreates its worker thread as needed.
</para>
<para>Last but not least, this executor can be shut down in
a few different ways (please refer to the Spring.NET SDK documentation).
Given its simplicity, it is very powerful.
</para>
<para>
The example project <literal>Spring.Examples.Pool</literal> provides
an implementation of a pooled executor, backed by n instances of
<literal>Spring.Threading.QueuedExecutor</literal>: please ignore
the fact that <literal>Spring.Threading</literal> includes already a
very different implementation of a <literal>PooledExecutor</literal>:
here we wanto to use a pool of <literal>QueuedExecutor</literal>s.
</para>
<para>
This executor will be used to implement a parallel
recursive <literal>grep</literal>-like console executable.
</para>
<sect2>
<title>Implementing <literal>Spring.Pool.IPoolableObjectFactory</literal></title>
<para>
In order to use the <literal>SimplePool</literal> implementation,
the first thing to do is to implement the <literal>IPoolableObjectFactory</literal>
interface. This interface is intended to be implemented by objects
that can create the type of objects that should be pooled.
The <literal>SimplePool</literal>
will call the lifecycle methods on <literal>IPoolableObjectFactory</literal> interface
(<literal>MakeObject, ActivateObject, ValidateObject, PassivateObject, and DestroyObject</literal>)
as appropriate when the pool is created, objects are borrowed and returned to the pool, and when
the pool is destroyed.
</para>
<para>
In our case, as already said, we want to to implement a pool
of <literal>QueuedExecutor</literal>. Ok, here the declaration:
<programlisting format='linespecific' xml:space='preserve'>
public class QueuedExecutorPoolableFactory : IPoolableObjectFactory
{</programlisting>
the first task a factory should do is to create objects:
<programlisting format='linespecific' xml:space='preserve'>
object IPoolableObjectFactory.MakeObject()
{
// to actually make this work as a pooled executor
// use a bounded queue of capacity 1.
// If we don't do this one of the queued executors
// will accept all the queued IRunnables as, by default
// its queue is unbounded, and the PooledExecutor
// will happen to always run only one thread ...
return new QueuedExecutor(new BoundedBuffer(1));
}</programlisting>
and should be also able to destroy them:
<programlisting format='linespecific' xml:space='preserve'>
void IPoolableObjectFactory.DestroyObject(object o)
{
// ah, self documenting code:
// Here you can see that we decided to let the
// executor process all the currently queued tasks.
QueuedExecutor executor = o as QueuedExecutor;
executor.ShutdownAfterProcessingCurrentlyQueuedTasks();
}</programlisting>
</para>
<para>
When an object is taken from the pool, to satisfy a client request,
may be the object should be activated. We can possibly implement the
activation like this:
<programlisting format='linespecific' xml:space='preserve'>
void IPoolableObjectFactory.ActivateObject(object o)
{
QueuedExecutor executor = o as QueuedExecutor;
executor.Restart();
}</programlisting>
even if a <literal>QueuedExecutor</literal> restarts itself as
needed and so a valid implementation could leave this method empty.
</para>
<para>
After activation, and before the pooled object can be succesfully
returned to the client, it is validated (should the object be
invalid, it will be discarded: this can lead to an empty unusable
pool
<footnote>
<para>You may think that we can provide a smarter
implementation and you are probably right. However, it is not so
difficult to create a new pool in case the old one became unusable.
It could not be your preferred choice but surely it leverages
simplicity and object immutability
</para>
</footnote>).
Here we check that the worker thread exists:
<programlisting format='linespecific' xml:space='preserve'>
bool IPoolableObjectFactory.ValidateObject(object o)
{
QueuedExecutor executor = o as QueuedExecutor;
return executor.Thread != null;
}</programlisting>
</para>
<para>
Passivation, symmetrical to activation, is the process a pooled
object is subject to when the object is returned to the pool. In our
case we simply do nothing:
<programlisting format='linespecific' xml:space='preserve'>
void IPoolableObjectFactory.PassivateObject(object o)
{
}</programlisting>
</para>
<para>
At this point, creating a pool is simply a matter of creating an
<literal>SimplePool</literal> as in:
<programlisting format='linespecific' xml:space='preserve'>
pool = new SimplePool(new QueuedExecutorPoolableFactory(), size);</programlisting>
</para>
</sect2>
<sect2>
<title>Being smart using pooled objects</title>
<para>
Taking advantage of the <literal>using</literal> keyword seems
to be very important in these <literal>c#</literal> days, so we
implement a very simple helper (<literal>PooledObjectHolder</literal>)
that can allow us to do things like:
<programlisting format='linespecific' xml:space='preserve'>
using (PooledObjectHolder holder = PooledObjectHolder.UseFrom(pool))
{
QueuedExecutor executor = (QueuedExecutor) holder.Pooled;
executor.Execute(runnable);
}</programlisting>
without worrying about obtaining and returning an object from/to the
pool.
</para>
<para>
Here is the implementation:
<programlisting format='linespecific' xml:space='preserve'>
public class PooledObjectHolder : IDisposable
{
IObjectPool pool;
object pooled;
/// &lt;summary&gt;
/// Builds a new &lt;see cref=&quot;PooledObjectHolder&quot;/&gt;
/// trying to borrow an object form it
/// &lt;/summary&gt;
/// &lt;param name=&quot;pool&quot;&gt;&lt;/param&gt;
private PooledObjectHolder(IObjectPool pool)
{
this.pool = pool;
this.pooled = pool.BorrowObject();
}
/// &lt;summary&gt;
/// Allow to access the borrowed pooled object
/// &lt;/summary&gt;
public object Pooled
{
get
{
return pooled;
}
}
/// &lt;summary&gt;
/// Returns the borrowed object to the pool
/// &lt;/summary&gt;
public void Dispose()
{
pool.ReturnObject(pooled);
}
/// &lt;summary&gt;
/// Creates a new &lt;see cref=&quot;PooledObjectHolder&quot;/&gt; for the
/// given pool.
/// &lt;/summary&gt;
public static PooledObjectHolder UseFrom(IObjectPool pool)
{
return new PooledObjectHolder(pool);
}
}</programlisting>
</para>
<para>
Please don't forget to destroy all the pooled istances once you have
finished! How? Well using something like this in
<literal>PooledQueuedExecutor</literal>:
<programlisting format='linespecific' xml:space='preserve'>
public void Stop ()
{
// waits for all the grep-task to have been queued ...
foreach (ISync sync in syncs)
{
sync.Acquire();
}
pool.Close();
}</programlisting>
</para>
</sect2>
<sect2>
<title>Using the executor to do a parallel <literal>grep</literal></title>
<para>
The use of the just built executor is quite straigtforward but a
little tricky if we want to really exploit the pool.
<programlisting format='linespecific' xml:space='preserve'>
private PooledQueuedExecutor executor;
public ParallelGrep(int size)
{
executor = new PooledQueuedExecutor(size);
}
public void Recurse(string startPath, string filePattern, string regexPattern)
{
foreach (string file in Directory.GetFiles(startPath, filePattern))
{
executor.Execute(new Grep(file, regexPattern));
}
foreach (string directory in Directory.GetDirectories(startPath))
{
Recurse(directory, filePattern, regexPattern);
}
}
public void Stop()
{
executor.Stop();
}</programlisting>
</para>
<para>
<programlisting format='linespecific' xml:space='preserve'>
public static void Main(string[] args)
{
if (args.Length &lt; 3)
{
Console.Out.WriteLine(&quot;usage: {0} regex directory file-pattern [pool-size]&quot;, Assembly.GetEntryAssembly().CodeBase);
Environment.Exit(1);
}
string regexPattern = args[0];
string startPath = args[1];
string filePattern = args[2];
int size = 10;
try
{
size = Int32.Parse(args[3]);
}
catch
{
}
Console.Out.WriteLine (&quot;pool size {0}&quot;, size);
ParallelGrep grep = new ParallelGrep(size);
grep.Recurse(startPath, filePattern, regexPattern);
grep.Stop();
}</programlisting>
</para>
</sect2>
</sect1>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="preface">
<title>Preface</title>
<para>Developing software applications is hard enough even with good tools
and technologies. Spring provides a light-weight solution for building
enterprise-ready applications. Spring provides a consistent and transparent
means to configure your application and integrate <link
linkend="aop-introduction-concepts">AOP</link> into your software. Highlights of
Spring's functionality are providing declarative transaction management for
your middle tier as well as a full-featured ASP.NET framework.</para>
<para>Spring could potentially be a one-stop-shop for many areas of
enterprise application development; however, Spring is modular, allowing you
to use just those parts of it that you need, without having to bring in the
rest. You can use just the IoC container to configure your application and
use traditional ADO.NET based data access code, but you could also choose to
use just the <link linkend="orm">Hibernate integration code</link>
or the <link linkend="ado-introduction">ADO.NET abstraction layer</link>.
Spring has been (and continues to be) designed to be non-intrusive, meaning
dependencies on the framework itself are generally none (or absolutely
minimal, depending on the area of use).</para>
<para>This document provides a reference guide to Spring's features. Since
this document is still to be considered very much work-in-progress, if you
have any requests or comments, please post them on the user mailing list or
on the support forums at <ulink
url="http://forum.springframework.net">forum.springframework.net</ulink>.</para>
<para>Before we go on, a few words of gratitude are due to Christian Bauer
(of the <ulink url="http://www.hibernate.org/">Hibernate</ulink> team), who
prepared and adapted the DocBook-XSL software in order to be able to create
Hibernate's reference guide, thus also allowing us to create this one. Also
thanks to Russell Healy for doing an extensive and valuable review of some
of the material.</para>
</chapter>

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="psa-intro">
<title>Introduction to Spring Services</title>
<sect1>
<title>Introduction</title>
<para>The goal of Spring's integration with distributed technologies is to
adapt plain .NET objects so they can be used with a specific distributed
technology. This integration is designed to be as non-intrusive as
possible. If you need to expose an object to a remote process then you can
define an exporter for that object. Similarly, on the client side you
define an corresponding endpoint accessor. Of course, the object's methods
still need to be suitable for remoting, i.e. coarse grained, to avoid
making unnecessary and expensive remote calls.</para>
<para>Since these exporters and client side endpoint accessors are defined
using meta data for Spring IoC container, you can easily use dependency
injection on them to set initial state and to 'wire up' the presentation
tier, such as web forms, to the service layer. In addition, you may apply
AOP aspects to the exported classes and/or service endpoints to apply
behavior such as logging, security, or other custom behavior that may not
be provided by the target distributed technology. The Spring specific
terminology for this approach to object distribution is known as Portable
Service Abstractions (PSA). As a result of this approach, you can decide
much later in the development process the technical details of how you
will distribute your objects as compared to traditional code centric
approaches. Changing of the implementation is done though configuration of
the IoC container and not by recompilation. Of course, you may choose to
not use the IoC container to manage these objects and use the exporter and
service endpoints programatically.</para>
<para>The diagram shown below is a useful way to demonstrate the key
abstractions in the Spring tool chest and their interrelationships. The
four key concepts are; plain .NET objects, Dependency Injection, AOP, and
Portable Service Abstractions. At the heart sits the plain .NET object
that can be instantiated and configured using dependency injection. Then,
optionally, the plain object can be adapted to a specific distributed
technology. Lastly, additional behavior can be applied to objects. This
behavior is typically that which can not be easily address by traditional
OO approaches such as inheritance. In the case of service layer, common
requirements such as 'the service layer must be transactional' are
implemented in a manner that naturally expresses that intention in a
single place, as compared to scattered code across the service
layer.</para>
<para><mediaobject>
<imageobject>
<imagedata fileref="images/spring-triangle.png" format="PNG"
scale="50" />
</imageobject>
</mediaobject>Spring implements this exporter functionality by creating
a proxy at runtime that meets the implementation requirements of a
specific distributed technology. In the case of .NET Remoting the proxy
will inherit from MarshalByRef, for EnterpriseServices it will inherit
from ServicedComponent and for aspx web services, WebMethod attributes
will be added to methods. Client side functionality is often implemented
by a thin layer over the client access mechanism of the underlying
distributed technology, though in some cases such as client side access to
web services, you have the option to create a proxy on the fly from the
.wsdl definition, much like you would have done using the command line
tools.</para>
<para>The common implementation theme for you as a provider of these
service objects is to implement an interface. This is generally considered
a best practice in its own right, you will see most pure WCF examples
following this practice, and also lends itself to a straightforward
approach to unit testing business functionality as stub or mock
implementations may be defined for testing purposes.</para>
<para>The assembly <literal>Spring.Services.dll</literal> contains support
for <link linkend="remoting">.NET Remoting</link>, <link
linkend="services">Enterprise Services</link> and <link
linkend="webservices">ASMX Web Services</link>. Support for WCF services
is planned for Spring 1.2 and is currently in the CVS repository if you
care to take an early look.</para>
</sect1>
</chapter>

View File

@@ -0,0 +1,646 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Copyright 2002-2005 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.
*/
-->
<chapter id="quickstarts">
<title>IoC Quickstarts</title>
<sect1>
<title>Introduction</title>
<para>This chapter includes a grab bag of quickstart examples for using
the Spring.NET framework.</para>
</sect1>
<sect1 id="qs-moviefinder">
<title>Movie Finder</title>
<para>The source material for this simple demonstration of Spring.NET's
IoC features is lifted straight from Martin Fowler's article that
discussed the ideas underpinning the IoC pattern. See <ulink
url="http://martinfowler.com/articles/injection.html">Inversion of Control
Containers and the Dependency Injection pattern</ulink> for more
information. The motivation for basing this quickstart example on said
article is because the article is pretty widely known, and most people who
are looking at IoC for the first time typically will have read the article
(at the time of writing a <ulink
url="http://www.google.co.uk/search?q=ioc">simple Google search for
'IoC'</ulink> yields the article in the first five results).</para>
<para>Fowler's article used the example of a search facility for movies to
illustrate IoC and Dependency Injection (DI). The article described how a
<literal>MovieLister</literal> object might receive a reference to an
implementation of the <literal>IMovieFinder</literal> interface (using
DI).</para>
<para>The <literal>IMovieFinder</literal> returns a list of all movies and
the <literal>MovieLister</literal> filters this list to return an array of
<literal>Movie</literal>objects that match a specified directors name.
This example demonstrates how the Spring.NET IoC container can be used to
supply an appropriate <literal>IMovieFinder</literal> implementation to an
arbitrary <literal>MovieLister</literal> instance.</para>
<para>The C# code listings for the MovieFinder application can be found in
the <literal>examples/Spring/Spring.Examples.MovieFinder</literal>
directory off the top level directory of the Spring.NET
distribution.</para>
<para><mediaobject>
<imageobject>
<imagedata fileref="images/movie-finder.gif" format="GIF" />
</imageobject>
</mediaobject></para>
<sect2 id="qs-mf-gettingstarted">
<title>Getting Started - Movie Finder</title>
<para>The startup class for the MovieFinder example is the
<literal>MovieApp</literal> class, which is an ordinary .NET class with
a single application entry point... <programlisting>using System;
namespace Spring.Examples.MovieFinder
{
public class MovieApp
{
public static void Main ()
{
}
}
}</programlisting></para>
<para>What we want to do is get a reference to an instance of the
<literal>MovieLister</literal> class... since this is a Spring.NET
example we'll get this reference from Spring.NET's IoC container, the
<literal>IApplicationContext</literal>. There are a number of ways to
get a reference to an <literal>IApplicationContext</literal> instance,
but for this example we'll be using an
<literal>IApplicationContext</literal> that is instantiated from a
custom configuration section in a standard .NET application config
file...</para>
<programlisting>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;configuration&gt;
&lt;configSections&gt;
&lt;sectionGroup name="spring"&gt;
&lt;section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/&gt;
&lt;section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /&gt;
&lt;/sectionGroup&gt;
&lt;/configSections&gt;
&lt;spring&gt;
&lt;context&gt;
&lt;resource uri="config://spring/objects"/&gt;
&lt;/context&gt;
&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;description&gt;An example that demonstrates simple IoC features.&lt;/description&gt;
&lt;/objects&gt;
&lt;/spring&gt;
&lt;/configuration&gt;</programlisting>
<para>The objects that will be used in the example application will be
configured as XML <literal>&lt;object/&gt;</literal> elements nested
inside the <literal>&lt;objects/&gt;</literal> element.</para>
<para>The body of the <literal>Main</literal> method in the
<literal>MovieApp</literal> class can now be fleshed out a little
further... <programlisting>
using System;
using Spring.Context;
...
public static void Main ()
{
IApplicationContext ctx = ContextRegistry.GetContext();
}
...</programlisting>As can be seen in the above C# snippet, a
<literal>using</literal> statement has been added to the
<literal>MovieApp</literal> source. The
<literal>Spring.Context</literal> namespace gives the application access
to the <literal>IApplicationContext</literal> class that will serve as
the primary means for the application to access the IoC container. The
line of code... <programlisting>IApplicationContext ctx = ContextRegistry.GetContext();</programlisting>
... retrieves a fully configured <literal>IApplicationContext</literal>
implementation that has been configured using the named
<literal>&lt;objects/&gt;</literal> section from the application config
file.</para>
</sect2>
<sect2 id="qs-mf-firstobject">
<title>First Object Definition</title>
<para>As yet, no objects have been defined in the application config
file, so let's do that now. The very miminal XML definition for the
<literal>MovieLister</literal> instance that we are going to use in the
application can be seen in the following XML snippet...</para>
<programlisting>&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;object name="MyMovieLister"
type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder"&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<para>Notice that the full, assembly-qualified name of the
<literal>MovieLister</literal> class has been specified in the
<literal>type</literal> attribute of the object definition, and that the
definition has been assigned the (unique) id of
<literal>MyMovieLister</literal>. Using this id, an instance of the
object so defined can be retrieved from the
<literal>IApplicationContext</literal> reference like so...</para>
<programlisting>...
public static void Main ()
{
IApplicationContext ctx = ContextRegistry.GetContext();
MovieLister lister = (MovieLister) ctx.GetObject ("MyMovieLister");
}
...</programlisting>
<para>The <literal>lister</literal> instance has not yet had an
appropriate implementation of the <literal>IMovieFinder</literal>
interface injected into it. Attempting to use the
<literal>MoviesDirectedBy</literal> method will most probably result in
a nasty <literal>NullReferenceException</literal> since the
<literal>lister</literal> instance does not yet have a reference to an
<literal>IMovieFinder</literal>. The XML configuration for the
<literal>IMovieFinder</literal> implementation that is going to be
injected into the <literal>lister</literal> instance looks like
this...</para>
<programlisting>&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;object name="MyMovieFinder"
type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
</sect2>
<sect2 id="qs-mf-setterinjection">
<title>Setter Injection</title>
<para>What we want to do is inject the <literal>IMovieFinder</literal>
instance identified by the <literal>MyMovieFinder</literal> id into the
<literal>MovieLister</literal> instance identified by the
<literal>MyMovieLister</literal> id, which can be accomplished using
Setter Injection and the following XML... <programlisting>&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;object name="MyMovieLister"
type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder"&gt;
&lt;!-- using setter injection... --&gt;
&lt;property name="movieFinder" ref="MyMovieFinder"/&gt;
&lt;/object&gt;
&lt;object name="MyMovieFinder"
type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>When the <literal>MyMovieLister</literal>
object is retrieved from (i.e. instantiated by) the
<literal>IApplicationContext</literal> in the application, the
Spring.NET IoC container will inject the reference to the
<literal>MyMovieFinder</literal> object into the
<literal>MovieFinder</literal> property of the
<literal>MyMovieLister</literal> object. The
<literal>MovieLister</literal> object that is referenced in the
application is then fully configured and ready to be used in the
application to do what is does best... list movies by director.
<programlisting>...
public static void Main ()
{
IApplicationContext ctx = ContextRegistry.GetContext();
MovieLister lister = (MovieLister) ctx.GetObject ("MyMovieLister");
Movie[] movies = lister.MoviesDirectedBy("Roberto Benigni");
Console.WriteLine ("\nSearching for movie...\n");
foreach (Movie movie in movies)
{
Console.WriteLine (
string.Format ("Movie Title = '{0}', Director = '{1}'.",
movie.Title, movie.Director));
}
Console.WriteLine ("\nMovieApp Done.\n\n");
}
...</programlisting>To help ensure that the XML configuration of the
MovieLister class must specify a value for the MovieFinder property, you
can add the [Required] attribute to the MovieLister's MovieFinder
property. The example code shows uses this attribute. For more
information on using and configuring the [Required] attribute, refer to
this <link
linkend="object-factory-extension-opp-examplesrapp">section</link> of
the reference documentation.</para>
</sect2>
<sect2 id="qs-mf-constructorinjection">
<title>Constructor Injection</title>
<para>Let's define another implementation of the
<literal>IMovieFinder</literal> interface in the application config
file...<programlisting>...
&lt;object name="AnotherMovieFinder"
type="Spring.Examples.MovieFinder.ColonDelimitedMovieFinder, Spring.Examples.MovieFinder"&gt;
&lt;/object&gt;
...</programlisting>This XML snippet describes an
<literal>IMovieFinder</literal> implementation that uses a colon
delimited text file as it's movie source. The C# source code for this
class defines a single constructor that takes a
<classname>System.IO.FileInfo</classname> as it's single constructor
argument. As this object definition currently stands, attempting to get
this object out of the <literal>IApplicationContext</literal> in the
application with a line of code like so... <programlisting>IMovieFinder finder = (IMovieFinder) ctx.GetObject ("AnotherMovieFinder");</programlisting>
will result in a fatal
<classname>Spring.Objects.Factory.ObjectCreationException</classname>,
because the
<classname>Spring.Examples.MovieFinder.ColonDelimitedMovieFinder</classname>
class does not have a default constructor that takes no arguments. If we
want to use this implementation of the <literal>IMovieFinder</literal>
interface, we will have to supply an appropriate constructor
argument...<programlisting>...
&lt;object name="AnotherMovieFinder"
type="Spring.Examples.MovieFinder.ColonDelimitedMovieFinder, Spring.Examples.MovieFinder"&gt;
&lt;constructor-arg index="0" value="movies.txt"/&gt;
&lt;/object&gt;
...</programlisting></para>
<para>Unsurprisingly, the &lt;constructor-arg/&gt; element is used to
supply constructor arguments to the constructors of managed objects. The
Spring.NET IoC container uses the functionality offered by
<classname>System.ComponentModel.TypeConverter</classname>
specializations to convert the <literal>movies.txt</literal> string into
an instance of the <classname>System.IO.FileInfo</classname> that is
required by the single constructor of the
<classname>Spring.Examples.MovieFinder.ColonDelimitedMovieFinder</classname>
(see <xref linkend="objects-objects-conversion" /> for a more in depth
treatment concerning the automatic type conversion functionality offered
by Spring.NET).</para>
<para>So now we have two implementations of the
<literal>IMovieFinder</literal> interface that have been defined as
distinct object definitions in the config file of the example
application; if we wanted to, we could switch the implementation that
the <literal>MyMovieLister</literal> object uses like
so...<programlisting>...
&lt;object name="MyMovieLister"
type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder"&gt;
&lt;!-- lets use the colon delimited implementation instead --&gt;
&lt;property name="movieFinder" ref="AnotherMovieFinder"/&gt;
&lt;/object&gt;
&lt;object name="MyMovieFinder"
type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/&gt;
&lt;/object&gt;
&lt;object name="AnotherMovieFinder"
type="Spring.Examples.MovieFinder.ColonDelimitedMovieFinder, Spring.Examples.MovieFinder"&gt;
&lt;constructor-arg index="0" value="movies.txt"/&gt;
&lt;/object&gt;
...</programlisting></para>
<para>Note that there is no need to recompile the application to effect
this change of implementation... simply changing the application config
file and then restarting the application will result in the Spring.NET
IoC container injecting the colon delimited implementation of the
<literal>IMovieFinder</literal> interface into the
<literal>MyMovieLister</literal> object.</para>
</sect2>
<sect2 id="qs-mf-summary">
<title>Summary</title>
<para>This example application is quite simple, and admittedly it
doesn't do a whole lot. It does however demonstrate the basics of wiring
together an object graph using an intuitive XML format. These simple
features will get you through pretty much 80% of your object wiring
needs. The remaining 20% of the available configuration options are
there to cover corner cases such as factory methods, lazy
initialization, and suchlike (all of the configuration options are
described in detail in the <xref linkend="objects" />).</para>
</sect2>
<sect2>
<title>Logging</title>
<para>Often enough the first use of Spring.NET is also a first
introduction to log4net. To kick start your understanding of log4net
this section gives a quick overview. The authoritative place for
information on log4net is the <ulink
url="http://logging.apache.org/log4net/">log4net website</ulink>. Other
good online tutorials are <ulink
url="http://www.ondotnet.com/pub/a/dotnet/2003/06/16/log4net.html?page=1">Using
log4net (OnDotNet article)</ulink> and <ulink
url="http://haacked.com/archive/2005/03/07/2317.aspx">Quick and Dirty
Guide to Configuring Log4Net For Web Applications</ulink>. Spring.NET is
using version 1.2.9 whereas most of the documentation out there is for
version 1.2.0. There have been some changes between the two so always
double check at the log4net web site for definitive information. Also
note that we are investigating using a "commons" logging library so that
Spring.NET will not be explicity tied to log4net but will be able to use
other logging packages such as NLog and Microsoft enterprise logging
application block.</para>
<para>The general usage pattern for log4net is to configure your
loggers, (either in App/Web.config or a seperate file), initialize
log4net in your main application, declare some loggers in code, and then
log log log. (Sing along...) We are using App.config to configure the
loggers. As such, we declare the log4net configuration section handler
as shown below <programlisting>&lt;section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" /&gt;</programlisting>
The corresponding configuration section looks like this <programlisting>
&lt;log4net&gt;
&lt;appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"&gt;
&lt;layout type="log4net.Layout.PatternLayout"&gt;
&lt;conversionPattern value="%date [%thread] %-5level %logger - %message%newline" /&gt;
&lt;/layout&gt;
&lt;/appender&gt;
&lt;!-- Set default logging level to DEBUG --&gt;
&lt;root&gt;
&lt;level value="DEBUG" /&gt;
&lt;appender-ref ref="ConsoleAppender" /&gt;
&lt;/root&gt;
&lt;!-- Set logging for Spring to INFO. Logger names in Spring correspond to the namespace --&gt;
&lt;logger name="Spring"&gt;
&lt;level value="INFO" /&gt;
&lt;/logger&gt;
&lt;/log4net&gt;
</programlisting> The appender is the output sink - in this case the
console. There are a large variety of output sinks such as files,
databases, etc. Refer to the log4net <ulink
url="http://logging.apache.org/log4net/release/config-examples.html">Config
Examples</ulink> for more information. Of interest as well is the
PatternLayout which defines exactly the information and format of what
gets logged. Usually this is the date, thread, logging level, logger
name, and then finally the log message. Refer to <ulink
url="http://logging.apache.org/log4net/release/sdk/log4net.Layout.PatternLayout.html">PatternLayout
Documentation</ulink> for information on how to customize.</para>
<para>The logging name is up to you to decide when you declare the
logger in code. In the case of this example we used the convention of
giving the logging name the name of the fully qualified class name.
<programlisting>private static readonly ILog LOG = LogManager.GetLogger(typeof (MovieApp));</programlisting>
Other conventions are to give the same logger name across multiple
classes that constitute a logical component or subsystem within the
application, for example a data access layer. One tip in selecting the
pattern layout is to shorten the logging name to only the last 2 parts
of the fully qualified name to avoid the message sneaking off to the
right too much (where can't see it) because of all the other information
logged that precedes it. Shortening the logging name is done using the
format %logger{2}.</para>
<para>To initialize the logging system add the following to the start of
your application <programlisting>XmlConfigurator.Configure();</programlisting>
Note that if you are using or reading information on version 1.2.0 this
used to be called DOMConfigurator.Configure();</para>
<para>The logger sections associate logger names with logging levels and
appenders. You have great flexibility to mix and match names, levels,
and appenders. In this case we have defined the root logger (using the
special tag root) to be at the debug level and have an console sink. We
can then specialize other loggers with different setting. In this case,
loggers that start with "Spring" in their name are logged at the info
level and also sent to the console. Setting the value of this logger
from INFO to DEBUG will show you detailed logging information as the
Spring container goes about its job of creating and configuring your
objects. Coincidentally, the example code itself uses Spring in the
logger name, so this logger also controls the output level you see from
running MainApp. Finally, you are ready to use the simple logger api to
log, i.e. <programlisting>LOG.Info("Searching for movie...");</programlisting>
Logging exceptions is another common task, which can be done using the
error level <programlisting>try {
//do work
{
catch (Exception e)
{
LOG.Error("Movie Finder is broken.", e);
}</programlisting></para>
</sect2>
</sect1>
<sect1 id="qs-appcontext-messagesource">
<title>ApplicationContext and IMessageSource</title>
<sect2>
<title>Introduction</title>
<para>The example program <literal>Spring.Examples.AppContext</literal>
shows the use of the application context for text localization,
retrieving objects contained in ResourceSets, and applying the values of
embedded resource properties to an object. The values that are retrieved
are displayed in a window.</para>
<para>The application context configuration file contains an object
definition with the name <literal>messageSource</literal> of the type
<literal>Spring.Context.Support.ResourceSetMessageSource</literal> which
implements the interface <literal>IMessageSource</literal>. This
interface provides various methods for retrieving localized resources
such as text and images as described in <xref
linkend="context-functionality-messagesource" />. When creating an
instance of IApplicationContext, an object with the name 'messageSource'
is searched for and used as the implementation for the context's
IMessageSource functionality.</para>
<para>The <literal>ResourceSetMessageSource</literal> takes a list of
ResourceManagers to define the collection of culture-specific resources.
The ResourceManager can be contructed in two ways. The first way is to
specifying a two part string consisting of the base resource name and
the containing assembly. In this example there is an embedded resource
file, Images.resx in the project. The second way is to use helper
factory class <literal>ResourceManagerFactoryObject</literal> that takes
a resource base name and assembly name as properties. This second way of
specifying a ResourceManager is useful if you would like direct access
to the ResourceManager in other parts of your application. In the
example program an embedded resource file, MyResource.resx and a Spanish
specific resource file, MyResources.es.resx are declared in this manner.
The corresponding XML fragment is shown below <programlisting>...
&lt;object name="messageSource" type="Spring.Context.Support.ResourceSetMessageSource, Spring.Core"&gt;
&lt;property name="resourceManagers"&gt;
&lt;list&gt;
&lt;value&gt;Spring.Examples.AppContext.Images, Spring.Examples.AppContext&lt;/value&gt;
&lt;ref object="myResourceManager"/&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;object name="myResourceManager" type="Spring.Objects.Factory.Config.ResourceManagerFactoryObject, Spring.Core"&gt;
&lt;property name="baseName"&gt;
&lt;value&gt;Spring.Examples.AppContext.MyResource&lt;/value&gt;
&lt;/property&gt;
&lt;property name="assemblyName"&gt;
&lt;value&gt;Spring.Examples.AppContext&lt;/value&gt;
&lt;/property&gt;
&lt;/object&gt;
...</programlisting></para>
<para>The main application creates the application context and then
retrieves various resources via their key names. In the code all the key
names are declared as static fields in the class
<literal>Keys.</literal> The resource file Images.resx contains image
data under the key name <literal>bubblechamber</literal> (aka
Keys.BUBBLECHAMBER). The code <literal>Image image =
(Image)ctx.GetResourceObject(Keys.BUBBLECHAMBER);</literal> is used to
retrieve the image from the context. The resource files MyResource.resx
contains a text resource, <literal>Hello {0} {1}</literal> under the key
name <literal>HelloMessage</literal> (aka Keys.HELLO_MESSAGE) that can
be used for string text formatting purposes. The example code
<programlisting>
string msg = ctx.GetMessage(Keys.HELLO_MESSAGE,
CultureInfo.CurrentCulture,
"Mr.", "Anderson");
</programlisting> retrieves the text string and replaces the placeholders in
the string with the passed argument values resulting in the text, "Hello
Mr. Anderson". The current culture is used to select the resource file
MyResource.resx. If instead the Spanish culture is specified
<programlisting>
CultureInfo spanishCultureInfo = new CultureInfo("es");
string esMsg = ctx.GetMessage(Keys.HELLO_MESSAGE,
spanishCultureInfo,
"Mr.", "Anderson");
</programlisting> Then the resource file MyResource.es.resx is used instead as
in standard .NET localization. Spring is simply delegating to .NET
ResourceManager to select the appropriate localized resource. The
Spanish version of the resource differs from the English one in that the
text under the key <literal>HelloMessage</literal> is <literal>Hola {0}
{1}</literal> resulting in the text <literal>"Hola Mr.
Anderson"</literal>.</para>
<para>As you can see in this example, the title "Mr." should not be used
in the case of the spanish localization. The title can be abstracted out
into a key of its own, called <literal>FemaleGreeting</literal> (aka
Keys.FEMALE_GREETING). The replacement value for the message argument
{0} can then be made localization aware by wrapping the key in a
convenience class DefaultMessageResolvable. The code <programlisting>
string[] codes = {Keys.FEMALE_GREETING};
DefaultMessageResolvable dmr = new DefaultMessageResolvable(codes, null);
msg = ctx.GetMessage(Keys.HELLO_MESSAGE,
CultureInfo.CurrentCulture,
dmr, "Anderson");
</programlisting> will assign msg the value, Hello Mrs. Anderson, since the
value for the key <literal>FemaleGreeting</literal> in MyResource.resx
is 'Mrs.' Similarly, the code <programlisting>
esMsg = ctx.GetMessage(Keys.HELLO_MESSAGE,
spanishCultureInfo,
dmr, "Anderson");
</programlisting> will assign esMsg the value, Hola Senora Anderson, since the
value for the key <literal>FemaleGreeting</literal> in
MyResource.es.resx is 'Senora'.</para>
<para>Localization can also apply to objects and not just strings. The
.NET 1.1 framework provides the utility class ComponentResourceManager
that can apply multiple resource values to object properties in a
performant manner. (VS.NET 2005 makes heavy use of this class in the
code it generates for winform applications.) The example program has a
simple class, Person, that has an integer property Age and a string
property Name. The resource file, Person.resx contains key names that
follow the pattern, person.&lt;PropertyName&gt;. In this case it
contains person.Name and person.Age. The code to assign these resource
values to an object is shown below <programlisting>
Person p = new Person();
ctx.ApplyResources(p, "person", CultureInfo.CurrentUICulture);
</programlisting> While you could also use the Spring itself to set the
properties of these objects, the configuration of simple properties
using Spring will not take into account localization. It may be
convenient to combine approaches and use Spring to configure the
Person's object references while using IApplicationContext inside an
AfterPropertiesSet callback (see IInitializingObject) to set the
Person's culture aware properties.</para>
</sect2>
</sect1>
<sect1>
<title>ApplicationContext and IEventRegistry</title>
<sect2>
<title>Introduction</title>
<para>The example program
<literal>Spring.Examples.EventRegistry</literal> shows how to use the
application context to wire .NET events in a loosely coupled
manner.</para>
<para>Loosely coupled eventing is normally associated with Message
Oriented Middleware (MOM) where a daemon process acts as a message
broker between other independent processes. Processes communicate
indirectly with each other by sending messages though the message
broker. The process that initiates the communication is known as a
publisher and the process that receives the message is known as the
subscriber. By using an API specific to the middleware these processes
register themselves as either publishers or subscribers with the message
broker. The communication between the publisher and subscriber is
considered loosely coupled because neither the publisher nor subscriber
has a direct reference to each other, the messages broker acts as an
intermediary between the two processes. The
<literal>IEventRegistry</literal> is the analogue of the message broker
as applied to .NET events. Publishers are classes that invoke a .NET
event, subscribers are the classes that register interest in these
events, and the messages sent between them are instances of
System.EventArgs. The implementation of
<literal>IEventRegistry</literal> determines the exact semantics of the
notification style and coupling between subscribers and
publishers.</para>
<para>The <literal>IApplicationContext</literal> interface extends the
<literal>IEventRegistry</literal> interface and implementations of
<literal>IApplicationContext</literal> delegate the event registry
functionality to an instance of
<literal>Spring.Objects.Events.Support.EventRegistry</literal>.
<literal>IEventRegistry</literal> is a simple inteface with one publish
method and two subscribe methods. Refer to <xref
linkend="context-functionality-pubsub" /> for a reminder of their
signatures. The
<literal>Spring.Objects.Events.Support.EventRegistry</literal>
implementation is essentially a convenience to decouple the event wiring
process between publisher and subscribers. In this implementation, after
the event wiring is finished, publishers are directly coupled to the
subscribers via the standard .NET eventing mechanisms. Alternate
implementations could increase the decoupling further by having the
event registry subscribe to the events and be responsible for then
notifying the subscribers.</para>
<para>In this example the class <literal>MyClientEventArgs</literal> is
a subclass of <literal>System.EventArgs</literal> that defines a string
property EventMessage. The class <literal>MyEventPublisher</literal>
defines a public event with the delegate signature <literal>void
SimpleClientEvent( object sender, MyClientEventArgs args )</literal> The
method <literal>void ClientMethodThatTriggersEvent1()</literal> fires
this event. On the subscribing side, the class
<literal>MyEventSubscriber</literal> contains a method,
<literal>HandleClientEvents</literal> that matches the delegate
signature and has a boolean property which is set to true if this method
is called.</para>
<para>The publisher and subscriber classes are defined in an application
context configuration file but that is not required in order to
participate with the event registry. The main program,
<literal>EventRegistryApp</literal> creates the application context and
asks it for an instance of <literal>MyEventPublisher</literal> The
publisher is registered with the event registry via the call,
<literal>ctx.PublishEvents( publisher )</literal>. The event registry
keeps a reference to this publisher for later use to register any
subscribers that match its event signature. Two subscribers are then
created and one of them is wired to the publisher by calling the method
<literal>ctx.Subscribe( subscriber, typeof(MyEventPublisher) )</literal>
Specifying the type indicates that the subscriber should be registered
only to events from objects of the type
<literal>MyEventPublisher</literal>. This acts as a simple filtering
mechanism on the subscriber.</para>
<para>The publisher then fires the event using normal .NET eventing
semantics and the subscriber is called. The subscriber prints a message
to the console and sets a state variable to indicate it has been called.
The program then simply prints the state variable of the two
subscribers, showing that only one of them (the one that registered with
the event registry) was called.</para>
</sect2>
</sect1>
&pooling-example;
<sect1>
<title>AOP</title>
<para>Refer to <xref linkend="aop-quickstart" />.</para>
</sect1>
</chapter>

View File

@@ -0,0 +1,817 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="remoting-quickstart">
<title>Portable Service Abstraction Quick Start</title>
<sect1 id="qs-remoting-introduction">
<title>Introduction</title>
<para>This quickstart demonstrates the basic usage of Spring.NET's
portable service abstraction functionality. Sections 2-5 demonstrate the
use of .NET Remoting, Section 6 shows the use of the
ServicedComponentExporter for .NET Enterprise Services, and Section 7
shows the use of the WebServiceExporter.</para>
</sect1>
<sect1 id="qs-remoting-projectstructure">
<title>.NET Remoting Example</title>
<para>The infrastructure classes are located in the
<literal>Spring.Services</literal> assembly under the
<literal>Spring.Services.Remoting</literal> namespace. The overall
strategy is to export .NET objects on the server side as either CAO or SAO
objects using <classname>CaoExporter</classname> or
<classname>SaoExporter</classname> and obtain references to these objects
on the client side using <classname>CaoFactoryObject</classname> and
<classname>SaoFactoryObject</classname>. This quickstart does assume
familiarity with .NET Remoting on the part of the reader. If you are new
to .NET remoting you may find the links to introductory remoting material
presented at the conclusion of this quickstart of some help.</para>
<para>As usual with quick start examples in Spring.NET, the classes used
in the quickstart are intentionally simple. In the specific case of this
remoting quickstart we are going to make a simple calculator that can be
accessed remotely. The same calculator class will be exported in multiple
ways reflecting the variety of .NET remoting options available (CAO,
SAO-SingleCall, SAO-Singleton) and also the use of adding AOP advice to
SAO hosted objects.</para>
<para>The example solution is located in the
<literal>examples\Spring\Spring.Calculator</literal> directory and
contains multiple projects.</para>
<para><mediaobject>
<imageobject>
<imagedata fileref="images/remoting-solution.gif" format="GIF" />
</imageobject>
</mediaobject></para>
<para>The <literal>Spring.Calculator.Contract</literal> project contains
the interface <classname>ICalculator</classname> that defines the basic
operations of a calculator and another interface
<classname>IAdvancedCalculator</classname> that adds support for memory
storage for results. (woo hoo - big feature - HP-12C beware!) These
interfaces are shown below. The
<literal>Spring.Calculator.Services</literal> project contains an
implementation of the these interfaces, namely the classes
<classname>Calculator</classname> and
<classname>AdvancedCalculator</classname>. The purpose of the
<classname>AdvancedCalculator</classname> implementation is to demonstrate
the configuration of object state for SAO-singleton objects. Note that the
calculator implementations <emphasis>do not</emphasis> inherit from the
<classname>MarshalByRefObject</classname> class. The
<literal>Spring.Calculator.ClientApp</literal> project contains the client
application and the <literal>Spring.Calculator.RemoteApp</literal> project
contains a console application that will host a Remoted instance of the
<classname>AdvancedCalculator</classname> class. The
<literal>Spring.Aspects</literal> project contains some logging advice
that will be used to demonstrate the application of aspects to remoted
objects. <literal>Spring.Calculator.RegisterComponentServices</literal> is
related to enterprise service exporters and is not relevant for this
quickstart. <literal>Spring.Calculator.Web</literal> is related to web
services exporters and is not relevant for this quickstart.</para>
<programlisting>public interface ICalculator
{
int Add(int n1, int n2);
int Subtract(int n1, int n2);
DivisionResult Divide(int n1, int n2);
int Multiply(int n1, int n2);
}
[Serializable]
public class DivisionResult
{
private int _quotient = 0;
private int _rest = 0;
public int Quotient
{
get { return _quotient; }
set { _quotient = value; }
}
public int Rest
{
get { return _rest; }
set { _rest = value; }
}
}</programlisting>
<para>An extension of this interface that supports having a slot for
calculator memory is shown below</para>
<programlisting>public interface IAdvancedCalculator : ICalculator
{
int GetMemory();
void SetMemory(int memoryValue);
void MemoryClear();
void MemoryAdd(int num);
}</programlisting>
<para>The structure of the VS.NET solution is a consequence of following
the best practice of using interfaces to share type information between a
.NET remoting client and server. The benefits of this approach are that
the client does not need a reference to the assembly that contains the
implementation class. Having the client reference the implementation
assembly is undesirable for a variety of reasons. One reason being
security since an untrusted client could potentially obtain the source
code to the implementation since Intermediate Language (IL) code is easily
reverse engineered. Another, more compelling, reason is to provide a
greater decoupling between the client and server so the server can update
its implementation of the interface in a manner that is quite transparent
to the client; i.e. the client code need not change. Independent of .NET
remoting best practices, using an interface to provide a service contract
is just good object-oriented design. This lets the client choose another
implementation unrelated to .NET Remoting, for example a local, test-stub
or a web services implementation. One of the major benefits of using
Spring.NET is that it reduces the cost of doing 'interface based
programming' to almost nothing. As such, this best practice approach to
.NET remoting fits naturally into the general approach to application
development that Spring.NET encourages you to follow. Ok, with that
barrage of OO design ranting finished, on to the implementation!</para>
</sect1>
<sect1 id="qs-remoting-implementation">
<title>Implementation</title>
<para>The implementation of the calculators contained in the
<literal>Spring.Calculator.Servies</literal> project is quite
straightforward. The only interesting methods are those that deal with the
memory storage, which is the state that we will be configuring explicitly
using constructor injection. A subset of the implementation is shown
below.</para>
<programlisting>public class Calculator : ICalculator
{
public int Add(int n1, int n2)
{
return n1 + n2;
}
public int Substract(int n1, int n2)
{
return n1 - n2;
}
public DivisionResult Divide(int n1, int n2)
{
DivisionResult result = new DivisionResult();
result.Quotient = n1 / n2;
result.Rest = n1 % n2;
return result;
}
public int Multiply(int n1, int n2)
{
return n1 * n2;
}
}
public class AdvancedCalculator : Calculator, IAdvancedCalculator
{
private int memoryStore = 0;
public AdvancedCalculator()
{}
public AdvancedCalculator(int initialMemory)
{
memoryStore = initialMemory;
}
public int GetMemory()
{
return memoryStore;
}
// other methods omitted in this listing...
}</programlisting>
<para>The <classname>Spring.Calculator.RemotedApp</classname> project
hosts remoted objects inside a console application. The code is also quite
simple and shown below</para>
<programlisting>public static void Main(string[] args)
{
try
{
// initialization of Spring.NET's IoC container
IApplicationContext ctx = ContextRegistry.GetContext();
Console.Out.WriteLine("Server listening...");
}
catch (Exception e)
{
Console.Out.WriteLine(e);
}
finally
{
Console.Out.WriteLine("--- Press &lt;return&gt; to quit ---");
Console.ReadLine();
}
}</programlisting>
<para>The configuration of the .NET remoting channels is done using the
standard <literal>system.runtime.remoting</literal> configuration section
inside the .NET configuration file of the application
(<literal>App.config</literal>). In this case we are using the
<literal>tcp</literal> channel on port <literal>8005</literal>.</para>
<programlisting>&lt;system.runtime.remoting&gt;
&lt;application&gt;
&lt;channels&gt;
&lt;channel ref="tcp" port="8005" /&gt;
&lt;/channels&gt;
&lt;/application&gt;
&lt;/system.runtime.remoting&gt;</programlisting>
<para>The objects created in Spring's application context are shown below.
Multiple resource files are used to export these objects under various
remoting configurations. The AOP advice used in this example is a simple
Log4Net based around advice.</para>
<programlisting> &lt;configSections&gt;
&lt;sectionGroup name="spring"&gt;
&lt;section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" /&gt;
&lt;section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /&gt;
&lt;section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" /&gt;
&lt;/sectionGroup&gt;
&lt;section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" /&gt;
&lt;/configSections&gt;
&lt;spring&gt;
&lt;parsers&gt;
&lt;parser type="Spring.Remoting.Config.RemotingNamespaceParser, Spring.Services" /&gt;
&lt;/parsers&gt;
&lt;context&gt;
&lt;resource uri="config://spring/objects" /&gt;
&lt;resource uri="assembly://RemoteServer/RemoteServer.Config/cao.xml" /&gt;
&lt;resource uri="assembly://RemoteServer/RemoteServer.Config/saoSingleCall.xml" /&gt;
&lt;resource uri="assembly://RemoteServer/RemoteServer.Config/saoSingleCall-aop.xml" /&gt;
&lt;resource uri="assembly://RemoteServer/RemoteServer.Config/saoSingleton.xml" /&gt;
&lt;resource uri="assembly://RemoteServer/RemoteServer.Config/saoSingleton-aop.xml" /&gt;
&lt;/context&gt;
&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;description&gt;Definitions of objects to be exported.&lt;/description&gt;
&lt;object type="Spring.Remoting.RemotingConfigurer, Spring.Services"&gt;
&lt;property name="Filename" value="Spring.Calculator.RemoteApp.exe.config" /&gt;
&lt;/object&gt;
&lt;object id="Log4NetLoggingAroundAdvice" type="Spring.Aspects.Logging.Log4NetLoggingAroundAdvice, Spring.Aspects"&gt;
&lt;property name="Level" value="Debug" /&gt;
&lt;/object&gt;
&lt;object id="singletonCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"&gt;
&lt;constructor-arg type="int" value="217"/&gt;
&lt;/object&gt;
&lt;object id="singletonCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"&gt;
&lt;property name="target" ref="singletonCalculator"/&gt;
&lt;property name="interceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;Log4NetLoggingAroundAdvice&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services" singleton="false"&gt;
&lt;constructor-arg type="int" value="217"/&gt;
&lt;/object&gt;
&lt;object id="prototypeCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"&gt;
&lt;property name="targetSource"&gt;
&lt;object type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop"&gt;
&lt;property name="TargetObjectName" value="prototypeCalculator"/&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;property name="interceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;Log4NetLoggingAroundAdvice&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/objects&gt;
&lt;/spring&gt;</programlisting>
<para>The declaration of the calculator instance,
<literal>singletonCalculator</literal> for example, and the setting of any
property values and / or object references is done as you would normally
do for any object declared in the Spring.NET configuration file. To expose
the calculator objects as .NET remoted objects the exporter
<classname>Spring.Remoting.CaoExporter</classname> is used for CAO objects
and <classname>Spring.Remoting.SaoExporter</classname> is used for SAO
objects. Both exporters require the setting of a
<literal>TargetName</literal> property that refers to the name of the
object in Spring's IoC container that will be remoted. The semantics of
SAO-SingleCall and CAO behavior are achieved by exporting a target object
that is declared as a "prototype" (i.e. singleton=false). For SAO objects,
the <literal>ServiceName</literal> property defines the name of the
service as it will appear in the URL that clients use to locate the remote
object. To set the remoting lifetime of the objects to be infinite, the
property <literal>Infinite</literal> is set to true.</para>
<para>The configuration for the exporting a SAO-Singleton is shown
below.<programlisting>&lt;objects
xmlns="http://www.springframework.net"
xmlns:r="http://www.springframework.net/remoting"&gt;
&lt;description&gt;Registers the calculator service as a SAO in 'Singleton' mode.&lt;/description&gt;
&lt;r:saoExporter
targetName="singletonCalculator"
serviceName="RemotedSaoSingletonCalculator" /&gt;
&lt;/objects&gt;</programlisting>The configuration shown above uses the Spring
Remoting schema but you can also choose to use the standard 'generic' XML
configuration shown below.<programlisting>&lt;object name="saoSingletonCalculator" type="Spring.Remoting.SaoExporter, Spring.Services"&gt;
&lt;property name="TargetName" value="singletonCalculator" /&gt;
&lt;property name="ServiceName" value="RemotedSaoSingletonCalculator" /&gt;
&lt;/object&gt;</programlisting> This will result in the remote object being
identified by the URL
<literal>tcp://localhost:8005/RemotedSaoSingletonCalculator</literal>. The
use of <classname>SaoExporter</classname> and
<classname>CaoExporter</classname> for other configuration are similar,
look at the configuration files in the
<classname>Spring.Calculator.RemotedApp</classname> project files for more
information.</para>
<para>On the client side, the client application will connect a specific
type of remote calculator service, object, ask it for it's current memory
value, which is pre-configured to <literal>217</literal>, then perform a
simple addition. As in the case of the server, the channel configuration
is done using the standard .NET Remoting configuration section of the .NET
application configuration file (<literal>App.config</literal>), as can
been seen below.</para>
<programlisting>&lt;system.runtime.remoting&gt;
&lt;application&gt;
&lt;channels&gt;
&lt;channel ref="tcp"/&gt;
&lt;/channels&gt;
&lt;/application&gt;
&lt;/system.runtime.remoting&gt;</programlisting>
<para>The client implementation code is shown below.</para>
<programlisting>public static void Main(string[] args)
{
try
{
Pause();
IApplicationContext ctx = ContextRegistry.GetContext();
Console.Out.WriteLine("Get Calculator...");
IAdvancedCalculator firstCalc = (IAdvancedCalculator) ctx.GetObject("calculatorService");
Console.WriteLine("Divide(11, 2) : " + firstCalc.Divide(11, 2));
Console.Out.WriteLine("Memory = " + firstCalc.GetMemory());
firstCalc.MemoryAdd(2);
Console.Out.WriteLine("Memory + 2 = " + firstCalc.GetMemory());
Console.Out.WriteLine("Get Calculator...");
IAdvancedCalculator secondCalc = (IAdvancedCalculator) ctx.GetObject("calculatorService");
Console.Out.WriteLine("Memory = " + secondCalc.GetMemory());
}
catch (Exception e)
{
Console.Out.WriteLine(e);
}
finally
{
Pause();
}
}</programlisting>
<para>Note that the client application code is not aware that it is using
a remote object. The <literal>Pause()</literal> method simply waits until
the <literal>Return</literal> key is pressed on the console so that the
client doesn't make a request to the server before the server has had a
chance to start. The standard configuration and initialization of the .NET
remoting infrastructure is done before the creation of the Spring.NET IoC
container. The configuration of the client application is constructed in
such a way that one can easily switch implementations of the
<literal>calculatorService</literal> retrieved from the application
context. In more complex applications the calculator service would be a
dependency on another object in your application, say in a workflow
processing layer. The following listing shows a configuration for use of a
local implementation and then several remote implementations. The same
Exporter approach can be used to create Web Services and Serviced
Components (Enterprise Services) of the calculator object but are not
discussed in this QuickStart.</para>
<programlisting>
&lt;spring&gt;
&lt;context&gt;
&lt;resource uri="config://spring/objects" /&gt;
&lt;!-- Only one at a time ! --&gt;
&lt;!-- ================================== --&gt;
&lt;!-- In process (local) implementations --&gt;
&lt;!-- ================================== --&gt;
&lt;resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.InProcess/inProcess.xml" /&gt;
&lt;!-- ======================== --&gt;
&lt;!-- Remoting implementations --&gt;
&lt;!-- ======================== --&gt;
&lt;!-- Make sure 'RemoteApp' console application is running and listening. --&gt;
&lt;!-- &lt;resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.Remoting/cao.xml" /&gt; --&gt;
&lt;!-- &lt;resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.Remoting/cao-ctor.xml" /&gt; --&gt;
&lt;!-- &lt;resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.Remoting/saoSingleton.xml" /&gt; --&gt;
&lt;!-- &lt;resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.Remoting/saoSingleton-aop.xml" /&gt; --&gt;
&lt;!-- &lt;resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.Remoting/saoSingleCall.xml" /&gt; --&gt;
&lt;!-- &lt;resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.Remoting/saoSingleCall-aop.xml" /&gt; --&gt;
&lt;!-- =========================== --&gt;
&lt;!-- Web Service implementations --&gt;
&lt;!-- =========================== --&gt;
&lt;!-- Make sure 'http://localhost/SpringCalculator/' web application is running --&gt;
&lt;!-- &lt;resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.WebServices/webServices.xml" /&gt; --&gt;
&lt;!-- &lt;resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.WebServices/webServices-aop.xml" /&gt; --&gt;
&lt;!-- ================================= --&gt;
&lt;!-- EnterpriseService implementations --&gt;
&lt;!-- ================================= --&gt;
&lt;!-- Make sure you register components with 'RegisterComponentServices' console application. --&gt;
&lt;!-- &lt;resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.EnterpriseServices/enterpriseServices.xml" /&gt; --&gt;
&lt;/context&gt;
&lt;/spring&gt;
</programlisting>
<para>The inProcess.xml configuration file creates an instance of
AdvancedCalculator directly <programlisting>
&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;description&gt;inProcess&lt;/description&gt;
&lt;object id="calculatorService" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services" /&gt;
&lt;/objects&gt;
</programlisting></para>
<para>Factory classes are used to create a client side reference to the
.NET remoting implementations. For SAO objects use the
<classname>SaoFactoryObject</classname> class and for CAO objects use the
<classname>CaoFactoryObject</classname> class. The configuration for
obtaining a reference to the previously exported SAO singleton
implementation is shown below <programlisting>&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;description&gt;saoSingleton&lt;/description&gt;
&lt;object id="calculatorService" type="Spring.Remoting.SaoFactoryObject, Spring.Services"&gt;
&lt;property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract" /&gt;
&lt;property name="ServiceUrl" value="tcp://localhost:8005/RemotedSaoSingletonCalculator" /&gt;
&lt;/object&gt;
&lt;/objects&gt;
</programlisting></para>
<para>You must specify the property <literal>ServiceInterface</literal> as
well as the location of the remote object via the
<literal>ServiceUrl</literal> property. The property replacement
facilities of Spring.NET can be leveraged here to make it easy to
configure the URL value based on environment variable settings, a standard
.NET configuration section, or an external property file. This is useful
to easily switch between test, QA, and production (yea baby!)
environments. An example of how this would be expressed is...</para>
<programlisting>&lt;property name="ServiceUrl" value="${protocol}://${host}:${port}/RemotedSaoSingletonCalculator" /&gt;</programlisting>
<para>The property values in this example are defined elsewhere; refer to
<xref linkend="objects-factory-placeholderconfigurer" /> for additional
information. As mentioned previously, more important in terms of
configuration flexibility is the fact that now you can swap out different
implementations (.NET remoting based or otherwise) of this interface by
making a simple change to the configuration file.</para>
<para>The configuration for obtaining a reference to the previously
exported CAO implementation is shown below <programlisting>&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;description&gt;cao&lt;/description&gt;
&lt;object id="calculatorService" type="Spring.Remoting.CaoFactoryObject, Spring.Services"&gt;
&lt;property name="RemoteTargetName" value="prototypeCalculator" /&gt;
&lt;property name="ServiceUrl" value="tcp://localhost:8005" /&gt;
&lt;/object&gt;
&lt;/objects&gt;
</programlisting></para>
</sect1>
<sect1>
<title>Running the application</title>
<para>Now that we have had a walk though of the implementation and
configuration it is finally time to run the application (if you haven't
yet pulled the trigger). Be sure to set up VS.NET to run multiple
applications on startup as shown below.</para>
<para><mediaobject>
<imageobject>
<imagedata fileref="images/remoting-startup.gif" format="GIF" />
</imageobject>
</mediaobject></para>
<para>Running the solution yields the following output in the server and
client window</para>
<para><programlisting> SERVER WINDOW
Server listening...
--- Press &lt;return&gt; to quit ---
CLIENT WINDOW
--- Press &lt;return&gt; to continue --- (hit return...)
Get Calculator...
Divide(11, 2) : Quotient: '5'; Rest: '1'
Memory = 0
Memory + 2 = 2
Get Calculator...
Memory = 2
--- Press &lt;return&gt; to continue ---</programlisting></para>
</sect1>
<sect1>
<title>Remoting Schema</title>
<para>The spring-remoting.xsd file in the doc directory provides a short
syntax to configure Spring.NET remoting features. To install the schema in
the VS.NET environment run the install-schema NAnt script in the doc
directory. Refer to the Chapter on VS.NET integration for more
details.</para>
<para>The various configuration files in the RemoteServer and Client
projects show the schema in action. Here is a condensed listing of those
definitions which should give you a good feel for how to use the
schema.</para>
<programlisting>&lt;!-- Calculator definitions --&gt;
&lt;object id="singletonCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"&gt;
&lt;constructor-arg type="int" value="217" /&gt;
&lt;/object&gt;
&lt;object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services" singleton="false"&gt;
&lt;constructor-arg type="int" value="217" /&gt;
&lt;/object&gt;
&lt;!-- CAO object --&gt;
&lt;r:caoExporter targetName="prototypeCalculator" infinite="false"&gt;
&lt;r:lifeTime initialLeaseTime="2m" renewOnCallTime="1m"/&gt;
&lt;/r:caoExporter&gt;
&lt;!-- SAO Single Call --&gt;
&lt;r:saoExporter
targetName="prototypeCalculator"
serviceName="RemotedSaoSingleCallCalculator"/&gt;
&lt;!-- SAO Singleton --&gt;
&lt;r:saoExporter
targetName="singletonCalculator"
serviceName="RemotedSaoSingletonCalculator" /&gt;</programlisting>
<para>Note that the singleton nature of the remoted object is based on the
Spring object definition. The "PrototypeCalculator" has its singleton
property set to false to that a new one will be created every time a
method on the remoted object is invoked for the SAO case.</para>
</sect1>
<sect1>
<title id="entsvc-example">.NET Enterprise Services Example</title>
<para>The .NET Enterprise Services example is located in the project
Spring.Calculator.RegisterComponentServices.2005.csproj or
Spring.Calculator.RegisterComponentServices.2003.csproj, depending on the
use of .NET 1.1 or 2.0. The example uses the previous AdvancedCalculator
implementation and then imports the embedded configuration file
'enterpriseServices.xml' from the namespace
Spring.Calculator.RegisterComponentServices.Config. The top level
configuration is shown below</para>
<programlisting> &lt;spring&gt;
&lt;context&gt;
&lt;resource uri="config://spring/objects" /&gt;
&lt;resource uri="assembly://Spring.Calculator.RegisterComponentServices/Spring.Calculator.RegisterComponentServices.Config/enterpriseServices.xml" /&gt;
&lt;/context&gt;
&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;description&gt;Definitions of objects to be registered.&lt;/description&gt;
&lt;object id="calculatorService" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services" /&gt;
&lt;/objects&gt;
&lt;/spring&gt;</programlisting>
<para>The exporter that adapts the AdvancedCalculator for use as an
Enterprise Service component is defined first in enterpriseServices.xml.
Second is defined an exporter that will host the exported Enterprise
Services component application by signing the assembly, registering it
with the specified COM+ application name. If application does not exist it
will create it and configure it using values specified for Description,
AccessControl and Roles properties. The configuration file for
enterpriseServices.xml is shown below</para>
<para><programlisting>&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;description&gt;enterpriseService&lt;/description&gt;
&lt;object id="calculatorComponent" type="Spring.EnterpriseServices.ServicedComponentExporter, Spring.Services"&gt;
&lt;property name="TargetName" value="calculatorService" /&gt;
&lt;property name="TypeAttributes"&gt;
&lt;list&gt;
&lt;object type="System.EnterpriseServices.TransactionAttribute, System.EnterpriseServices" /&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="MemberAttributes"&gt;
&lt;dictionary&gt;
&lt;entry key="*"&gt;
&lt;list&gt;
&lt;object type="System.EnterpriseServices.AutoCompleteAttribute, System.EnterpriseServices" /&gt;
&lt;/list&gt;
&lt;/entry&gt;
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;object type="Spring.EnterpriseServices.EnterpriseServicesExporter, Spring.Services"&gt;
&lt;property name="ApplicationName"&gt;
&lt;value&gt;Spring Calculator Application&lt;/value&gt;
&lt;/property&gt;
&lt;property name="Description"&gt;
&lt;value&gt;Spring Calculator application.&lt;/value&gt;
&lt;/property&gt;
&lt;property name="AccessControl"&gt;
&lt;object type="System.EnterpriseServices.ApplicationAccessControlAttribute, System.EnterpriseServices"&gt;
&lt;property name="AccessChecksLevel"&gt;
&lt;value&gt;ApplicationComponent&lt;/value&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;property name="Roles"&gt;
&lt;list&gt;
&lt;value&gt;Admin : Administrator role&lt;/value&gt;
&lt;value&gt;User : User role&lt;/value&gt;
&lt;value&gt;Manager : Administrator role&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="Components"&gt;
&lt;list&gt;
&lt;ref object="calculatorComponent" /&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="Assembly"&gt;
&lt;value&gt;Spring.Calculator.EnterpriseServices&lt;/value&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting></para>
</sect1>
<sect1 id="websvc-example">
<title>Web Services Example</title>
<para>The WebServices example shows how to export the AdvancedCalculator
as a web service that is an AOP proxy of AdvancedCalculator that has
logging advice applied to it. The main configuration file, Web.config,
includes information from three locations as shown below</para>
<programlisting> &lt;context&gt;
&lt;resource uri="config://spring/objects"/&gt;
&lt;resource uri="~/Config/webServices.xml"/&gt;
&lt;resource uri="~/Config/webServices-aop.xml"/&gt;
&lt;/context&gt;</programlisting>
<para>The config section 'spring/objects' in Web.config contains the
definition for the 'plain' Advanced calculator, as well as the definitions
to create an AOP proxy of an AdvancedCalculator that adds logging advice.
These definitions are shown below<programlisting> &lt;objects xmlns="http://www.springframework.net"&gt;
&lt;!-- Aspect --&gt;
&lt;object id="CommonLoggingAroundAdvice" type="Spring.Aspects.Logging.CommonLoggingAroundAdvice, Spring.Aspects"&gt;
&lt;property name="Level" value="Debug"/&gt;
&lt;/object&gt;
&lt;!-- Service --&gt;
&lt;!-- 'plain object' for AdvancedCalculator --&gt;
&lt;object id="calculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"/&gt;
&lt;!-- AdvancedCalculator object with AOP logging advice applied. --&gt;
&lt;object id="calculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"&gt;
&lt;property name="target" ref="calculator"/&gt;
&lt;property name="interceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;CommonLoggingAroundAdvice&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>The configuration file webService.xml
simply exports the named calculator object</para>
<programlisting> &lt;object id="calculatorService" type="Spring.Web.Services.WebServiceExporter, Spring.Web"&gt;
&lt;property name="TargetName" value="calculator" /&gt;
&lt;property name="Namespace" value="http://SpringCalculator/WebServices" /&gt;
&lt;property name="Description" value="Spring Calculator Web Services" /&gt;
&lt;/object&gt;</programlisting>
<para>Whereas the webService-aop.xml exports the calculator instance that
has AOP advice applied to it.</para>
<programlisting> &lt;object id="calculatorServiceWeaved" type="Spring.Web.Services.WebServiceExporter, Spring.Web"&gt;
&lt;property name="TargetName" value="calculatorWeaved" /&gt;
&lt;property name="Namespace" value="http://SpringCalculator/WebServices" /&gt;
&lt;property name="Description" value="Spring Calculator Web Services" /&gt;
&lt;/object&gt;
</programlisting>
<para>Setting the solution to run the web project as the startup, you will
be presented with a screen as shown below</para>
<para><mediaobject>
<imageobject>
<imagedata fileref="images/web-exporter-calc-svc-main.jpg"
format="JPG" />
</imageobject>
</mediaobject>Selecting the CalculatorService and
CalculatorServiceWeaved links will bring you to the standard user
interface generated for browsing a web service, as shown below<mediaobject>
<imageobject>
<imagedata fileref="images/web-exporter-calc-svc.jpg" />
</imageobject>
</mediaobject></para>
<para>And similarly for the calculator service with AOP applied</para>
<mediaobject>
<imageobject>
<imagedata fileref="images/web-exporter-calc-svc-aop.jpg" />
</imageobject>
</mediaobject>
<para>Invoking the Add method for calculatorServiceWeaved shows the
screen</para>
<mediaobject>
<imageobject>
<imagedata fileref="images/web-exporter-calc-svc-aop-add.jpg" />
</imageobject>
</mediaobject>
<para>Invoking add will then show the result '4' in a new browser instance
and the log file log.txt will contain the following entires</para>
<programlisting>2007-10-15 17:59:47,375 [DEBUG] Spring.Aspects.Logging.CommonLoggingAroundAdvice - Intercepted call : about to invoke method 'Add'
2007-10-15 17:59:47,421 [DEBUG] Spring.Aspects.Logging.CommonLoggingAroundAdvice - Intercepted call : returned '4'</programlisting>
</sect1>
<sect1 id="qs-remoting-additional">
<title>Additional Resources</title>
<para>Some introductory articles on .NET remoting can be found online at
MSDN. Ingo Rammer is also a very good authority on .NET remoting, and the
.NET Remoting FAQ (link below) which is maintained by Ingo is chock full
of useful information.</para>
<para><itemizedlist>
<listitem>
<para><ulink
url="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/introremoting.asp">An
Introduction to Microsoft .NET Remoting Framework</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/hawkremoting.asp">Microsoft
.NET Remoting: A Technical Overview</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://www.apress.com/book/bookDisplay.html?bID=374">Advanced
.NET Remoting</ulink> (authored by Ingo Rammer)</para>
</listitem>
<listitem>
<para><ulink
url="http://www.thinktecture.com/resources/remotingfaq/default.html">.NET
Remoting FAQ</ulink></para>
</listitem>
</itemizedlist></para>
</sect1>
</chapter>

View File

@@ -0,0 +1,470 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="remoting">
<title>.NET Remoting</title>
<section id="remoting-introduction">
<title>Introduction</title>
<para>Spring's .NET Remoting support allows you to export a 'plain .NET
object' as a .NET Remoted object. By "plain .NET object" we mean classes
that do not inherit from a specific infrastructure base class such as
MarshalByRefObject. On the server side, Spring's .NET Remoting exporters
will automatically create a proxy that implements MarshalByRefObject. You
register SAO types as either SingleCall or Singleton and also configure on
a per-object basis lifetime and leasing parameters. On the client side you
can obtain CAO references to server proxy objects in a manner that
promotes interface based design best practices when developing .NET
remoting applications. The current implementation requires that your plain
.NET objects implements a business service interface. Additionally you can
add AOP advice to both SAO and CAO objects.</para>
<para>You can leverage the IoC container to configure the exporter and
service endpoints. A remoting specific xml-schema is provided to simplify
the remoting configuration but you can still use the standard
reflection-like property based configuration schema. You may also opt to
not use the IoC container to configure the objects and use Spring's .NET
Remoting classes Programatically, as you would with any third party
library.</para>
<para>A sample application, often referred to in this documentation, is in
the distribution under the directory "examples\Spring\Spring.Calculator"
and may also be found via the start menu by selecting the 'Calculator'
item.</para>
</section>
<section id="remoting-publishsao">
<title>Publishing SAOs on the Server</title>
<para>Exposing a Singleton SAO service can be done in two ways. The first
is through programmatic or administrative type registration that makes
calls to
<literal>RemotingConfiguration.RegisterWellKnownServiceType</literal>.
This method has the limitation that you must use a default constructor and
you can not easily configure the singleton state at runtime since it is
created on demand. The second way is to publish an object instance using
<literal>RemotingServices.Marshal</literal>. This method overcomes the
limitations of the first method. Example server side code for publishing
an SAO singleton object with a predefined state is shown below
<programlisting>AdvancedMBRCalculator calc = new AdvancedMBRCalculator(217);
RemotingServices.Marshal(calc, "MyRemotedCalculator");</programlisting></para>
<para>The class AdvancedMBRCalculator used above inherits from
MarshalByRefObject.</para>
<para>If your design calls for configuring a singleton SAO, or using a
non-default constructor, you can use the Spring IoC container to create
the SAO instance, configure it, and register it with the .NET remoting
infrastructure. The <classname>SaoExporter</classname> class performs this
task and most importantly, will automatically create a proxy class that
inherits from MarshalbyRefObject if your business object does not already
do so. The following XML taken from the <link
linkend="remoting-quickstart">Remoting QuickStart</link> demonstrates its
usage to an SAO Singleton object</para>
<section id="sao-singleton">
<title>SAO Singleton</title>
</section>
<programlisting>&lt;object id="singletonCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"&gt;
&lt;constructor-arg type="int" value="217"/&gt;
&lt;/object&gt;
&lt;!-- Registers the calculator service as a SAO in 'Singleton' mode. --&gt;
&lt;object name="saoSingletonCalculator" type="Spring.Remoting.SaoExporter, Spring.Services"&gt;
&lt;property name="TargetName" value="singletonCalculator" /&gt;
&lt;property name="ServiceName" value="RemotedSaoSingletonCalculator" /&gt;
&lt;/object&gt;</programlisting>
<para>This XML fragment shows how an existing object "singletonCalculator"
defined in the Spring context is exposed under the url-path name
"RemotedSaoSingletonCalculator". (The fully qualified url is
tcp://localhost:8005/RemotedSaoSingleCallCalculator using the standard
.NET channel configuration shown further below.)
<classname>AdvancedCalculator</classname> class implements the business
interface <classname>IAdvancedCalculator</classname>. The current proxy
implementation requires that your business objects implement an interface.
The interfaces' methods will be the ones exposed in the generated .NET
remoting proxy. The initial memory of the calculator is set to 217 via the
constructor. The class <classname>AdvancedCalculator</classname>
<emphasis>does not</emphasis> inherit from
<classname>MarshalByRefObject</classname>. Also note that the exporter
sets the lifetime of the SAO Singleton to infinite so that the singleton
will not be garbage collected after 5 minutes (the .NET default lease
time). If you would like to vary the lifetime properties, they are
InitialLeaseTime, RenewOnCallTime, and SponsorshipTimeout.</para>
<para>A custom schema is provided to make the object declaration even
easier and with intellisense support for the attributes. This is shown
below<programlisting>&lt;objects xmlns="http://www.springframework.net"
xmlns:r="http://www.springframework.net/remoting"&gt;
&lt;r:saoExporter targetName="singletonCalculator"
serviceName="RemotedSaoSingletonCalculator" /&gt;
... other object definitions
&lt;/objects&gt;</programlisting>Refer to the end of this chapter for more
information on Spring's .NET custom schema.</para>
<section id="sao-singlecall">
<title>SAO SingleCall</title>
</section>
<para>The following XML fragment shows how to expose the calculator
service in SAO 'SingleCall' mode. <programlisting>&lt;object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"
singleton="false"&gt;
&lt;constructor-arg type="int" value="217"/&gt;
&lt;/object&gt;
&lt;object name="saoSingleCallCalculator" type="Spring.Remoting.SaoExporter, Spring.Services"&gt;
&lt;property name="TargetName" value="prototypeCalculator" /&gt;
&lt;property name="ServiceName" value="RemotedSaoSingleCallCalculator" /&gt;
&lt;/object&gt;</programlisting></para>
<para>Note that we change the singleton attribute of the plain .NET object
as configured by Spring in the &lt;object&gt; definition and not an
attribute on the SaoExporter. The object referred to in the
<literal>TargetName</literal> parameter can be an AOP proxy to a business
object. For example, if we were to apply some simple logging advice to the
singleton calculator, the following standard AOP configuration is used to
create the target for the SaoExporter</para>
<programlisting>&lt;object id="singletonCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"&gt;
&lt;property name="target" ref="singletonCalculator"/&gt;
&lt;property name="interceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;Log4NetLoggingAroundAdvice&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;object name="saoSingletonCalculatorWeaved" type="Spring.Remoting.SaoExporter, Spring.Services"&gt;
&lt;property name="TargetName" value="singletonCalculatorWeaved" /&gt;
&lt;property name="ServiceName" value="RemotedSaoSingletonCalculatorWeaved" /&gt;
&lt;/object&gt;</programlisting>
<note>As generally required with a .NET Remoting application, the
arguments to your service methods should be Serializable.</note>
<sect2 id="remoting-configuration">
<title>Console Application Configuration</title>
<para>When using <classname>SaoExporter</classname> you can still use
the standard remoting administration section in the application
configuration file to register the channel.
<classname>ChannelServices</classname> as shown below</para>
<programlisting>&lt;system.runtime.remoting&gt;
&lt;application&gt;
&lt;channels&gt;
&lt;channel ref="tcp" port="8005" /&gt;
&lt;/channels&gt;
&lt;/application&gt;
&lt;/system.runtime.remoting&gt;</programlisting>
<para>A console application that will host this Remoted object needs to
initialize the .NET Remoting infrastructure with a call to
RemotingConfiguration (since we are using the .config file for channel
registration) and then start the Spring application context. This is
shown below <programlisting>RemotingConfiguration.Configure("RemoteApp.exe.config");
IApplicationContext ctx = ContextRegistry.GetContext();
Console.Out.WriteLine("Server listening...");
Console.ReadLine();
</programlisting></para>
<para>You can also put in the configuration file an instance of the
object <classname>Spring.Remoting.RemotingConfigurer</classname> to make
the RemotingConfiguration call show above on your behalf during
initialization of the IoC container. The
<classname>RemotingConfigurer</classname> implements the
<interfacename>IObjectFactoryPostProcessor</interfacename> interface,
which gets called after all object definitions have been loaded but
before they have been instantiated, (See<xref
linkend="objects-factory-customizing-factory-postprocessors" /> for more
information). The RemotingConfigurer has two properties you can
configure. <classname>Filename</classname>, that specifies the filename
to load the .NET remoting configuration from (if null the default file
name is used) and <classname>EnsureSecurity</classname> which makes sure
the channel in encrypted (available only on .NET 2.0). As a convenience,
the custom Spring remoting schema can be used to define an instance of
this class as shown below, taken from the <link
linkend="remoting-quickstart">Remoting QuickStart</link>
<programlisting>&lt;objects xmlns="http://www.springframework.net"
xmlns:r="http://www.springframework.net/remoting"&gt;
&lt;r:configurer filename="Spring.Calculator.RemoteApp.exe.config" /&gt;
&lt;/objects&gt;</programlisting></para>
<para>The ReadLine prevents the console application from exiting. You
can refer to the code in RemoteApp in the <link
linkend="remoting-quickstart">Remoting QuickStart</link> to see this
code in action.</para>
</sect2>
<section id="iis-application">
<title>IIS Application Configuration</title>
<para>If you are deploying a .NET remoting application inside IIS there
is a <ulink
url="http://forum.springframework.net/showthread.php?t=469">sample
project </ulink> that demonstrates the necessary configuration using
Spring.Web.</para>
<para>Spring.Web ensures the application context is initialized, but if
you don't use Spring.Web the idea is to start the initialization of the
Spring IoC container inside the application start method defined in
Global.asax, as shown below</para>
<programlisting> void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
// Ensure Spring has loaded configuration registering context
Spring.Context.IApplicationContext ctx = new Spring.Context.Support.XmlApplicationContext(
HttpContext.Current.Server.MapPath("Spring.Config"));
Spring.Context.Support.ContextRegistry.RegisterContext(ctx);
}</programlisting>
<para>In this example, the Spring configuration file is named
Spring.Config. Inside Web.config you add a standard
&lt;system.runtime.remoting&gt; section. Note that you do not need to
specify the port number of your channels as they will use the port
number of your web site. Ambiguous results have been reported if you do
specify the port number. Also, in order for IIS to recognize the
remoting request, you should add the suffix '.rem' or '.soap' to the
target name of your exported remote object so that the correct IIS
handler can be invoked.</para>
</section>
</section>
<section id="remoting-clientsao">
<title>Accessing a SAO on the Client</title>
<para>Administrative type registration on the client side lets you easily
obtain a reference to a SAO object. When a type is registered on the
client, using the new operator or using the reflection API will return a
proxy to the remote object instead of a local reference. Administrative
type registration on the client for a SAO object is performed using the
<literal>wellknown</literal> element in the client configuration section.
However, this approach requires that you expose the implementation of the
class on the client side. Practically speaking this would mean linking in
the server assembly to the client application, a generally recognized bad
practice. This dependency can be removed by developing remote services
based on a business interface. Aside from remoting considerations, the
separation of interface and implementation is considered a good practice
when designing OO systems. In the context of remoting, this means that the
client can obtain a proxy to a specific implementation with
<emphasis>only</emphasis> a reference to the interface assembly. To
achieve the decoupling of client and server, a separate assembly
containing the interface definitions is created and shared between the
client and server applications.</para>
<para>There is a simple means for following this design when the remote
object is a SAO object. A call to <literal>Activator.GetObject</literal>
will instantiate a SAO proxy on the client. For CAO objects another
mechanism is used and is discussed later. The code to obtain the SAO proxy
is shown below <programlisting>ICalculator calc = (ICalculator)Activator.GetObject (
typeof (ICalculator),
"tcp://localhost:8005/MyRemotedCalculator");</programlisting></para>
<para>To obtain a reference to a SAO proxy within the IoC container, you
can use the object factory <classname>SaoFactoryObject</classname> in the
Spring configuration file. The following XML taken from the <link
linkend="remoting-quickstart"> Remoting QuickStart</link> demonstrates its
usage.</para>
<programlisting>&lt;object id="calculatorService" type="Spring.Remoting.SaoFactoryObject, Spring.Services"&gt;
&lt;property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract" /&gt;
&lt;property name="ServiceUrl" value="tcp://localhost:8005/RemotedSaoSingletonCalculator" /&gt;
&lt;/object&gt;</programlisting>
<para>The ServiceInterface property specifies the type of proxy to create
while the ServiceUrl property creates a proxy bound to the specified
server and published object name.</para>
<para>Other objects in the IoC container that depend on an implementation
of the interface <classname>ICalculator</classname> can now refer to the
object "calculatorService", thereby using a remote implementation of this
interface. The exposure of dependencies among objects within the IoC
container lets you easily switch the implementation of
<classname>ICalculator</classname>. By using the IoC container changing
the application to use a local instead of remote implementation is a
configuration file change, not a code change. By promoting interface based
programing, the ability to switch implementation makes it easier to unit
test the client application, since unit testing can be done with a mock
implementation of the interface. Similarly, development of the client can
proceed independent of the server implementation. This increases
productivity when there are separate client and server development teams.
The two teams agree on interfaces before starting development. The client
team can quickly create a simple, but functional implementation and then
integrate with the server implementation when it is ready.</para>
</section>
<section id="remoting-cao-introduction">
<title>CAO best practices</title>
<para>Creating a client activated object (CAO) is typically done by
administrative type registration, either Programatically or via the
standard .NET remoting configuration section. The registration process
allows you to use the 'new' operator to create the remote object and
requires that the implementation of the object be distributed to the
client. As mentioned before, this is not a desirable approach to
developing distributed systems. The best practice approach that avoids
this problem is to create an SAO based factory class on the server that
will return CAO references to the client. In a manner similar to how
Spring's generic object factory can be used as a replacement creating a
factory per class, we can create a generic SAO object factory to return
CAO references to objects defined in Spring's application context. This
functionality is encapsulated in Spring's
<classname>CaoExporter</classname> class. On the client side a reference
is obtained using <literal>CaoFactoryObject</literal>. The client side
factory object supports creation of the CAO object using constructor
arguments. In addition to reducing the clutter and tedium around creating
factory classes specific to each object type you wish to expose in this
manner, this approach has the additional benefit of not requiring any type
registration on the client or server side. This is because the act of
returning an instance of a class that inherits from MarshalByRefObject
across a remoting boundary automatically returns a CAO object reference.
For more information on this best-practice, refer to the last section,
<xref linkend="remoting-additional" />, for some links to additional
resources.</para>
</section>
<section id="remoting-publishcao">
<title>Registering a CAO object on the Server</title>
<para>To expose an object as a CAO on the server you should declare an
object in the standard Spring configuration that is a 'prototype', that is
the singleton property is set to false. This results in a new object being
created each time it is retrieved from Spring's IoC container. An
implementation of <interfacename>ICaoRemoteFactory</interfacename> is what
is exported via a call to RemotingServices.Marshal. This implementation
uses Spring's IoC container to create objects and then dynamically create
a .NET remoting proxy for the retrieved object. Note that the default
lifetime of the remote object is set to infinite (null is returned from
the implementation of InitializeLifetimeService()).</para>
<para>This is best shown using an example from the Remoting Quickstart
application. Here is the definition of a simple calculator object,</para>
<para><programlisting>&lt;object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"
singleton="false"&gt;
&lt;constructor-arg type="int" value="217" /&gt;
&lt;/object&gt;</programlisting>To export this as a CAO object we can declare
the <classname>CaoExporter</classname> object directly in the server's XML
configuration file, as shown below</para>
<programlisting>&lt;object id="caoCalculator" type="Spring.Remoting.CaoExporter, Spring.Services"&gt;
&lt;property name="TargetName" value="prototypeCalculator" /&gt;
&lt;property name="Infinite" value="false" /&gt;
&lt;property name="InitialLeaseTime" value="2m" /&gt;
&lt;property name="RenewOnCallTime" value="1m" /&gt;
&lt;/object&gt;</programlisting>
<para>Note the property 'TargetName' is set to the name, not the
reference, of the non-singleton declaration of the 'AdvancedCalculator'
class.</para>
<para>Alternatively, you can use the remoting schema and declare the CAO
object as shown below</para>
<programlisting>&lt;r:caoExporter targetName="prototypeCalculator" infinite="false"&gt;
&lt;r:lifeTime initialLeaseTime="2m" renewOnCallTime="1m" /&gt;
&lt;/r:caoExporter&gt;</programlisting>
<para></para>
<section>
<title>Applying AOP advice to exported CAO objects</title>
<para>Applying AOP advice to exported CAO objects is done by referencing
the adviced object name to the CAO exporter. Again, taking an example
from the Remoting QuickStart, a calculator with logging around advice is
defined as shown below.</para>
<programlisting>&lt;object id="prototypeCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"&gt;
&lt;property name="targetSource"&gt;
&lt;object type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop"&gt;
&lt;property name="TargetObjectName" value="prototypeCalculator" /&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;property name="interceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;ConsoleLoggingAroundAdvice&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>If this declaration is unfamiliar to you, please refer to <xref
linkend="aop" /> for more information. The CAO exporter then references
with the name 'prototypeCalculatorWeaved' as shown below.</para>
<programlisting>&lt;r:caoExporter targetName="prototypeCalculatorWeaved" infinite="false"&gt;
&lt;r:lifeTime initialLeaseTime="2m" renewOnCallTime="1m" /&gt;
&lt;/r:caoExporter&gt;</programlisting>
</section>
</section>
<section id="remoting-clientcao">
<title>Accessing a CAO on the Client</title>
<para>On the client side a CAO reference is obtained by using the
<classname>CaoFactoryObject</classname> as shown below</para>
<programlisting>&lt;object id="calculatorService" type="Spring.Remoting.CaoFactoryObject, Spring.Services"&gt;
&lt;property name="RemoteTargetName" value="prototypeCalculator" /&gt;
&lt;property name="ServiceUrl" value="tcp://localhost:8005" /&gt;
&lt;/object&gt;</programlisting>
<para>This definition corresponds to the exported calculator from the
previous section. The property 'RemoteTargetName' identifies the object on
the server side. Using this approach the client can obtain an reference
though standard DI techniques to a remote object that implements the
<interfacename>IAdvancedCalculator</interfacename> interface. (As always,
that doesn't mean the client should treat the object as if it was an
in-process object).</para>
<para>Alternatively, you can use the Remoting schema to shorten this
definition and provide intellisense code completion</para>
<programlisting>&lt;r:caoFactory id="calculatorService"
remoteTargetName="prototypeCalculator"
serviceUrl="tcp://localhost:8005" /&gt;</programlisting>
<section>
<title>Applying AOP advice to client side CAO objects.</title>
<para>Applying AOP advice to a client side CAO object is done just like
any other object. Simply use the id of the object created by the
<classname>CaoFactoryObject</classname> as the AOP target, i.e.
'calculatorService' in the previous example.</para>
</section>
</section>
<section id="remoting-schema">
<title>XML Schema for configuration</title>
<para>Please install the XSD schemas into VS.NET as described in <xref
linkend="vsnet" />. XML intellisense for the attributes of the
saoExporter, caoExporter and caoFactory should be self explanatory as they
mimic the standard property names used to configure .NET remote
objects.</para>
</section>
<section id="remoting-additional">
<title>Additional Resources</title>
<para>Two articles that describe the process of creating a standard SAO
factory for returning CAO objects are <ulink
url="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpatterns/html/ImpBrokerClient.asp">Implementing
Broker with .NET Remoting Using Client-Activated Objects</ulink> on MSDN
and <ulink
url="http://www.glacialcomponents.com/ArticleDetail/CAOGuide.aspx">Step by
Step guide to CAO creation through SAO class factories</ulink> on Glacial
Components website.</para>
</section>
</chapter>

View File

@@ -0,0 +1,362 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="resources">
<title>Resources</title>
<section id="objects-iresource">
<title>Introduction</title>
<para>The <literal>IResource</literal> interface contained in the
<literal>Spring.Core.IO</literal> namespace provides a common interface to
describe and access data from diverse resource locations. This abstraction
lets you treat the <classname>InputStream</classname> from a file and from
a URL in a polymorphic and protocol-independent manner... the .NET BCL
does not provide such an abstraction. The <literal>IResource</literal>
interface inherits from <literal>IInputStream</literal> that provides a
single property <literal>Stream InputStream</literal>. The
<literal>IResource</literal> interface adds descriptive information about
the resource via a number of additional properties. Several
implementations for common resource locations, i.e. file, assembly, uri,
are provided and you may also register custom IResource
implementations.</para>
</section>
<section>
<title>The <interfacename>IResource</interfacename> interface</title>
<para>The IResource interface is shown below</para>
<programlisting>public interface IResource : IInputStreamSource
{
bool IsOpen { get; }
Uri Uri { get; }
FileInfo File { get; }
string Description { get; }
bool Exists { get; }
IResource CreateRelative(string relativePath);
}</programlisting>
<table frame="all">
<title>IResource Properties</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="2*" />
<colspec colname="c2" colwidth="5*" />
<thead>
<row>
<entry>Property</entry>
<entry>Explanation</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>InputStream</literal></entry>
<entry>Inherited from IInputStream. Opens and returns a
<classname>System.IO.Stream</classname>. It is expected that each
invocation returns a fresh Stream. It is the responsibility of the
caller to close the stream.</entry>
</row>
<row>
<entry><literal>Exists</literal></entry>
<entry>returns a boolean indicating whether this resource actually
exists in physical form.</entry>
</row>
<row>
<entry><literal>IsOpen</literal></entry>
<entry>returns a boolean indicating whether this resource
represents a handle with an open stream. If true, the InputStream
cannot be read multiple times, and must be read once only and then
closed to avoid resource leaks. Will be false for all usual
resource implementations, with the exception of
<interfacename>InputStreamResource</interfacename>.</entry>
</row>
<row>
<entry><literal>Description</literal></entry>
<entry>Returns a description of the resource, such as the fully
qualified file name or the actual URL.</entry>
</row>
<row>
<entry><literal>Uri</literal></entry>
<entry>The Uri representation of the resource.</entry>
</row>
<row>
<entry><literal>File</literal></entry>
<entry>Returns a <classname>System.IO.FileInfo</classname> for
this resource if it can be resolved to an absolute file
path.</entry>
</row>
</tbody>
</tgroup>
</table>
<para>and the methods</para>
<table frame="all">
<title>IResource Methods</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="2*" />
<colspec colname="c2" colwidth="5*" />
<thead>
<row>
<entry>Method</entry>
<entry>Explanation</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>IResource CreateRelative (string
relativePath)</literal></entry>
<entry>Creates a resource relative to this resource using relative
path like notation (./ and ../).</entry>
</row>
</tbody>
</tgroup>
</table>
<para>You can obtain an actual URL or File object representing the
resource if the underlying implementation is compatible and supports that
functionality.</para>
<para>The Resource abstraction is used extensively in Spring itself, as an
argument type in many method signatures when a resource is needed. Other
methods in some Spring APIs (such as the constructors to various
<interfacename>IApplicationContext</interfacename> implementations), take
a String which is used to create a Resource appropriate to that context
implementation</para>
<para>While the Resource interface is used a lot with Spring and by
Spring, it's actually very useful to use as a general utility class by
itself in your own code, for access to resources, even when your code
doesn't know or care about any other parts of Spring. While this couples
your code to Spring, it really only couples it to this small set of
utility classes and can be considered equivalent to any other library you
would use for this purpose</para>
</section>
<section>
<title>Built-in IResource implementations</title>
<para>The resource implementations provided are <itemizedlist
spacing="compact">
<listitem>
<literal>AssemblyResource</literal>
accesses data stored as .NET resources inside an assembly. Uri syntax is
<literal>assembly://&lt;AssemblyName&gt;/&lt;NameSpace&gt;/&lt;ResourceName&gt;</literal>
</listitem>
<listitem>
<literal>ConfigSectionResource</literal>
accesses Spring.NET configuration data stored in a custom configuration section in the .NET application configuration file (i.e. App.config). Uri syntax is
<literal>config://&lt;path to section&gt;</literal>
</listitem>
<listitem>
<literal>FileSystemResource</literal>
accesses file system data. Uri syntax is
<literal>file://&lt;filename&gt;</literal>
</listitem>
<listitem>
<literal>InputStreamResource</literal>
a wrapper around a raw
<classname>System.IO.Stream</classname>
. Uri syntax is not supported.
</listitem>
<listitem>
<literal>UriResource</literal>
accesses data from the standard System.Uri protocols such as http and https. In .NET 2.0 you can use this also for the ftp protocol. Standard Uri syntax is supported.
</listitem>
</itemizedlist> Refer to the MSDN documentation for more information on
<ulink
url="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemurimemberstopic.asp">supported
Uri scheme types</ulink>.</para>
<section>
<title>Registering custom IResource implementations</title>
<para>The configuration section handler,
<classname>ResourceHandlersSectionHandler</classname>, is used to
register any custom <interfacename>IResource</interfacename>
implementations you have created. In the configuration section you list
the type of <interfacename>IResource</interfacename> implementation and
the protocol prefix. Your custom
<interfacename>IResource</interfacename> implementation must provide a
constructor that takes a string as it's sole argument that represents
the URI string. Refer to the SDK documentation for
<classname>ResourceHandlersSectionHandler</classname> for more
information. An example of the
<classname>ResourceHandlersSectionHandler</classname> is shown below for
a fictional <interfacename>IResource</interfacename> implementation that
interfaces with a database.</para>
<programlisting>&lt;configuration&gt;
&lt;configSections&gt;
&lt;sectionGroup name="spring"&gt;
&lt;section name='context' type='Spring.Context.Support.ContextHandler, Spring.Core'/&gt;
<emphasis role="bold"> &lt;section name="resourceHandlers"
type="Spring.Context.Support.ResourceHandlersSectionHandler, Spring.Core"/&gt;</emphasis>
&lt;/sectionGroup&gt;
&lt;/configSections&gt;
&lt;spring&gt;
<emphasis role="bold"> &lt;resourceHandlers&gt;
&lt;handler protocol="db" type="MyCompany.MyApp.Resources.MyDbResource, MyAssembly"/&gt;
&lt;/resourceHandlers&gt;</emphasis>
&lt;context&gt;
&lt;resource uri="db://user:pass@dbName/MyDefinitionsTable"/&gt;
&lt;/context&gt;
&lt;/spring&gt;
&lt;/configuration&gt;</programlisting>
</section>
</section>
<section>
<title>The <interfacename>IResourceLoader</interfacename></title>
<para>To load resources given their Uri syntax, an implementation of the
<classname>IResourceLoader</classname> is used. The default implementation
is <classname>ConfigurableResourceLoader</classname>. Typically you will
not need to access this class directly since the
<classname>IApplicationContext</classname> implements the
<classname>IResourceLoader</classname> interface that contains the single
method <literal>IResource GetResource(string location)</literal>. The
provided implementations of <literal>IApplicationContext</literal>
delegate this method to an instance of
<classname>ConfigurableResourceLoader</classname> which supports the Uri
protocols/schemes listed previously. If you do not specify a protocol then
the file protocol is used. The following shows some sample
usage.<programlisting>IResource resource = appContext.GetResource("http://www.springframework.net/license.html");
resource = appContext.GetResource("assembly://Spring.Core.Tests/Spring/TestResource.txt");
resource = appContext.GetResource("https://sourceforge.net/");
resource = appContext.GetResource("file:///C:/WINDOWS/ODBC.INI");
StreamReader reader = new StreamReader(resource.InputStream);
Console.WriteLine(reader.ReadToEnd());</programlisting> Other protocols can be
registered along with a new implementations of an IResource that must
correctly parse a Uri string in its constructor. An example of this can be
seen in the <literal>Spring.Web</literal> namespace that uses
<literal>Server.MapPath</literal> to resolve the filename of a
resource.</para>
<para>The <literal>CreateRelative</literal> method allows you to easily
load resources based on a relative path name. In the case of relative
assembly resources, the relative path navigates the namespace within an
assembly. For example: <programlisting>IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Spring/TestResource.txt");
IResource res2 = res.CreateRelative("./IO/TestIOResource.txt");</programlisting>
This loads the resource <literal>TestResource.txt</literal> and then
navigates to the <literal>Spring.Core.IO</literal> namespace and loads the
resource <literal>TestIOResource.txt</literal></para>
</section>
<section>
<title>The <interfacename>IResourceLoaderAware</interfacename>
interface</title>
<para>The <interfacename>IResourceLoaderAware</interfacename> interface is
a special marker interface, identifying objects that expect to be provided
with a <interfacename>IResourceLoader</interfacename> reference.</para>
<programlisting>public interface IResourceLoaderAware
{
IResourceLoader ResourceLoader
{
set;
get;
}
}</programlisting>
<para>When a class implements
<interfacename>IResourceLoaderAware</interfacename> and is deployed into
an application context (as a Spring-managed object), it is recognized as
<interfacename>IResourceLoaderAware</interfacename> by the application
context. The application context will then invoke the ResourceLoader
property, supplying itself as the argument (remember, all application
contexts in Spring implement the
<interfacename>IResourceLoader</interfacename> interface).</para>
<para>Of course, since an
<interfacename>IApplicationContext</interfacename> is a
<interfacename>IResourceLoader</interfacename>, the object could also
implement the <interfacename>IApplicationContextAware</interfacename>
interface and use the supplied application context directly to load
resources, but in general, it's better to use the specialized
<interfacename>IResourceLoader</interfacename> interface if that's all
that's needed. The code would just be coupled to the resource loading
interface, which can be considered a utility interface, and not the whole
Spring <interfacename>IApplicationContext</interfacename>
interface.</para>
</section>
<section>
<title>Application contexts and <interfacename>IResource</interfacename>
paths</title>
<para>An application context constructor (for a specific application
context type) generally takes a string or array of strings as the location
path(s) of the resource(s) such as XML files that make up the definition
of the context. For example, you can create an XmlApplicationContext from
two resources as follows:</para>
<programlisting>IApplicationContext context = new XmlApplicationContext(
"file://objects.xml", "assembly://MyAssembly/MyProject/objects-dal-layer.xml");
</programlisting>
</section>
</chapter>

View File

@@ -0,0 +1,209 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="services">
<title>.NET Enterprise Services</title>
<sect1 id="services-introduction">
<title>Introduction</title>
<para>Spring's .NET Enterprise Services support allows you to export a
'plain .NET object' as a .NET Remoted object. By "plain .NET object" we
mean classes that do not inherit from a specific infrastructure base class
such as ServicedComponent..</para>
<para>You can leverage the IoC container to configure the exporter and
service endpoints. You may also opt to not use the IoC container to
configure the objects and use Spring's .NET Enterprise Services classes
Programatically, as you would with any third party library.</para>
</sect1>
<sect1 id="services-servicedcomponents">
<title>Serviced Components</title>
<para>Services components in .NET are able to use COM+ services such as
declarative and distributed transactions, role based security, object
pooling messaging. To access these services your class needs to derive
from the class
<classname>System.EnterpriseServices.ServicedComponent</classname>, adorn
your class and assemblies with relevant attributes, and configure your
application by registering your serviced components with the COM+ catalog.
The overall landscape of accessing and using COM+ services within .NET
goes by the name .NET Enterprise Services.</para>
<para>Many of these services can be provided without the need to derive
from a ServicedComponent though the use of Spring's Aspect-Oriented
Programming functionality. Nevertheless, you may be interested in
exporting your class as a serviced component and having client access that
component in a location transparent manner. By using Spring's
<literal>ServicedComponentExporter</literal>,
<literal>EnterpriseServicesExporter</literal> and
<literal>ServicedComponentFactory</literal> you can easily create and
consume serviced components without having your class inherit from
<literal>ServicedComponent</literal> and automate the manual deployment
process that involves strongly signing your assembly and using the
<literal>regsvcs</literal> utility.</para>
<para>Note that the following sections do not delve into the details of
programming .NET Enterprise Services. An excellent reference for such
information is Christian Nagel's "Enterprise Services with the .NET
Framework" Spring.NET includes an example of using these classes, the
'calculator' example. More information can be found in the section, .<link
linkend="entsvc-example">NET Enterprise Services example.</link></para>
</sect1>
<sect1 id="services-serverside">
<title>Server Side</title>
<para>One of the main challenges for the exporting of a serviced component
to the host is the need for them to be contained within a physical
assembly on the file system in order to be registered with the COM+
Services. To make things more complicated, this assembly has to be
strongly named before it can be successfully registered.</para>
<para>Spring provides two classes that allow all of this to happen.
<itemizedlist>
<listitem>
<classname>Spring.Enterprise.ServicedComponentExporter</classname>
is responsible for exporting a single component and making sure that it derives from ServicedComponent class. It also allows you to specify class-level and method-level attributes for the component in order to define things such as transactional behavior, queuing, etc.
</listitem>
<listitem>
<classname>Spring.Enterprise.EnterpriseServicesExporter</classname>
corresponds to a COM+ application, and it allows you to specify list of components that should be included in the application, as well as the application name and other assembly-level attributes
</listitem>
</itemizedlist></para>
<para>Let's say that we have a simple service interface and implementation
class, such as these:</para>
<programlisting>namespace MyApp.Services
{
public interface IUserManager
{
User GetUser(int userId);
void SaveUser(User user);
}
public class SimpleUserManager : IUserManager
{
private IUserDao userDao;
public IUserDao UserDao
{
get { return userDao; }
set { userDao = value; }
}
public User GetUser(int userId)
{
return UserDao.FindUser(userId);
}
public void SaveUser(User user)
{
if (user.IsValid)
{
UserDao.SaveUser(user);
}
}
}
} </programlisting>
<para>And the corresponding object definition for it in the application
context config file:</para>
<programlisting>&lt;object id="userManager" type="MyApp.Services.SimpleUserManager"&gt;
&lt;property name="UserDao" ref="userDao"/&gt;
&lt;/object&gt;</programlisting>
<para>Let's say that we want to expose user manager as a serviced
component so we can leverage its support for transactions. First we need
to export our service using the exporter
<literal>ServicedComponentExporter</literal> as shown below</para>
<programlisting>&lt;object id="MyApp.EnterpriseServices.UserManager" type="Spring.Enterprise.ServicedComponentExporter, Spring.Services"&gt;
&lt;property name="TargetName" value="userManager"/&gt;
&lt;property name="TypeAttributes"&gt;
&lt;list&gt;
&lt;object type="System.EnterpriseServices.TransactionAttribute, System.EnterpriseServices"/&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="MemberAttributes"&gt;
&lt;dictionary&gt;
&lt;entry key="*"&gt;
&lt;list&gt;
&lt;object type="System.EnterpriseServices.AutoCompleteAttribute, System.EnterpriseServices"/&gt;
&lt;/list&gt;
&lt;/entry&gt;
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>The exporter defined above will create a composition proxy for our
SimpleUserManager class that extends <literal>ServicedComponent</literal>
and delegates method calls to SimpleUserManager instance. It will also
adorn the proxy class with a <literal>TransactionAtribute</literal> and
all methods with an <literal>AutoCompleteAttribute</literal>.</para>
<para>The next thing we need to do is configure an exporter for the COM+
application that will host our new component:</para>
<programlisting>&lt;object id="MyComponentExporter" type="Spring.Enterprise.EnterpriseServicesExporter, Spring.Services"&gt;
&lt;property name="ApplicationName" value="My COM+ Application"/&gt;
&lt;property name="Description" value="My enterprise services application."/&gt;
&lt;property name="AccessControl"&gt;
&lt;object type="System.EnterpriseServices.ApplicationAccessControlAttribute, System.EnterpriseServices"&gt;
&lt;property name="AccessChecksLevel" value="ApplicationComponent"/&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;property name="Roles"&gt;
&lt;list&gt;
&lt;value&gt;Admin : Administrator role&lt;/value&gt;
&lt;value&gt;User : User role&lt;/value&gt;
&lt;value&gt;Manager : Administrator role&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="Components"&gt;
&lt;list&gt;
&lt;ref object="MyApp.EnterpriseServices.UserManager"/&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="Assembly" value="MyComPlusApp"/&gt;
&lt;/object&gt;</programlisting>
<para>This exporter will put all proxy classes for the specified list of
components into the specified assembly, sign the assembly, and register it
with the specified COM+ application name. If application does not exist it
will create it and configure it using values specified for Description,
AccessControl and Roles properties.</para>
</sect1>
<sect1 id="services-clientside">
<title>Client Side</title>
<para>Because serviced component classes are dynamically generated and
registered, you cannot instantiate them in your code using the new
operator. Instead, you need to use
<classname>Spring.Enterprise.ServicedComponentFactory</classname>
definition, which also allows you to specify the configuration template
for the component as well as the name of the remote server the component
is running on, if necessary. An example is shown below</para>
<programlisting>&lt;object id="enterpriseUserManager" type="Spring.Enterprise.ServicedComponentFactory, Spring.Services"&gt;
&lt;property name="Name" value="MyApp.EnterpriseServices.UserManager"/&gt;
&lt;property name="Template" value="userManager"/&gt;
&lt;/object&gt;</programlisting>
<para>You can then inject this instance of the IUserManager into a client
class and use it just like you would use original SimpleUserManager
implementation. As you can see, by coding your services as plain .Net
objects, against well defined service interfaces, you gain easy
pluggability for your service implementation though this configuration,
while keeping the core business logic in a technology agnostic PONO, i.e.
Plain Ordinary .Net Object.</para>
</sect1>
</chapter>

View File

@@ -0,0 +1,321 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="springair">
<title>SpringAir - Reference Application</title>
<sect1>
<title>Introduction</title>
<para>The SpringAir sample application demonstrates a selection of
Spring.NET's powerful features making a .NET programmer's life easier. It
demonstrates the following features of Spring.Web</para>
<itemizedlist>
<listitem>
<para>Spring.NET IoC container configuration</para>
</listitem>
<listitem>
<para>Dependency Injection as applied to ASP.NET pages</para>
</listitem>
<listitem>
<para>Master Page support</para>
</listitem>
<listitem>
<para>Web Service support</para>
</listitem>
<listitem>
<para>Bi-directional data binding</para>
</listitem>
<listitem>
<para>Declarative validation of domain objects</para>
</listitem>
<listitem>
<para>Internationalization</para>
</listitem>
<listitem>
<para>Result mapping to better encapsulate page navigation
flows</para>
</listitem>
</itemizedlist>
<para>The application models a flight reservation system where you can
browse flights, book a trip and even attach your own clients by leveraging
the web services exposed by the SpringAir application.</para>
<para>All pages within the application are fully Spring managed.
Dependencies get injected as configured within a Spring Application
Context. For NET 1.1 it shows how to apply centrally managed layouts to
all pages in an application by using master pages - a well-known feature
from NET 2.0.</para>
<para>When selecting your flights, you are already experiencing a fully
localized form. Select your preferred language from the bottom of the form
and see, how the new language is immediately applied. As soon as you
submit your desired flight, the submitted values are automatically unbound
from the form onto the application's data model by leveraging Spring.Web's
support for Data Binding. With Data Binding you can easily associate
properties on your PONO model with elements on your ASP.NET form.</para>
</sect1>
<sect1>
<title>Getting Started</title>
<para>The application is located in the installation directory under
'examples/SpringAir. The directory 'SpringAir.Web.2003' contains the .NET
1.1 version of the application and the directory 'SpringAir.Web.2005'
contains the .NET 2.0 version. For .NET 1.1 you will need to create a
virtual directory named 'SpringAir.2003' using IIS Administrator and point
it to the following directory
examples\Spring\SpringAir\src\SpringAir.Web.2003. The solution file for
.NET 1.1 is examples\Spring\SpringAir\SpringAir.2003.sln. For .NET 2.0
simply open the solution examples\Spring\SpringAir\SpringAir.2005.sln. Set
your startup project to be SpringAir.Web and the startpage to
.\Web\Home.aspx</para>
</sect1>
<sect1>
<title>Container configuration</title>
<para>The web project's top level Web.config configures the IoC container
that is used within the web application. You do not need to explicitly
instantiate the IoC container. The important parts of that configuration
are shown below</para>
<programlisting>&lt;spring&gt;
&lt;parsers&gt;
&lt;parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" /&gt;
&lt;/parsers&gt;
&lt;context&gt;
&lt;resource uri="~/Config/Aspects.xml"/&gt;
&lt;resource uri="~/Config/Web.xml"/&gt;
&lt;resource uri="~/Config/Services.xml"/&gt;
&lt;!-- TEST CONFIGURATION --&gt;
&lt;resource uri="~/Config/Test/Services.xml"/&gt;
&lt;resource uri="~/Config/Test/Dao.xml"/&gt;
&lt;!-- PRODUCTION CONFIGURATION --&gt;
&lt;!--
&lt;resource uri="~/Config/Production/Services.xml"/&gt;
&lt;resource uri="~/Config/Production/Dao.xml"/&gt;
--&gt;
&lt;/context&gt;
&lt;/spring&gt;</programlisting>
<para>In this example there are separate configuration files for test and
production configuration. The Services.xml file is in fact the same
between the two, and the example will be refactored in future to remove
that duplication. The Dao layer in the test configuration is an in-memory
version, faking database access, whereas the production version uses an
ADO.NET based solution.</para>
<para>The pages that comprise the application are located in the directory
'Web/BookTrip'. In that directory is another Web.config that is
responsible for configuring that directory's .aspx pages. There are three
main pages in the flow of the application.</para>
<itemizedlist>
<listitem>
<para>TripForm - form to enter in airports, times, round-trip or
one-way</para>
</listitem>
<listitem>
<para>Suggested Flights - form to select flights</para>
</listitem>
<listitem>
<para>ReservationConfirmationPage - your confirmation ID from the
booking process.</para>
</listitem>
</itemizedlist>
<para>The XML configuration to configure the TripForm form is shown
below</para>
<programlisting> &lt;object type="TripForm.aspx" parent="standardPage"&gt;
&lt;property name="BookingAgent" ref="bookingAgent" /&gt;
&lt;property name="AirportDao" ref="airportDao" /&gt;
&lt;property name="TripValidator" ref="tripValidator" /&gt;
&lt;property name="Results"&gt;
&lt;dictionary&gt;
&lt;entry key="displaySuggestedFlights" value="redirect:SuggestedFlights.aspx" /&gt;
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>As you can see the various services it needs are set using standard
DI techniques. The Results property externalizes the page flow,
redirecting to the next page in the flow, SuggestedFlights. The 'parent'
attribute lets this page inherit properties from a template. The is
located in the top level Web.config file, packaged under the Config
directory. The standardPage sets up properties of Spring's base page
class, from which all the pages in this application inherit from. (Note
that to perform only dependency injection on pages you do not need to
inherit from Spring's Page class).</para>
</sect1>
<sect1>
<title>Bi-directional data binding</title>
<para>The TripForm page demonstrates the bi-directional data binding
features. A Trip object is used to back the information of the form. The
family of methods that are overridden to support the bi-directional data
binding are listed below.</para>
<programlisting> protected override void InitializeModel()
{
trip = new Trip();
trip.Mode = TripMode.RoundTrip;
trip.StartingFrom.Date = DateTime.Today;
trip.ReturningFrom.Date = DateTime.Today.AddDays(1);
}
protected override void LoadModel(object savedModel)
{
trip = (Trip)savedModel;
}
protected override object SaveModel()
{
return trip;
}
protected override void InitializeDataBindings()
{
BindingManager.AddBinding("tripMode.Value", "Trip.Mode");
BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.AirportCode");
BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.AirportCode");
BindingManager.AddBinding("leavingFromDate.SelectedDate", "Trip.StartingFrom.Date");
BindingManager.AddBinding("returningOnDate.SelectedDate", "Trip.ReturningFrom.Date");
}</programlisting>
<para>This is all you need to set up in order to have values from the Trip
object 'marshaled' to and from the web controls. The
InitializeDataBindings method set this up, using the Spring Expression
Language to define the UI element property that is associate with the
model (Trip) property.</para>
</sect1>
<sect1>
<title>Declarative Validation</title>
<para>The method called when the Search button is clicked will perform
validation. If validation succeeds as well as additional business logic
checks, the next page in the flow is loaded. This is shown in the code
below. Notice how much cleaner and more business focused the code reads
than if you were using standard ASP.NET APIs.</para>
<programlisting> protected void SearchForFlights(object sender, EventArgs e)
{
if (Validate(trip, tripValidator))
{
FlightSuggestions suggestions = this.bookingAgent.SuggestFlights(Trip);
if (suggestions.HasOutboundFlights)
{
Session[Constants.SuggestedFlightsKey] = suggestions;
SetResult(DisplaySuggestedFlights);
}
}
}</programlisting>
<para>The 'Validate' method of the page takes as arguments the object to
validate and a IValidator instance. The TripForm property TripValidator is
set via dependency injection (as shown above). The validation logic is
defined declaratively in the XML configuration file and is shown
below.</para>
<programlisting> &lt;v:group id="tripValidator"&gt;
&lt;v:required id="departureAirportValidator" test="StartingFrom.AirportCode"&gt;
&lt;v:message id="error.departureAirport.required" providers="departureAirportErrors, validationSummary"/&gt;
&lt;/v:required&gt;
&lt;v:group id="destinationAirportValidator"&gt;
&lt;v:required test="ReturningFrom.AirportCode"&gt;
&lt;v:message id="error.destinationAirport.required" providers="destinationAirportErrors, validationSummary"/&gt;
&lt;/v:required&gt;
&lt;v:condition test="ReturningFrom.AirportCode != StartingFrom.AirportCode" when="ReturningFrom.AirportCode != ''"&gt;
&lt;v:message id="error.destinationAirport.sameAsDeparture" providers="destinationAirportErrors, validationSummary"/&gt;
&lt;/v:condition&gt;
&lt;/v:group&gt;
&lt;v:group id="departureDateValidator"&gt;
&lt;v:required test="StartingFrom.Date"&gt;
&lt;v:message id="error.departureDate.required" providers="departureDateErrors, validationSummary"/&gt;
&lt;/v:required&gt;
&lt;v:condition test="StartingFrom.Date &gt;= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue"&gt;
&lt;v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"/&gt;
&lt;/v:condition&gt;
&lt;/v:group&gt;
&lt;v:group id="returnDateValidator" when="Mode == 'RoundTrip'"&gt;
&lt;v:required test="ReturningFrom.Date"&gt;
&lt;v:message id="error.returnDate.required" providers="returnDateErrors, validationSummary"/&gt;
&lt;/v:required&gt;
&lt;v:condition test="ReturningFrom.Date &gt;= StartingFrom.Date" when="ReturningFrom.Date != DateTime.MinValue"&gt;
&lt;v:message id="error.returnDate.beforeDeparture" providers="returnDateErrors, validationSummary"/&gt;
&lt;/v:condition&gt;
&lt;/v:group&gt;
&lt;/v:group&gt;</programlisting>
<para>The validation logic has 'when' clauses so that return dates can be
ignored if the Mode property of the Trip object is set to
'RoundTrip'.</para>
</sect1>
<sect1>
<title>Internationalization</title>
<para>Both image and text based internationalization are supported. You
can see this in action by clicking on the English, Srpski, or Српски links
on the bottom of the page.</para>
</sect1>
<sect1>
<title>Web Services</title>
<para>The class BookingAgent that was used by the TripForm class is a
standard .NET class, i.e no WebMethod attributes are on any of its
methods. Spring can expose this object as a web service by declaring the
following XML defined in the top level Config/Services.xml file</para>
<programlisting> &lt;object id="bookingAgentWebService" type="Spring.Web.Services.WebServiceExporter, Spring.Web"&gt;
&lt;property name="TargetName" value="bookingAgent"/&gt;
&lt;property name="Name" value="BookingAgent"/&gt;
&lt;property name="Namespace" value="http://SpringAir/WebServices"/&gt;
&lt;property name="Description" value="SpringAir Booking Agent Web Service"/&gt;
&lt;property name="MemberAttributes"&gt;
&lt;dictionary&gt;
&lt;entry key="SuggestFlights"&gt;
&lt;object type="System.Web.Services.WebMethodAttribute, System.Web.Services"&gt;
&lt;property name="Description" value="Gets those flight suggestions that are applicable for the supplied trip."/&gt;
&lt;/object&gt;
&lt;/entry&gt;
&lt;entry key="Book"&gt;
&lt;object type="System.Web.Services.WebMethodAttribute, System.Web.Services"&gt;
&lt;property name="Description" value="Goes ahead and actually books what up until this point has been a transient reservation."/&gt;
&lt;/object&gt;
&lt;/entry&gt;
&lt;entry key="GetAirportList"&gt;
&lt;object type="System.Web.Services.WebMethodAttribute, System.Web.Services"&gt;
&lt;property name="Description" value="Return a collection of all those airports that can be used for the purposes of booking."/&gt;
&lt;/object&gt;
&lt;/entry&gt;
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para></para>
</sect1>
</chapter>

Binary file not shown.

View File

@@ -0,0 +1,162 @@
<%@ CodeTemplate %>
<%@ Import Namespace="System.IO" %>
<%@ Assembly Name="AgileDocs.Core" %>
<%@ Import Namespace="AgileDocs.Core" %>
<script runat="template">
string Example(string example, string what)
{
return "<programlisting format='linespecific' xml:space='preserve'>"
+ XmlPeek.HtmlEncode (
XmlPeek.ExtractAndQueryXPath(
Path.Combine("test/Spring/Spring.Core.Tests/Data/PathMatcher", "Examples.test"),
String.Format("examples/example[@name='{0}']/{1}", example, what),
new ExtractXml("#", null)))
+ "</programlisting>";
}
</script>
<chapter id="misc">
<title>Spring.NET miscellanea</title>
<sect1>
<title>Introduction</title>
<para>
This chapter contains miscellanea information on features, goodies, caveats
that does not belong to any paricular area.
</para>
</sect1>
<sect1>
<title>PathMatcher</title>
<para>
<emphasis>Note, Spring.Util.PathMatcher is
currently only available in CVS, not the RC3 release. If you want to use these feature
please get the code from CVS
<ulink url="http://opensource.atlassian.com/confluence/spring/display/NET/Project+Structure">(instructions)</ulink>
or from the download section of the
Spring.NET website that contains an .zip with the full CVS tree.
</emphasis>
</para>
<para>
<literal>Spring.Util.PathMatcher</literal> provides <literal>Ant/NAnt</literal>-like path name matching
features.
</para>
<para>To do the match, you use the method:
<programlisting format='linespecific' xml:space='preserve'><%=
XmlPeek.HtmlEncode (
XmlPeek.ExtractAndQueryXPath(
"src/Spring/Spring.Core/Util/PathMatcher.cs",
"fragments/fragment[@name='match-method']"))
%></programlisting>
</para>
<para>If you want to decide if case is important or not use the method:
<programlisting format='linespecific' xml:space='preserve'><%=
XmlPeek.HtmlEncode (
XmlPeek.ExtractAndQueryXPath(
"src/Spring/Spring.Core/Util/PathMatcher.cs",
"fragments/fragment[@name='match-method-nocase']"))
%></programlisting>
</para>
<sect2>
<title>General rules</title>
<para>
To build your pattern, you use the <literal>*</literal>, <literal>?</literal>
and <literal>**</literal> building blocks:
<itemizedlist spacing="compact">
<listitem>
<para><literal>*</literal>: matches any number of non slash
characters;
</para>
</listitem>
<listitem>
<para><literal>?</literal>: matches exactly 1 (one) non slash/dot
character;
</para>
</listitem>
<listitem>
<para><literal>**</literal>: matches any subdirectory, without
taking care of the depth;
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title>Matching filenames</title>
<para>
A file name can be matched using the following
notation:
<%= Example("filename", "pattern") %>
matches:
<%= Example("filename", "match") %>
does not match:
<%= Example("filename", "dont.match") %>
</para>
<para>
The classical all files pattern:
<%= Example("filename-all", "pattern") %>
matches:
<%= Example("filename-all", "match") %>
does not match:
<%= Example("filename-all", "dont.match") %>
</para>
</sect2>
<sect2>
<title>Matching subdirectories</title>
<para>
A directory name can be matched at any depth level using the following
notation:
<%= Example("subdir", "pattern") %>
That pattern matches the following paths:
<%= Example("subdir", "match") %>
but does not match these:
<%= Example("subdir", "dont.match") %>
</para>
<para>
You can compose subdirectories to match like this:
<%= Example("double-subdir", "pattern") %>
That pattern matches the following paths:
<%= Example("double-subdir", "match") %>
but does not match these:
<%= Example("double-subdir", "dont.match") %>
</para>
<para>
You can use more advanced patterns:
<%= Example("subdir-2", "pattern") %>
matches:
<%= Example("subdir-2", "match") %>
does not match:
<%= Example("subdir-2", "dont.match") %>
</para>
</sect2>
<sect2>
<title>Case does matter, slashes don't</title>
<para>
.NET is expected to be a cross-platform development ... platform. So,
<literal>PathMatcher</literal> will match taking care of the case of the pattern
and the case of the path. For example:
<%= Example("case-sensitive", "pattern") %>
matches:
<%= Example("case-sensitive", "match") %>
but does not match:
<%= Example("case-sensitive", "dont.match") %>
</para>
<para>If you do not matter about case, you should explicitly tell the
<literal>Pathmatcher</literal>.</para>
<para>
Back and forward slashes, in the very same cross-platform spirit, are
not important:
<%= Example("slashes", "pattern") %>
matches all the following paths:
<%= Example("slashes", "match") %>
</para>
</sect2>
</sect1>
</chapter>

View File

@@ -0,0 +1,158 @@
<%@ CodeTemplate %>
<%@ Import Namespace="System.IO" %>
<%@ Assembly Name="AgileDocs.Core" %>
<%@ Import Namespace="AgileDocs.Core" %>
<script runat="template">
string PoolExample (string fileName, string xpath)
{
return "<programlisting format='linespecific' xml:space='preserve'>"
+ XmlPeek.HtmlEncode (
XmlPeek.ExtractAndQueryXPath(
Path.Combine("examples/Spring/Spring.Examples.Pool/Examples/Pool", fileName),
String.Format("examples/example[@name='{0}']", xpath)))
+ "</programlisting>";
}
</script>
<sect1>
<title>Pooling example</title>
<para>
The idea is to build an executor backed by a pool of
<literal>QueuedExecutor</literal>: this will show how Spring.NET
provides some useful low-level/high-quality reusable threading and
pooling abstractions.
This executor will provide parallel executions (in our case
<literal>grep</literal>-like file scans). <emphasis>Note: This example
is not in the 1.0.0 release to its use of classes in the Spring.Threading
namespace scheduled for release in Spring 1.1. To access ths example
please get the code from CVS <ulink url="http://opensource.atlassian.com/confluence/spring/display/NET/Project+Structure">(instructions)</ulink> or from the download section of the
Spring.NET website that contains an .zip with the full CVS tree.</emphasis>
</para>
<para>
Some information on <literal>QueuedExecutor</literal> is helpful to
better understand the implementation and to possibly disagree with it.
Keep in mind that the point is to show how to develop your own
object-pool.
</para>
<para>
A <literal>QueuedExecutor</literal> is an executor where
<literal>IRunnable</literal> instances are run serialy by a worker
thread. When you <literal>Execute</literal> with a
<literal>QueuedExecutor</literal>, your request is queued; at some
point in the future your request will be taken and executed by the
worker thread: in case of error the thread is terminated.
However
this executor recreates its worker thread as needed.
</para>
<para>Last but not least, this executor can be shut down in
a few different ways (please refer to the Spring.NET SDK documentation).
Given its simplicity, it is very powerful.
</para>
<para>
The example project <literal>Spring.Examples.Pool</literal> provides
an implementation of a pooled executor, backed by n instances of
<literal>Spring.Threading.QueuedExecutor</literal>: please ignore
the fact that <literal>Spring.Threading</literal> includes already a
very different implementation of a <literal>PooledExecutor</literal>:
here we wanto to use a pool of <literal>QueuedExecutor</literal>s.
</para>
<para>
This executor will be used to implement a parallel
recursive <literal>grep</literal>-like console executable.
</para>
<sect2>
<title>Implementing <literal>Spring.Pool.IPoolableObjectFactory</literal></title>
<para>
In order to use the <literal>SimplePool</literal> implementation,
the first thing to do is to implement the <literal>IPoolableObjectFactory</literal>
interface. This interface is intended to be implemented by objects
that can create the type of objects that should be pooled.
The <literal>SimplePool</literal>
will call the lifecycle methods on <literal>IPoolableObjectFactory</literal> interface
(<literal>MakeObject, ActivateObject, ValidateObject, PassivateObject, and DestroyObject</literal>)
as appropriate when the pool is created, objects are borrowed and returned to the pool, and when
the pool is destroyed.
</para>
<para>
In our case, as already said, we want to to implement a pool
of <literal>QueuedExecutor</literal>. Ok, here the declaration:
<%= PoolExample("PooledQueuedExecutor.cs", "factory-declaration") %>
the first task a factory should do is to create objects:
<%= PoolExample("PooledQueuedExecutor.cs", "make") %>
and should be also able to destroy them:
<%= PoolExample("PooledQueuedExecutor.cs", "destroy") %>
</para>
<para>
When an object is taken from the pool, to satisfy a client request,
may be the object should be activated. We can possibly implement the
activation like this:
<%= PoolExample("PooledQueuedExecutor.cs", "activate") %>
even if a <literal>QueuedExecutor</literal> restarts itself as
needed and so a valid implementation could leave this method empty.
</para>
<para>
After activation, and before the pooled object can be succesfully
returned to the client, it is validated (should the object be
invalid, it will be discarded: this can lead to an empty unusable
pool
<footnote>
<para>You may think that we can provide a smarter
implementation and you are probably right. However, it is not so
difficult to create a new pool in case the old one became unusable.
It could not be your preferred choice but surely it leverages
simplicity and object immutability
</para>
</footnote>).
Here we check that the worker thread exists:
<%= PoolExample("PooledQueuedExecutor.cs", "validate") %>
</para>
<para>
Passivation, symmetrical to activation, is the process a pooled
object is subject to when the object is returned to the pool. In our
case we simply do nothing:
<%= PoolExample("PooledQueuedExecutor.cs", "passivate") %>
</para>
<para>
At this point, creating a pool is simply a matter of creating an
<literal>SimplePool</literal> as in:
<%= PoolExample("PooledQueuedExecutor.cs", "create-pool") %>
</para>
</sect2>
<sect2>
<title>Being smart using pooled objects</title>
<para>
Taking advantage of the <literal>using</literal> keyword seems
to be very important in these <literal>c#</literal> days, so we
implement a very simple helper (<literal>PooledObjectHolder</literal>)
that can allow us to do things like:
<%= PoolExample("PooledQueuedExecutor.cs", "execute") %>
without worrying about obtaining and returning an object from/to the
pool.
</para>
<para>
Here is the implementation:
<%= PoolExample("PooledQueuedExecutor.cs", "holder") %>
</para>
<para>
Please don't forget to destroy all the pooled istances once you have
finished! How? Well using something like this in
<literal>PooledQueuedExecutor</literal>:
<%= PoolExample("PooledQueuedExecutor.cs", "stop") %>
</para>
</sect2>
<sect2>
<title>Using the executor to do a parallel <literal>grep</literal></title>
<para>
The use of the just built executor is quite straigtforward but a
little tricky if we want to really exploit the pool.
<%= PoolExample("Grep.cs", "parallel-grep-class") %>
</para>
<para>
<%= PoolExample("Grep.cs", "parallel-grep-main") %>
</para>
</sect2>
</sect1>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="us-ascii"?>
<codeSmith>
<propertySet>
<!--
<property name="MyPurchaseOrder">
<PurchaseOrder xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.ericjsmith.net">
<ShipTo Name="Eric J. Smith">
<Line1>123 Test Dr.</Line1>
<City>Dallas</City>
<State>TX</State>
<Zip>75075</Zip>
</ShipTo>
<OrderDate>05-01-2003</OrderDate>
<Items>
<OrderedItem>
<ItemName>Item #1</ItemName>
<Description>Item #1 Description</Description>
<UnitPrice>5.45</UnitPrice>
<Quantity>3</Quantity>
<LineTotal>16.35</LineTotal>
</OrderedItem>
<OrderedItem>
<ItemName>Item #2</ItemName>
<Description>Item #2 Description</Description>
<UnitPrice>12.75</UnitPrice>
<Quantity>8</Quantity>
<LineTotal>102.00</LineTotal>
</OrderedItem>
</Items>
<SubTotal>45.23</SubTotal>
<ShipCost>5.23</ShipCost>
<TotalCost>50.46</TotalCost>
</PurchaseOrder>
</property>
-->
</propertySet>
</codeSmith>

View File

@@ -0,0 +1,441 @@
<%@ CodeTemplate %>
<%@ Assembly Name="System.Web" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.Web" %>
<%@ Import Namespace="System.Text" %>
<%@ Assembly Name="AgileDocs.Core" %>
<%@ Import Namespace="AgileDocs.Core" %>
<script runat="template">
string XmlExample (string fileName, string xpath)
{
return "<programlisting format='linespecific'>"
+ XmlPeek.HtmlEncode (
XmlPeek.ExtractAndQueryXPath(fileName, xpath,
new ExtractXml("<!--@", "@-->")))
+ "</programlisting>";
}
string CsExample (string fileName, string xpath)
{
return "<programlisting format='linespecific'>"
+ XmlPeek.HtmlEncode (
XmlPeek.ExtractAndQueryXPath(fileName, xpath,
new ExtractXml("////", null)))
+ "</programlisting>";
}
string EntireFile (string fileName)
{
return "<programlisting format='linespecific'>"
+ XmlPeek.HtmlEncode (
XmlPeek.GetFileContent(fileName))
+ "</programlisting>";
}
</script>
<chapter id="windows-service">
<title>Windows Services</title>
<sect1>
<title>Remarks</title>
<para>
This is functionality that will be included after the
1.0 release. If you want to use these features please get the
code from CVS <ulink url="http://opensource.atlassian.com/confluence/spring/display/NET/Project+Structure"></ulink>
(instructions) or from the download section of the Spring.NET website that contains an
.zip with the full CVS tree.
In addition to this documentation
you can refer to the example program located at
<literal>examples\Spring\Spring.Examples.WindowsService</literal>
to better understand the package. Please check the Spring.NET
<ulink url="http://www.springframework.net/doc/reference/windows-service.html">website</ulink>
for the latest updates to this document.
</para>
</sect1>
<sect1>
<title>Introduction</title>
<para>
Developers usually create Windows Services using the
Visual Studio .NET wizard. While not difficult to do, this
procedure is repetative and does not encourage separation between
infrastructure code (windows service) and application code. This is
generally considered a "bad thing" but you can certainly disagree.
</para>
<para>
As Spring.NET can provide an explicitly managed
initialize/destroy lifecycle for singleton objects, there is
a natural synergy with the lifecycle of a Windows service.
As such, it could be very convenient to expose a Spring application
context as a Windows service. Starting and stopping the service corresponds
to creating and destroying an application context and its
contained objects. This approach provides a high level means to
declare what objects are created and destroyed when developing
a Windows service.
</para>
<para>
To do that, Spring.NET requires the installation of one physical
service able to run as services as many applications as you want - each a
logical independent service in their own application domain.
By default, the deployment and updating of the service can also
be done by copying the relevant executables to a special directory.
</para>
<para>
The executable that at present provides these features is the
<literal>Spring.Services.WindowsService.Process.exe</literal>
assembly. It makes heavy use of classes and interfaces definde in
the <literal>Spring.Services.WindowsService.Common.dll</literal>
assembly. You should reference the common assembly it if you want to
follow the advice on customization contained in the following sections
</para>
<para>
The benefits of this approach, a part from those given by separating
infrastructure code and application code (a field where Spring.NET
tries hard to succeed) is that you can think about installing a new
service at client site by simply dropping a new application assembly
in a remote directory<footnote></footnote>.
</para>
</sect1>
<sect1>
<title>The <literal>Spring.Services.WindowsService.Process.exe</literal> application</title>
<sect2>
<title>Installing</title>
<para>
The installation can be done in two ways, using the .NET SDK
<literal>installutil.exe</literal> tool or using the more mundane
<literal>Spring.Services.WindowsService.Installer.exe</literal>;
while the former is the standard, the latter is probably
more flexible. It allows you to customize the name/display name of the
service and has the ability to install multiple times the same assembly
with different names. This can be useful in a
number of scenarios, especially where you don't like, for some
reasons, to run several different logical services under the
same physical windows service.
</para>
<para><emphasis>
Be aware of the fact that the service will be installed as
running with the system account (installing with a specific
user account seems a bit buggy on Windows XP)
</emphasis></para>
<para>
That said, while <literal>installutil</literal>
<ulink url="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cptools/html/cpconinstallerutilityinstallutilexe.asp">
<citetitle>is documented on its own </citetitle></ulink>,
the command line for
<literal>Spring.Services.WindowsService.Installer.exe</literal>
is as follow:
<programlisting format='linespecific'>Spring.Services.WindowsService.Installer.exe
usage:
install service-exe-path service-display-name service-name
uninstall service-name [i|u] service-exe-path service-display-name service-name</programlisting>
for example, to install, you can invoke it with the following:
<programlisting format='linespecific'>... install Spring.Services.WindowsService.Process.exe "Spring.Service Support" spring-service</programlisting>
and to uninstall it:
<programlisting format='linespecific'>... uninstall spring-service</programlisting>
</para>
</sect2>
<sect2>
<title>Configuration</title>
<para>
The standard .NET <literal>.config</literal> file
can be used to tune some parameters of
<literal>Spring.Services.WindowsService.Process.exe</literal>,
(including log4net settings, for which it is recomended to consult
the log4net documentation).
</para>
<para>
This file also define the context run by this process; here the file in its current beauty:
<%= XmlExample("src/Spring/Spring.Services/App.config", "code") %>
</para>
<para>
As you see, the context is defined in another file: let's review the objects it defines.
</para>
<para>
Firstly, it is worth notice that in order to 'localize' the service (i.e. to know where it is installed to use that directory as
base for the deploy dir as in the above file) you should define an object like this: the name is not
very important, it is important that it is an <classname>IObjectFactoryPostProcessor</classname> and so will be
automatically applied to this application context:
<%= XmlExample("src/Spring/Spring.Services/WindowsService/Process/service-process-definition.xml", "code/localizer") %>
</para>
<para>
In that object definition you can customize the prefix for the following string
<%= CsExample("src/Spring/Spring.Services/WindowsService/Common/Localizer.cs", "code/process.format") %>
but you usually won't need it; the default value is
<%= CsExample("src/Spring/Spring.Services/WindowsService/Common/Localizer.cs", "code/localizer.def.prefix") %>
</para>
<para>
The sole important object defined by this context, i.e. the main object run by the service.
The thing you can (and should) configure is the path to the folder you will use as the deploy location;
the current definition, to avoid the need for a fully qualified path (e.g.: <literal>c:\spring\services</literal>) uses
the properties made available by the <literal>localizer</literal> above:
<%= XmlExample("src/Spring/Spring.Services/WindowsService/Process/service-process-definition.xml", "code/service") %>
</para>
<para>
The above object is then easily remoted using spring remoting utilities (please notice you should tune the remoting configuration
listed in the standard .NET <literal>.config</literal> file, listed above):
<%= XmlExample("src/Spring/Spring.Services/WindowsService/Process/service-process-definition.xml", "code/remoted.service") %>
</para>
</sect2>
</sect1>
<sect1>
<title>Running an application context as a windows service</title>
<para>
If you package an application using the layout and
conventions described here, you'll be able to run an
application context as a Windows Service.
The conventions used are modeled after those used by ASP.NET
and are very easy to follow.
</para>
<para>
As already said, you'll have a Spring.NET application context running in
a dedicated <literal>AppDomain</literal> hosted in a process running
as a windows service: that process is able to run many application contexts
simultaneously.
</para>
<para>A complete application runable as service consists of a
directory containing:
<itemizedlist spacing="compact">
<listitem>
<para>The .NET configuration file
<literal>service.config</literal>:
this file should define your application context.
Moreover this files will be used
by the CLR to configure the application domain
your application will run in, exactly as you expect.
This file has the same role of ASP.NET <literal>Web.config</literal>
file.
</para>
</listitem>
<listitem>
<para>Optional: an xml context file (<literal>watcher.xml</literal>)
defining the watcher for your application.</para>
<para>The watcher controls the automatic redeployment of the
service and is discussed more in the following section.</para>
</listitem>
<listitem>
<para>Recomended: along the lines of ASP.NET convention, a <literal>bin</literal>
subdirectory containing all
the assemblies your application needs; you can of course put
your assemblies in the same directory where you put
<literal>service.config</literal> but this is not encouraged ...
</para>
</listitem>
</itemizedlist>
</para>
<sect2>
<title><literal>service.config</literal></title>
<para>
This is the standard .NET configuration file for the
<literal>AppDomain</literal> that will host your application. It is
semantically equivalent to the ASP.NET <literal>Web.config</literal>
file.
<footnote>
<para>
<literal>log4net</literal> users please notice that (as
of 1.2 beta 9) file appenders, when dealing with a relative
file name, assume it is relative to the application
domain code base. If you use log4net, it is very handy with the mechanics used by
Spring Windows Service as every log file you will specify will
be relative the directory containing the service application.
</para>
</footnote>
</para>
<para>
This file should also define your application context. When the
service is started and stopped, the corresponding lifecycle methods
are called on all the singletons defined. Of course, singletons are
automatically instantiated by the application context when the
service starts. For more information on lifecycles in Spring.NET see
<xref linkend="objects-factory-lifecycle"/>
Here an example taken from the tests:
<%= XmlExample("test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/service.config", "code") %>
</para>
<para>
In this case the context is (again!) defined in another file (author's personal taste...) and the only 'service' is the
<literal>echo</literal> object (there is also a <literal>PropertyPlaceholderConfigurer</literal> just to make the example
more realistic):
<%= XmlExample("test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Echo/service.xml", "code") %>
</para>
<sect3>
<title>Let the application know where it is</title>
<para>
There are some properties you may need at runtime, when your services
will run, and you cannot know in advance. Hopefully, your xml
definition file will allow to find the information it needs using some
predefined variables you can use inside the service definition file
with the standard
NAnt style <literal>${property name}</literal> syntax.</para>
<para>These properies are:
<itemizedlist spacing="compact">
<listitem>
<para><literal>spring.services.application.fullpath</literal>
that will be replaced with the full path of the application's
<literal>AppDomain.BaseDirectory</literal>, i.e., where your
application has been deployed;</para>
</listitem>
<listitem>
<para><literal>spring.services.application.name</literal> that
will be replaced with the name of the subdirectory where the
application has been deployed. Each application is deployed in
its own directory, of course;</para>
</listitem>
</itemizedlist>
</para>
<para>
These properties are accessible only if one defines a localizer in the
context like this (the localizer is a special <literal>IObjectFactoryPostProcessor</literal>:
<%= XmlExample("test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/service.xml", "code/localizer") %>
</para>
<para>
As you can see above, one can easily change the prefix used by that localizer and then write someting like:
<%= XmlExample("test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Simple/service.xml", "code/simple") %>
</para>
</sect3>
</sect2>
<sect2>
<title><literal>watcher.xml</literal> - optional</title>
<para>
This file allows you to optionally define a watcher for your application
that can automatically redeploy it when needed.
</para>
<para>
The important thing to notice is that you can define your own
application watcher, named <literal>watcher</literal>. Here it is used
a watcher that listen for changes on the filesystem, configured to
listen for some changes and to ignore others.
</para>
<para>
You can provide your own implementation defining an object named
<literal>watcher</literal> that implements
<literal>Spring.Services.WindowsService.Common.Deploy.IApplicationWatcher</literal>:
<%= CsExample("src/Spring/Spring.Services/WindowsService/Common/Deploy/IApplicationWatcher.cs", "code/interface") %>
</para>
<para>
Please notice that this interface is currently a movable target and
will probably change before the first official release (this will probably
affect also the way a watcher will know about the application it should
monitor, as shown in a few lines).
</para>
<para>A tipical example of this file is give here:
<%= XmlExample("test/Spring/Spring.Services.Tests/Data/Spring/WindowsService/Cassini/watcher.xml", "code") %>
</para>
<para>
As you can see, if you need it, you can reference the
<literal>Spring.Services.WindowsService.Common.IApplication</literal>
object that your watcher should watch using the name
<literal>.injected.application</literal>.</para>
</sect2>
<sect2>
<title><literal>bin</literal> directory - optional</title>
<para>
This is, by default, the folder where your assemblies are placed
in the same way they are in an ASP.NET application.
</para>
<para>
Putting assemblies there is more a convention and maybe a good
practice (they are isolated from other artifacts, but maybe you will
prefer to use another directory (modify the
<literal>service.config</literal> file accordingly) or the application
directory directly (= <literal>bin</literal> parent).
</para>
<para>
Be aware of the fact that the process in which your application will
run will have its own PATH environmental variable. As such
don't expect to be successfull using dlls imported
with [DllImport] if they are not in the system PATH of the
hosting machine: while it is well known that the CLR fusion
algorithm will not consider the PATH variable, you may be biten
by assemblies using non-system dlls (SQLite and Firebird ADO.NET
providers are good examples).
</para>
<para>Reiterating, one can put assemblies in another directory
under the application directory tree, and write
the .NET configuration file (<literal>service.config</literal>)
accordingly: .NET probing algorithm is always in place.
</para>
<para>
Please notice that it is not required that
your application uses or include any of the Spring.NET assemblies:
any object in any assembly, given it has lifecycle methods, can
be run as a service: non invasive infrastructure support courtesy
of Spring.NET!
</para>
</sect2>
</sect1>
<sect1>
<title>Customizing or extending</title>
<para>
It should be said that support for windows service has been initially
developed with a clear but limited set of 'extension points' in mind,
mainly related to the way you can deploy your services:
deploy location (filesystem, zip archives, mailbox, urls, ...),
(auto-)updating features, and so on.
</para>
<para>
To better understand the following discussion, the following figure
depicts some of the inner details of
<literal>Spring.Services.WindowsService.Process.exe</literal>
at run-time:
<mediaobject>
<imageobject>
<imagedata align="center"
fileref="images/spring.windows-service.png" format="png"/>
</imageobject>
<textobject>
<phrase>Spring.Services.WindowsService.Process.exe run-time details</phrase>
</textobject>
</mediaobject>
</para>
<sect2>
<title>The <literal>.config</literal> file</title>
</sect2>
<para>
The executable <literal>Spring.Services.WindowsService.Process.exe</literal>
is somewhat configured by the corresponding
<literal>.config</literal> file.
Please notice that this file is the most important extension point
for windows service support, and it will probably be made more powerful
and flexible in the future.
</para>
<para>
For applications deployed in the standard way (i.e. on the filesystem
as explained above) the updating features are configured by the
<literal>watcher.xml</literal> file, <emphasis>if present</emphasis>,
as already seen.
</para>
<para>
There should be however, other ways to deploy your applications,
maybe just as zip files dropped somewhere on the web or sent via
e-mail.
</para>
<para>
For these scenarios, your deploy location will be something that
implements
<literal>Spring.Services.WindowsService.Common.Deploy.IDeployLocation</literal>.
<para>
Please notice that, while questionable, it actually entends
<literal>IDisposable</literal> <footnote><para>this has been done
as it is possible that a deploy location holds resources that should be
released, for example network connections, lock files or the like</para></footnote>:
</para>
<%= CsExample("src/Spring/Spring.Services/WindowsService/Common/Deploy/IDeployLocation.cs", "code/interface") %>
<%= CsExample("src/Spring/Spring.Services/WindowsService/Common/Deploy/IDeployEventSource.cs", "code/interface") %>
</para>
</sect1>
</chapter>

View File

@@ -0,0 +1,414 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="testing">
<title>Testing</title>
<section id="testing-introduction">
<title>Introduction</title>
<para>The Spring team considers developer testing to be an absolutely
integral part of enterprise software development. A thorough treatment of
testing in the enterprise is beyond the scope of this chapter; rather, the
focus here is on the value add that the adoption of the IoC principle can
bring to <link linkend="unit-testing">unit testing</link>; and on the
benefits that the Spring Framework provides in <link
linkend="integration-testing">integration testing</link>.</para>
</section>
<section id="unit-testing">
<title>Unit testing</title>
<para>One of the main benefits of Dependency Injection is that your code
is much less likely to have any hidden dependencies on the runtime
environment or other configuration subsystems. This allows for unit tests
to be written in a manner such that the object under test can be simply
instantiated with the <literal>new</literal> operator and have its
dependences set in the unit test code. You can use mock objects (in
conjunction with many other valuable testing techniques) to test your code
in isolation. If you follow the architecture recommendations around Spring
you will find that the resulting clean layering and componentization of
your codebase will naturally faciliate <emphasis>easier</emphasis> unit
testing. For example, you will be able to test service layer objects by
stubbing or mocking DAO interfaces, without any need to access persistent
data while running unit tests.</para>
<para>True unit tests typically will run extremely quickly, as there is no
runtime infrastructure to set up, i.e., database, ORM tool, or whatever.
Thus emphasizing true unit tests as part of your development methodology
will boost your productivity. The upshot of this is that you do not need
this section of the testing chapter to help you write effective
<emphasis>unit</emphasis> tests for your IoC-based applications.</para>
</section>
<section id="integration-testing">
<title>Integration testing</title>
<para>However, it is also important to be able to perform some integration
testing enabling you to test things such as:</para>
<itemizedlist>
<listitem>
<para>The correct wiring of your Spring IoC container contexts.</para>
</listitem>
<listitem>
<para>Data access using ADO.NET or an ORM tool. This would include
such things such as the correctness of SQL statements / or NHibernate
XML mapping files.</para>
</listitem>
</itemizedlist>
<para>The Spring Framework provides first class support for integration
testing in the form of the classes that are packaged in the <filename
class="libraryfile">Spring.Testing.NUnit.dll</filename> library.
<emphasis>Please note that these test classes are NUnit-specific. Support
for mbUnit and VSTS are under consideration for future
versions.</emphasis></para>
<note>
<para>The Spring.Testing.NUnit.dll library is compiled against NUnit
2.4.1. At the time of this writing the latest version of NUnit is 2.4.6.
Note that add-in have their own versions of NUnit they use. For example,
ReSharper 3.0 uses 2.2.8. If you are using the GUI-runner that comes
with NUnit then you should add the following to your .config file, (in
the form of MyAssembly.dll.config)</para>
<programlisting>&lt;runtime&gt;
&lt;assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"&gt;
&lt;dependentAssembly&gt;
&lt;assemblyIdentity name="nunit.framework"
publicKeyToken="96d09a1eb7f44a77"
culture="neutral"/&gt;
&lt;bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535
newVersion="2.4.6.0"/&gt;
&lt;/dependentAssembly&gt;
&lt;/assemblyBinding&gt;
&lt;/runtime&gt;</programlisting>
</note>
<para>The <literal>Spring.Testing.NUnit</literal> namespace provides
valuable NUnit <classname>TestCase</classname> superclasses for
integration testing using a Spring container. Note that as of NUnit 2.4
these can be rewritten in terms of custom attributes via NUnit's new
extensibility mechanism. This will be an additional option in an upcoming
release of Spring.NET and is already present in the Java version of the
Spring framework.</para>
<para>These superclasses provide the following functionality:</para>
<itemizedlist>
<listitem>
<para><link linkend="testing-ctx-management">Spring IoC container
caching</link> between test case execution.</para>
</listitem>
<listitem>
<para>The pretty-much-transparent <link
linkend="testing-fixture-di">Dependency Injection of test fixture
instances</link> (this is nice).</para>
</listitem>
<listitem>
<para><link linkend="testing-tx">Transaction management</link>
appropriate to integration testing (this is even nicer).</para>
</listitem>
<listitem>
<para>A number of Spring-specific <link
linkend="testing-superclasses">inherited instance variables</link>
that are really useful when integration testing.</para>
</listitem>
</itemizedlist>
<section id="testing-ctx-management">
<title>Context management and caching</title>
<para>The <literal><literal>Spring.Testing.NUnit</literal></literal>
package provides support for consistent loading of Spring contexts, and
caching of loaded contexts. Support for the caching of loaded contexts
is important, because if you are working on a large project, startup
time may become an issue - not because of the overhead of Spring itself,
but because the objects instantiated by the Spring container will
themselves take time to instantiate. For example, a project with 50-100
NHibernate mapping files might take 10-20 seconds to load the mapping
files, and incurring that cost before running every single test case in
every single test fixture will lead to slower overall test runs that
could reduce productivity.</para>
<para>To address this issue, the
<classname>AbstractDependencyInjectionSpringContextTests</classname> has
an <literal>protected</literal> property that subclasses must implement
to provide the location of context definition files:</para>
<programlisting>protected abstract string[] ConfigLocations { get; }</programlisting>
<para>Implementations of this method must provide an array containing
the IResource locations of XML configuration metadata used to configure
the application. This will be the same, or nearly the same, as the list
of configuration locations specified in
<literal>App.config/Web.config</literal> or other deployment
configuration.</para>
<para>By default, once loaded, the configuration file set will be reused
for each test case. Thus the setup cost will be incurred only once (per
test fixture), and subsequent test execution will be much faster. In the
unlikely case that a test may 'dirty' the config location, requiring
reloading - for example, by changing an object definition or the state
of an application object - you can call the
<methodname>SetDirty()</methodname> method on
<classname>AbstractDependencyInjectionSpringContextTests</classname> to
cause the test fixture to reload the configurations and rebuild the
application context before executing the next test case.</para>
</section>
<section id="testing-fixture-di">
<title>Dependency Injection of test fixtures</title>
<para>When
<classname>AbstractDependencyInjectionSpringContextTests</classname>
(and subclasses) load your application context, they can optionally
configure instances of your test classes by Setter Injection. All you
need to do is to define instance variables and the corresponding
setters.
<classname>AbstractDependencyInjectionSpringContextTests</classname>
will automatically locate the corresponding object in the set of
configuration files specified in the
<methodname>ConfigLocations</methodname> property.</para>
<para>Consider the scenario where we have a class,
<classname>HibernateTitleDao</classname>, that performs data access
logic for say, the <classname>Title</classname> domain object. We want
to write integration tests that test all of the following areas:</para>
<itemizedlist>
<listitem>
<para>The Spring configuration; basically, is everything related to
the configuration of the <classname>HibernateTitleDao</classname>
object correct and present?</para>
</listitem>
<listitem>
<para>The Hibernate mapping file configuration; is everything mapped
correctly and are the correct lazy-loading settings in place?</para>
</listitem>
<listitem>
<para>The logic of the <classname>HibernateTitleDao</classname>;
does the configured instance of this class perform as
anticipated?</para>
</listitem>
</itemizedlist>
<para>Let's look at the test class itself (we will look at the
configuration immediately afterwards).</para>
<programlisting>[TestFixture]
public class HibernateTitleDaoTests <emphasis role="bold">: AbstractDependencyInjectionSpringContextTests</emphasis> {
<lineannotation>// this instance will be (automatically) dependency injected</lineannotation>
private HibernateTitleDao titleDao;
<lineannotation>// a setter method to enable DI of the 'titleDao' instance variable</lineannotation>
public HibernateTitleDao HibernateTitleDao {
set { titleDao = value; }
}
[Test]
public void LoadTitle() {
Title title = this.titleDao.LoadTitle(10);
Assert.IsNotNull(title);
}
<lineannotation>// specifies the Spring configuration to load for this test fixture</lineannotation>
protected override string[] ConfigLocations {
return new String[] { "assembly://MyAssembly/MyNamespace/daos.xml" };
}
}</programlisting>
<para>The file referenced by the ConfigLocations method
(<literal>'classpath:com/foo/daos.xml'</literal>) looks like
this:</para>
<programlisting>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;objects xmlns="http://www.springframework.net"&gt;
<lineannotation>&lt;!-- this object will be injected into the <classname>HibernateTitleDaoTests</classname> class --&gt;</lineannotation>
&lt;object id="titleDao" type="Spring.Samples.HibernateTitleDao, Spring.Samples"&gt;
&lt;property name="sessionFactory" ref="sessionFactory"/&gt;
&lt;/object&gt;
&lt;object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate"&gt;
<lineannotation>&lt;!-- dependencies elided for clarity --&gt;</lineannotation>
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<para>The
<classname>AbstractDependencyInjectionSpringContextTests</classname>
classes uses <link linkend="objects-factory-autowire"><emphasis>autowire
by type</emphasis></link>. Thus if you have multiple object definitions
of the same type, you cannot rely on this approach for those particular
object. In that case, you can use the inherited
<literal>applicationContext</literal> instance variable, and explicit
lookup using (for example) an explicit call to
<methodname>applicationContext.GetObject("titleDao")</methodname>.</para>
<para>If you don't want dependency injection applied to your test cases,
simply don't declare any set properties. Alternatively, you can extend
the <classname>AbstractSpringContextTests</classname> - the root of the
class hierarchy in the <literal>Spring.Testing.NUnit</literal>
namespace. It merely contains convenience methods to load Spring
contexts, and performs no Dependency Injection of the test
fixture.</para>
<section id="testing-fixture-di-field">
<title>Field level injection</title>
<para>If, for whatever reason, you don't fancy having setter
properties in your test fixtures, Spring can (in this one case) inject
dependencies into <literal>protected</literal> fields. Find below a
reworking of the previous example to use field level injection (the
Spring XML configuration does not need to change, merely the test
fixture).</para>
<programlisting>[TestFixture]
public class HibernateTitleDaoTests <emphasis role="bold">: AbstractDependencyInjectionSpringContextTests</emphasis> {
public HibernateTitleDaoTests() {
<lineannotation> // switch on field level injection</lineannotation>
PopulateProtectedVariables = true;
}
<lineannotation>// this instance will be (automatically) dependency injected</lineannotation>
<lineannotation><emphasis>protected</emphasis></lineannotation> HibernateTitleDao <lineannotation><emphasis>titleDao</emphasis></lineannotation>;
[Test]
public void LoadTitle() {
Title title = this.titleDao.LoadTitle(10);
Assert.IsNotNull(title);
}
<lineannotation>// specifies the Spring configuration to load for this test fixture</lineannotation>
protected override string[] ConfigLocations {
return new String[] { "assembly://MyAssembly/MyNamespace/daos.xml" };
}
}</programlisting>
<para>In the case of field injection, there is no autowiring going on:
the name of your <literal>protected</literal> instances variable(s)
are used as the lookup object name in the configured Spring
container.</para>
</section>
</section>
<section id="testing-tx">
<title>Transaction management</title>
<para>One common issue in tests that access a real database is their
effect on the state of the persistence store. Even when you're using a
development database, changes to the state may affect future tests.
Also, many operations - such as inserting to or modifying persistent
data - cannot be done (or verified) outside a transaction.</para>
<para>The
<classname>AbstractTransactionalDbProviderSpringContextTests</classname>
superclass (and subclasses) exist to meet this need. By default, they
create and roll back a transaction for each test. You simply write code
that can assume the existence of a transaction. If you call
transactionally proxied objects in your tests, they will behave
correctly, according to their transactional semantics.</para>
<para><classname>AbstractTransactionalSpringContextTests</classname>
depends on a <classname>IPlatformTransactionManager</classname> object
being defined in the application context. The name doesn't matter, due
to the use of autowire by type.</para>
<para>Typically you will extend the subclass,
<classname>AbstractTransactionalDbProviderSpringContextTests</classname>.
This also requires that a <classname>DbProvider</classname> object
definition - again, with any name - be present in the configurations. It
creates an <classname>AdoTemplate</classname> instance variable that is
useful for convenient querying, and provides handy methods to delete the
contents of selected tables (remember that the transaction will roll
back by default, so this is safe to do).</para>
<para>If you want a transaction to commit - unusual, but occasionally
useful when you want a particular test to populate the database - you
can call the <methodname>SetComplete()</methodname> method inherited
from <classname>AbstractTransactionalSpringContextTests</classname>.
This will cause the transaction to commit instead of roll back.</para>
<para>There is also convenient ability to end a transaction before the
test case ends, through calling the
<methodname>EndTransaction()</methodname> method. This will roll back
the transaction by default, and commit it only if
<methodname>SetComplete()</methodname> had previously been called. This
functionality is useful if you want to test the behavior of
'disconnected' data objects, such as Hibernate-mapped objects that will
be used in a web or remoting tier outside a transaction. Often, lazy
loading errors are discovered only through UI testing; if you call
<methodname>EndTransaction()</methodname> you can ensure correct
operation of the UI through your NUnit test suite.</para>
</section>
<section id="testing-superclasses">
<title>Convenience variables</title>
<para>When you extend the
<classname>AbstractTransactionalDbProviderSpringContextTests</classname>
class you will have access to the following <literal>protected</literal>
instance variables:</para>
<itemizedlist>
<listitem>
<para><literal>applicationContext</literal> (a
<interfacename>IConfigurableApplicationContext</interfacename>):
inherited from the
<classname>AbstractDependencyInjectionSpringContextTests</classname>
superclass. Use this to perform explicit object lookup, or test the
state of the context as a whole.</para>
</listitem>
<listitem>
<para><literal>adoTemplate</literal>: inherited from
<classname>AbstractTransactionalDbProviderSpringContextTests</classname>.
Useful for querying to confirm state. For example, you might query
before and after testing application code that creates an object and
persists it using an ORM tool, to verify that the data appears in
the database. (Spring will ensure that the query runs in the scope
of the same transaction.) You will need to tell your ORM tool to
'flush' its changes for this to work correctly, for example using
the <methodname>Flush()</methodname> method on NHibernate's
<classname>ISession</classname> interface.</para>
</listitem>
</itemizedlist>
<para>Often you will provide an application-wide superclass for
integration tests that provides further useful instance variables used
in many tests</para>
</section>
<section id="testing-examples-petclinic"></section>
</section>
<section id="testing-resources">
<title>Further Resources</title>
<para>This section contains links to further resources about testing in
general.</para>
<itemizedlist>
<listitem>
<para>The <ulink url="http://www.nunit.org/index.htm">NUnit
homepage</ulink>. The Spring Framework's unit test suite is written
using NUnit as the testing framework.</para>
</listitem>
</itemizedlist>
</section>
</chapter>

View File

@@ -0,0 +1,247 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="threading">
<title>Threading and Concurrency Support</title>
<sect1 id="threading-introduction">
<title>Introduction</title>
<para>The purpose of the <classname>Spring.Threading</classname> namespace
is to provide a place to keep useful concurrency abstractions that augment
those in the BCL. Since Doug Lea has provided a wealth of mature public
domain concurrency abstractions in his Java based
'EDU.oswego.cs.dl.util.concurrent' libraries we decided to port a few of
his abstractions to .NET. So far, we've only ported three classes, the
minimum necessary to provide basic object pooling functionality to support
an AOP based pooling aspect and to provide a Semaphore class that was
mistakenly not included in .NET 1.0/1.1.</para>
<para>There is also an important abstraction, IThreadStorage, for
performing thread local storage.</para>
</sect1>
<sect1>
<title>Thread Local Storage</title>
<para>Depending on your runtime environment there are different strategies
to use for storing objects in thread local storage. If you are in web
applications a single Request may be executed on different threads. As
such, the location to store thread local objects is in
<classname>HttpContext.Current</classname>. For other environments
<classname>System.Runtime.Remoting.Messaging.CallContext</classname> is
used. For more background information on the motivation behind these
choices, say as compared to the attribute [ThreadStatic] refer to
"Piers7"'s <ulink
url="http://piers7.blogspot.com/2005/11/threadstatic-callcontext-and_02.html">blog</ulink>
and this <ulink
url="http://forum.springframework.net/showthread.php?t=572&amp;highlight=LogicalThreadContext">forum
post</ulink>. The interface IThreadStorage serves as the basis for the
thread local storage abstraction and various implementations can be
selected from depending on your runtime requirements. Configuring the
implementation of IThreadStorage makes it easier to have more portability
across runtime environments.</para>
<para>The API is quite simple and shown below<programlisting>public interface IThreadStorage
{
object GetData(string name)
void SetData(string name, object value)
void FreeNamedDataSlot(string name)
}
</programlisting></para>
<para>The methods <methodname>GetData</methodname> and
<methodname>SetData</methodname> are responsible for retrieving and
setting the object that is to be bound to thread local storage and
associating it with a name. Clearing the thread local storage is done via
the method <methodname>FreeNamedDataSlot</methodname>.</para>
<para>In <literal>Spring.Core</literal> is the implementation,
<classname>CallContextStorage</classname>, that directly uses
<classname>CallContext</classname> and also the implementation
<classname>LogicalThreadContext</classname> which by default uses
<classname>CallContextStorage</classname> but can be configured via the
static method <methodname>SetStorage(IThreadStorage)</methodname>. The
methods on CallContextStorage and LogicalThreadContext are static.</para>
<para>In <literal>Spring.Web</literal> is the implementation
<classname>HttpContextStorage</classname> which uses the
<classname>HttpContext</classname> to store thread local data and
<classname>HybridContextStorage</classname> that uses
<classname>HttpContext</classname> if within a web environment, i.e.
<literal>HttpContext.Current != null</literal>, and
<classname>CallContext</classname> otherwise.</para>
<para>Spring internally uses <classname>LogicalThreadContext</classname>
as this doesn't require a coupling to the <package>System.Web</package>
namespace. In the case of Spring based web applications, Spring's
<classname>WebSupportModule</classname> sets the storage strategy of
<classname>LogicalThreadContext</classname> to be
<classname>HybridContextStorage</classname>.</para>
</sect1>
<sect1>
<title>Synchronization Primitives</title>
<para>When you take a look at these synchronization classes, you'll wonder
why it's even necessary when <literal>System.Threading</literal> provides
plenty of synchronization options. Although
<literal>System.Threading</literal> provides great synchronization
classes, it doesn't provide well-factored abstractions and interfaces for
us. Without these abstractions, we will tend to code at a low-level. With
enough experience, you'll eventually come up with some abstractions that
work well. Doug Lea has already done a lot of that research and has a
class library that we can take advantage of.</para>
<sect2>
<title>ISync</title>
<para><literal>ISync</literal> is the central interface for all classes
that control access to resources from multiple threads. It's a simple
interface which has two basic use cases. The first case is to block
indefinitely until a condition is met:</para>
<programlisting>void ConcurrentRun(ISync lock) {
lock.Acquire(); // block until condition met
try {
// ... access shared resources
}
finally {
lock.Release();
}
}
</programlisting>
<para>The other case is to specify a maximum amount of time to block
before the condition is met:</para>
<programlisting>void ImpatientConcurrentRun(ISync lock) {
// block for at most 10 milliseconds for condition
if ( lock.Attempt(10) ) {
try {
// ... access shared resources
}
finally {
lock.Release();
}
} else {
// complain of time out
}
}
</programlisting>
</sect2>
<sect2>
<title>SyncHolder</title>
<para>The <literal>SyncHolder</literal> class implements the
<literal>System.IDisposable</literal> interface and so provides a way to
use an <literal>ISync</literal> with the <literal>using</literal> C#
keyword: the <literal>ISync</literal> will be automatically
<literal>Acquire</literal>d and then <literal>Release</literal>d on
exiting from the block.</para>
<para>This should simplify the programming model for code using (!) an
<literal>ISync</literal>: <programlisting>
ISync sync = ...
...
using (new SyncHolder(sync))
{
// ... code to be executed
// holding the ISync lock
}
</programlisting> There is also the timed version, a little more
cumbersome as you must deal with timeouts: <programlisting>
ISync sync = ...
long msecs = 100;
...
// try to acquire the ISync for msecs milliseconds
try
{
using (new SyncHolder(sync, msecs))
{
// ... code to be executed
// holding the ISync lock
}
}
catch (TimeoutException)
{
// deal with failed lock acquisition
}
</programlisting></para>
</sect2>
<sect2>
<title>Latch</title>
<para>The <literal>Latch</literal> class implements the
<literal>ISync</literal> interface and provides an implementation of a
<emphasis>latch</emphasis>. A latch is a boolean condition that is set
at most once, ever. Once a single release is issued, all acquires will
pass. It is similar to a <literal>ManualResetEvent</literal> initialized
unsignalled (Reset) and can only be <literal>Set()</literal>. A typical
use is to act as a start signal for a group of worker threads.</para>
<programlisting>class Boss {
Latch _startPermit;
void Worker() {
// very slow worker initialization ...
// ... attach to messaging system
// ... connect to database
_startPermit.Acquire();
// ... use resources initialized in Mush
// ... do real work
}
void Mush() {
_startPermit = new Latch();
for (int i=0; i&lt;10; ++i) {
new Thread(new ThreadStart(Worker)).Start();
}
// very slow main initialization ...
// ... parse configuration
// ... initialize other resources used by workers
_startPermit.Release();
}
}</programlisting>
</sect2>
<sect2>
<title>Semaphore</title>
<para>The <literal>Semaphore</literal> class implements the
<literal>ISync</literal> interface and provides an implementation of a
semaphore. Conceptually, a semaphore maintains a set of permits. Each
<literal>Acquire()</literal> blocks if necessary until a permit is
available, and then takes it. Each <literal>Release()</literal> adds a
permit. However, no actual permit objects are used; the Semaphore just
keeps a count of the number available and acts accordingly. A typical
use is to control access to a pool of shared objects.</para>
<programlisting>class LimitedConcurrentUploader {
// ensure we don't exceed maxUpload simultaneous uploads
Semaphore _available;
public LimitedConcurrentUploader(maxUploads) {
_available = new Semaphore(maxUploads);
}
// no matter how many threads call this method no more
// than maxUploads concurrent uploads will occur.
public Upload(IDataTransfer upload) {
_available.Acquire();
try {
upload.TransferData();
}
finally {
_available.Release();
}
}
}
</programlisting>
</sect2>
</sect1>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,553 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="tx-quickstart">
<title>Transactions QuickStart</title>
<section>
<title>Introduction</title>
<para>The Transaction Quickstart demonstrates Spring's transaction
management features. The database schema are two simple tables, credit and
debit, which contain an Identifier and an Amount. The quick start shows
the use of declarative transactions using attributes and also the ability
to change the transaction manager (local or distributed) via changes to
only the configuration files - no code changes are required. It also
demonstrates some techniques for unit and integration testing an
application as well as separating Spring's configuration files so that one
is responsible for describing how the core business classes are configured
and others that are responsible for the database environment and
application of AOP.</para>
<para>This quickstart assumes you have installed a way to run NUnit tests
within your IDE. Some excellent tools that let you do this are <ulink
url="http://www.testdriven.net/">TestDriven.NET</ulink> and <ulink
url="http://www.jetbrains.com/resharper/">ReSharper</ulink>.</para>
</section>
<section>
<title>Application Overview</title>
<para>The design of the application is very simple and consists of two
logical layers, a business service layer in the namespace
<package>Spring.TxQuickStart.Services</package> and a DAO layer in the
namespace <package>Spring.TxQuickStart.Dao</package>. As this is just a
toy example the business service layer does nothing more than call two DAO
objects. The business service is to transfer money in a bank account and
is blatantly taken from the book <ulink
url="http://www.apress.com/book/bookDisplay.html?bID=10002">Pro
ADO.NET</ulink> by Sahil Malik. The transfer service is defined by the
interface <interfacename>IAccountManager</interfacename> with the
implementation <classname>AccountManager</classname> located in the
namespace <classname>Spring.TxQuickStart.Services</classname>. The money
is recorded in a credit and debit table in the database. The SQL Server
schema for the tables is located in the file CreditsDebitsSchema.sql.
Transferring the money requires an ACID operation on these two tables. The
credit operation is defined via a
<interfacename>IAccountCreditDao</interfacename> interface and the debit
operation via an <interfacename>IAccountDebitDao</interfacename>
interface. Implementations of these interfaces using
<classname>AdoTemplate</classname> are in the namespace
<package>Spring.TxQuickStart.Dao.Ado</package>.</para>
<section>
<title>Interfaces</title>
<para>The Manager and DAO interfaces are shown below</para>
<programlisting> public interface IAccountManager
{
void DoTransfer(float creditAmount, float debitAmount);
}
public interface IAccountCreditDao
{
void CreateCredit(float creditAmount);
}
public interface IAccountDebitDao
{
void DebitAccount(float debitAmount);
}</programlisting>
</section>
</section>
<section>
<title>Implementation</title>
<para>The implementation of the Account Credit DAO is shown below</para>
<programlisting> public class AccountCreditDao : AdoDaoSupport, IAccountCreditDao
{
public void CreateCredit(float creditAmount)
{
AdoTemplate.ExecuteNonQuery(CommandType.Text,
"insert into Credits (CreditAmount) VALUES (@amount)", "amount", DbType.Decimal, 0,
creditAmount);
}
}</programlisting>
<para>and for the Debit DAO</para>
<programlisting> public class AccountDebitDao : AdoDaoSupport, IAccountDebitDao
{
public void DebitAccount(float debitAmount)
{
AdoTemplate.ExecuteNonQuery(CommandType.Text,
"insert into dbo.Debits (DebitAmount) VALUES (@amount)", "amount", DbType.Decimal, 0,
debitAmount);
}
}</programlisting>
<para>Both of these DAO implementations inherit from Spring's
<classname>AdoDaoSupport</classname> class that provides convenient access
to an <classname>AdoTemplate</classname> for performing data access
operations. With no other properties that can be configured in these
implementations, the only configuration required is setting of
AdoDaoSupport's <classname>DbProvider</classname> property representing
the connection to the database.</para>
<para>The implementation of the service layer interface,
<classname>IAccountManager</classname>, is shown below.</para>
<programlisting> public class AccountManager : IAccountManager
{
private IAccountCreditDao accountCreditDao;
private IAccountDebitDao accountDebitDao;
private float maxTransferAmount = 1000000;
public AccountManager(IAccountCreditDao accountCreditDao, IAccountDebitDao accountDebitDao)
{
this.accountCreditDao = accountCreditDao;
this.accountDebitDao = accountDebitDao;
}
public float MaxTransferAmount
{
get { return maxTransferAmount; }
set { maxTransferAmount = value; }
}
[Transaction]
public void DoTransfer(float creditAmount, float debitAmount)
{
accountCreditDao.CreateCredit(creditAmount);
if (creditAmount &gt; maxTransferAmount || debitAmount &gt; maxTransferAmount)
{
throw new ArithmeticException("see a teller big spender...");
}
accountDebitDao.DebitAccount(debitAmount);
}
}</programlisting>
<para>The if statement is a poor-mans representation of business logic,
namely that there is a policy that does not allow the use of this service
for amounts larger than $1,000,000. If the credit or debit amount is
larger than 1,000,000 then and exception will be thrown. We can write a
unit test that will test for this business logic and provide stub
implementations of the DAO objects so that our tests are not only
independent of the database but will also execute very quickly.<note>
<para>Notice the Transaction attribute on the
<literal>DoTransfer</literal> method. This attribute can be read by
Spring and used to create a transactional proxy to AccountManager in
order to perform declarative transaction management.</para>
</note></para>
<para>The NUnit unit test for AccountManager is shown below</para>
<programlisting> public class AccountManagerUnitTests
{
private IAccountManager accountManager;
[SetUp]
public void Setup()
{
IAccountCreditDao stubCreditDao = new StubAccountCreditDao();
IAccountDebitDao stubDebitDao = new StubAccountDebitDao();
accountManager = new AccountManager(stubCreditDao, stubDebitDao);
}
[Test]
public void TransferBelowMaxAmount()
{
accountManager.DoTransfer(217, 217);
}
[Test]
[ExpectedException(typeof(ArithmeticException))]
public void TransferAboveMaxAmount()
{
accountManager.DoTransfer(2000000, 200000);
}
}</programlisting>
<para>Running these tests we exercise both code pathways through the
method <literal>DoTransfer</literal>. Nothing we have done so far is
Spring specific (aside from the presence of the [Transaction] attribute.
Now that we know the class works in isolation, we can now 'wire' up the
application for use in production by specifying how the service and DAO
layers are related. This configuration file is shown below and can loosely
be referred to as your 'application blueprint'. This configuration file is
named application-config.xml and is an embedded resource inside the 'main'
project, Spring.TxQuickStart.</para>
<programlisting>&lt;objects xmlns='http://www.springframework.net'&gt;
&lt;!-- DAO Implementations --&gt;
&lt;object id="accountCreditDao" type="Spring.TxQuickStart.Dao.Ado.AccountCreditDao, Spring.TxQuickStart"&gt;
&lt;property name="DbProvider" ref="CreditDbProvider"/&gt;
&lt;/object&gt;
&lt;object id="accountDebitDao" type="Spring.TxQuickStart.Dao.Ado.AccountDebitDao, Spring.TxQuickStart"&gt;
&lt;property name="DbProvider" ref="DebitDbProvider"/&gt;
&lt;/object&gt;
&lt;!-- The service that performs multiple data access operations --&gt;
&lt;object id="accountManager"
type="Spring.TxQuickStart.Services.AccountManager, Spring.TxQuickStart"&gt;
&lt;constructor-arg name="accountCreditDao" ref="accountCreditDao"/&gt;
&lt;constructor-arg name="accountDebitDao" ref="accountDebitDao"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<para>This configuration is selecting the real ADO.NET implementations
that will insert records into the database. We can now write a NUnit
integration test that will test the service and DAO layers. To do this we
add on configuration information specific to our test environment. This
extra configuration information will determine what databases we speak to
and what transaction manager (local or distribute) to use. The code for
this integration style NUnit test is shown below</para>
<programlisting> [TestFixture]
public class AccountManagerTests
{
private AdoTemplate adoTemplateCredit;
private AdoTemplate adoTemplateDebit;
private IAccountManager accountManager;
[SetUp]
public void SetUp()
{
// Configure Spring programmatically
NamespaceParserRegistry.RegisterParser(typeof(DatabaseNamespaceParser));
NamespaceParserRegistry.RegisterParser(typeof(TxNamespaceParser));
NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser));
IApplicationContext context = new XmlApplicationContext(
"assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/system-test-local-config.xml"
);
accountManager = context["accountManager"] as IAccountManager;
CleanDb(context);
}
[Test]
public void TransferBelowMaxAmount()
{
accountManager.DoTransfer(217, 217);
int numCreditRecords = (int)adoTemplateCredit.ExecuteScalar(CommandType.Text, "select count(*) from Credits");
int numDebitRecords = (int)adoTemplateDebit.ExecuteScalar(CommandType.Text, "select count(*) from Debits");
Assert.AreEqual(1, numCreditRecords);
Assert.AreEqual(1, numDebitRecords);
}
[Test]
[ExpectedException(typeof(ArithmeticException))]
public void TransferAboveMaxAmount()
{
accountManager.DoTransfer(2000000, 200000);
}
private void CleanDb(IApplicationContext context)
{
IDbProvider dbProvider = (IDbProvider)context["DebitDbProvider"];
adoTemplateDebit = new AdoTemplate(dbProvider);
adoTemplateDebit.ExecuteNonQuery(CommandType.Text, "truncate table Debits");
dbProvider = (IDbProvider)context["CreditDbProvider"];
adoTemplateCredit = new AdoTemplate(dbProvider);
adoTemplateCredit.ExecuteNonQuery(CommandType.Text, "truncate table Credits");
}
}</programlisting>
<para>The essential element is to create an instance of Spring's
application context where the relevant layers of the application are
'wired' together. The <classname>IAccountManager</classname>
implementation is retrieved from the IoC container and stored as a field
of the test class. The basic logic of the test is the same as in the unit
test but in addition there is the verification of actions performed in the
database. The set up method puts the database tables into a known state
before running the tests. Other techniques for performing integration
testing that can alleviate the need to do extensive database state
management for integration tests is described in the <link
linkend="testing">testing</link> section.</para>
</section>
<section>
<title>Configuration</title>
<para>The configuration file system-test-local-config.xml shown in the
previous program listing includes application-config.xml and specifies the
database to use and the local (not distributed) transaction manager
AdoPlatformTransactionManager. This configuration file is shown
below</para>
<programlisting>&lt;objects xmlns="http://www.springframework.net"
xmlns:db="http://www.springframework.net/database"
xmlns:tx="http://www.springframework.net/tx"&gt;
&lt;!-- Imports application configuration --&gt;
&lt;import resource="assembly://Spring.TxQuickStart/Spring.TxQuickStart/application-config.xml"/&gt;
&lt;!-- Imports additional aspects --&gt;
&lt;!--
&lt;import resource="assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/aspects-config.xml"/&gt;
--&gt;
&lt;!-- Database Providers --&gt;
&lt;db:provider id="DebitDbProvider"
provider="System.Data.SqlClient"
connectionString="Data Source=MARKT60\SQL2005;Initial Catalog=CreditsAndDebits;User ID=springqa; Password=springqa"/&gt;
&lt;db:provider id="CreditDbProvider"
provider="System.Data.SqlClient"
connectionString="Data Source=MARKT60\SQL2005;Initial Catalog=CreditsAndDebits;User ID=springqa; Password=springqa"/&gt;
&lt;alias name="DebitDbProvider" alias="CreditDbProvider"/&gt;
&lt;!-- Transaction Manager if using a single database that contain both credit and debit tables --&gt;
&lt;object id="transactionManager"
type="Spring.Data.Core.AdoPlatformTransactionManager, Spring.Data"&gt;
&lt;property name="DbProvider" ref="DebitDbProvider"/&gt;
&lt;/object&gt;
&lt;!-- Transaction aspect --&gt;
&lt;tx:attribute-driven/&gt;
&lt;/objects&gt;</programlisting>
<para>Moving from top to bottom in the configuration file, the
'application-blueprint' configuration file is included. Then the database
type and connection parameters are specified for the two databases. The
names of these providers must match those specific in
application-config.xml. Since the two names point to the same database, an
alias configuration element is used to have them point to the same
dbProvider under different names. The type of transaction manager is then
selected, in this case we are showing the use of local transactions with
AdoPlatformTransactionManager. Running the tests will result in 217 being
entered into the Credits and Debits table of each database. You can fire
up SQL Server Management Studio or equivalent to verify this.</para>
<para>To switch to a distributed transaction you can refer to the
configuration file system-test-dtc-config.xml, which is shown below</para>
<programlisting>objects xmlns='http://www.springframework.net'
xmlns:db="http://www.springframework.net/database"
xmlns:tx="http://www.springframework.net/tx"&gt;
&lt;!-- Imports application configuration --&gt;
&lt;import resource="assembly://Spring.TxQuickStart/Spring.TxQuickStart/application-config.xml"/&gt;
&lt;!-- Imports additional aspects --&gt;
&lt;!--
&lt;import resource="assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/aspects-config.xml"/&gt;
--&gt;
&lt;db:provider id="DebitDbProvider"
provider="System.Data.SqlClient"
connectionString="Data Source=MARKT60\SQL2005;Initial Catalog=Debits;User ID=springqa; Password=springqa"/&gt;
&lt;db:provider id="CreditDbProvider"
provider="System.Data.SqlClient"
connectionString="Data Source=MARKT60\SQL2005;Initial Catalog=Credits;User ID=springqa; Password=springqa"/&gt;
&lt;!-- Transaction Manager if using two databases, one containing the credit table and the other a debit table --&gt;
&lt;object id="transactionManager"
type="Spring.Data.Core.TxScopeTransactionManager, Spring.Data"&gt;
&lt;/object&gt;
&lt;!-- Transaction aspect --&gt;
&lt;tx:attribute-driven/&gt;
&lt;/objects&gt;</programlisting>
<para>TxScopeTransactionManager uses .NET 2.0 System.Transactions as the
implementation, allowing for distributed transactions between the two
different databases listed. In a larger application the different layers
would typically be broken up into individual configuration files and
imported into the main configuration file. This allows your configuration
to mirror your architecture.</para>
<para>You can also use the configuration file
system-test-dtc-es-config.xml that will use EnterpriseServices to perform
transaction management.</para>
<section>
<title>Rollback Rules</title>
<para>Using Rollback rules allows you to specify which exceptions will
not cause a rollback and instead only stop execution flow, committing
the work done up to the exception. An alternative implementation of
AccountManager's DoTransfer method (included in the sample code) is
shown below.</para>
<programlisting> [Transaction(NoRollbackFor = new Type[] { typeof(ArithmeticException) })]
public void DoTransfer(float creditAmount, float debitAmount)
{
accountCreditDao.CreateCredit(creditAmount);
if (creditAmount &gt; maxTransferAmount || debitAmount &gt; maxTransferAmount)
{
throw new ArithmeticException("see a teller big spender...");
}
accountDebitDao.DebitAccount(debitAmount);
} </programlisting>
<para>All that has changed is the use of the NoRollbackFor property on
the transaction attribute.</para>
<para>The expected behavior is that the credit table will be updated
even though the exception is thrown. This is due to specifying that
exceptions of the type ArithmethicException should not rollback the
database transaction. Running the test code below verifies that the
exception still propagates out of the method.</para>
<programlisting> [Test]
public void DeclarativeWithAttributesNoRollbackFor()
{
try
{
accountManager.DoTransfer(2000000, 2000000);
Assert.Fail("Should have thrown Arithmetic Exception");
} catch (ArithmeticException) {
int numCreditRecords = (int)adoTemplateCredit.ExecuteScalar(CommandType.Text, "select count(*) from Credits");
int numDebitRecords = (int)adoTemplateDebit.ExecuteScalar(CommandType.Text, "select count(*) from Debits");
Assert.AreEqual(1, numCreditRecords);
Assert.AreEqual(0, numDebitRecords);
}
}</programlisting>
</section>
</section>
<section>
<title>Adding additional Aspects</title>
<para>Transactional advice is just one type of advice that can be applied
to the service layer. You can also configure other pieces of advice to be
executed as part of the general advice chain that is associated with
methods that have the Transaction attribute applied. In this example we
will add logging of thrown exceptions using Spring's
ExceptionHandlerAdvice as well as logging of the service layer method
invocation. No code is required to be changed in order to have this
additional functionality. Instead all you have to do is uncomment the
line</para>
<programlisting> &lt;import resource="assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/aspects-config.xml"/&gt;</programlisting>
<para>in either system-test-dtc-config.xml or system-test-local-config.xml
The aspect configuration file is shown below</para>
<programlisting>&lt;objects xmlns='http://www.springframework.net'
xmlns:aop="http://www.springframework.net/aop"&gt;
&lt;object name="exceptionAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop"&gt;
&lt;property name="exceptionHandlers"&gt;
&lt;list&gt;
&lt;value&gt;on exception name ArithmeticException log 'Logging an exception thrown from method ' + #method.Name &lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&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="Separator" value=";"/&gt;
&lt;property name="HideProxyTypeNames" value="true"/&gt;
&lt;property name="UseDynamicLogger" value="true"/&gt;
&lt;property name="LogLevel" value="Info"/&gt;
&lt;/object&gt;
&lt;object id="txAttributePointcut" type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop"&gt;
&lt;property name="Attribute" value="Spring.Transaction.Interceptor.TransactionAttribute, Spring.Data"/&gt;
&lt;/object&gt;
&lt;aop:config&gt;
&lt;aop:advisor id="exceptionProcessAdvisor" order="1"
advice-ref="exceptionAdvice"
pointcut-ref="txAttributePointcut"/&gt;
&lt;aop:advisor id="loggingAdvisor" order="2"
advice-ref="loggingAdvice"
pointcut-ref="txAttributePointcut"/&gt;
&lt;/aop:config&gt;
&lt;/objects&gt;</programlisting>
<para>The transaction aspect is now additionally configured with an order
value of "10", which will place it after the execution of the exception
aspect, which is configured to use an order value of 1. The behavior for
logging the exception is specified by creating and configuring an instance
of
<classname>Spring.Aspects.Exceptions.ExceptionHandlerAdvice</classname>.
The location where that behavior is applied, the pointcut, is the
Transaction attribute. The logging of method arguments and execution time
is specified by configuring an instance of
<classname>Spring.Aspects.Logging.SimpleLoggingAdvice</classname>.</para>
<para>The AOP configuration section on the bottom is what ties together
the behavior and where it will take place in the program flow. Under the
covers the transaction configuration, &lt;tx:attribute-driven/&gt; creates
similar advice and pointcut definitions. Running the test
TransferBelowMaxAmount will then log the following messages</para>
<programlisting>INFO - Entering DoTransfer;45b6af04-b736-4efa-a489-45462726ddf2;creditAmount=217; debitAmount=217
INFO - Exiting DoTransfer;45b6af04-b736-4efa-a489-45462726ddf2;1328.125 ms;return=
</programlisting>
<para>When the test case of the test TransferAboveMaxAmount is run the
following messages are logged</para>
<programlisting>INFO - Entering DoTransfer;d94bc81b-a4ff-4ca1-9aaa-f2834f262307;creditAmount=2000000; debitAmount=200000
INFO - Exception thrown in DoTransferDoTransfer;d94bc81b-a4ff-4ca1-9aaa-f2834f262307;1140.625
System.ArithmeticException: see a teller big spender...
at Spring.TxQuickStart.Services.AccountManager.DoTransfer(Single creditAmount, Single debitAmount) in L:\projects\Spring.Net\examples\Spring\Spring.TxQuickStart\src\Spring\Spring.TxQuickStart\TxQuickStart\Services\AccountManager.cs:line 36
at Spring.DynamicReflection.Method_DoTransfer_ec48557f22b149958fd2243413136600.Invoke(Object target, Object[] args)
at Spring.Reflection.Dynamic.SafeMethod.Invoke(Object target, Object[] arguments) in l:\projects\Spring.Net\src\Spring\Spring.Core\Reflection\Dynamic\DynamicMethod.cs:line 108
at Spring.Aop.Framework.DynamicMethodInvocation.InvokeJoinpoint() in l:\projects\Spring.Net\src\Spring\Spring.Aop\Aop\Framework\DynamicMethodInvocation.cs:line 89
at Spring.Aop.Framework.AbstractMethodInvocation.Proceed() in l:\projects\Spring.Net\src\Spring\Spring.Aop\Aop\Framework\AbstractMethodInvocation.cs:line 257
at Spring.Transaction.Interceptor.TransactionInterceptor.Invoke(IMethodInvocation invocation) in l:\projects\Spring.Net\src\Spring\Spring.Data\Transaction\Interceptor\TransactionInterceptor.cs:line 80
at Spring.Aop.Framework.AbstractMethodInvocation.Proceed() in l:\projects\Spring.Net\src\Spring\Spring.Aop\Aop\Framework\AbstractMethodInvocation.cs:line 282
at Spring.Aspects.Logging.SimpleLoggingAdvice.InvokeUnderLog(IMethodInvocation invocation, ILog log) in l:\projects\Spring.Net\src\Spring\Spring.Aop\Aspects\Logging\SimpleLoggingAdvice.cs:line 185
TRACE - Logging an exception thrown from method DoTransfer
</programlisting>
<para></para>
</section>
</chapter>

View File

@@ -0,0 +1,866 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="validation">
<title id="val-title">Validation Framework</title>
<section>
<title>Introduction</title>
<para>Data validation is a very important part of any enterprise
application. ASP.NET has a validation framework but it is very limited in
scope and starts falling apart as soon as you need to perform more complex
validations. Problems with the out of the box ASP.NET validation framework
are well <ulink
url="http://www.peterblum.com/VAM/ValMain.aspx">documented</ulink> by
Peter Blum on his web site, so we are not going to repeat them here. Peter
has also built a nice replacement for the standard ASP.NET validation
framework, which is worth looking into if you prefer the standard ASP.NET
validation mechanism to the one offered by Spring.NET for some reason.
Both frameworks will allow you to perform very complex validations but we
designed the Spring.NET validation framework differently for the reasons
described below.</para>
<para>On the Windows Forms side the situation is even worse. Out of the
box data validation features are completely inadequate as pointed out by
Ian Griffiths in this <ulink
url="http://pluralsight.com/wiki/default.aspx/Craig/WinFormsValidationBroken.html">article</ulink>.
One of the major problems we saw in most validation frameworks available
today, both open source and commercial, is that they are tied to a
specific presentation technology. The ASP.NET validation framework uses
ASP.NET controls to define validation rules, so these rules end up in the
HTML markup of your pages. Peter Blum's framework uses the same approach.
In our opinion, validation is not applicable only to the presentation
layer so there is no reason to tie it to any particular technology. As
such, the Spring.NET Validation Framework is designed in a way that
enables data validation in different application layers using the same
validation rules.</para>
<para>The goals of the validation framework are the following:</para>
<orderedlist>
<listitem>
<para>Allow for the validation of any object, whether it is a UI
control or a domain object.</para>
</listitem>
<listitem>
<para>Allow the same validation framework to be used in both Windows
Forms and ASP.NET applications, as well as in the service layer (to
validate parameters passed to the service, for example).</para>
</listitem>
<listitem>
<para>Allow composition of the validation rules so arbitrarily complex
validation rule sets can be constructed.</para>
</listitem>
<listitem>
<para>Allow validators to be conditional so they only execute if a
specific condition is met.</para>
</listitem>
</orderedlist>
<para>The following sections will describe in more detail how these goals
were achieved and show you how to use the Spring.NET Validation Framework
in your applications.</para>
</section>
<section>
<title>Example Usage</title>
<para>Decoupling validation from presentation was the major goal that
significantly influenced design of the validation framework. We wanted to
be able to define a set of validation rules that are completely
independent from the presentation so we can reuse them (or at least have
the ability to reuse them) in different application layers. This meant
that the approach taken by Microsoft ASP.NET team would not work and
custom validation controls were not an option. The approach taken was to
configure validation rules just like any other object managed by Spring -
within the application context. However, due to possible complexity of the
validation rules we decided not to use the standard Spring.NET
configuration schema for validator definitions but to instead provide a
more specific and easier to use custom configuration schema for
validation. Note that the validation framework is not tied to the use of
XML, you can use its API Programatically. The following example shows
validation rules defined for the Trip object in the SpringAir sample
application:</para>
<para><programlisting>&lt;objects xmlns="http://www.springframework.net" xmlns:v="http://www.springframework.net/validation"&gt;
&lt;object type="TripForm.aspx" parent="standardPage"&gt;
&lt;property name="TripValidator" ref="tripValidator" /&gt;
&lt;/object&gt;
&lt;v:group id="tripValidator"&gt;
&lt;v:required id="departureAirportValidator" test="StartingFrom.AirportCode"&gt;
&lt;v:message id="error.departureAirport.required" providers="departureAirportErrors, validationSummary"/&gt;
&lt;/v:required&gt;
&lt;v:group id="destinationAirportValidator"&gt;
&lt;v:required test="ReturningFrom.AirportCode"&gt;
&lt;v:message id="error.destinationAirport.required" providers="destinationAirportErrors, validationSummary"/&gt;
&lt;/v:required&gt;
&lt;v:condition test="ReturningFrom.AirportCode != StartingFrom.AirportCode" when="ReturningFrom.AirportCode != ''"&gt;
&lt;v:message id="error.destinationAirport.sameAsDeparture" providers="destinationAirportErrors, validationSummary"/&gt;
&lt;/v:condition&gt;
&lt;/v:group&gt;
&lt;v:group id="departureDateValidator"&gt;
&lt;v:required test="StartingFrom.Date"&gt;
&lt;v:message id="error.departureDate.required" providers="departureDateErrors, validationSummary"/&gt;
&lt;/v:required&gt;
&lt;v:condition test="StartingFrom.Date &gt;= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue"&gt;
&lt;v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"/&gt;
&lt;/v:condition&gt;
&lt;/v:group&gt;
&lt;v:group id="returnDateValidator" when="Mode == 'RoundTrip'"&gt;
&lt;v:required test="ReturningFrom.Date"&gt;
&lt;v:message id="error.returnDate.required" providers="returnDateErrors, validationSummary"/&gt;
&lt;/v:required&gt;
&lt;v:condition test="ReturningFrom.Date &gt;= StartingFrom.Date" when="ReturningFrom.Date != DateTime.MinValue"&gt;
&lt;v:message id="error.returnDate.beforeDeparture" providers="returnDateErrors, validationSummary"/&gt;
&lt;/v:condition&gt;
&lt;/v:group&gt;
&lt;/v:group&gt;
&lt;/objects&gt;</programlisting>There are a few things to note in the example
above:<itemizedlist>
<listitem>
<para>You need to reference the validation schema by adding a
<literal>xmlns:v="http://www.springframework.net/validation"</literal>
namespace declaration to the root element.</para>
</listitem>
<listitem>
<para>You can mix standard object definitions and validator
definitions in the same configuration file as long as both schemas
are referenced.</para>
</listitem>
<listitem>
<para>The Validator defined in the configuration file is identified
by and id attribute and can be referenced in the standard Spring
way, i.e. the injection of tripValidator into TripForm.aspx page
definition in the first &lt;object&gt; tag above.</para>
</listitem>
<listitem>
<para>The validation framework uses Spring's powerful expression
evaluation engine to evaluate both validation rules and
applicability conditions for the validator. As such, any valid
Spring expression can be specified within the test and when
attributes of any validator.</para>
</listitem>
</itemizedlist></para>
<para>The example above shows many of the features of the framework, so
let's discuss them one by one in the following sections.</para>
</section>
<section>
<title>Validator Groups</title>
<para>Validators can be grouped together. This is important for many
reasons but the most typical usage scenario is to group multiple
validation rules that apply to the same value. In the example above there
is a validator group for almost every property of the Trip instance. There
is also a top-level group for the Trip object itself that groups all other
validators.</para>
<para>There are three types of validator groups each with a different
behavior:</para>
<para>While the first type (AND) is definitely the most useful, the other
two allow you to implement some specific validation scenarios in a very
simple way, so you should keep them in mind when designing your validation
rules.</para>
<table>
<title>Validator Groups</title>
<tgroup cols="3">
<colspec align="left" colname="c1" colwidth="1*" />
<colspec colname="c2" colwidth="5*" />
<colspec colname="c3" colwidth="18*" />
<thead>
<row>
<entry>Type</entry>
<entry>XML Tag</entry>
<entry>Behavior</entry>
</row>
</thead>
<tbody>
<row>
<entry>AND</entry>
<entry><literal>group</literal></entry>
<entry>Returns <emphasis role="bold"><emphasis
role="bold"><emphasis>true</emphasis></emphasis></emphasis> only
if all contained validators return <emphasis
role="bold"><emphasis>true</emphasis></emphasis>. This is the most
commonly used validator group.</entry>
</row>
<row>
<entry>OR</entry>
<entry><literal>any</literal></entry>
<entry>Returns <emphasis
role="bold"><emphasis>true</emphasis></emphasis> if one or more of
the contained validators return <emphasis
role="bold"><emphasis>true</emphasis></emphasis>.</entry>
</row>
<row>
<entry>XOR</entry>
<entry><literal>exclusive</literal></entry>
<entry>Returns true if only one of the contained validators return
true.</entry>
</row>
</tbody>
</tgroup>
</table>
<para>One thing to remember is that a validator group is a validator like
any other and can be used anywhere validator is expected. You can nest
groups within other groups and reference them using validator reference
syntax (described later), so they really allow you to structure your
validation rules in the most reusable way.</para>
</section>
<section>
<title>Validators</title>
<para>Ultimately, you will have one or more validator definitions for each
piece of data that you want to validate. Spring.NET has several built-in
validators that are sufficient for most validations, even fairly complex
ones. The framework is extensible so you can write your own custom
validators and use them in the same way as the built-in ones.</para>
<section>
<title>Condition Validator</title>
<para>The condition validator evaluates any logical expression that is
supported by Spring's evaluation engine. The syntax is</para>
<programlisting>&lt;v:<emphasis role="bold">condition</emphasis> id="id" test="testCondition" when="applicabilityCondition" parent="parentValidator"&gt;
actions
&lt;/v:<emphasis role="bold">condition</emphasis>&gt;</programlisting>
<para>An example is shown below</para>
<programlisting>&lt;v:condition test="StartingFrom.Date &gt;= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue"&gt;
&lt;v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"/&gt;
&lt;/v:condition&gt;</programlisting>
<para>In this example the StartingFrom property of the Trip object is
compared to see if it is later than the current date, i.e. DateTime but
only when the date has been set (the initial value of StartingFrom.Date
was set to DateTime.MinValue).</para>
<para>The condition validator could be considered "the mother of all
validators". You can use it to achieve almost anything that can be
achieved by using other validator types, but in some cases the test
expression might be very complex, which is why you should use more
specific validator type if possible. However, condition validator is
still your best bet if you need to check whether particular value
belongs to a particular range, or perform a similar test, as those
conditions are fairly easy to write.</para>
<para><note>
<para>Keep in mind that Spring.NET Validation Framework typically
works with domain objects. This is after data binding from the
controls has been performed so that the object being validated is
strongly typed. This means that you can easily compare numbers and
dates without having to worry if the string representation is
comparable.</para>
</note></para>
</section>
<section>
<title>Required Validator</title>
<para>This validator ensures that the specified test value is not empty.
The syntax is</para>
<programlisting>&lt;v:<emphasis role="bold">required</emphasis> id="id" test="requiredValue" when="applicabilityCondition" parent="parentValidator"&gt;
actions
&lt;/v:<emphasis role="bold">required</emphasis>&gt;</programlisting>
<para>An example is shown below</para>
<programlisting>&lt;v:required test="ReturningFrom.AirportCode"&gt;
&lt;v:message id="error.destinationAirport.required" providers="destinationAirportErrors, validationSummary"/&gt;
&lt;/v:required&gt;</programlisting>
<para>The specific tests done to determine if the required value is set
is listed below</para>
<table>
<title>Rules to determine if required value is valid</title>
<tgroup cols="2">
<colspec align="center" />
<thead>
<row>
<entry>System.Type</entry>
<entry>Test</entry>
</row>
</thead>
<tbody>
<row>
<entry>System.Type</entry>
<entry>Type exists</entry>
</row>
<row>
<entry>System.String</entry>
<entry>not null or an empty string</entry>
</row>
</tbody>
</tgroup>
</table>
<para></para>
<para>Required validator is also one of the most commonly used ones, and
it is much more powerful than the ASP.NET Required validator, because it
works with many other data types other than strings. For example, it
will allow you to validate <literal>DateTime</literal> instances (both
<literal>MinValue</literal> and <literal>MaxValue</literal> return
<literal>false</literal>), integer and decimal numbers, as well as any
reference type, in which case it returns <literal>true</literal> for a
non-null value and <literal>false</literal> for
<literal>{{null}}</literal>s.</para>
<para>The test attribute for the required validator will typically
specify an expression that resolves to a property of a domain object,
but it could be any valid expression that returns a value, including a
method call.</para>
</section>
<section>
<title>Regular Expression Validator</title>
<para>The syntax is</para>
<programlisting>&lt;v:<emphasis role="bold">regex</emphasis> id="id" test="valueToEvaluate" when="applicabilityCondition" parent="parentValidator"&gt;
&lt;<emphasis role="bold">v:property name="Expression" value="regularExpressionToMatch"</emphasis>/&gt;
&lt;v:property name="Options" value="regexOptions"/&gt;
actions
&lt;/v:<emphasis role="bold">regex</emphasis>&gt;</programlisting>
<para>An example is shown below</para>
<programlisting>&lt;v:regex test="ReturningFrom.AirportCode"&gt;
&lt;v:property name="Expression" value="[A-Z][A-Z][A-Z]"/&gt;
&lt;v:message id="error.destinationAirport.threeCharacters" providers="destinationAirportErrors, validationSummary"/&gt;
&lt;/v:regex&gt;</programlisting>
<para>Regular expression validator is very useful when validating values
that need to conform to some predefined format, such as telephone
numbers, email addresses, URLs, etc.</para>
<para>One major difference of the regular expression validator compared
to other built-in validator types is that you need to set a required
<literal>Expression</literal> property to a regular expression to match
against.</para>
</section>
<section>
<title>Generic Validator</title>
<para>The syntax is</para>
<programlisting>&lt;v:<emphasis role="bold">validator</emphasis> id="id" test="requiredValue" when="applicabilityCondition" type="validatorType" parent="parentValidator"&gt;
actions
&lt;/v:<emphasis role="bold">validator</emphasis>&gt;</programlisting>
<para>An example is shown below</para>
<programlisting>&lt;v:validator test="ReturningFrom.AirportCode" type="MyNamespace.MyAirportCodeValidator, MyAssembly"&gt;
&lt;v:message id="error.destinationAirport.invalid" providers="destinationAirportErrors, validationSummary"/&gt;
&lt;/v:required&gt;</programlisting>
<para>Generic validator allows you to plug in your custom validator by
specifying its type name. Custom validators are very simple to
implement, because all you need to do is extend
<literal>BaseValidator</literal> class and implement abstract
<literal>bool Validate(object objectToValidate)</literal> method. Your
implementation simply needs to return <literal>true</literal> if it
determines that object is valid, or <literal>false</literal>
otherwise</para>
</section>
<section>
<title>Conditional Validator Execution</title>
<para>As you can see from the examples above, each validator (and
validator group) allows you to define its applicability condition by
specifying a logical expression as the value of the when attribute. This
feature is very useful and is one of the major deficiencies in the
standard ASP.NET validation framework, because in many cases specific
validators need to be turned on or off based on the values of the object
being validated.</para>
<para>For example, when validating a Trip object we need to validate
return date only if the Trip.Mode property is set to the
TripMode.RoundTrip enum value. In order to achieve that we created
following validator definition:</para>
<programlisting>&lt;v:group id="returnDateValidator" when="Mode == 'RoundTrip'"&gt;
// nested validators
&lt;/v:group&gt;</programlisting>
<para>Validators within this group will only be evaluated for round
trips.</para>
<note>
<para>You should also note that you can compare enums using the string
value of the enumeration. You can also use fully qualified enum name,
such as:</para>
<para><literal>Mode == TripMode.RoundTrip</literal></para>
<para>However, in this case you need to make sure that alias for the
TripMode enum type is registered using Spring's standard type aliasing
mechanism.</para>
</note>
</section>
</section>
<section>
<title>Validator Actions</title>
<para>Validation actions are executed every time the containing validator
is executed. They allow you to do anything you want based on the result of
the validation. By far the most common use of the validation action is to
add validation error message to the errors collection, but theoretically
you could do anything you want. Because adding validation error messages
to the errors collection is such a common scenario, Spring.NET validation
schema defines a separate XML tag for this type of validation
action.</para>
<section>
<title>Error Message Action</title>
<para>The syntax is</para>
<programlisting>&lt;v:message id="messageId" providers="errorProviderList" when="messageApplicabilityCondition"&gt;
&lt;v:param value="paramExpression"/&gt;
&lt;/v:message&gt;</programlisting>
<para>An example is shown below</para>
<programlisting>&lt;v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"&gt;
&lt;v:param value="StartingFrom.Date.ToString('D')"/&gt;
&lt;v:param value="DateTime.Today.ToString('D')"/&gt;
&lt;/v:message&gt;</programlisting>
<para>There are several things that you have to be aware of when dealing
with error messages:</para>
<itemizedlist>
<listitem>
<para><literal>id</literal> is used to look up the error message in
the appropriate Spring.NET message source.</para>
</listitem>
<listitem>
<para><literal>providers</literal> specifies a comma separated list
of "error buckets" particular error message should be added to.
These "buckets" will later be used by the particular presentation
technology in order to display error messages as necessary.</para>
</listitem>
<listitem>
<para>a message can have zero or more parameters. Each parameter is
an expression that will be resolved using current validation context
and the resolved values will be passed as parameters to
<literal>IMessageSource.GetMessage</literal> method, which will
return the fully resolved message.</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Generic Actions</title>
<para>The syntax is</para>
<programlisting>&lt;v:action type="actionType" when="actionApplicabilityCondition"&gt;
properties
&lt;/v:action&gt;</programlisting>
<para>An example is shown below</para>
<programlisting>&lt;v:action type="Spring.Validation.Actions.ExpressionAction, Spring.Core" when="#page != null"&gt;
&lt;v:property name="Valid" value="#page.myPanel.Visible = true"/&gt;
&lt;v:property name="Invalid" value="#page.myPanel.Visible = false"/&gt;
&lt;/v:action&gt;</programlisting>
<para>Generic actions can be used to perform all kinds of validation
actions. In simple cases, such as in the example above where we turn
control's visibility on or off depending on the validation result, you
can use the built-in <literal>ExpressionAction</literal> class and
simply specify expressions to be evaluated based on the validator
result.</para>
<para>In other situations you may want to create your own action
implementation, which is fairly simple thing to do all you need to do
is implement <literal>IValidationAction</literal> interface:</para>
<programlisting>public interface IValidationAction
{
/// &lt;summary&gt;
/// Executes the action.
/// &lt;/summary&gt;
/// &lt;param name="isValid"&gt;Whether associated validator is valid or not.&lt;/param&gt;
/// &lt;param name="validationContext"&gt;Validation context.&lt;/param&gt;
/// &lt;param name="contextParams"&gt;Additional context parameters.&lt;/param&gt;
/// &lt;param name="errors"&gt;Validation errors container.&lt;/param&gt;
void Execute(bool isValid, object validationContext, IDictionary contextParams, ValidationErrors errors);
}</programlisting>
</section>
</section>
<section>
<title>Validator References</title>
<para>Sometimes it is not possible (or desirable) to nest all the
validation rules within a single top-level validator group. For example,
if you have an object graph where both ObjectA and ObjectB have a
reference to ObjectC, you might want to set up validation rules for
ObjectC only once and reference them from the validation rules for both
ObjectA and ObjectB, instead of duplicating them within both
definitions.</para>
<para>The syntax is shown below</para>
<programlisting>&lt;v:ref name="referencedValidatorId" context="validationContextForTheReferencedValidator"/&gt;</programlisting>
<para>An example is shown below</para>
<programlisting>&lt;v:group id="objectA.validator"&gt;
&lt;v:ref name="objectC.validator" context="MyObjectC"/&gt;
// other validators for ObjectA
&lt;/v:group&gt;
&lt;v:group id="objectB.validator"&gt;
&lt;v:ref name="objectC.validator" context="ObjectCProperty"/&gt;
// other validators for ObjectB
&lt;/v:group&gt;
&lt;v:group id="objectC.Validator"&gt;
// validators for ObjectC
&lt;/v:group&gt;</programlisting>
<para>It is as simple as that — you define validation rules for ObjectC
separately and reference them from within other validation groups.
Important thing to realize that in most cases you will also want to
"narrow" the context for the referenced validator, typically by specifying
the name of the property that holds referenced object. In the example
above, ObjectA.MyObjectC and ObjectB.ObjectCProperty are both of type
ObjectC, which objectC.validator expects to receive as the validation
context.</para>
</section>
<section>
<title>Progammatic usage</title>
<para>You can also create Validators programmatically using the API. An
example is shown below</para>
<programlisting>UserInfo userInfo = new UserInfo(); // has Name and Password props
ValidatorGroup userInfoValidator = new ValidatorGroup();
userInfoValidator.Validators
.Add(new RequiredValidator("Name", null));
userInfoValidator.Validators
.Add(new RequiredValidator("Password", null));
ValidationErrors errors = new ValidationErrors();
bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors);
</programlisting>
<para>No matter if you create your validators programmatically or
declaratively, you can invoke them in service side code via the 'Validate'
method shown above and then handle error conditions. Spring provides AOP
parameter validation advice as part of ithe <link
linkend="aop-aspect-library">aspect library</link> which may also be
useful for performing server-side validation.</para>
</section>
<section id="validation-aspnet-usage">
<title>Usage tips within ASP.NET</title>
<para>Now that you know how to configure validation rules, let's see what
it takes to evaluate those rules within your typical ASP.NET application
and to display error messages.</para>
<para>The first thing you need to do is inject validators you want to use
into your ASP.NET page, as shown in the example below:</para>
<programlisting>&lt;objects xmlns="http://www.springframework.net" xmlns:v="http://www.springframework.net/validation"&gt;
&lt;object type="TripForm.aspx" parent="standardPage"&gt;
&lt;property name="TripValidator" ref="tripValidator" /&gt;
&lt;/object&gt;
&lt;v:group id="tripValidator"&gt;
// our validation rules
&lt;/v:group&gt;
&lt;/objects&gt;</programlisting>
<para>Once that's done, you need to perform validation in one or more of
the page event handlers, which typically looks similar to this:</para>
<programlisting>public void SearchForFlights(object sender, EventArgs e)
{
if (Validate(Controller.Trip, tripValidator))
{
Process.SetView(Controller.SearchForFlights());
}
}</programlisting>
<note>
<para>Keep in mind that your ASP.NET page needs to extend
Spring.Web.UI.Page in order for the code above to work.</para>
</note>
<para>Finally, you need to define where validation errors should be
displayed by adding one or more
<literal>&lt;spring:validationError/&gt;</literal> and
<literal>&lt;spring:validationSummary/&gt;</literal> controls to the
ASP.NET form:</para>
<programlisting>&lt;%@ Page Language="c#" MasterPageFile="~/Web/StandardTemplate.master" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %&gt;
&lt;%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %&gt;
&lt;asp:Content ID="head" ContentPlaceHolderID="head" runat="server"&gt;
&lt;script language="javascript" type="text/javascript"&gt;
&lt;!--
function showReturnCalendar(isVisible)
{
document.getElementById('&lt;%= returningOnDate.ClientID %&gt;').style.visibility = isVisible? '': 'hidden';
document.getElementById('returningOnCalendar').style.visibility = isVisible? '': 'hidden';
}
--&gt;
&lt;/script&gt;
&lt;/asp:Content&gt;
&lt;asp:Content ID="body" ContentPlaceHolderID="body" runat="server"&gt;
&lt;div style="text-align: center"&gt;
&lt;h4&gt;&lt;asp:Label ID="caption" runat="server"&gt;&lt;/asp:Label&gt;&lt;/h4&gt;
<emphasis role="bold">&lt;spring:ValidationSummary ID="validationSummary" runat="server" /&gt;</emphasis>
&lt;table&gt;
&lt;tr class="formLabel"&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td colspan="3"&gt;
&lt;spring:RadioButtonGroup ID="tripMode" runat="server"&gt;
&lt;asp:RadioButton ID="OneWay" onclick="showReturnCalendar(false);" runat="server" /&gt;
&lt;asp:RadioButton ID="RoundTrip" onclick="showReturnCalendar(true);" runat="server" /&gt;
&lt;/spring:RadioButtonGroup&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class="formLabel" align="right"&gt;
&lt;asp:Label ID="leavingFrom" runat="server" /&gt;&lt;/td&gt;
&lt;td nowrap="nowrap"&gt;
&lt;asp:DropDownList ID="leavingFromAirportCode" AutoCallBack="true" runat="server" /&gt;
<emphasis role="bold">&lt;spring:ValidationError id="departureAirportErrors" runat="server" /&gt;</emphasis>
&lt;/td&gt;
&lt;td class="formLabel" align="right"&gt;
&lt;asp:Label ID="goingTo" runat="server" /&gt;&lt;/td&gt;
&lt;td nowrap="nowrap"&gt;
&lt;asp:DropDownList ID="goingToAirportCode" AutoCallBack="true" runat="server" /&gt;
<emphasis role="bold">&lt;spring:ValidationError id="destinationAirportErrors" runat="server" /&gt;</emphasis>
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class="formLabel" align="right"&gt;
&lt;asp:Label ID="leavingOn" runat="server" /&gt;&lt;/td&gt;
&lt;td nowrap="nowrap"&gt;
&lt;spring:Calendar ID="leavingFromDate" runat="server" Width="75px" AllowEditing="true" Skin="system" /&gt;
<emphasis role="bold">&lt;spring:ValidationError id="departureDateErrors" runat="server" /&gt;</emphasis>
&lt;/td&gt;
&lt;td class="formLabel" align="right"&gt;
&lt;asp:Label ID="returningOn" runat="server" /&gt;&lt;/td&gt;
&lt;td nowrap="nowrap"&gt;
&lt;div id="returningOnCalendar"&gt;
&lt;spring:Calendar ID="returningOnDate" runat="server" Width="75px" AllowEditing="true" Skin="system" /&gt;
<emphasis role="bold">&lt;spring:ValidationError id="returnDateErrors" runat="server" /&gt;</emphasis>
&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class="buttonBar" colspan="4"&gt;
&lt;br/&gt;
&lt;asp:Button ID="findFlights" runat="server"/&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;script language="javascript" type="text/javascript"&gt;
if (document.getElementById('&lt;%= tripMode.ClientID %&gt;').value == 'OneWay')
showReturnCalendar(false);
else
showReturnCalendar(true);
&lt;/script&gt;
&lt;/asp:Content&gt;</programlisting>
<section>
<title>Rendering Validation Errors</title>
<para>Spring.NET allows you to render validation errors within the page
in several different ways, and if none of them suits your needs you can
implement your own validation errors renderer. Implementations of the
<literal>Spring.Web.Validation.IValidationErrorsRenderer</literal> that
ship with the framework are:</para>
<table>
<title>Validation Renderers</title>
<tgroup cols="3">
<colspec colname="c1" colwidth="1*" />
<colspec colname="c2" colwidth="5*" />
<colspec colname="c3" colwidth="10*" />
<thead>
<row>
<entry align="left">Name</entry>
<entry align="center">Class</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>Block</entry>
<entry><literal>Spring.Web.Validation.DivValidationErrorsRenderer
</literal></entry>
<entry>Renders validation errors as list items within a
<literal>&lt;div&gt;</literal> tag. Default renderer for
<literal>&lt;spring:validationSummary&gt;</literal>
control.</entry>
</row>
<row>
<entry>Inline</entry>
<entry><literal>Spring.Web.Validation.SpanValidationErrorsRenderer
</literal></entry>
<entry>Renders validation errors within a
<literal>&lt;span&gt;</literal> tag. Default renderer for
<literal>&lt;spring:validationError&gt;</literal>
control.</entry>
</row>
<row>
<entry>Icon</entry>
<entry><literal>Spring.Web.Validation.IconValidationErrorsRenderer</literal></entry>
<entry>Renders validation errors as error icon, with error
messages displayed in a tooltip. Best option when saving screen
real estate is important.</entry>
</row>
</tbody>
</tgroup>
</table>
<para>These three error renderers should be sufficient for most
applications, but in case you want to display errors in some other way
you can write your own renderer by implementing
<literal>Spring.Web.Validation.IValidationErrorsRenderer</literal>
interface:</para>
<programlisting>namespace Spring.Web.Validation
{
/// &lt;summary&gt;
/// This interface should be implemented by all validation errors renderers.
/// &lt;/summary&gt;
/// &lt;remarks&gt;
/// &lt;para&gt;
/// Validation errors renderers are used to decouple rendering behavior from the
/// validation errors controls such as &lt;see cref="ValidationError"/&gt; and
/// &lt;see cref="ValidationSummary"/&gt;.
/// &lt;/para&gt;
/// &lt;para&gt;
/// This allows users to change how validation errors are rendered by simply plugging in
/// appropriate renderer implementation into the validation errors controls using
/// Spring.NET dependency injection.
/// &lt;/para&gt;
/// &lt;/remarks&gt;
public interface IValidationErrorsRenderer
{
/// &lt;summary&gt;
/// Renders validation errors using specified &lt;see cref="HtmlTextWriter"/&gt;.
/// &lt;/summary&gt;
/// &lt;param name="page"&gt;Web form instance.&lt;/param&gt;
/// &lt;param name="writer"&gt;An HTML writer to use.&lt;/param&gt;
/// &lt;param name="errors"&gt;The list of validation errors.&lt;/param&gt;
void RenderErrors(Page page, HtmlTextWriter writer, IList errors);
}
}</programlisting>
<section>
<title>Configuring which Error Renderer to use.</title>
<para>The best part of the errors renderer mechanism is that you can
easily change it across the application by modifying configuration
templates for <literal>&lt;spring:validationSummary&gt;</literal> and
<literal>&lt;spring:validationError&gt;</literal> controls:</para>
<programlisting>&lt;!-- Validation errors renderer configuration --&gt;
&lt;object id="Spring.Web.UI.Controls.ValidationError" abstract="true"&gt;
&lt;property name="Renderer"&gt;
&lt;object type="Spring.Web.Validation.IconValidationErrorsRenderer, Spring.Web"&gt;
&lt;property name="IconSrc" value="validation-error.gif"/&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;object id="Spring.Web.UI.Controls.ValidationSummary" abstract="true"&gt;
&lt;property name="Renderer"&gt;
&lt;object type="Spring.Web.Validation.DivValidationErrorsRenderer, Spring.Web"&gt;
&lt;property name="CssClass" value="validationError"/&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>It's as simple as that!</para>
</section>
</section>
</section>
</chapter>

204
doc/reference/src/vsnet.xml Normal file
View File

@@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="vsnet">
<title>Visual Studio.NET Integration</title>
<sect1 id="vsnet-config-section">
<title>XML Editing and Validation</title>
<para>Most of this section is well travelled territory for those familiar
with editing XML files in their favorite XML editor. The XML configuration
data that defines the objects that Spring will manage for you are
validated against the Spring.NET XML Schema at runtime. The location of
the XML configuration data to create an
<literal>IApplicationContext</literal> can be any of the resource
locations supported by Spring's <classname>IResource</classname>
abstraction. (See <xref linkend="objects-iresource" /> for more
information.) To create an <classname>IApplicationContext</classname>
using a "standalone" XML configuration file the custom configuration
section in the standard .NET application configuration would read:</para>
<programlisting>&lt;spring&gt;
&lt;context&gt;
&lt;resource uri="file://objects.xml"/&gt;
&lt;/context&gt;
&lt;/spring&gt;</programlisting>
The VS.NET 2005 XML editor can use the attribute
<literal>xsi:schemaLocation</literal>
as a hint to associate the physical location of a schema file with the XML document being edited. VS.NET 2002/2003 do not recognize the
<literal>xsi:schemaLocation</literal>
element. If you reference the Spring.NET XML schema as shown below, you can get intellisense and validation support while editing a Spring configuration file in VS.NET 2005. In order to get this functionality in VS.NET 2002/2003 you will need to register the schema with VS.NET or include the schema as part of your application project.
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd"&gt;
&lt;object id="..." type="..."&gt;
...
&lt;/object&gt;
&lt;object id="..." type="..."&gt;
...
&lt;/object&gt;
...
&lt;/objects&gt;</programlisting>
<para>It is typically more convenient to install the schema in VS.NET,
even for VS.NET 2005, as it makes the xml a little less verbose and you
don't need to keep copying the XSD file for each project you create. For
VS.NET 2003 the schema directory will be either</para>
<para><literal>C:\Program Files\Microsoft Visual Studio .NET
2003\Common7\Packages\schemas\xml</literal> for VS 2003</para>
<para>or</para>
<para><literal>C:\Program Files\Microsoft Visual Studio
.NET\Common7\Packages\schemas\xml</literal> for VS.NET 2002</para>
<para>The VS.NET 2005 directory for XML schemas is</para>
<para>
<literal>C:\Program Files\Microsoft Visual Studio
8\Xml\Schemas</literal>
</para>
<para>Spring's .xsd schemas are located in the directory doc/schema. In
that directory is also a NAnt build file to help copy over the .xsd files
to the appropriate VS.NET locations. To execute this script simply type
'<literal>nant</literal>' in the doc/schema directory.</para>
<para>Once you have registered the schema with VS.NET you can adding only
the namespace declaration to the objects element,</para>
<para>
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;object id="..." type="..."&gt;
...
&lt;/object&gt;
&lt;object id="..." type="..."&gt;
...
&lt;/object&gt;
...
&lt;/objects&gt;</programlisting>
</para>
<para>Once registered, the namespace declaration alone is sufficient to
get intellisense and validation of the configuration file from within
VS.NET. Alternatively, you can select the .xsd file to use by setting the
targetSchema property in the Property Sheet for the configuration
file.</para>
<para>As shown in the section <xref linkend="objects-factory-client" />
Spring.NET supports using .NET's application configuration file as the
location to store the object definitions that will be managed by the
object factory.</para>
<programlisting>
&lt;configuration&gt;
&lt;configSections&gt;
&lt;sectionGroup name="spring"&gt;
&lt;section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/&gt;
&lt;section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /&gt;
&lt;/sectionGroup&gt;
&lt;/configSections&gt;
&lt;spring&gt;
&lt;context&gt;
&lt;resource uri="config://spring/objects"/&gt;
&lt;/context&gt;
&lt;objects xmlns="http://www.springframework.net"&gt;
...
&lt;/objects&gt;
&lt;/spring&gt;
&lt;/configuration&gt;
</programlisting>
<para>In this case VS.NET 2002/2003 will still provide you with
intellisense help but you will not be able to fully validate the document
as the entire schema for App.config is not known. To be able to validate
this document one would need to install the <ulink
url="http://www.radsoftware.com.au/articles/intellisensewebconfig.aspx">.NET
Configuration File schema</ulink> and an additional schema that
incorporates the <literal>&lt;spring&gt;</literal> and
<literal>&lt;context&gt;</literal> section in addition to the
<literal>&lt;objects&gt;</literal> would need to be created.</para>
<para>Validating schema is a new feature in VS 2005 it is validating all
the time while you edit, you will see any errors that it finds in the
Error List window.</para>
<para>Keep these trade offs in mind as you decide where to place the bulk
of your configuration information. Conventional wisdom is do quick
prototyping with App.config and use another IResource location, file or
embedded assembly resource, for serious development.</para>
</sect1>
<sect1 id="vsnet-schema-versions">
<title>Versions of XML Schema</title>
<para>The schema was updated from Spring 1.0.1 to 1.0.2 in order to
support generics. The schema for version 1.0.1 is located under
<literal>http://www.springframework.net/xsd/1.0.1/</literal> The schema
for the latest version will always be located under
<literal>http://www.springframework.net/xsd/</literal></para>
</sect1>
<sect1 id="vsnet-api-help">
<title>Integrated API help</title>
<para>Spring provides API documentation that can be integrated within
Visual Studio. There are two versions of the documentation, one for VS.NET
2002/2003 and the other for VS 2005. They differ only in the format
applied, VS 2005 using the sexy new format. Enjoy!</para>
</sect1>
</chapter>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="web-quickstart">
<title>Web Quickstarts</title>
<section>
<title>Introduction</title>
<para>The Web Quickstart solution provides basic 'Hello World' examples
for using Spring.Web features. You can use this solution as a starting
point and then move on to the SpringAir application that uses a wider
range of Spring.Web features. </para>
</section>
</chapter>

2474
doc/reference/src/web.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,536 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="webservices">
<title>Web Services</title>
<sect1>
<title>Introduction</title>
<para>While the out-of-the-box support for web services in .NET is
excellent, there are a few areas that the Spring.NET thought could use
some improvement. Spring adds the ability to perform dependency injection
on standard asmx web services. Spring's .NET Web Services support also
allows you to export a 'plain .NET object' as a .NET web service By "plain
.NET object" we mean classes that do not contain infrastructure specific
attributes, such as WebMethod. On the server side, Spring's .NET web
service exporters will automatically create a proxy that adds web service
attributes. On the client side you can use Spring IoC container to
configure a client side proxy that you generated with standard command
line tools. Additionally, Spring provides the functionality to create the
web service proxy dynamically at runtime (much like running the command
line tools but at runtime and without some of the tools quirks) and use
dependency injection to configure the resulting proxy class. On both the
server and client side, you can apply AOP advice to add behavior such as
logging, exception handling, etc. that is not easily encapsulated within
an inheritance hierarchy across the application.</para>
</sect1>
<sect1 id="server-side">
<title>Server-side</title>
<para>One thing that the Spring.NET team didn't like much is that we had
to have all these .asmx files lying around when all said files did was
specify which class to instantiate to handle web service requests.</para>
<para>Second, the Spring.NET team also wanted to be able to use the
Spring.NET IoC container to inject dependencies into our web service
instances. Typically, a web service will rely on other objects, service
objects for example, so being able to configure which service object
implementation to use is very useful.</para>
<para>Last, but not least, the Spring.NET team did not like the fact that
creating a web service is an implementation task. Most (although not all)
services are best implemented as normal classes that use coarse-grained
service interfaces, and the decision as to whether a particular service
should be exposed as a remote object, web service, or even an enterprise
(COM+) component, should only be a matter of configuration, and not
implementation.</para>
<para>An example using the web service exporter can be found in quickstart
example named 'calculator'. More information can be found here '<link
linkend="websvc-example">Web Services example</link>'.</para>
<sect2>
<title>Removing the need for .asmx files</title>
<para>Unlike web pages, which use <literal>.aspx</literal> files to
store presentation code, and code-behind classes for the logic, web
services are completely implemented within the code-behind class. This
means that .asmx files serve no useful purpose, and as such they should
neither be necessary nor indeed required at all.</para>
<para>Spring.NET allows application developers to expose existing web
services easily by registering a custom implementation of the
<classname>WebServiceHandlerFactory</classname> class and by creating a
standard Spring.NET object definition for the service.</para>
<para>By way of an example, consider the following web service...</para>
<programlisting>
namespace MyComany.MyApp.Services
{
[WebService(Namespace="http://myCompany/services")]
public class HelloWorldService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World!";
}
}
}
</programlisting>
<para>This is just a standard class that has methods decorated with the
<classname>WebMethod</classname> attribute and (at the class-level) the
<classname>WebService</classname> attribute. Application developers can
create this web service within Visual Studio just like any other
class.</para>
<para>All that one need to do in order to publish this web service
is:</para>
<para><emphasis> 1. Register the
<classname>Spring.Web.Services.WebServiceFactoryHandler</classname> as
the HTTP handler for <literal>*.asmx</literal> requests within one's
<literal>web.config</literal> file. </emphasis></para>
<programlisting>
&lt;system.web&gt;
&lt;httpHandlers&gt;
&lt;add verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/&gt;
&lt;/httpHandlers&gt;
&lt;/system.web&gt;
</programlisting>
<para>Of course, one can register any other extension as well, but
typically there is no need as Spring.NET's handler factory will behave
exactly the same as a standard handler factory if said handler factory
cannot find the object definition for the specified service name. In
that case the handler factory will simply look for an .asmx file.</para>
<para>If you are using IIS7 the following configuration is needed</para>
<programlisting>&lt;system.webServer&gt;
&lt;validation validateIntegratedModeConfiguration="false"/&gt;
&lt;handlers&gt;
&lt;add name="SpringWebServiceSupport" verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/&gt;
&lt;/handlers&gt;
&lt;/system.webServer&gt;</programlisting>
<para><emphasis>2. Create an object definition for one's web
service.</emphasis></para>
<programlisting>&lt;object name="HelloWorld" type="MyComany.MyApp.Services.HelloWorldService, MyAssembly" abstract="true"/&gt;</programlisting>
<para>Note that one is not absolutely required to make the web service
object definition <literal>abstract</literal> (via the
<literal>abstract="true"</literal> attribute), but this is a recommended
best practice in order to avoid creating an unnecessary instance of the
service. Because the .NET infrastructure creates instances of the target
service object internally for each request, all Spring.NET needs to
provide is the <classname>System.Type</classname> of the service class,
which can be retrieved from the object definition even if it is marked
as <literal>abstract</literal>.</para>
<para>That's pretty much it as we can access this web service using the
value specified for the <literal>name</literal> attribute of the object
definition as the service name:</para>
<programlisting>http://localhost/MyWebApp/HelloWorld.asmx</programlisting>
</sect2>
<sect2>
<title>Injecting dependencies into web services</title>
<para>For arguments sake, let's say that we want to change the
implementation of the <literal>HelloWorld</literal> method to make the
returned message configurable.</para>
<para>One way to do it would be to use some kind of message locator to
retrieve an appropriate message, but that locator needs to implemented.
Also, it would certainly be an odd architecture that used dependency
injection throughout the application to configure objects, but that
resorted to the service locator approach when dealing with web
services.</para>
<para>Ideally, one should be able to define a property for the message
within one's web service class and have Spring.NET inject the message
value into it:</para>
<programlisting>
namespace MyApp.Services
{
public interface IHelloWorld
{
string HelloWorld();
}
[WebService(Namespace="http://myCompany/services")]
public class HelloWorldService : IHelloWorld
{
private string message;
public string Message
{
set { message = value; }
}
[WebMethod]
public string HelloWorld()
{
return this.message;
}
}
}
</programlisting>
<para>The problem with standard Spring.NET DI usage in this case is that
Spring.NET does not control the instantiation of the web service. This
happens deep in the internals of the .NET framework, thus making it
quite difficult to plug in the code that will perform the
configuration.</para>
<para>The solution is to create a dynamic server-side proxy that will
wrap the web service and configure it. That way, the .NET framework gets
a reference to a proxy type from Spring.NET and instantiates it. The
proxy then asks a Spring.NET application context for the actual web
service instance that will process requests.</para>
<para>This proxying requires that one export the web service explicitly
using the <classname>Spring.Web.Services.WebServiceExporter</classname>
class; in the specific case of this example, one must also not forget to
configure the <literal>Message</literal> property for said
service:</para>
<programlisting>
&lt;object id="HelloWorld" type="MyApp.Services.HelloWorldService, MyApp"&gt;
&lt;property name="Message" value="Hello, World!"/&gt;
&lt;/object&gt;
&lt;object id="HelloWorldExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web"&gt;
&lt;property name="TargetName" value="HelloWorld"/&gt;
&lt;/object&gt;
</programlisting>
<para>The <classname>WebServiceExporter</classname> 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
<classname>WebServiceExporter</classname>.</para>
<tip>
<title>Interface Requirements</title>
<para>In order to support some advanced usage scenarios, such as the
ability to expose an AOP proxy as a web service (allowing the addition
of AOP advices to web service methods), Spring.NET requires those
objects that need to be exported as web services to implement a
(service) interface.</para>
<para>Only methods that belong to an interface will be exported by the
<classname>WebServiceExporter</classname>.</para>
</tip>
</sect2>
<sect2>
<title>Exposing PONOs as Web Services</title>
<para>Now that we are generating a server-side proxy for the service,
there is really no need for it to have all the attributes that web
services need to have, such as <classname>WebMethod</classname>. Because
.NET infrastructure code never really sees the "real" service, those
attributes are redundant as the proxy needs to have them on its methods,
because that's what .NET deals with, but they are not necessary on the
target service's methods.</para>
<para>This means that we can safely remove the
<classname>WebService</classname> and <classname>WebMethod</classname>
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
<classname>WebMethod</classname> 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 <classname>WebService</classname> and
<classname>WebMethod</classname> attributes provide, such as service
namespace, description, transaction mode, etc. One way to keep those
values is to leave them within the service class and the proxy generator
will simply copy them to the proxy class instead of creating empty ones,
but that really does defeat the purpose.</para>
<para>To add specific attributes to the exported web service, you can
set all the necessary values within the definition of the service
exporter, like so...</para>
<programlisting>
&lt;object id="HelloWorldExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web"&gt;
&lt;property name="TargetName" value="HelloWorld"/&gt;
&lt;property name="Namespace" value="http://myCompany/services"/&gt;
&lt;property name="Description" value="My exported HelloWorld web service"/&gt;
&lt;property name="MemberAttributes"&gt;
&lt;dictionary&gt;
&lt;entry key="HelloWorld"&gt;
&lt;object type="System.Web.Services.WebMethodAttribute, System.Web.Services"&gt;
&lt;property name="Description" value="My Spring-configured HelloWorld method."/&gt;
&lt;property name="MessageName" value="ZdravoSvete"/&gt;
&lt;/object&gt;
&lt;/entry&gt;
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;/object&gt;
// or, once configuration improvements are implemented...
&lt;web:service targetName="HelloWorld" namespace="http://myCompany/services"&gt;
&lt;description&gt;My exported HelloWorld web service.&lt;/description&gt;
&lt;methods&gt;
&lt;method name="HelloWorld" messageName="ZdravoSvete"&gt;
&lt;description&gt;My Spring-configured HelloWorld method.&lt;/description&gt;
&lt;/method&gt;
&lt;/methods&gt;
&lt;/web:service&gt;
</programlisting>
<para>Based on the configuration above, Spring.NET will generate a web
service proxy for all the interfaces implemented by a target and add
attributes as necessary. This accomplishes the same goal while at the
same time moving web service metadata from implementation class to
configuration, which allows one to export pretty much
<emphasis>any</emphasis> class as a web service.</para>
<para>The WebServiceExporter also has a
<literal>TypeAttributes</literal> IList property for applying attributes
at the type level.<note>
<para>The attribute to confirms to the WSI basic profile 1.1 is not
added by default. This will be added in a future release. In the
meantime use the TypeAttributes IList property to add
<literal>[WebServiceBinding(ConformsTo=WsiProfiles.BasicProfile1_1)]</literal>
to the generated proxy.</para>
</note></para>
<para>One can also export only certain interfaces that a service class
implements by setting the <literal>Interfaces</literal> property of the
<classname>WebServiceExporter</classname>.</para>
<warning>
<title>Distributed Objects Warning</title>
<para>Distributed Objects Warning</para>
<para>Just because you <emphasis>can</emphasis> export any object as a
web service, doesn't mean that you <emphasis>should</emphasis>.
Distributed computing principles still apply and you need to make sure
that your services are not chatty and that arguments and return values
are Serializable.</para>
<para>You still need to exercise common sense when deciding whether to
use web services (or remoting in general) at all, or if local service
objects are all you need.</para>
</warning>
</sect2>
<sect2>
<title>Exporting an AOP Proxy as a Web Service</title>
<para>It is often useful to be able to export an AOP proxy as a web
service. For example, consider the case where you have a service that is
wrapped with an AOP proxy that you want to access both locally and
remotely (as a web service). The local client would simply obtain a
reference to an AOP proxy directly, but any remote client needs to
obtain a reference to an exported web service proxy, that delegates
calls to an AOP proxy, that in turn delegates them to a target object
while applying any configured AOP advice.</para>
<para>Effecting this setup is actually fairly straightforward; because
an AOP proxy is an object just like any other object, all you need to do
is set the <classname>WebServiceExporter</classname>'s
<literal>TargetName</literal> property to the <literal>id</literal> (or
indeed the <literal>name</literal> or <literal>alias</literal>) of the
AOP proxy. The following code snippets show how to do this...</para>
<programlisting>
&lt;object id="DebugAdvice" type="MyApp.AOP.DebugAdvice, MyApp"/&gt;
&lt;object id="TimerAdvice" type="MyApp.AOP.TimerAdvice, MyApp"/&gt;
&lt;object id="MyService" type="MyApp.Services.MyService, MyApp"/&gt;
&lt;object id="MyServiceProxy" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"&gt;
&lt;property name="TargetName" value="MyService"/&gt;
&lt;property name="IsSingleton" value="true"/&gt;
&lt;property name="InterceptorNames"&gt;
&lt;list&gt;
&lt;value&gt;DebugAdvice&lt;/value&gt;
&lt;value&gt;TimerAdvice&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;object id="MyServiceExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web"&gt;
&lt;property name="TargetName" value="MyServiceProxy"/&gt;
&lt;property name="Name" value="MyService"/&gt;
&lt;property name="Namespace" value="http://myApp/webservices"/&gt;
&lt;property name="Description" value="My web service"/&gt;
&lt;/object&gt;
</programlisting>
<para>That's it as every call to the methods of the exported web service
will be intercepted by the target AOP proxy, which in turn will apply
the configured debugging and timing advice to it.</para>
</sect2>
</sect1>
<sect1 id="client-side">
<title>Client-side</title>
<para>On the client side, the main objection the Spring.NET team has is
that client code becomes tied to a proxy <emphasis>class</emphasis>, and
not to a service <emphasis>interface</emphasis>. Unless you make the proxy
class implement the service interface manually, as described by Juval Lowy
in his book "Programming .NET Components", application code will be less
flexible and it becomes very difficult to plug in different service
implementation in the case when one decides to use a new and improved web
service implementation or a local service instead of a web service.</para>
<para>The goal for Spring.NET's web services support is to enable the easy
generation of client-side proxies that implement a specific service
interface.</para>
<sect2>
<title>Using VS.NET generated proxy</title>
<para>The problem with the web-service proxy classes that are generated
by VS.NET or the WSDL command line utility is that they don't implement
a service interface. This tightly couples client code with web services
and makes it impossible to change the implementation at a later date
without modifying and recompiling the client.</para>
<para>Spring.NET provides a simple <classname>IFactoryObject</classname>
implementation that will generate a <emphasis>"proxy for
proxy"</emphasis> (however obtuse that may sound). Basically, the
<classname>Spring.Web.Services.WebServiceProxyFactory</classname> 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
that we wish to expose as a web service...</para>
<programlisting>
namespace MyCompany.Services
{
public interface IHelloWorld
{
string HelloWorld();
}
}
</programlisting>
<para>In order to be able to reference a web service endpoint through
this interface, you need to add a definition similar to the example
shown below to your client's application context:</para>
<programlisting>
&lt;object id="HelloWorld" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services"&gt;
&lt;property name="ProxyType" value="MyCompany.WebServices.HelloWorld, MyClientApp"/&gt;
&lt;property name="ServiceInterface" value="MyCompany.Services.IHelloWorld, MyServices"/&gt;
&lt;/object&gt;
</programlisting>
<para>What is important to notice is that the underlying implementation
class for the web service does not have to implement the same
<classname>IHelloWorld</classname> 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
<classname>WebServiceExporter</classname>, which requires an interface
in order to work.</para>
</sect2>
<sect2>
<title>Generating proxies dynamically</title>
<para>The <classname>WebServiceProxyFactory</classname> can also
dynamically generate a web-service proxy. The XML object definition for
this factory object is shown below</para>
<programlisting>
&lt;object id="calculatorService" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services"&gt;
&lt;property name="ServiceUri" value="http://myServer/Calculator/calculatorService.asmx"/&gt;
&lt;!--&lt;property name="ServiceUri" value="file://~/calculatorService.wsdl"/&gt;--&gt;
&lt;property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract"/&gt;
&lt;!-- Dependency injection on Factory's product : the proxy instance of type SoapHttpClientProtocol --&gt;
&lt;property name="ProductTemplate"&gt;
&lt;object&gt;
&lt;property name="Timeout" value="10000" /&gt; &lt;!-- 10s --&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;/object&gt;
</programlisting>
<para>One use-case where this proxy is very useful is when dealing with
typed data sets through a web service. Leaving the pros and cons of this
approach aside, the current behavior of the proxy generator in .NET is
to create wrapper types for the typed dataset. This not only pollutes
the solution with extraneous classes but also results in multiple
wrapper types being created, one for each web service that uses the
typed dataset. This can quickly get confusing. The proxy created by
Spring allows you to reference you typed datasets directly, avoiding the
above mentioned issues.</para>
</sect2>
<sect2>
<title>Configuring the proxy instance</title>
<para>The <classname>WebServiceProxyFactory</classname> also implements
the interface,
<classname>Spring.Objects.Factory.IConfigurableFactoryObject</classname>,
allowing to specify configuration for the product that the
<classname>WebServiceProxyFactory</classname> creates. This is done by
specifying the ProductTemplate property. This is particularly useful for
securing the web service. An example is shown below.</para>
<programlisting>
&lt;object id="PublicarAltasWebService" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services"&gt;
&lt;property name="ProxyType" value="My.WebService" /&gt;
&lt;property name="ServiceInterface" value="My.IWebServiceInterface" /&gt;
<emphasis role="bold">&lt;property name="ProductTemplate"&gt; </emphasis>
&lt;object&gt;
&lt;!-- Configure the web service URL --&gt;
&lt;property name="Url" value="https://localhost/MyApp/webservice.jws" /&gt;
<emphasis role="bold"> &lt;!-- Configure the Username and password for the web service --&gt; </emphasis>
&lt;property name="Credentials"&gt;
&lt;object type="System.Net.NetworkCredential, System"&gt;
&lt;property name="UserName" value="user"/&gt;
&lt;property name="Password" value="password"/&gt;
&lt;/object&gt;
&lt;/property&gt;
<emphasis role="bold">&lt;!-- Configure client certificate for the web service --&gt; </emphasis>
&lt;property name="ClientCertificates"&gt;
&lt;list&gt;
&lt;object id="MyCertificate" type="System.Security.Cryptography.X509Certificates.X509Certificate2, System"&gt;
&lt;constructor-arg name="fileName" value="Certificate.p12" /&gt;
&lt;constructor-arg name="password" value="notgoingtotellyou" /&gt;
&lt;/object&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;/object&gt;
</programlisting>
<para>For an example of how using SOAP headers for authentication using
the WebServiceExporter and WebServiceProxyFactory, refer to this <ulink
url="http://opensource.atlassian.com/confluence/spring/download/attachments/708/Spring.Examples.SoapHeader.rar?version=1">solution</ulink>
on our wiki.</para>
</sect2>
</sect1>
</chapter>

View File

@@ -0,0 +1,643 @@
<chapter id="windows-service">
<title>Windows Services</title>
<sect1>
<title>Remarks</title>
<para>
This is functionality that will be included after the
1.0 release. If you want to use these features please get the
code from CVS <ulink url="http://opensource.atlassian.com/confluence/spring/display/NET/Project+Structure"></ulink>
(instructions) or from the download section of the Spring.NET website that contains an
.zip with the full CVS tree.
In addition to this documentation
you can refer to the example program located at
<literal>examples\Spring\Spring.Examples.WindowsService</literal>
to better understand the package. Please check the Spring.NET
<ulink url="http://www.springframework.net/doc/reference/windows-service.html">website</ulink>
for the latest updates to this document.
</para>
</sect1>
<sect1>
<title>Introduction</title>
<para>
Developers usually create Windows Services using the
Visual Studio .NET wizard. While not difficult to do, this
procedure is repetative and does not encourage separation between
infrastructure code (windows service) and application code. This is
generally considered a "bad thing" but you can certainly disagree.
</para>
<para>
As Spring.NET can provide an explicitly managed
initialize/destroy lifecycle for singleton objects, there is
a natural synergy with the lifecycle of a Windows service.
As such, it could be very convenient to expose a Spring application
context as a Windows service. Starting and stopping the service corresponds
to creating and destroying an application context and its
contained objects. This approach provides a high level means to
declare what objects are created and destroyed when developing
a Windows service.
</para>
<para>
To do that, Spring.NET requires the installation of one physical
service able to run as services as many applications as you want - each a
logical independent service in their own application domain.
By default, the deployment and updating of the service can also
be done by copying the relevant executables to a special directory.
</para>
<para>
The executable that at present provides these features is the
<literal>Spring.Services.WindowsService.Process.exe</literal>
assembly. It makes heavy use of classes and interfaces definde in
the <literal>Spring.Services.WindowsService.Common.dll</literal>
assembly. You should reference the common assembly it if you want to
follow the advice on customization contained in the following sections
</para>
<para>
The benefits of this approach, a part from those given by separating
infrastructure code and application code (a field where Spring.NET
tries hard to succeed) is that you can think about installing a new
service at client site by simply dropping a new application assembly
in a remote directory<footnote></footnote>.
</para>
</sect1>
<sect1>
<title>The <literal>Spring.Services.WindowsService.Process.exe</literal> application</title>
<sect2>
<title>Installing</title>
<para>
The installation can be done in two ways, using the .NET SDK
<literal>installutil.exe</literal> tool or using the more mundane
<literal>Spring.Services.WindowsService.Installer.exe</literal>;
while the former is the standard, the latter is probably
more flexible. It allows you to customize the name/display name of the
service and has the ability to install multiple times the same assembly
with different names. This can be useful in a
number of scenarios, especially where you don't like, for some
reasons, to run several different logical services under the
same physical windows service.
</para>
<para><emphasis>
Be aware of the fact that the service will be installed as
running with the system account (installing with a specific
user account seems a bit buggy on Windows XP)
</emphasis></para>
<para>
That said, while <literal>installutil</literal>
<ulink url="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cptools/html/cpconinstallerutilityinstallutilexe.asp">
<citetitle>is documented on its own </citetitle></ulink>,
the command line for
<literal>Spring.Services.WindowsService.Installer.exe</literal>
is as follow:
<programlisting format='linespecific'>Spring.Services.WindowsService.Installer.exe
usage:
install service-exe-path service-display-name service-name
uninstall service-name [i|u] service-exe-path service-display-name service-name</programlisting>
for example, to install, you can invoke it with the following:
<programlisting format='linespecific'>... install Spring.Services.WindowsService.Process.exe "Spring.Service Support" spring-service</programlisting>
and to uninstall it:
<programlisting format='linespecific'>... uninstall spring-service</programlisting>
</para>
</sect2>
<sect2>
<title>Configuration</title>
<para>
The standard .NET <literal>.config</literal> file
can be used to tune some parameters of
<literal>Spring.Services.WindowsService.Process.exe</literal>,
(including log4net settings, for which it is recomended to consult
the log4net documentation).
</para>
<para>
This file also define the context run by this process; here the file in its current beauty:
<programlisting format='linespecific'>
&lt;configuration&gt;
&lt;configSections&gt;
&lt;section name=&quot;log4net&quot; type=&quot;System.Configuration.IgnoreSectionHandler&quot; /&gt;
&lt;sectionGroup name=&quot;spring&quot;&gt;
&lt;section name=&quot;context&quot; type=&quot;Spring.Context.Support.ContextHandler, Spring.Core&quot; /&gt;
&lt;section name=&quot;objects&quot; type=&quot;Spring.Context.Support.DefaultSectionHandler, Spring.Core&quot; /&gt;
&lt;/sectionGroup&gt;
&lt;/configSections&gt;
&lt;spring&gt;
&lt;context type=&quot;Spring.Context.Support.XmlApplicationContext, Spring.Core&quot;&gt;
&lt;resource uri=&quot;file://~/service-process-definition.xml&quot; /&gt;
&lt;/context&gt;
&lt;/spring&gt;
&lt;system.runtime.remoting&gt;
&lt;application&gt;
&lt;channels&gt;
&lt;channel ref=&quot;http&quot; port=&quot;1234&quot; /&gt;
&lt;/channels&gt;
&lt;/application&gt;
&lt;/system.runtime.remoting&gt;
&lt;log4net&gt;
&lt;!-- see http://logging.apache.org/log4net/release/manual/introduction.html --&gt;
&lt;appender name=&quot;RollingFile&quot; type=&quot;log4net.Appender.RollingFileAppender&quot;&gt;
&lt;layout type=&quot;log4net.Layout.PatternLayout&quot;&gt;
&lt;conversionPattern value=&quot;%d [%t] %-5p %c{1} - %m%n&quot; /&gt;
&lt;/layout&gt;
&lt;file value=&quot;logs/Spring.Service.Process.log&quot; /&gt;
&lt;appendToFile value=&quot;true&quot; /&gt;
&lt;maximumFileSize value=&quot;500KB&quot; /&gt;
&lt;maxSizeRollBackups value=&quot;5&quot; /&gt;
&lt;/appender&gt;
&lt;appender name=&quot;OutputDebugString&quot; type=&quot;log4net.Appender.OutputDebugStringAppender&quot;&gt;
&lt;layout type=&quot;log4net.Layout.PatternLayout&quot;&gt;
&lt;conversionPattern value=&quot;%d{HH:mm:ss,fff} %-5p %c{2}(line:%L) - %m%n&quot; /&gt;
&lt;/layout&gt;
&lt;/appender&gt;
&lt;root&gt;
&lt;level value=&quot;OFF&quot; /&gt;
&lt;/root&gt;
&lt;logger name=&quot;Spring.Services&quot;&gt;
&lt;level value=&quot;ALL&quot; /&gt;
&lt;appender-ref ref=&quot;RollingFile&quot; /&gt;
&lt;appender-ref ref=&quot;OutputDebugString&quot; /&gt;
&lt;/logger&gt;
&lt;/log4net&gt;
&lt;/configuration&gt;</programlisting>
</para>
<para>
As you see, the context is defined in another file: let's review the objects it defines.
</para>
<para>
Firstly, it is worth notice that in order to 'localize' the service (i.e. to know where it is installed to use that directory as
base for the deploy dir as in the above file) you should define an object like this: the name is not
very important, it is important that it is an <classname>IObjectFactoryPostProcessor</classname> and so will be
automatically applied to this application context:
<programlisting format='linespecific'>
&lt;!-- provides access to the ${spring.services.process.base.dir} property --&gt;
&lt;object
name=&quot;localizer&quot;
type=&quot;Spring.Services.WindowsService.Common.Localizer+ForProcess, Spring.Services.WindowsService.Common&quot;&gt;
&lt;!-- change this to access the property with another prefix, for example ${foo.process.base.dir}
&lt;property name=&quot;prefix&quot; value=&quot;foo&quot;/&gt;
--&gt;
&lt;/object&gt;</programlisting>
</para>
<para>
In that object definition you can customize the prefix for the following string
<programlisting format='linespecific'>
public static readonly string SpringServicesProcessBaseDirFormat = &quot;{0}.process.base.dir&quot;;</programlisting>
but you usually won't need it; the default value is
<programlisting format='linespecific'>
public static readonly string DefaultPrefix = &quot;spring.services&quot;;</programlisting>
</para>
<para>
The sole important object defined by this context, i.e. the main object run by the service.
The thing you can (and should) configure is the path to the folder you will use as the deploy location;
the current definition, to avoid the need for a fully qualified path (e.g.: <literal>c:\spring\services</literal>) uses
the properties made available by the <literal>localizer</literal> above:
<programlisting format='linespecific'>
&lt;object
name=&quot;service&quot;
type=&quot;Spring.Services.WindowsService.Common.DefaultService, Spring.Services.WindowsService.Common&quot;
init-method=&quot;Start&quot;
destroy-method=&quot;Stop&quot;&gt;
&lt;property name=&quot;DeployPath&quot; value=&quot;${spring.services.process.base.dir}/deploy&quot;/&gt;
&lt;/object&gt;</programlisting>
</para>
<para>
The above object is then easily remoted using spring remoting utilities (please notice you should tune the remoting configuration
listed in the standard .NET <literal>.config</literal> file, listed above):
<programlisting format='linespecific'>
&lt;object name=&quot;remoted.service&quot; type=&quot;Spring.Remoting.SaoExporter, Spring.Services&quot;&gt;
&lt;property name=&quot;TargetName&quot; value=&quot;service&quot;/&gt;
&lt;property name=&quot;ServiceName&quot; value=&quot;SpringWindowsService.rem&quot;/&gt;
&lt;/object&gt;</programlisting>
</para>
</sect2>
</sect1>
<sect1>
<title>Running an application context as a windows service</title>
<para>
If you package an application using the layout and
conventions described here, you'll be able to run an
application context as a Windows Service.
The conventions used are modeled after those used by ASP.NET
and are very easy to follow.
</para>
<para>
As already said, you'll have a Spring.NET application context running in
a dedicated <literal>AppDomain</literal> hosted in a process running
as a windows service: that process is able to run many application contexts
simultaneously.
</para>
<para>A complete application runable as service consists of a
directory containing:
<itemizedlist spacing="compact">
<listitem>
<para>The .NET configuration file
<literal>service.config</literal>:
this file should define your application context.
Moreover this files will be used
by the CLR to configure the application domain
your application will run in, exactly as you expect.
This file has the same role of ASP.NET <literal>Web.config</literal>
file.
</para>
</listitem>
<listitem>
<para>Optional: an xml context file (<literal>watcher.xml</literal>)
defining the watcher for your application.</para>
<para>The watcher controls the automatic redeployment of the
service and is discussed more in the following section.</para>
</listitem>
<listitem>
<para>Recomended: along the lines of ASP.NET convention, a <literal>bin</literal>
subdirectory containing all
the assemblies your application needs; you can of course put
your assemblies in the same directory where you put
<literal>service.config</literal> but this is not encouraged ...
</para>
</listitem>
</itemizedlist>
</para>
<sect2>
<title><literal>service.config</literal></title>
<para>
This is the standard .NET configuration file for the
<literal>AppDomain</literal> that will host your application. It is
semantically equivalent to the ASP.NET <literal>Web.config</literal>
file.
<footnote>
<para>
<literal>log4net</literal> users please notice that (as
of 1.2 beta 9) file appenders, when dealing with a relative
file name, assume it is relative to the application
domain code base. If you use log4net, it is very handy with the mechanics used by
Spring Windows Service as every log file you will specify will
be relative the directory containing the service application.
</para>
</footnote>
</para>
<para>
This file should also define your application context. When the
service is started and stopped, the corresponding lifecycle methods
are called on all the singletons defined. Of course, singletons are
automatically instantiated by the application context when the
service starts. For more information on lifecycles in Spring.NET see
<xref linkend="objects-factory-lifecycle"/>
Here an example taken from the tests:
<programlisting format='linespecific'>
&lt;configuration&gt;
&lt;configSections&gt;
&lt;sectionGroup name=&quot;spring&quot;&gt;
&lt;section name=&quot;context&quot; type=&quot;Spring.Context.Support.ContextHandler, Spring.Core&quot; /&gt;
&lt;section name=&quot;objects&quot; type=&quot;Spring.Context.Support.DefaultSectionHandler, Spring.Core&quot; /&gt;
&lt;/sectionGroup&gt;
&lt;/configSections&gt;
&lt;appSettings&gt;
&lt;add key=&quot;port&quot; value=&quot;10&quot;/&gt;
&lt;/appSettings&gt;
&lt;spring&gt;
&lt;context type=&quot;Spring.Context.Support.XmlApplicationContext, Spring.Core&quot;&gt;
&lt;resource uri=&quot;file://~/service.xml&quot; /&gt;
&lt;/context&gt;
&lt;/spring&gt;
&lt;/configuration&gt;</programlisting>
</para>
<para>
In this case the context is (again!) defined in another file (author's personal taste...) and the only 'service' is the
<literal>echo</literal> object (there is also a <literal>PropertyPlaceholderConfigurer</literal> just to make the example
more realistic):
<programlisting format='linespecific'>
&lt;objects xmlns=&quot;http://www.springframework.net&quot;
xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
xsi:schemaLocation=&quot;http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd&quot;&gt;
&lt;object name=&quot;echo&quot;
type=&quot;Spring.Services.WindowsService.Samples.Echo, Spring.Services.WindowsService.Tests&quot;
init-method=&quot;Start&quot; destroy-method=&quot;Stop&quot;&gt;
&lt;property name=&quot;port&quot;&gt;&lt;value&gt;${port}&lt;/value&gt;&lt;/property&gt;
&lt;/object&gt;
&lt;object id=&quot;configurer&quot; type=&quot;Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core&quot;&gt;
&lt;property name=&quot;locations&quot;&gt;
&lt;list&gt;
&lt;value&gt;file://~/service.config&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name=&quot;configSections&quot;&gt;
&lt;list&gt;
&lt;value&gt;appSettings&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
</para>
<sect3>
<title>Let the application know where it is</title>
<para>
There are some properties you may need at runtime, when your services
will run, and you cannot know in advance. Hopefully, your xml
definition file will allow to find the information it needs using some
predefined variables you can use inside the service definition file
with the standard
NAnt style <literal>${property name}</literal> syntax.</para>
<para>These properies are:
<itemizedlist spacing="compact">
<listitem>
<para><literal>spring.services.application.fullpath</literal>
that will be replaced with the full path of the application's
<literal>AppDomain.BaseDirectory</literal>, i.e., where your
application has been deployed;</para>
</listitem>
<listitem>
<para><literal>spring.services.application.name</literal> that
will be replaced with the name of the subdirectory where the
application has been deployed. Each application is deployed in
its own directory, of course;</para>
</listitem>
</itemizedlist>
</para>
<para>
These properties are accessible only if one defines a localizer in the
context like this (the localizer is a special <literal>IObjectFactoryPostProcessor</literal>:
<programlisting format='linespecific'>
&lt;!-- provides access to the ${spring.services.application.*} properties --&gt;
&lt;object
name=&quot;localizer&quot;
type=&quot;Spring.Services.WindowsService.Common.Localizer+ForApplication, Spring.Services.WindowsService.Common&quot;&gt;
&lt;!-- change this to access the property with another prefix, for example ${foo.application.base.dir}
--&gt;
&lt;property name=&quot;prefix&quot; value=&quot;myPrefix&quot;/&gt;
&lt;/object&gt;</programlisting>
</para>
<para>
As you can see above, one can easily change the prefix used by that localizer and then write someting like:
<programlisting format='linespecific'>
&lt;object name=&quot;simple&quot;
type=&quot;Spring.Services.WindowsService.Samples.Simple, Spring.Services.WindowsService.Tests&quot;
init-method=&quot;Start&quot; destroy-method=&quot;Stop&quot;&gt;
&lt;constructor-arg index=&quot;0&quot; value=&quot;${myPrefix.application.name},${myPrefix.application.fullPath}&quot;/&gt;
&lt;property name=&quot;AppName&quot;&gt;
&lt;value&gt;${myPrefix.application.name}&lt;/value&gt;
&lt;/property&gt;
&lt;property name=&quot;AppFullPath&quot;&gt;
&lt;value&gt;${myPrefix.application.fullpath}&lt;/value&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
</para>
</sect3>
</sect2>
<sect2>
<title><literal>watcher.xml</literal> - optional</title>
<para>
This file allows you to optionally define a watcher for your application
that can automatically redeploy it when needed.
</para>
<para>
The important thing to notice is that you can define your own
application watcher, named <literal>watcher</literal>. Here it is used
a watcher that listen for changes on the filesystem, configured to
listen for some changes and to ignore others.
</para>
<para>
You can provide your own implementation defining an object named
<literal>watcher</literal> that implements
<literal>Spring.Services.WindowsService.Common.Deploy.IApplicationWatcher</literal>:
<programlisting format='linespecific'>
/// &lt;summary&gt;
Interface defining the contract for an application watcher.
&lt;p&gt;An application watcher is responsible to dispatch an
&lt;see cref=&quot;IApplicationWatcherFactory&quot;&gt;event&lt;/see&gt; whenever it thinks the
application has been updated.&lt;/p&gt;
&lt;p&gt;Usually it should not raise other kind of events
as they are usually raised by the &lt;see cref=&quot;FileSystemApplicationWatcher&quot;/&gt;
that creates the watcher itself&lt;/p&gt;
&lt;/summary&gt;
&lt;remarks&gt;Usually instances of this interface need to be disposed&lt;/remarks&gt;
&lt;seealso cref=&quot;DeployEventArgs&quot;/&gt;
&lt;seealso cref=&quot;IDeployLocation&quot;/&gt;
&lt;seealso cref=&quot;DeployEventType.ApplicationUpdated&quot;/&gt;
&lt;seealso cref=&quot;DeployEventAggregator&quot;/&gt;
&lt;seealso cref=&quot;IDeployLocation&quot;/&gt;
&lt;seealso cref=&quot;DeployEventType&quot;/&gt;
public interface IApplicationWatcher : IDisposable
{
/// &lt;summary&gt;
/// The watched application
/// &lt;/summary&gt;
IApplication Application {get; }
/// &lt;summary&gt;
/// Start to watch the application, using the given dispatcher to
/// dispatch deply events
/// &lt;/summary&gt;
/// &lt;param name=&quot;dispatcher&quot;&gt;the dispatcher used to raise deploy events&lt;/param&gt;
void StartWatching (IDeployEventDispatcher dispatcher);
/// &lt;summary&gt;
/// Stop to watch the application.
/// &lt;/summary&gt;
void StopWatching ();
/// &lt;summary&gt;
/// If physical events watched by this watcher should be filtered, this methods
/// will allow to set filters that allows and disallows the event to be raised
/// by the watcher.
/// &lt;/summary&gt;
/// &lt;param name=&quot;allows&quot;&gt;the list of allowing filters&lt;/param&gt;
/// &lt;param name=&quot;disallows&quot;&gt;the list of disallowing filters&lt;/param&gt;
/// &lt;seealso cref=&quot;FilteringSupport&quot;/&gt;
/// &lt;seealso cref=&quot;RegularExpressionFilter&quot;/&gt;
void SetFilters (IList allows, IList disallows);
}</programlisting>
</para>
<para>
Please notice that this interface is currently a movable target and
will probably change before the first official release (this will probably
affect also the way a watcher will know about the application it should
monitor, as shown in a few lines).
</para>
<para>A tipical example of this file is give here:
<programlisting format='linespecific'>
&lt;objects&gt;
&lt;object name='watcher'
type='Spring.Services.WindowsService.Common.Deploy.FileSystem.FileSystemApplicationWatcher'&gt;
&lt;!--
we can get access to the IApplication we are asked to monitor
using a reference like the following
--&gt;
&lt;constructor-arg ref='.injected.application'/&gt;
&lt;!-- sometimes the windows OS will decide to not give you the same case you see in explorer:
in fact one should consider this OS case-insensitive with regard to file names ...
The following property, true by default can however be tuned
&lt;property name=&quot;ignoreCase&quot; value=&quot;false&quot;/&gt;
--&gt;
&lt;property name=&quot;includes&quot;&gt;
&lt;list&gt;
&lt;value&gt;wwwroot/bin/*.*&lt;/value&gt;
&lt;value&gt;service.config&lt;/value&gt;
&lt;value&gt;service.xml&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;!--
&lt;property name=&quot;excludes&quot;&gt;
&lt;list&gt;
&lt;value&gt;Db/**/*.*&lt;/value&gt;
&lt;value&gt;Jobs&lt;/value&gt;
&lt;value&gt;Jobs&lt;/value&gt;
&lt;value&gt;**/*.log&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
--&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
</para>
<para>
As you can see, if you need it, you can reference the
<literal>Spring.Services.WindowsService.Common.IApplication</literal>
object that your watcher should watch using the name
<literal>.injected.application</literal>.</para>
</sect2>
<sect2>
<title><literal>bin</literal> directory - optional</title>
<para>
This is, by default, the folder where your assemblies are placed
in the same way they are in an ASP.NET application.
</para>
<para>
Putting assemblies there is more a convention and maybe a good
practice (they are isolated from other artifacts, but maybe you will
prefer to use another directory (modify the
<literal>service.config</literal> file accordingly) or the application
directory directly (= <literal>bin</literal> parent).
</para>
<para>
Be aware of the fact that the process in which your application will
run will have its own PATH environmental variable. As such
don't expect to be successfull using dlls imported
with [DllImport] if they are not in the system PATH of the
hosting machine: while it is well known that the CLR fusion
algorithm will not consider the PATH variable, you may be biten
by assemblies using non-system dlls (SQLite and Firebird ADO.NET
providers are good examples).
</para>
<para>Reiterating, one can put assemblies in another directory
under the application directory tree, and write
the .NET configuration file (<literal>service.config</literal>)
accordingly: .NET probing algorithm is always in place.
</para>
<para>
Please notice that it is not required that
your application uses or include any of the Spring.NET assemblies:
any object in any assembly, given it has lifecycle methods, can
be run as a service: non invasive infrastructure support courtesy
of Spring.NET!
</para>
</sect2>
</sect1>
<sect1>
<title>Customizing or extending</title>
<para>
It should be said that support for windows service has been initially
developed with a clear but limited set of 'extension points' in mind,
mainly related to the way you can deploy your services:
deploy location (filesystem, zip archives, mailbox, urls, ...),
(auto-)updating features, and so on.
</para>
<para>
To better understand the following discussion, the following figure
depicts some of the inner details of
<literal>Spring.Services.WindowsService.Process.exe</literal>
at run-time:
<mediaobject>
<imageobject>
<imagedata align="center"
fileref="images/spring.windows-service.png" format="png"/>
</imageobject>
<textobject>
<phrase>Spring.Services.WindowsService.Process.exe run-time details</phrase>
</textobject>
</mediaobject>
</para>
<sect2>
<title>The <literal>.config</literal> file</title>
</sect2>
<para>
The executable <literal>Spring.Services.WindowsService.Process.exe</literal>
is somewhat configured by the corresponding
<literal>.config</literal> file.
Please notice that this file is the most important extension point
for windows service support, and it will probably be made more powerful
and flexible in the future.
</para>
<para>
For applications deployed in the standard way (i.e. on the filesystem
as explained above) the updating features are configured by the
<literal>watcher.xml</literal> file, <emphasis>if present</emphasis>,
as already seen.
</para>
<para>
There should be however, other ways to deploy your applications,
maybe just as zip files dropped somewhere on the web or sent via
e-mail.
</para>
<para>
For these scenarios, your deploy location will be something that
implements
<literal>Spring.Services.WindowsService.Common.Deploy.IDeployLocation</literal>.
<para>
Please notice that, while questionable, it actually entends
<literal>IDisposable</literal> <footnote><para>this has been done
as it is possible that a deploy location holds resources that should be
released, for example network connections, lock files or the like</para></footnote>:
</para>
<programlisting format='linespecific'>
/// &lt;summary&gt;
/// Interface defining how a deploy location should look like
/// &lt;/summary&gt;
public interface IDeployLocation : IDeployEventSource, IDisposable
{
/// &lt;summary&gt;
/// The list of applications deployed at this location
/// Usually non-valid applications are not listed
/// &lt;/summary&gt;
/// &lt;seealso cref=&quot;Application&quot;/&gt;
IList Applications { get; }
}</programlisting>
<programlisting format='linespecific'>
/// &lt;summary&gt;
/// Interface defining the contract for an object acting as the source of
/// deploy events (application added, removed, updated)
/// &lt;/summary&gt;
/// &lt;seealso cref=&quot;DeployEventArgs&quot;/&gt;
/// &lt;seealso cref=&quot;DeployEventHandler&quot;/&gt;
public interface IDeployEventSource
{
/// &lt;summary&gt;
/// The multicaster for deploy events
/// &lt;/summary&gt;
event DeployEventHandler DeployEvent;
}</programlisting>
</para>
</sect1>
</chapter>

View File

@@ -0,0 +1,609 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="xml-config-reference">
<title>XML Configuration Reference</title>
<sect1>
<title>Introduction</title>
<para>This chapter contains an exhaustive listing for pretty much every
possible XML configuration scenario for Spring.NET's XML based
configuration. If you need to configure an object in a Spring.NET IoC
container, and you are using Spring.NET's XML configuration option to do
so (which, short of programmatic configuration, is pretty much all you can
use for configuration right now), then this chapter will most probably
have an example XML fragment that can illustrate what you need to
do.</para>
<para>Please note that this chapter is not a knee-jerk or belated response
to addressing any perceived complexity in the Spring.NET XML
configuration. Spring.NET's XML configuration syntax is, in the opinion of
the developers (for what that's worth), eminently readable... one has
<literal>&lt;objects/&gt;</literal>, these objects have zero or more
<literal>&lt;constructor-arg/&gt;</literal> or
<literal>&lt;property/&gt;</literal> elements that are generally
<literal>&lt;ref/&gt;erences</literal> to other
<literal>&lt;object/&gt;s</literal>. To use an analogy, Spring.NET's XML
configuration reads like a William Weaver translation of an Umberto Eco
novel... the words (the XML elements) are easy, but the devil (and
salvation) is in the detail.</para>
</sect1>
<sect1>
<title>Object Configuration</title>
<para>This section details the configuration of one's object definitions.
It contains fragments of XML that illustrate the absolute basics, such as
how to create a simple object with no dependencies, all the way through to
often overlooked features such as instantiating an object from of a method
call to another object.</para>
<sect2>
<title>Objects</title>
<para>This section details how to define an object in Spring.NET XML. If
you need somewhere to start, this is the place.</para>
<para>The section starts off with the absolute basics of defining an
object (the &lt;object/&gt; element), and then describes the setting of
constructor arguments and properties. Once you are down with those three
cornerstones of configuration (yes, that is really it), the rest of the
text in this reference is spent describing the values that one can
supply to those constructor arguments and property values.</para>
<sect3>
<title>Plain Object Definition</title>
<para>Find below an example of defining an object that has no
dependencies.</para>
<programlisting>&lt;object name="service" <co
id="xcf-plain-vanilla-object-name" />
type="Example.Foo, FooAssembly"/&gt; <co
id="xcf-plain-vanilla-object-type" /> <co
id="xcf-plain-vanilla-object-scope" /></programlisting>
<calloutlist>
<callout arearefs="xcf-plain-vanilla-object-name">
This is the name of the object (
<xref linkend="objects-objectname" />
).
</callout>
<callout arearefs="xcf-plain-vanilla-object-type">
This is the assembly qualified name of the object's Type or class (
<xref linkend="objects-factory-class" />
).
</callout>
<callout arearefs="xcf-plain-vanilla-object-scope">
Please note that the
<literal>scope</literal>
of the object is implicitly
<literal>singleton</literal>
(see
<xref linkend="xcf-singleton" />
of this chapter and
<xref linkend="objects-factory-modes" />
in the reference documentation).
</callout>
</calloutlist>
<para>Defining this object in one's context and then retrieving said
object from said context will result in the creation of an instance of
the <classname>Foo</classname> class. The default constructor of the
<classname>Foo</classname> class will be invoked, and since no
properties and other other configuration elementts are present, the
resulting object will be returned as is. The simple case really is as
simple as that.</para>
<para>Further (un-annotated) examples of defining an object that has
no dependencies can be found below...</para>
<programlisting>&lt;object id="anException" type="System.ArgumentException, Mscorlib"/&gt;</programlisting>
<programlisting>&lt;object id="anEmptyList" type="System.Collections.ArrayList, Mscorlib"/&gt;</programlisting>
<programlisting>&lt;object id="anSqlCommand" type="System.Data.SqlClient.SqlCommand, System.Data"/&gt;</programlisting>
</sect3>
<sect3>
<title>Constructor Arguments</title>
<para></para>
<programlisting></programlisting>
</sect3>
<sect3>
<title>Properties</title>
<para></para>
<programlisting></programlisting>
</sect3>
</sect2>
<sect2>
<title>Object Types</title>
<para></para>
<sect3 id="xcf-primitives">
<title>Primitives</title>
<para>This section details the various configuration options available
for injecting, handoing, and otherwise defining the classic primitive
types. The <classname>string</classname> and
<classname>date</classname> types are not primitives, but they are
described here nevertheless.</para>
<para>Spring.NET uses the <classname>TypeConverter</classname>
mechanism that is part of the SDK to handle the conversion from string
values in one's XML configuration to the appropriate type. This
reference does not go into detail about this mechanism, so you may
wish to consult the attendant section of the reference material proper
if you are having type conversion issues... <xref
linkend="objects-objects-conversion" /></para>
<sect4 id="xcf-numbers">
<title>Numbers</title>
<para>This section describes configuring the various numeric types
supported by the CLR. Any numeric type can be injected into an
object, or made available as an object definition in its own
right.</para>
<para>Find below the class definition that is used to illustrate
configuring numeric values in the following examples.</para>
<programlisting>[C#]
namespace Example
{
public class Gauge
{
private int setting;
private float sensitivity;
public int Setting
{
set { this.setting = value; }
}
public float Sensitivity
{
set { this.sensitivity = value; }
}
}
}</programlisting>
<programlisting>&lt;object id="aGauge" type="Example.Gauge, FooAssembly"&gt;
&lt;property name="setting" value="213"/&gt;
&lt;/object&gt;</programlisting>
<para>We can also use any of the normal supported conventions (such
as hexadecimal) to set values, as shown below.</para>
<programlisting>&lt;object id="aGauge" type="Example.Gauge, FooAssembly"&gt;
&lt;property name="setting" value="0x10"/&gt;
&lt;/object&gt;</programlisting>
<programlisting>&lt;object id="aGauge" type="Example.Gauge, FooAssembly"&gt;
&lt;property name="sensitivity" value="31000.00"/&gt;
&lt;/object&gt;</programlisting>
<para>Given the above examples, it is trivial to extrapolate the
configuration of longs and the various unsigned variants of the
numeric types, so no examples of such configuration will be
given.</para>
</sect4>
<sect4>
<title>Dates</title>
<para></para>
<para>Find below the class definition that is used to illustrate
configuring date values in the following examples.</para>
<programlisting>[C#]
namespace Example
{
public class Gauge
{
private DateTime lastChecked;
public DateTime LastChecked
{
set { this.lastChecked = value; }
}
}
}</programlisting>
<programlisting>&lt;object id="aGauge" type="Example.Gauge, FooAssembly"&gt;
&lt;property name="lastChecked" value=""/&gt;
&lt;/object&gt;</programlisting>
<programlisting>&lt;object id="aGauge" type="Example.Gauge, FooAssembly"&gt;
&lt;property name="lastChecked" value=""/&gt;
&lt;/object&gt;</programlisting>
<programlisting>&lt;object id="aGauge" type="Example.Gauge, FooAssembly"&gt;
&lt;property name="lastChecked" value=""/&gt;
&lt;/object&gt;</programlisting>
</sect4>
<sect4 id="xcf-booleans">
<title>Booleans</title>
<para>Configuring boolean values in one's configuration file (s) is
pretty much the same as configuring numeric and date values... one
simply uses the <literal>value</literal> attribute or
<literal>&lt;value/&gt;</literal> element (as appropriate). The only
caveat (if indeed it can be considered to be a caveat) is that the
value <emphasis role="bold">must</emphasis> be one of the following
two values... <itemizedlist>
<listitem>
<para><emphasis role="bold">true</emphasis></para>
</listitem>
<listitem>
<para><emphasis role="bold">false</emphasis></para>
</listitem>
</itemizedlist></para>
<para>Find below the class definition that is used to illustrate
configuring boolean values in the following examples.</para>
<programlisting>[C#]
namespace Example
{
public class Gauge
{
private bool isSwitchedOn;
public bool IsSwitchedOn
{
set { this.isSwitchedOn = value; }
}
}
}</programlisting>
<programlisting>&lt;object id="aGauge" type="Example.Gauge, FooAssembly"&gt;
&lt;property name="IsSwitchedOn" value="true"/&gt;
&lt;/object&gt;</programlisting>
<programlisting>&lt;object id="aGauge" type="Example.Gauge, FooAssembly"&gt;
&lt;property name="IsSwitchedOn" value="false"/&gt;
&lt;/object&gt;</programlisting>
<para>Please note that as with pretty much everything in Spring.NET,
the <literal>true</literal> and <literal>false</literal> string
values are not case sensitive. The string values
<literal>TRUE</literal>, <literal>FALSE</literal>,
<literal>True</literal>, etc. are all valid.</para>
<para>If you wanted to use different values for the
<literal>true</literal> and <literal>false</literal> string values
(perhaps <literal>on</literal> and <literal>off</literal> values in
the case of the preceding <classname>Gauge</classname> example), you
would need to register a custom <classname>TypeConverter</classname>
implementation (see <xref
linkend="objects-objects-conversion" />).</para>
</sect4>
<sect4 id="xcf-strings">
<title>Strings</title>
<para>Unsurprisingly, <classname>String</classname> values are the
easiest to configure. Consider the following example of strings that
are defined as top level objects...</para>
<programlisting>&lt;object id="supportTeamEmail" type="string"&gt;
&lt;constructor-arg index="0" value="support@my.company.com"/&gt;
&lt;/object&gt;</programlisting>
<programlisting>&lt;object id="projectManagerEmail" type="string"&gt;
&lt;constructor-arg index="0" value="projectManager@my.company.com"/&gt;
&lt;/object&gt;</programlisting>
<para>The <literal>index="0"</literal> attribute value pair of the
<literal>constructor-arg</literal> element is required so that the
correct constructor of the <classname>String</classname> class can
be invoked... don't forget to put it in. (If you do forget to put it
in, then a not-very-helpful
<classname>UnsatisfiedDependencyException</classname> will be thrown
by the Spring.NET container).</para>
</sect4>
<sect4 id="xcf-enums">
<title>Enumerations</title>
<para>Find below the class definition and XML snippets that
illustrate the configuration of enumerations.</para>
<programlisting>[C#]
namespace Example
{
public enum RunningMode
{
Off,
Starting,
Started,
SwitchingOff,
Off
}
public class Gauge
{
private RunningMode runMode;
public RunningMode RunMode
{
set { this.runMode = value; }
}
}
}</programlisting>
<programlisting>&lt;object id="aGauge" type="Example.Gauge, FooAssembly"&gt;
&lt;property name="RunMode" value="Starting"/&gt;
&lt;/object&gt;</programlisting>
<programlisting>&lt;object id="aGauge" type="Example.Gauge, FooAssembly"&gt;
&lt;property name="RunMode" value="SwitchingOff"/&gt;
&lt;/object&gt;</programlisting>
<para>Please note that as with pretty much everything in Spring.NET,
the string passed to the value of the <literal>value</literal>
attribute is not case sensitive. In the case of this specific
example, the string values <literal>starting</literal> and
<literal>SWITCHINGOFF</literal> are both valid (though not
recommended; it's always best to stick to the casing of the original
enum, to aid in refactorings).</para>
<para>See also <xref
linkend="objects-type-conversion-enums" />.</para>
</sect4>
</sect3>
<sect3>
<title>Collections</title>
<para></para>
<sect4>
<title>Arrays</title>
<para></para>
</sect4>
<sect4>
<title>Lists</title>
<para></para>
</sect4>
<sect4>
<title>Dictionaries</title>
<para></para>
</sect4>
<sect4>
<title>Sets</title>
<para></para>
</sect4>
<sect4>
<title>Custom Collection Types</title>
<para></para>
</sect4>
<sect4>
<title>Everything Else</title>
<para></para>
</sect4>
</sect3>
<sect3>
<title>Nulls</title>
<para></para>
</sect3>
</sect2>
<sect2>
<title>Scope</title>
<para></para>
<sect3 id="xcf-singleton">
<title>Singleton</title>
<para></para>
</sect3>
<sect3 id="xcf-prototype">
<title>Prototype</title>
<para></para>
</sect3>
<sect3>
<title>Everything Else Scope Related</title>
<para></para>
</sect3>
</sect2>
<sect2>
<title>Factories</title>
<para>Perhaps unsurprisingly, implementations of the classic Factory
pattern can be found all over the Spring.NET codebase... indeed, the
core <classname>IApplicationContext</classname> class is a compelling
example of a factory implementation (albeit a very sophisticated
example). Spring.NET's support for the factory pattern extends into two
distinct areas... supporting factories that are external to the
framework, and factories that are internal to the framework.</para>
<para>External factory classes would include any factory classes that
you may have written: examples of this would include (perhaps)
<classname>IWiGFactory</classname> (to create
<classname>IWiG</classname> implementations), etc. You can integrate any
such existing factory classes directly into the Spring.NET container
using the factory method support provided by the Spring IoC container.
Examples of such integration are are provided below, but do see <xref
linkend="objects-factory-class-static-factory-method" /> and <xref
linkend="objects-factory-class-instance-factory-method" /> for the full
lowdown.</para>
<para>Spring.NET also has the notion of a special <emphasis>Factory
Object</emphasis> (and this notion is encapsulated by the
<classname>IFactoryObject</classname> interface). The
<classname>IFactoryObject</classname> interface is (unsurprisingly) a
factory for creating one or more objects. Please do read <xref
linkend="objects-factory-class-instance-factory-method" /> for a
comprehensive explanation of the <classname>IFactoryObject</classname>
interface and the Spring.NET container's special treatment of objects
that implement said interface. This section of the documentation will
show some example configuration for all (well, most) of the
<classname>IFactoryObject</classname> implementations that come provided
out of the box with every Spring.NET release.</para>
<sect3>
<title>Factory Methods</title>
<para></para>
</sect3>
<sect3>
<title>Factory Objects</title>
<para>This section of the documentation presents examples for most of
the <classname>IFactoryObject</classname> implementations that come
out of the box with every Spring.NET release. A notable exception to
this catalogue of <classname>IFactoryObject</classname> configuration
examples is the AOP-specific
<classname>ProxyFactoryObject</classname>... see <xref
linkend="aop-quickstart" /> for more details regarding that particular
<classname>IFactoryObject</classname> implementation.</para>
<para>Most (if not all) of the <classname>IFactoryObject</classname>
implementations referenced in the following configuration examples can
be found in the <literal>Spring.Objects.Factory.Config</literal>
namespace; do also consult the attendant API documentation (because
most of the <classname>IFactoryObject</classname> implementations
carry configuration examples specific to the objects that they
create).</para>
<sect4>
<title>DelegateFactoryObject</title>
<para>One can use the <classname>DelegateFactoryObject</classname>
to (unsurprisingly) create and configure
<classname>Delegate</classname> objects. One trenchant use case for
this <classname>IFactoryObject</classname> (and indeed the very
reason that prompted it's creation) is to create declaratively a
<classname>ConfigListener</classname> delegate for use with the
IBatis.NET project's <classname>SqlMapper</classname> class. This
approach (of using the <classname>DelegateFactoryObject</classname>)
allows one to keep all of one's <classname>SqlMapper</classname>
configuration together, nice and tidy, in the one place.</para>
<para>So lets say we have a service object that we need to inject
with a delegate; class definitions for the class that has the
dependency on the delegate, the delegate class itself, and a class
that supplies the method that will be passed to the delegate when it
is created can be found below.</para>
<programlisting>[C#]
namespace Example
{
public delegate void GaugeCallback (object sender, GuageEventArgs e);
public class Gauge
{
private GaugeCallback callback;
public GaugeCallback Callback
{
set { this.callback = value; }
}
public void SomeOperation() {
// some logic...
callback(this, new GaugeEventArgs());
}
}
public class MyGaugeListener() {
public void HandleGaugeOperation(object sender, GuageEventArgs e) {
// do something...
}
}
}</programlisting>
<para>The attendant configuration to supply an instance of the
<classname>Gauge</classname> class with a configured
<classname>GuageCallback</classname> delegate would look like
so...</para>
<programlisting>&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;object id="gauge" type="Example.Gauge, FooAssembly"&gt;
&lt;property name="callback"&gt;
&lt;object type="Spring.Objects.Factory.Config.DelegateFactoryObject"&gt;
&lt;property name="delegateType" value="Example.GaugeCallback, FooAssembly"/&gt;
&lt;property name="targetObject"&gt;
&lt;object type="Example.MyGaugeCallback, FooAssembly"/&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
</sect4>
<sect4>
<title>DictionaryFactoryObject</title>
<para></para>
</sect4>
<sect4>
<title>Log4NetFactoryObject</title>
<para></para>
</sect4>
</sect3>
</sect2>
<sect2>
<title></title>
<para></para>
</sect2>
</sect1>
<sect1>
<title>Context Configuration</title>
<para>This section details the configuration of one or more contexts...
i.e. not the objects themselves, but rather of the hierarchy of contexts
in which one's object definitions are contained.</para>
</sect1>
</chapter>

View File

@@ -0,0 +1,388 @@
<?xml version="1.0" encoding="UTF-8"?>
<appendix id="extensible-xml">
<title>Extensible XML authoring</title>
<section id="extensible-xml-introduction">
<title>Introduction</title>
<para>Spring supports adding custom schema-based extensions to the basic
Spring XML format for defining and configuring objects. This section is
devoted to detailing how you would go about writing your own custom XML
object definition parsers and integrating such parsers into the Spring IoC
container.</para>
<para>To facilitate the authoring of configuration files using a
schema-aware XML editor, Spring's extensible XML configuration mechanism
is based on XML Schema. If you are not familiar with Spring's current XML
configuration extensions that come with the standard Spring distribution,
please first read the appendix entitled <xref
linkend="xsd-config" />.</para>
<para>Creating new XML configuration extensions can be done by following
these (relatively) simple steps:</para>
<para><orderedlist numeration="arabic">
<listitem>
<para><link linkend="extensible-xml-schema">Authoring</link> an XML
schema to describe your custom element(s).</para>
</listitem>
<listitem>
<para><link linkend="extensible-xml-namespaceparser">Coding</link>
a custom <interfacename>INamespaceParser</interfacename>
implementation (this is an easy step, don't worry).</para>
</listitem>
<listitem>
<para><link linkend="extensible-xml-parser">Coding</link> one or
more <interfacename>IObjectDefinitionParser</interfacename>
implementations (this is where the real work is done).</para>
</listitem>
<listitem>
<para><link linkend="extensible-xml-registration">Registering</link>
the above artifacts with Spring (this too is an easy step).</para>
</listitem>
</orderedlist></para>
<para>What follows is a description of each of these steps. For the
example, we will create an XML extension (a custom XML element) that
allows us to configure objects of the type <classname>Regex</classname>
(from the <literal>System.Text.RegularExpressions</literal> namespace) in
an easy manner. When we are done, we will be able to define object
definitions of type <classname>Regex</classname> like this:</para>
<programlisting>&lt;myns:regex id="regex"
pattern="(^\d{5}$)|(^\d{5}-\d{4}$)"
options="Compiled"/&gt;
</programlisting>
</section>
<section id="extensible-xml-schema">
<title>Authoring the schema</title>
<para>Creating an XML configuration extension for use with Spring's IoC
container starts with authoring an XML Schema to describe the extension.
What follows is the schema we'll use to configure
<classname>Regex</classname> objects.</para>
<programlisting>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;xsd:schema id="myns"
xmlns="http://www.mycompany.com/schema/myns"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:objects="http://www.springframework.net"
xmlns:vs="http://schemas.microsoft.com/Visual-Studio-Intellisense"
targetNamespace="http://www.mycompany.com/schema/myns"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
vs:friendlyname="Spring Regex Configuration" vs:ishtmlschema="false"
vs:iscasesensitive="true" vs:requireattributequotes="true"
vs:defaultnamespacequalifier="" vs:defaultnsprefix=""
&gt;
&lt;xsd:import namespace="http://www.springframework.net"/&gt;
&lt;xsd:element name="regex"&gt;
&lt;xsd:complexType&gt;
&lt;xsd:complexContent&gt;
<emphasis role="bold">&lt;xsd:extension base="objects:identifiedType"&gt;</emphasis>
&lt;xsd:attribute name="pattern" type="xsd:string" use="required"/&gt;
&lt;xsd:attribute name="options" type="xsd:string" use="optional"/&gt;
&lt;/xsd:extension&gt;
&lt;/xsd:complexContent&gt;
&lt;/xsd:complexType&gt;
&lt;/xsd:element&gt;
&lt;/xsd:schema&gt; </programlisting>
<para>The emphasized line contains an extension base for all tags that
will be identifiable (meaning they have an <literal>id</literal> attribute
that will be used as the object identifier in the container). We are able
to use this attribute because we imported the Spring-provided
<literal>'objects'</literal> namespace. The <literal>vs:</literal>
prefixed elements are for better integration with intellisense in
VS.NET.</para>
<para>The above schema will be used to configure
<classname>Regex</classname> objects, directly in an XML application
context file using the <literal>&lt;myns:regex/&gt;</literal>
element.</para>
<programlisting>&lt;myns:regex id="usZipCodeRegex"
pattern="(^\d{5}$)|(^\d{5}-\d{4}$)"
options="Compiled"/&gt;</programlisting>
<para>Note that after we've created the infrastructure classes, the above
snippet of XML will essentially be exactly the same as the following XML
snippet. In other words, we're just creating an object in the container,
identified by the name <literal>'usZipCodeRegex'</literal> of type
<classname>Regex</classname>, with a couple of constructor arguments
set.</para>
<programlisting> &lt;object id="usZipCodeRegex" type="System.Text.RegularExpressions.Regex, System"&gt;
&lt;constructor-arg name="pattern" value="(^\d{5}$)|(^\d{5}-\d{4}$)"/&gt;
&lt;constructor-arg name="options" value="Compiled"/&gt;
&lt;/object&gt;</programlisting>
<note>
<para>The schema-based approach to creating configuration format allows
for tight integration with an IDE that has a schema-aware XML editor.
Using a properly authored schema, you can use intellisense to have a
user choose between several configuration options defined in the
enumeration. The schema for creating IDbProvider instances shows the use
of XSD enumerations.</para>
</note>
</section>
<section id="extensible-xml-namespaceparser">
<title>Coding a <interfacename>INamespaceParser</interfacename></title>
<para>In addition to the schema, we need an
<interfacename>INamespaceParser</interfacename> that will parse all
elements of this specific namespace Spring encounters while parsing
configuration files. The <interfacename>INamespaceParser</interfacename>
should in our case take care of the parsing of the
<literal>myns:regex</literal> element.</para>
<para>The <interfacename>INamespaceParser</interfacename> interface is
pretty simple in that it features just two methods:</para>
<itemizedlist spacing="compact">
<listitem>
<para><methodname>Init()</methodname> - allows for initialization of
the <interfacename>INamespaceParser</interfacename> and will be
called by Spring before the handler is used</para>
</listitem>
<listitem>
<para><methodname>IObjectDefinition Parse(Element,
ParserContext)</methodname> - called when Spring encounters a
top-level element (not nested inside a object definition or a
different namespace). This method can register object definitions
itself and/or return a object definition.</para>
</listitem>
</itemizedlist>
<para>Although it is perfectly possible to code your own
<interfacename>INamespaceParser</interfacename> for the entire namespace
(and hence provide code that parses each and every element in the
namespace), it is often the case that each top-level XML element in a
Spring XML configuration file results in a single object definition (as in
our case, where a single <literal>&lt;myns:regex/&gt;</literal> element
results in a single <classname>Regex</classname> object definition).
Spring features a number of convenience classes that support this
scenario. In this example, we'll make use the
<classname>NamespaceParserSupport</classname> class:</para>
<programlisting>using Spring.Objects.Factory.Xml;
namespace CustomNamespace
{
[NamespaceParser(
Namespace = "http://www.mycompany.com/schema/myns",
SchemaLocationAssemblyHint = typeof(MyNamespaceParser),
SchemaLocation = "/CustomNamespace/myns.xsd"
)
]
public class MyNamespaceParser : NamespaceParserSupport
{
public override void Init()
{
<emphasis role="bold">RegisterObjectDefinitionParser</emphasis>("regex", new RegexObjectDefinitionParser());
}
}
}</programlisting>
<para>Notice that there isn't actually a whole lot of parsing logic in
this class. Indeed... the <classname>NamespaceParserSupport</classname>
class has a built in notion of delegation. It supports the registration of
any number of <interfacename>IObjectDefinitionParser</interfacename>
instances, to which it will delegate to when it needs to parse an element
in it's namespace. This clean separation of concerns allows an
<interfacename>INamespaceParser</interfacename> to handle the
orchestration of the parsing of <emphasis>all</emphasis> of the custom
elements in it's namespace, while delegating to
<literal>IObjectDefinitionParsers</literal> to do the grunt work of the
XML parsing; this means that each
<interfacename>IObjectDefinitionParser</interfacename> will contain just
the logic for parsing a single custom element, as we can see in the next
step.</para>
<para>To help in the registration of the parser for this namespace, the
<literal>NamespaceParser</literal> attribute is used to map the XML
namespace string, i.e.
<literal>http://www.mycompany.com/schema/myns</literal>, to the location
of the XML Schema file as an embedded assembly resource.</para>
</section>
<section id="extensible-xml-parser">
<title>Coding an
<interfacename>IObjectDefinitionParser</interfacename></title>
<para>A <interfacename>IObjectDefinitionParser</interfacename> will be
used if the <interfacename>INamespaceParser</interfacename> encounters an
XML element of the type that has been mapped to the specific object
definition parser (which is <literal>'regex'</literal> in this case). In
other words, the <interfacename>IObjectDefinitionParser</interfacename> is
responsible for parsing <emphasis>one</emphasis> distinct top-level XML
element defined in the schema. In the parser, we'll have access to the XML
element (and thus it's subelements too) so that we can parse our custom
XML content, as can be seen in the following example:</para>
<programlisting>using System;
using System.Text.RegularExpressions;
using System.Xml;
using Spring.Objects.Factory.Support;
using Spring.Objects.Factory.Xml;
using Spring.Util;
namespace CustomNamespace
{
public class RegexObjectDefinitionParser : AbstractSimpleObjectDefinitionParser { <co
id="extensible-xml-parser-simpledateformat-co-1" />
protected override Type GetObjectType(XmlElement element)
{
return typeof (Regex); <co
id="extensible-xml-parser-simpledateformat-co-2" />
}
protected override void DoParse(XmlElement element, ObjectDefinitionBuilder builder)
{
<lineannotation> // this will never be null since the schema explicitly requires that a value be supplied</lineannotation>
string pattern = element.GetAttribute("pattern");
builder.AddConstructorArg(pattern);
<lineannotation> // this however is an optional property</lineannotation>
string options = element.GetAttribute("options");
if (StringUtils.HasText(options))
{
RegexOptions regexOptions = (RegexOptions)Enum.Parse(typeof (RegexOptions), options);
builder.AddConstructorArg(regexOptions);
}
}
protected override bool ShouldGenerateIdAsFallback
{
get { return true; }
}
}
</programlisting>
<calloutlist>
<callout arearefs="extensible-xml-parser-simpledateformat-co-1">
<para>We use the Spring-provided
<classname>AbstractSingleObjectDefinitionParser</classname> to handle
a lot of the basic grunt work of creating a
<emphasis>single</emphasis>
<interfacename>IObjectDefinition</interfacename>.</para>
</callout>
<callout arearefs="extensible-xml-parser-simpledateformat-co-2">
<para>We supply the
<classname>AbstractSingleObjectDefinitionParser</classname> superclass
with the type that our single
<interfacename>IObjectDefinition</interfacename> will
represent.</para>
</callout>
</calloutlist>
<para>In this simple case, this is all that we need to do. The creation of
our single <interfacename>IObjectDefinition</interfacename> is handled by
the <classname>AbstractSingleObjectDefinitionParser</classname>
superclass, as is the extraction and setting of the object definition's
unique identifier. The property
<literal>ShouldGenerateIdAsFallback</literal> will generate a throw-away
object id incase one is not specified, this is useful when nesting object
definitions.</para>
</section>
<section id="extensible-xml-registration">
<title>Registering the handler and the schema</title>
<para>The coding is finished! All that remains to be done is to somehow
make the Spring XML parsing infrastructure aware of our custom element; we
do this by registering our custom
<interfacename>INamespaceParser</interfacename> using a special
configuration section handler. The location of the XML Schema in this
example has been directly assoicated with the parser though the use of the
<literal>Namespace</literal> attribute.</para>
<section id="extensible-xml-registration-spring-handlers">
<title><filename>NamespaceParsersSectionHandler</filename></title>
<para>The custom configuration section handler is of the type
<classname>Spring.Context.Support.NamespaceParsersSectionHandler</classname>
and is registered with .NET in the normal manner. The custom
configuration section will simply point to the
<classname>INamespaceParser</classname> implementation that has the
<classname>Namespace</classname> attribute. For our example, we need to
write the following:</para>
<programlisting>&lt;configuration&gt;
&lt;configSections&gt;
&lt;sectionGroup name="spring"&gt;
&lt;section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/&gt;
&lt;/sectionGroup&gt;
&lt;/configSections&gt;
&lt;spring&gt;
&lt;parsers&gt;
&lt;parser type="CustomNamespace.MyNamespaceParser, CustomNamespace" /&gt;
&lt;/parsers&gt;
&lt;/spring&gt;
&lt;/configuration&gt;</programlisting>
</section>
</section>
<section id="extensible-xml-using">
<title>Using a custom extension in your Spring XML configuration</title>
<para>Using a custom extension that you yourself have implemented is no
different from using one of the 'custom' extensions that Spring provides
straight out of the box. Find below an example of using the custom
<literal>&lt;regex/&gt;</literal> element developed in the previous steps
in a Spring XML configuration file.</para>
<programlisting>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;objects xmlns="http://www.springframework.net"
xmlns:myns="http://www.mycompany.com/schema/myns"&gt;
&lt;!-- as a top level object definition --&gt;
&lt;myns:regex id="usZipCodeRegex"
pattern="(^\d{5}$)|(^\d{5}-\d{4}$)"/&gt;
&lt;object id="jobDetailTemplate" abstract="true"&gt;
&lt;property name="regex"&gt;
&lt;!-- as an inner object definition --&gt;
&lt;myns:regex pattern="(^\d{5}$)|(^\d{5}-\d{4}$)"
options="Compiled"/&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
</section>
<section id="extensible-xml-resources">
<title>Further Resources</title>
<para>Find below links to further resources concerning XML Schema and the
extensible XML support described in this chapter.</para>
<itemizedlist>
<listitem>
<para>The <ulink
url="http://www.w3.org/TR/2004/REC-xmlschema-1-20041028/">XML Schema
Part 1: Structures Second Edition</ulink></para>
</listitem>
<listitem>
<para>The <ulink
url="http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/">XML Schema
Part 2: Datatypes Second Edition</ulink></para>
</listitem>
</itemizedlist>
</section>
</appendix>

View File

@@ -0,0 +1,225 @@
<?xml version="1.0" encoding="UTF-8"?>
<appendix id="xsd-config">
<title>XML Schema-based configuration</title>
<section id="xsd-config-introduction">
<title>Introduction</title>
<para>This appendix details the use of XML Schema-based configuration in
Spring.</para>
<para> The <emphasis>'classic' </emphasis>
<literal>&lt;object/&gt;</literal>-based schema is good, but its
generic-nature comes with a price in terms of configuration overhead.
Creating a custom XML Schema-based configuration makes Spring XML
configuration files substantially clearer to read. In addition, it allows
you to express the intent of an object definition.</para>
<para>The key thing to remember is that creating custom schema tags work
best for infrastructure or integration objects: for example, AOP,
collections, transactions, integration with 3rd-party frameworks, etc.,
while the existing object tags are best suited to application-specific
objects, such as DAOs, service layer objects, etc.</para>
<para>Please note the fact that the XML configuration mechanism is totally
customisable and extensible. This means you can write your own
domain-specific configuration tags that would better represent your
application's domain; the process involved in doing so is covered in the
appendix entitled <xref linkend="extensible-xml" />.</para>
</section>
<section id="xsd-config-body">
<title>XML Schema-based configuration</title>
<section id="xsd-config-body-referencing">
<title>Referencing the schemas</title>
<para>As a reminder, you reference the standard objects schema as shown
below</para>
<programlisting>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/schema/objects/spring-objects-1.1.xsd"&gt;
<lineannotation> &lt;!-- <literal>&lt;object/&gt;</literal> definitions here --&gt;</lineannotation>
&lt;/objects&gt;</programlisting>
<note>
<para>The <literal>'xsi:schemaLocation'</literal> fragment is not
actually required, but can be included to reference a local copy of a
schema (which can be useful during development) and assumes the XML
editor will look to that location and load the schema.</para>
</note>
<para>The above Spring XML configuration fragment is boilerplate that
you can copy and paste (!) and then plug
<literal>&lt;object/&gt;</literal> definitions into like you have always
done. However, the entire point of using custom schema tags is to make
configuration easier. </para>
</section>
<para>The rest of this chapter gives an overview of custom XML Schema
based configuration that are included with the release.</para>
<section id="xsd-config-body-schemas-tx">
<title>The <literal>tx</literal> (transaction) schema</title>
<para>The <literal>tx</literal> tags deal with configuring objects in
Spring's comprehensive support for transactions. These tags are covered
in the chapter entitled <xref linkend="transaction" />.</para>
<tip>
<para>You are strongly encouraged to look at the
<filename>'spring-tx-1.1.xsd'</filename> file that ships with the
Spring distribution. This file is (of course), the XML Schema for
Spring's transaction configuration, and covers all of the various tags
in the <literal>tx</literal> namespace, including attribute defaults
and suchlike. This file is documented inline, and thus the information
is not repeated here in the interests of adhering to the DRY (Don't
Repeat Yourself) principle.</para>
</tip>
<para>In the interest of completeness, to use the tags in the
<literal>tx</literal> schema, you need to have the following preamble at
the top of your Spring XML configuration file; the emboldened text in
the following snippet references the correct schema so that the tags in
the <literal>tx</literal> namespace are available to you.</para>
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;object xmlns="http://www.springframework.net"
xmlns:aop="http://www.springframework.org/schema/aop"
<emphasis role="bold"> xmlns:tx="http://www.springframework.org/schema/tx"</emphasis>&gt;
<lineannotation>&lt;!-- <literal>&lt;object/&gt;</literal> definitions here --&gt;</lineannotation>
&lt;/object&gt;</programlisting>
<note>
<para>Often when using the tags in the <literal>tx</literal> namespace
you will also be using the tags from the <literal>aop</literal>
namespace (since the declarative transaction support in Spring is
implemented using AOP). The above XML snippet contains the relevant
lines needed to reference the <literal>aop</literal> schema so that
the tags in the <literal>aop</literal> namespace are available to
you.</para>
</note>
</section>
<section id="xsd-config-body-schemas-aop">
<title>The <literal>aop</literal> schema</title>
<para>The <literal>aop</literal> tags deal with configuring all things
AOP in Spring. These tags are comprehensively covered in the chapter
entitled <xref linkend="aop" />.</para>
<para>In the interest of completeness, to use the tags in the
<literal>aop</literal> schema, you need to have the following preamble
at the top of your Spring XML configuration file; the emboldened text in
the following snippet references the correct schema so that the tags in
the <literal>aop</literal> namespace are available to you.</para>
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<emphasis role="bold">xmlns:aop="http://www.springframework.org/schema/aop"</emphasis>&gt;
<lineannotation>&lt;!-- <literal>&lt;object/&gt;</literal> definitions here --&gt;</lineannotation>
&lt;/objects&gt;</programlisting>
</section>
<section id="xsd-config-body-schemas-db">
<title>The <literal>db</literal> schema</title>
<para>The <literal>db</literal> tags deal with creating
<classname>IDbProvider</classname> instances for a given database client
library. The following snippet references the correct schema so that the
tags in the <literal>db</literal> namespace are available to you. The
tags are comprehensively covered in the chapter entitled <xref
linkend="dbprovider" />.</para>
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<emphasis role="bold">xmlns:db="http://www.springframework.org/schema/db"</emphasis>&gt;
<lineannotation>&lt;!-- <literal>&lt;object/&gt;</literal> definitions here --&gt;</lineannotation>
&lt;/objects&gt;</programlisting>
</section>
<section id="xsd-config-body-schemas-remoting">
<title>The <literal>remoting</literal> schema</title>
<para>The <literal>remoting</literal> tags are for use when you want to
export an existing POCO object as a .NET remoted object or to create a
client side .NET remoting proxy. The tags are comprehensively covered in
the chapter <xref linkend="remoting" /></para>
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;objects xmlns="http://www.springframework.org/schema/objects"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<emphasis role="bold">xmlns:r="http://www.springframework.org/schema/remoting"</emphasis>&gt;
<lineannotation>&lt;!-- <literal>&lt;object/&gt;</literal> definitions here --&gt;</lineannotation>
&lt;/objects&gt;</programlisting>
</section>
<section id="xsd-config-body-schemas-validation">
<title>The <literal>validation</literal> schema</title>
<para>The <literal>validation</literal> tags are for use when you want
definte <literal>IValidator</literal> object instances. The tags are
comprehensively covered in the chapter <xref
linkend="validation" /></para>
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<emphasis role="bold">xmlns:v="http://www.springframework.org/schema/validation"</emphasis>&gt;
<lineannotation>&lt;!-- <literal>&lt;object/&gt;</literal> definitions here --&gt;</lineannotation>
&lt;/objects&gt;</programlisting>
</section>
<section id="xsd-config-body-schemas-objects">
<title>The <literal>objects</literal> schema</title>
<para>Last but not least we have the tags in the
<literal>objects</literal> schema. Examples of the various tags in the
<literal>objects</literal> schema are not shown here because they are
quite comprehensively covered in the section entitled <xref
linkend="object-factory-properties-detailed" /> (and indeed in that
entire <link linkend="objects">chapter</link>).</para>
<programlisting>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/schema/objects/spring-objects-1.1.xsd"&gt;
&lt;object id="foo" class="X.Y.Foo, X"&gt;
&lt;property name="name" value="Rick"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
</section>
</section>
<section id="xsd-config-setup">
<title>Setting up your IDE</title>
<para>To setup VS.NET to provide intellisence while editing XML file for
your custom XML schemas you will need to copy your XSD files to an
appropriate VS.NET directory. Refer to the following chapter for details,
<xref linkend="vsnet" /></para>
<para>For SharpDevelop, follow the directions on the "<ulink
url="http://community.sharpdevelop.net/blogs/mattward/articles/FeatureTourEditingXml.aspx">Editing
XML</ulink>" product documentation.</para>
</section>
</appendix>

View File

@@ -0,0 +1,4 @@
<appendix id="springobjectsxsd">
<title>Spring.NET's <literal>spring-objects.xsd</literal></title>
<programlisting><![CDATA[@xsd-include@]]></programlisting>
</appendix>

524
doc/reference/src/xsd.xml Normal file
View File

@@ -0,0 +1,524 @@
<appendix id="springobjectsxsd">
<title>Spring.NET's <literal>spring-objects.xsd</literal></title>
<programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns="http://www.springframework.net" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:vs="http://schemas.microsoft.com/Visual-Studio-Intellisense" targetNamespace="http://www.springframework.net" elementFormDefault="qualified" attributeFormDefault="unqualified" vs:friendlyname="Spring.NET Configuration" vs:ishtmlschema="false" vs:iscasesensitive="true" vs:requireattributequotes="true" vs:defaultnamespacequalifier="" vs:defaultnsprefix="">
<xs:annotation>
<xs:documentation>
Spring Objects XML Schema Definition
Based on Spring Beans DTD, authored by Rod Johnson &amp; Juergen Hoeller
Author: Griffin Caprio
This defines a simple and consistent way of creating a namespace
of managed objects configured by a Spring XmlObjectFactory.
This document type is used by most Spring functionality, including
web application contexts, which are based on object factories.
Each object element in this document defines an object.
Typically the object type (System.Type is specified, along with plain vanilla
object properties.
Object instances can be "singletons" (shared instances) or "prototypes"
(independent instances).
References among objects are supported, i.e. setting an object property
to refer to another object in the same factory or an ancestor factory.
As alternative to object references, "inner object definitions" can be used.
Singleton flags and names of such "inner object" are always ignored:
Inner object are anonymous prototypes.
There is also support for lists, dictionaries, and sets.
</xs:documentation>
</xs:annotation>
<xs:annotation>
<xs:documentation>Defines a base type for any required string. Defines a string with a minimum length of 0</xs:documentation>
</xs:annotation>
<xs:simpleType name="nonNullString">
<xs:restriction base="xs:string">
<xs:minLength value="0"/>
</xs:restriction>
</xs:simpleType>
<xs:annotation>
<xs:documentation>
Element containing informative text describing the purpose of the enclosing
element. Always optional.
Used primarily for user documentation of XML object definition documents.
</xs:documentation>
</xs:annotation>
<xs:simpleType name="description">
<xs:restriction base="nonNullString"/>
</xs:simpleType>
<xs:complexType name="valueObject">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="type" type="nonNullString" use="optional"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="expression">
<xs:sequence>
<xs:element name="property" type="property" minOccurs="0" maxOccurs="2"/>
</xs:sequence>
<xs:attribute name="value" type="nonNullString" use="required"/>
</xs:complexType>
<!--
Defines a reference to another object in this factory or an external
factory (parent or included factory).
-->
<xs:complexType name="objectReference">
<xs:attribute name="object" type="nonNullString" use="optional"/>
<xs:attribute name="local" type="xs:IDREF" use="optional"/>
<xs:attribute name="parent" type="nonNullString" use="optional"/>
<!--
References must specify a name of the target object.
The "object" attribute can reference any name from any object in the context,
to be checked at runtime.
Local references, using the "local" attribute, have to use object ids;
they can be checked by this DTD, thus should be preferred for references
within the same object factory XML file.
-->
</xs:complexType>
<!-- Defines a reference to another object or a type. -->
<xs:complexType name="objectOrClassReference">
<xs:attribute name="object" type="nonNullString" use="optional"/>
<xs:attribute name="local" type="xs:IDREF" use="optional"/>
<xs:attribute name="type" type="nonNullString" use="optional"/>
</xs:complexType>
<xs:group name="objectList">
<xs:sequence>
<xs:element name="description" type="description" minOccurs="0"/>
<xs:choice>
<xs:element name="object" type="vanillaObject"/>
<!--
Defines a reference to another object in this factory or an external
factory (parent or included factory).
-->
<xs:element name="ref" type="objectReference"/>
<!--
Defines a string property value, which must also be the id of another
object in this factory or an external factory (parent or included factory).
While a regular 'value' element could instead be used for the same effect,
using idref in this case allows validation of local object ids by the xml
parser, and name completion by helper tools.
-->
<xs:element name="idref" type="objectReference"/>
<!--
A objectList can contain multiple inner object, ref, collection, or value elements.
Lists are untyped, pending generics support, although references will be
strongly typed.
A objectList can also map to an array type. The necessary conversion
is automatically performed by AbstractObjectFactory.
-->
<xs:element name="list">
<xs:complexType>
<xs:group ref="objectList" minOccurs="0" maxOccurs="unbounded"/>
<xs:attribute name="element-type" type="nonNullString" use="optional"/>
</xs:complexType>
</xs:element>
<!--
A set can contain multiple inner object, ref, collection, or value elements.
Sets are untyped, pending generics support, although references will be
strongly typed.
-->
<xs:element name="set">
<xs:complexType>
<xs:group ref="objectList" minOccurs="0" maxOccurs="unbounded"/>
</xs:complexType>
</xs:element>
<!--
A Spring map is a mapping from a string key to object (a .NET IDictionary).
Maps may be empty.
-->
<xs:element name="dictionary" type="objectMap"/>
<!--
Name-values elements differ from map elements in that values must be strings.
Name-values may be empty.
-->
<xs:element name="name-values" type="objectNameValues"/>
<!--
Contains a string representation of a property value.
The property may be a string, or may be converted to the
required type using the System.ComponentModel.TypeConverter
machinery. This makes it possible for application developers
to write custom TypeConverter implementations that can
convert strings to objects.
Note that this is recommended for simple objects only.
Configure more complex objects by setting properties to references
to other objects.
-->
<xs:element name="value" type="valueObject"/>
<!--
Contains a string representation of an expression.
-->
<xs:element name="expression" type="expression"/>
<!--
Denotes a .NET null value. Necessary because an empty "value" tag
will resolve to an empty String, which will not be resolved to a
null value unless a special TypeConverter does so.
-->
<xs:element name="null"/>
</xs:choice>
</xs:sequence>
</xs:group>
<xs:complexType name="objectNameValues">
<xs:sequence>
<!--
The "value" attribute is the string value of the property. The "key"
attribute is the name of the property.
-->
<xs:element name="add" minOccurs="0" maxOccurs="unbounded">
<xs:complexType mixed="true">
<xs:attribute name="key" type="nonNullString" use="required"/>
<xs:attribute name="value" use="required" type="xs:string"/>
<xs:attribute name="delimiters" use="optional" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="importElement">
<xs:attribute name="resource" type="nonNullString" use="required"/>
</xs:complexType>
<xs:complexType name="aliasElement">
<xs:attribute name="name" type="nonNullString" use="required"/>
<xs:attribute name="alias" type="nonNullString" use="required"/>
</xs:complexType>
<xs:complexType name="objectMap">
<xs:sequence>
<xs:element type="mapEntryElement" name="entry" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="key-type" type="nonNullString" use="optional"/>
<xs:attribute name="value-type" type="nonNullString" use="optional"/>
</xs:complexType>
<xs:complexType name="mapEntryElement">
<xs:sequence>
<xs:element type="mapKeyElement" name="key" minOccurs="0" maxOccurs="1"/>
<xs:group ref="objectList" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="key" type="nonNullString" use="optional"/>
<xs:attribute name="value" type="nonNullString" use="optional"/>
<xs:attribute name="expression" type="nonNullString" use="optional"/>
<xs:attribute name="key-ref" type="nonNullString" use="optional"/>
<xs:attribute name="value-ref" type="nonNullString" use="optional"/>
</xs:complexType>
<xs:complexType name="mapKeyElement">
<xs:group ref="objectList" minOccurs="1"/>
</xs:complexType>
<xs:annotation>
<xs:documentation>Defines constructor argument.</xs:documentation>
</xs:annotation>
<xs:complexType name="lookupMethod">
<xs:attribute name="name" type="nonNullString" use="required"/>
<xs:attribute name="object" type="nonNullString" use="required"/>
</xs:complexType>
<xs:complexType name="constructorArgument">
<xs:group ref="objectList" minOccurs="0"/>
<!--
The constructor-arg tag can have an optional named parameter attribute,
to specify a named parameter in the constructor argument list.
-->
<xs:attribute name="name" type="nonNullString" use="optional"/>
<!--
The constructor-arg tag can have an optional index attribute,
to specify the exact index in the constructor argument list. Only needed
to avoid ambiguities, e.g. in case of 2 arguments of the same type.
-->
<xs:attribute name="index" type="nonNullString" use="optional"/>
<!--
The constructor-arg tag can have an optional type attribute,
to specify the exact type of the constructor argument. Only needed
to avoid ambiguities, e.g. in case of 2 single argument constructors
that can both be converted from a String.
-->
<xs:attribute name="type" type="nonNullString" use="optional"/>
<xs:attribute name="value" type="nonNullString" use="optional"/>
<xs:attribute name="expression" type="nonNullString" use="optional"/>
<xs:attribute name="ref" type="nonNullString" use="optional"/>
</xs:complexType>
<xs:annotation>
<xs:documentation>Defines property.</xs:documentation>
</xs:annotation>
<xs:complexType name="property">
<xs:group ref="objectList" minOccurs="0"/>
<!-- The property name attribute is the name of the objects property. -->
<xs:attribute name="name" type="nonNullString" use="required"/>
<xs:attribute name="value" type="nonNullString" use="optional"/>
<xs:attribute name="expression" type="nonNullString" use="optional"/>
<xs:attribute name="ref" type="nonNullString" use="optional"/>
</xs:complexType>
<xs:annotation>
<xs:documentation>Defines a single named object.</xs:documentation>
</xs:annotation>
<xs:complexType name="vanillaObject">
<xs:sequence>
<xs:element name="description" type="description" minOccurs="0" maxOccurs="1"/>
<!--
Object definitions can specify zero or more constructor arguments.
They correspond to either a specific index of the constructor argument list
or are supposed to be matched generically by type.
This is an alternative to "autowire constructor".
-->
<xs:element name="constructor-arg" type="constructorArgument" minOccurs="0" maxOccurs="unbounded"/>
<!--
Object definitions can have zero or more properties.
Spring supports primitives, references to other objects in the same or
related factories, lists, dictionaries and properties.
-->
<xs:element name="property" type="property" minOccurs="0" maxOccurs="unbounded"/>
<!--
Object definitions can specify zero or more lookup-methods.
-->
<xs:element name="lookup-method" type="lookupMethod" minOccurs="0" maxOccurs="unbounded"/>
<!-- Object definitions can have zero or more replaced-methods. -->
<xs:element name="replaced-method" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="arg-type" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="match" type="nonNullString" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="name" type="nonNullString" use="required"/>
<xs:attribute name="replacer" type="nonNullString" use="required"/>
</xs:complexType>
</xs:element>
<!-- Object definitions can have zero or more subscriptions. -->
<xs:element name="listener" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="ref" type="objectOrClassReference" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<!-- The event(s) the object is interested in. -->
<xs:attribute name="event" type="nonNullString" use="optional"/>
<!-- The name or name pattern of the method that will handle the event(s). -->
<xs:attribute name="method" type="nonNullString" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<!--
Objects can be identified by an id, to enable reference checking.
There are constraints on a valid XML id: if you want to reference your object
in .NET code using a name that's illegal as an XML id, use the optional
"name" attribute. If neither given, the object type name is used as id.
-->
<xs:attribute name="id" type="xs:ID" use="optional"/>
<!--
Optional. Can be used to create one or more aliases illegal in an id.
Multiple aliases can be separated by any number of spaces or commas.
-->
<xs:attribute name="name" type="nonNullString" use="optional"/>
<!--
Each object definition must specify the full, assembly qualified of the type,
or the name of the parent object from which the type can be worked out.
Note that a child object definition that references a parent will just
add respectively override property values and be able to change the
singleton status. It will inherit all of the parent's other parameters
like lazy initialization or autowire settings.
-->
<xs:attribute name="type" type="nonNullString" use="optional"/>
<xs:attribute name="parent" type="nonNullString" use="optional"/>
<!--
Is this object "abstract", i.e. not meant to be instantiated itself but
rather just serving as parent for concrete child object definitions?
Default is false. Specify true to tell the object factory to not try to
instantiate that particular object in any case.
-->
<xs:attribute name="abstract" type="xs:boolean" use="optional" default="false"/>
<!--
Is this object a "singleton" (one shared instance, which will
be returned by all calls to GetObject() with the id),
or a "prototype" (independent instance resulting from each call to
getObject(). Default is singleton.
Singletons are most commonly used, and are ideal for multi-threaded
service objects.
-->
<xs:attribute name="singleton" type="xs:boolean" use="optional" default="true"/>
<!--
Optional attribute controlling the scope of singleton instances. It is
only applicable to ASP.Net web applications and it has no effect on prototype
objects. Applications other than ASP.Net web applications simply ignore this attribute.
It has 3 possible values:
1. "application"
Default object scope. Objects defined with application scope will behave like
traditional singleton objects. Same instance will be returned from every call
to IApplicationContext.GetObject()
2. "session"
Objects with this scope will be stored within user's HTTP session. Session scope
is typically used for objects such as shopping cart, user profile, etc.
3. "request"
Object with this scope will be initialized for each HTTP request, but unlike with prototype
objects, same instance will be returned from all calls to IApplicationContext.GetObject()
within the same HTTP request. For example, if one ASP page forwards request to another using
Server.Transfer method, they can easily share the state by configuring dependency to the same
request-scoped object.
-->
<xs:attribute name="scope" use="optional" default="application">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="application"/>
<xs:enumeration value="session"/>
<xs:enumeration value="request"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<!--
Is this object to be lazily initialized?
If false, it will get instantiated on startup by object factories
that perform eager initialization of singletons.
-->
<xs:attribute name="lazy-init" use="optional" default="default">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="true"/>
<xs:enumeration value="false"/>
<xs:enumeration value="default"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<!--
Optional attribute controlling whether to "autowire" object properties.
This is an automagical process in which object references don't need to be coded
explicitly in the XML object definition file, but Spring works out dependencies.
There are 5 modes:
1. "no"
The traditional Spring default. No automagical wiring. Object references
must be defined in the XML file via the <ref> element. We recommend this
in most cases as it makes documentation more explicit.
2. "byName"
Autowiring by property name. If a object of class Cat exposes a dog property,
Spring will try to set this to the value of the object "dog" in the current factory.
3. "byType"
Autowiring if there is exactly one object of the property type in the object factory.
If there is more than one, a fatal error is raised, and you can't use byType
autowiring for that object. If there is none, nothing special happens - use
dependency-check="objects" to raise an error in that case.
4. "constructor"
Analogous to "byType" for constructor arguments. If there isn't exactly one object
of the constructor argument type in the object factory, a fatal error is raised.
5. "autodetect"
Chooses "constructor" or "byType" through introspection of the object class.
If a default constructor is found, "byType" gets applied.
The latter two are similar to PicoContainer and make object factories simple to
configure for small namespaces, but doesn't work as well as standard Spring
behaviour for bigger applications.
Note that explicit dependencies, i.e. "property" and "constructor-arg" elements,
always override autowiring. Autowire behaviour can be combined with dependency
checking, which will be performed after all autowiring has been completed.
-->
<xs:attribute name="autowire" use="optional" default="default">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="no"/>
<xs:enumeration value="byName"/>
<xs:enumeration value="byType"/>
<xs:enumeration value="constructor"/>
<xs:enumeration value="autodetect"/>
<xs:enumeration value="default"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<!--
Optional attribute controlling whether to check whether all this
objects dependencies, expressed in its properties, are satisfied.
Default is no dependency checking.
"simple" type dependency checking includes primitives and String
"object" includes collaborators (other objects in the factory)
"all" includes both types of dependency checking
-->
<xs:attribute name="dependency-check" use="optional" default="default">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="none"/>
<xs:enumeration value="objects"/>
<xs:enumeration value="simple"/>
<xs:enumeration value="all"/>
<xs:enumeration value="default"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<!--
The names of the objects that this object depends on being initialized.
The object factory will guarantee that these objects get initialized before.
Note that dependencies are normally expressed through object properties or
constructor arguments. This property should just be necessary for other kinds
of dependencies like statics (*ugh*) or database preparation on startup.
-->
<xs:attribute name="depends-on" type="nonNullString" use="optional"/>
<!--
Optional attribute for the name of the custom initialization method
to invoke after setting object properties. The method must have no arguments,
but may throw any exception.
-->
<xs:attribute name="init-method" type="nonNullString" use="optional"/>
<!--
Optional attribute for the name of the custom destroy method to invoke
on object factory shutdown. The method must have no arguments,
but may throw any exception. Note: Only invoked on singleton objects!
-->
<xs:attribute name="destroy-method" type="nonNullString" use="optional"/>
<xs:attribute name="factory-method" type="nonNullString" use="optional"/>
<xs:attribute name="factory-object" type="nonNullString" use="optional"/>
</xs:complexType>
<xs:annotation>
<xs:documentation>The document root. At least one object definition is required.</xs:documentation>
</xs:annotation>
<xs:element name="objects">
<xs:complexType>
<xs:sequence>
<xs:element name="description" type="description" minOccurs="0" maxOccurs="1"/>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="import" type="importElement"/>
<xs:element name="alias" type="aliasElement"/>
<xs:element name="object" type="vanillaObject"/>
<xs:any namespace="##other" processContents="strict"/>
</xs:choice>
</xs:sequence>
<!--
Default values for all object definitions. Can be overridden at
the "object" level. See those attribute definitions for details.
-->
<xs:attribute name="default-lazy-init" type="xs:boolean" use="optional" default="false"/>
<xs:attribute name="default-dependency-check" use="optional" default="none">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="none"/>
<xs:enumeration value="objects"/>
<xs:enumeration value="simple"/>
<xs:enumeration value="all"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="default-autowire" use="optional" default="no">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="no"/>
<xs:enumeration value="byName"/>
<xs:enumeration value="byType"/>
<xs:enumeration value="constructor"/>
<xs:enumeration value="autodetect"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
]]></programlisting>
</appendix>