Initial import!
2307
doc/reference/src/ado.xml
Normal file
154
doc/reference/src/ajax.xml
Normal 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>
|
||||
<object id="ContactWebService" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
|
||||
<property name="TargetName" value="ContactService"/>
|
||||
<property name="Namespace" value="http://Spring.Examples.Atlas/ContactService"/>
|
||||
<property name="Description" value="Contact Web Services"/>
|
||||
<property name="TypeAttributes">
|
||||
<list>
|
||||
<object type="System.Web.Script.Services.ScriptServiceAttribute, System.Web.Extensions"/>
|
||||
</list>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
</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>
|
||||
<sectionGroup name="spring">
|
||||
<section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/>
|
||||
</sectionGroup>
|
||||
|
||||
</programlisting> <programlisting>
|
||||
<spring>
|
||||
<context>
|
||||
<resource uri="~/Spring.config"/>
|
||||
</context>
|
||||
</spring>
|
||||
|
||||
</programlisting></para>
|
||||
|
||||
<para><emphasis> 2. Register the HTTP handler and the Spring HttpModule
|
||||
under the <literal>system.web</literal> section. </emphasis>
|
||||
<programlisting>
|
||||
<httpHandlers>
|
||||
<remove verb="*" path="*.asmx"/>
|
||||
<add verb="*" path="*.asmx" validate="false" type="Spring.Web.Script.Services.ScriptHandlerFactory, Spring.Web.Extensions"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
</httpHandlers>
|
||||
|
||||
<httpModules>
|
||||
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="SpringModule" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
|
||||
</httpModules>
|
||||
|
||||
</programlisting></para>
|
||||
|
||||
<para><emphasis> 3. Register the HTTP handler and the Spring HttpModule
|
||||
under <literal>system.webServer</literal> section. </emphasis>
|
||||
<programlisting>
|
||||
<modules>
|
||||
<add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="SpringModule" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
|
||||
</modules>
|
||||
<handlers>
|
||||
<remove name="WebServiceHandlerFactory-Integrated" />
|
||||
<add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode"
|
||||
type="Spring.Web.Script.Services.ScriptHandlerFactory, Spring.Web.Extensions"/>
|
||||
<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"/>
|
||||
<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" />
|
||||
</handlers>
|
||||
|
||||
</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>
|
||||
733
doc/reference/src/aop-aspect-library.xml
Normal 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> <object id="CacheAspect" type="Spring.Aspects.Cache.CacheAspect, Spring.Aop"/>
|
||||
<object id="AspNetCache" type="Spring.Caching.AspNetCache, Spring.Web">
|
||||
<property name="SlidingExpiration" value="true"/>
|
||||
<property name="Priority" value="CachePriority.Low"/>
|
||||
<property name="TimeToLive" value="00:02:00"/>
|
||||
</object>
|
||||
|
||||
|
||||
<!-- Apply aspects to DAOs -->
|
||||
<object type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
|
||||
<property name="ObjectNames">
|
||||
<list>
|
||||
<value>*Dao</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="InterceptorNames">
|
||||
<list>
|
||||
<value>CacheAspect</value>
|
||||
</list>
|
||||
</property>
|
||||
</object></programlisting>
|
||||
|
||||
<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><object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop">
|
||||
<property name="exceptionHandlers">
|
||||
<list>
|
||||
<value><emphasis role="bold">on exception name ArithmeticException wrap System.InvalidOperationException</emphasis></value>
|
||||
</list>
|
||||
</property>
|
||||
</object></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
|
||||
<list> shown above. The processing flow is on exception, the name of
|
||||
the exception listed in the statement is compared to the thrown exception
|
||||
to see if there is a match. A comma separated list of exceptions can be
|
||||
used to group together the same action taken for different exception
|
||||
names. If the action to take is logging, then the logging action is
|
||||
performed and the search for other matching exception names continues. For
|
||||
all other actions, namely translate, wrap, replace, swallow, return, once
|
||||
an exception handler is matched, those in the chain are no longer
|
||||
evaluated. Note, do not confuse this handler chain with the general advice
|
||||
AOP advice chain. For translate, wrap, and replace actions a SpEL
|
||||
expression is created and used to instantiate a new exception (in addition
|
||||
to any other processing that may occur when evaluating the expression)
|
||||
which is then thrown.</para>
|
||||
|
||||
<para>The exception handling DSL also supports the ability to provide a
|
||||
SpEL boolean expression to determine if the advice will apply instead of
|
||||
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) && #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><object name="loggingAdvice" type="Spring.Aspects.Logging.SimpleLoggingAdvice, Spring.Aop">
|
||||
<property name="logUniqueIdentifier" value="true"/>
|
||||
<property name="logExecutionTime" value="true"/>
|
||||
<property name="logMethodArguments" value="true"/>
|
||||
<property name="LogReturnValue" value="true"/>
|
||||
|
||||
<property name="Separator" value=";"/>
|
||||
<property name="LogLevel" value="Info"/>
|
||||
|
||||
|
||||
<property name="HideProxyTypeNames" value="true"/>
|
||||
<property name="UseDynamicLogger" value="true"/>
|
||||
</object></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><object name="exceptionHandlingAdvice" type="Spring.Aspects.RetryAdvice, Spring.Aop">
|
||||
<property name="retryExpression" value="<emphasis role="bold">on exception name ArithmeticException retry 3x delay 1s</emphasis>"/>
|
||||
</object></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> <object id="<emphasis role="bold">validationAdvice</emphasis>" type="Spring.Aspects.Validation.ParameterValidationAdvice, Spring.Aop"/>
|
||||
|
||||
<object type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
|
||||
<property name="ObjectNames">
|
||||
<list>
|
||||
<value>bookingAgent</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="InterceptorNames">
|
||||
<list>
|
||||
<value><emphasis role="bold">validationAdvice</emphasis></value>
|
||||
</list>
|
||||
</property>
|
||||
</object></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>
|
||||
1099
doc/reference/src/aop-quickstart.xml
Normal file
2470
doc/reference/src/aop.xml
Normal file
34
doc/reference/src/background.xml
Normal 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
@@ -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><objects xmlns='http://www.springframework.net'>
|
||||
|
||||
<alias name='SqlServer-2.0' alias='SqlServer2005'/>
|
||||
|
||||
<object name="appConfigPropertyOverride" type="Spring.Objects.Factory.Config.PropertyOverrideConfigurer, Spring.Core">
|
||||
<property name="Properties">
|
||||
<name-values>
|
||||
<add key="SqlServer2005.DbMetadata.ErrorCodes.DataIntegrityViolationCodes"
|
||||
value="544,2601,2627,8114,8115"/>
|
||||
</name-values>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
</objects></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>
|
||||
172
doc/reference/src/data-quickstart.xml
Normal 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><objects xmlns="http://www.springframework.net"
|
||||
xmlns:db="http://www.springframework.net/database">
|
||||
|
||||
<db:provider id="dbProvider"
|
||||
provider="SqlServer-1.1"
|
||||
connectionString="Data Source=(local);Database=Northwind;User ID=springqa;Password=springqa;Trusted_Connection=False"/>
|
||||
|
||||
|
||||
<! -- other definitions not shown
|
||||
|
||||
|
||||
</objects></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&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> <object id="adoTemplate" type="Spring.Data.Generic.AdoTemplate, Spring.Data">
|
||||
<property name="DbProvider" ref="dbProvider"/>
|
||||
<property name="DataReaderWrapperType" value="Spring.Data.Support.NullMappingDataReader, Spring.Data"/>
|
||||
</object>
|
||||
</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> <object id="commandCallbackDao" type="Spring.DataQuickStart.Dao.GenericTemplate.CommandCallbackDao, Spring.DataQuickStart">
|
||||
<property name="AdoTemplate" ref="adoTemplate"/>
|
||||
</object></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<int>(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>
|
||||
464
doc/reference/src/dbprovider.xml
Normal 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><dependentAssembly>
|
||||
<assemblyIdentity name="MySql.Data"
|
||||
publicKeyToken="c5687fc88969c44d"
|
||||
culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535"
|
||||
newVersion="1.0.10.1"/>
|
||||
</dependentAssembly></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><objects xmlns='http://www.springframework.net'
|
||||
xmlns:db="http://www.springframework.net/database">
|
||||
|
||||
<db:provider id="DbProvider"
|
||||
provider="System.Data.SqlClient"
|
||||
connectionString="Data Source=(local);Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"/>
|
||||
|
||||
<object id="adoTemplate" type="Spring.Data.AdoTemplate, Spring.Data">
|
||||
<property name="DbProvider" ref="DbProvider"/>
|
||||
</object>
|
||||
|
||||
</objects></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><configuration>
|
||||
|
||||
<configSections>
|
||||
<sectionGroup name="spring">
|
||||
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<spring>
|
||||
<parsers>
|
||||
<parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
|
||||
</parsers>
|
||||
</spring>
|
||||
|
||||
</configuration></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><configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="spring">
|
||||
<section name='context' type='Spring.Context.Support.ContextHandler, Spring.Core'/>
|
||||
</sectionGroup>
|
||||
|
||||
<section name="databaseSettings" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
|
||||
</configSections>
|
||||
|
||||
<spring>
|
||||
<context>
|
||||
<resource uri="Aspects.xml" />
|
||||
<resource uri="Services.xml" />
|
||||
<resource uri="Dao.xml" />
|
||||
</context>
|
||||
</spring>
|
||||
|
||||
<!-- These properties are referenced in Dao.xml -->
|
||||
<databaseSettings>
|
||||
<add key="db.datasource" value="(local)" />
|
||||
<add key="db.user" value="springqa" />
|
||||
<add key="db.password" value="springqa" />
|
||||
<add key="db.database" value="Northwind" />
|
||||
</databaseSettings>
|
||||
|
||||
|
||||
</configuration></programlisting>
|
||||
|
||||
<para>Where <literal>Dao.xml</literal> has a connection string as shown
|
||||
below</para>
|
||||
|
||||
<programlisting><objects xmlns='http://www.springframework.net'
|
||||
xmlns:db="http://www.springframework.net/database">
|
||||
|
||||
<db:provider id="DbProvider"
|
||||
provider="System.Data.SqlClient"
|
||||
connectionString="${db.datasource};Database=${db.database};User ID=${db.user};Password=${db.password};Trusted_Connection=False"/>
|
||||
|
||||
<object id="adoTemplate" type="Spring.Data.AdoTemplate, Spring.Data">
|
||||
<property name="DbProvider" ref="DbProvider"/>
|
||||
</object>
|
||||
|
||||
<!-- configuration of what values to substitute for ${ } variables listed above -->
|
||||
<object name="appConfigPropertyHolder"
|
||||
type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core">
|
||||
<property name="configSections" value="DatabaseConfiguration"/>
|
||||
</object>
|
||||
|
||||
</objects></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><object id="DbProvider" type="Spring.Data.Common.UserCredentialsDbProvider, Spring.Data">
|
||||
<property name="TargetDbProvider" ref="targetDbProvider"/>
|
||||
<property name="Username" value="User ID=defaultName"/>
|
||||
<property name="Password" value="Password=defaultPass"/>
|
||||
</object>
|
||||
|
||||
<db:provider id="targetDbProvider" provider="SqlServer-2.0"
|
||||
connectionString="Data Source=MARKT60\SQL2005;Database=Spring;Trusted_Connection=False"/></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>
|
||||
990
doc/reference/src/expressions.xml
Normal 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 < -5.0") // false
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "DateTime.Today <= date('1974-08-24')") // false
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "'Test' >= '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 > $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 <= 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 <= 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 & 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>
|
||||
BIN
doc/reference/src/images/Copy of S2-banner-rhs.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
doc/reference/src/images/DataAccessException.gif
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
doc/reference/src/images/S2-banner-rhs.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
doc/reference/src/images/Thumbs.db
Normal file
BIN
doc/reference/src/images/aop-chain.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
doc/reference/src/images/aop-uml.gif
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
doc/reference/src/images/bean-lifecycle-overview.gif
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
doc/reference/src/images/container-in-action.gif
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
doc/reference/src/images/i21-banner-rhs.jpg
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
doc/reference/src/images/link.png
Normal file
|
After Width: | Height: | Size: 376 B |
BIN
doc/reference/src/images/logo.gif
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
doc/reference/src/images/logo.jpg
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
doc/reference/src/images/logo.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
doc/reference/src/images/logo.psd
Normal file
BIN
doc/reference/src/images/logo.xcf
Normal file
BIN
doc/reference/src/images/movie-finder.gif
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
doc/reference/src/images/overview.gif
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
doc/reference/src/images/remoting-solution.gif
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
doc/reference/src/images/remoting-startup.gif
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
doc/reference/src/images/spring-triangle.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
doc/reference/src/images/spring.sxd
Normal file
BIN
doc/reference/src/images/spring.vsd
Normal file
BIN
doc/reference/src/images/spring.windows-service.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
doc/reference/src/images/spring.windows-service.vsd
Normal file
BIN
doc/reference/src/images/tx.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
doc/reference/src/images/web-exporter-calc-svc-aop-add.jpg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
doc/reference/src/images/web-exporter-calc-svc-aop-add001.jpg
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
doc/reference/src/images/web-exporter-calc-svc-aop.jpg
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
doc/reference/src/images/web-exporter-calc-svc-main.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
doc/reference/src/images/web-exporter-calc-svc.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
doc/reference/src/images/xdev-spring_logo.jpg
Normal file
|
After Width: | Height: | Size: 36 KiB |
383
doc/reference/src/index.xml
Normal 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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
234
doc/reference/src/javadevelopers.xml
Normal 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><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
|
||||
<beans>
|
||||
<bean id="MyMovieLister" class="MovieFinder.MovieLister">
|
||||
<property name="finder" ref="MyMovieFinder"/>
|
||||
</bean>
|
||||
<bean id="MyMovieFinder" class="MovieFinder.SimpleMovieFinder"/>
|
||||
</beans></programlisting> Here is the corresponding file in Spring.NET
|
||||
<programlisting><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">
|
||||
<object name="MyMovieLister"
|
||||
type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder">
|
||||
<property name="movieFinder" ref="MyMovieFinder"/>
|
||||
</object>
|
||||
<object name="MyMovieFinder"
|
||||
type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/>
|
||||
</objects></programlisting> As you can easily see the <beans> and
|
||||
<bean> elements are replaced by <objects> and <object>
|
||||
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 <props> as shown below <programlisting
|
||||
format="linespecific" xml:space="preserve">
|
||||
<property name="people">
|
||||
<props>
|
||||
<prop key="PennAndTeller">The magic property</prop>
|
||||
<prop key="GeorgeCarlin">The funny property</prop>
|
||||
</props>
|
||||
</property></programlisting> In .NET the analogous class is
|
||||
System.Collections.Specialized.NameValueCollection and is represented by
|
||||
the xml element <name-values>. The listing of the elements also
|
||||
follows the .NET convention of application configuration files using the
|
||||
<add> element with 'key' and 'value' attributes. This is show below
|
||||
<programlisting format="linespecific" xml:space="preserve">
|
||||
<property name="people">
|
||||
<name-values>
|
||||
<add key="PennAndTeller" value="The magic property"/>
|
||||
<add key="GeorgeCarlin" value="The funny property"/>
|
||||
</name-values>
|
||||
</property></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 <MyExe>.exe.config for WinForms and console
|
||||
applications. <MyExe> 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
|
||||
<MyExe>.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><context></literal> section and
|
||||
another to configure the context with object definitions contained in an
|
||||
<literal><objects></literal> section. The
|
||||
<literal><context></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><?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
|
||||
<configSections>
|
||||
<sectionGroup name="spring">
|
||||
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
|
||||
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<spring>
|
||||
|
||||
<context>
|
||||
<resource uri="config://spring/objects"/>
|
||||
</context>
|
||||
|
||||
<objects>
|
||||
<description>An example that demonstrates simple IoC features.</description>
|
||||
<object name="MyMovieLister" type="Spring.Examples.MovieFinder.MovieLister, MovieFinder">
|
||||
<property name="movieFinder" ref="AnotherMovieFinder"/>
|
||||
</object>
|
||||
<object name="MyMovieFinder" type="Spring.Examples.MovieFinder.SimpleMovieFinder, MovieFinder"/>
|
||||
<!--
|
||||
An IMovieFinder implementation that uses a text file as it's movie source...
|
||||
-->
|
||||
<object name="AnotherMovieFinder" type="Spring.Examples.MovieFinder.ColonDelimitedMovieFinder, MovieFinder">
|
||||
<constructor-arg index="0" value="movies.txt"/>
|
||||
</object>
|
||||
</objects>
|
||||
|
||||
</spring>
|
||||
|
||||
</configuration></programlisting>
|
||||
|
||||
<para>The <configSections> and <section> 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
|
||||
<context> and <objects> 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><?xml version="1.0" encoding="utf-8" ?>
|
||||
<objects xmlns="http://www.springframework.net">
|
||||
<object id="target" type="Spring.Objects.TestObject">
|
||||
<property name="name" value="Bingo"/>
|
||||
</object>
|
||||
|
||||
<object id="nopInterceptor" type="Spring.Aop.Interceptor.NopInterceptor"/>
|
||||
|
||||
<object id="prototypeTarget" type="Spring.Aop.Framework.ProxyFactoryObject">
|
||||
<property name="interceptorNames" value="nopInterceptor,target"/> <!-- not valid! -->
|
||||
</object>
|
||||
</objects></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>
|
||||
34
doc/reference/src/logging.xml
Normal 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>
|
||||
131
doc/reference/src/migration.xml
Normal 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><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"></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><configuration>
|
||||
|
||||
<configSections>
|
||||
<sectionGroup name="spring">
|
||||
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<spring>
|
||||
<parsers>
|
||||
<parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" />
|
||||
<parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
|
||||
<parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" />
|
||||
</parsers>
|
||||
</spring></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 <db:provider ... /> and not <db:dbProvider
|
||||
.../></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
@@ -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>
|
||||
75
doc/reference/src/navigation.xml
Normal 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>
|
||||
438
doc/reference/src/objects-misc.xml
Normal 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><objects xmlns="http://www.springframework.net">
|
||||
<object id="parserFactory" type="Example.XmlParserFactory, ExamplesLibrary"
|
||||
destroy-method="Close">
|
||||
<property name="ParserClass" value="Example.Xml.SAXParser, ExamplesLibrary"/>
|
||||
</object>
|
||||
</objects></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><object id="rod" type="Spring.Objects.TestObject, Spring.Core.Tests">
|
||||
<property name="name" value="Rod"/>
|
||||
<property name="FileMode" value="Create"/>
|
||||
</object></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>
|
||||
5786
doc/reference/src/objects.xml
Normal file
948
doc/reference/src/orm.xml
Normal 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> <object id="HibernateTransactionManager"
|
||||
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate">
|
||||
|
||||
<property name="DbProvider" ref="DbProvider"/>
|
||||
<property name="SessionFactory" ref="MySessionFactory"/>
|
||||
|
||||
</object></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><objects xmlns="http://www.springframework.net"
|
||||
xmlns:db="http://www.springframework.net/database">
|
||||
|
||||
|
||||
<!-- Property placeholder configurer for database settings -->
|
||||
|
||||
<object type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core">
|
||||
<property name="ConfigSections" value="databaseSettings"/>
|
||||
</object>
|
||||
|
||||
<!-- Database and NHibernate Configuration -->
|
||||
|
||||
<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;"/>
|
||||
|
||||
|
||||
|
||||
<object id="MySessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate">
|
||||
<property name="DbProvider" ref="DbProvider"/>
|
||||
<property name="MappingAssemblies">
|
||||
<list>
|
||||
<value>Spring.Northwind.Dao.NHibernate</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="HibernateProperties">
|
||||
<dictionary>
|
||||
|
||||
<entry key="hibernate.connection.provider"
|
||||
value="NHibernate.Connection.DriverConnectionProvider"/>
|
||||
|
||||
<entry key="hibernate.dialect"
|
||||
value="NHibernate.Dialect.MsSql2000Dialect"/>
|
||||
|
||||
<entry key="hibernate.connection.driver_class"
|
||||
value="NHibernate.Driver.SqlClientDriver"/>
|
||||
|
||||
</dictionary>
|
||||
</property>
|
||||
|
||||
</object>
|
||||
|
||||
</objects></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><objects>
|
||||
|
||||
<object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerDao, Spring.Northwind.Dao.NHibernate">
|
||||
<property name="SessionFactory" ref="MySessionFactory"/>
|
||||
</object>
|
||||
|
||||
</objects></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<Supplier> suppliers = HibernateTemplate.ExecuteFind<Supplier>(
|
||||
delegate(ISession session)
|
||||
{
|
||||
return session.CreateQuery("from Supplier s were s.Code = ?")
|
||||
.SetParameter(0, code)
|
||||
.List<Supplier>();
|
||||
});</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<Product> LoadProductsByCategory(String category) {
|
||||
return SessionFactory.GetCurrentSession()
|
||||
.CreateQuery("from test.Product product where product.category=?")
|
||||
.SetParameter(0, category)
|
||||
.List<Product>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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>
|
||||
<objects>
|
||||
|
||||
<object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerDao, Spring.Northwind.Dao.NHibernate">
|
||||
<property name="sessionFactory" ref="MySessionFactory"/>
|
||||
</object>
|
||||
|
||||
</objects></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><object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
|
||||
|
||||
<emphasis role="bold"><property name="ExposeTransactionAwareSessionFactory" value="true" /></emphasis>
|
||||
|
||||
<!-- other configuration settings omitted -->
|
||||
|
||||
</object></programlisting>
|
||||
|
||||
<para>Which is simply a shortcut for the following configuration</para>
|
||||
|
||||
<programlisting><object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
|
||||
|
||||
<!-- other configuration settings omitted -->
|
||||
|
||||
<property name="HibernateProperties">
|
||||
<dictionary>
|
||||
|
||||
<!-- other dictionary entries omitted -->
|
||||
|
||||
<emphasis role="bold"><entry key="hibernate.current_session_context_class"
|
||||
value="Spring.Data.NHibernate.SpringSessionContext, Spring.Data.NHibernate12"/></emphasis>
|
||||
|
||||
</dictionary>
|
||||
</property>
|
||||
|
||||
</object></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><objects>
|
||||
|
||||
TO BE DONE
|
||||
|
||||
</objects></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><objects>
|
||||
|
||||
<object id="HibernateTransactionManager"
|
||||
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate">
|
||||
|
||||
<property name="DbProvider" ref="DbProvider"/>
|
||||
<property name="SessionFactory" ref="MySessionFactory"/>
|
||||
|
||||
</object>
|
||||
|
||||
<!-- DAO definition not listed, see above for an example. -->
|
||||
|
||||
<object id="FulfillmentService" type="Spring.Northwind.Service.FulfillmentService, Spring.Northwind.Service">
|
||||
<property name="CustomerDao" ref="CustomerDao"/>
|
||||
<property name="OrderDao" ref="OrderDao"/>
|
||||
<property name="ShippingService" ref="ShippingService"/>
|
||||
</object>
|
||||
|
||||
<!-- Import 'standard xml' configuration for attribute driven declarative tx management -->
|
||||
<import resource="DeclarativeServicesAttributeDriven.xml"/>
|
||||
|
||||
</objects></programlisting>
|
||||
|
||||
<para>Note that with the new transaction namespace, you can replace the
|
||||
importing of DeclarativeServicesAttributeDriven.xml with the following
|
||||
single line, <code><tx:attribute-driven/></code> that more clearly
|
||||
expresses the intent as compared to the contents of
|
||||
DeclarativeServicesAttributeDriven.xml.</para>
|
||||
|
||||
<programlisting><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">
|
||||
|
||||
|
||||
<object id="HibernateTransactionManager"
|
||||
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate">
|
||||
|
||||
<property name="DbProvider" ref="DbProvider"/>
|
||||
<property name="SessionFactory" ref="MySessionFactory"/>
|
||||
|
||||
</object>
|
||||
|
||||
<!-- DAO definition not listed, see above for an example. -->
|
||||
|
||||
<object id="FulfillmentService" type="Spring.Northwind.Service.FulfillmentService, Spring.Northwind.Service">
|
||||
<property name="CustomerDao" ref="CustomerDao"/>
|
||||
<property name="OrderDao" ref="OrderDao"/>
|
||||
<property name="ShippingService" ref="ShippingService"/>
|
||||
</object>
|
||||
|
||||
|
||||
<emphasis><tx:attribute-driven/></emphasis>
|
||||
|
||||
|
||||
</objects></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 <tx:attribute-driven/></para>
|
||||
|
||||
<programlisting> <object id="TxProxyConfigurationTemplate" abstract="true"
|
||||
type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data">
|
||||
|
||||
<property name="PlatformTransactionManager" ref="HibernateTransactionManager"/>
|
||||
|
||||
<property name="TransactionAttributes">
|
||||
<name-values>
|
||||
<!-- Add common methods across your services here -->
|
||||
<add key="Process*" value="PROPAGATION_REQUIRED"/>
|
||||
</name-values>
|
||||
</property>
|
||||
</object></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> <system.web>
|
||||
<httpModules>
|
||||
<add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewModule, Spring.Data.NHibernate"/>
|
||||
</httpModules>
|
||||
|
||||
...
|
||||
|
||||
</system.web></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> <appSettings>
|
||||
<add key="Spring.Data.NHibernate.Support.OpenSessionInViewModule.SessionFactoryObjectName" value="SessionFactory"/>
|
||||
</appSettings></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>
|
||||
307
doc/reference/src/overview.xml
Normal 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 Spring’s 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>
|
||||
71
doc/reference/src/pool.xml
Normal 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>
|
||||
279
doc/reference/src/pooling-example.xml
Normal 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;
|
||||
|
||||
/// <summary>
|
||||
/// Builds a new <see cref="PooledObjectHolder"/>
|
||||
/// trying to borrow an object form it
|
||||
/// </summary>
|
||||
/// <param name="pool"></param>
|
||||
private PooledObjectHolder(IObjectPool pool)
|
||||
{
|
||||
this.pool = pool;
|
||||
this.pooled = pool.BorrowObject();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allow to access the borrowed pooled object
|
||||
/// </summary>
|
||||
public object Pooled
|
||||
{
|
||||
get
|
||||
{
|
||||
return pooled;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the borrowed object to the pool
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
pool.ReturnObject(pooled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="PooledObjectHolder"/> for the
|
||||
/// given pool.
|
||||
/// </summary>
|
||||
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 < 3)
|
||||
{
|
||||
Console.Out.WriteLine("usage: {0} regex directory file-pattern [pool-size]", 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 ("pool size {0}", size);
|
||||
|
||||
ParallelGrep grep = new ParallelGrep(size);
|
||||
grep.Recurse(startPath, filePattern, regexPattern);
|
||||
grep.Stop();
|
||||
}</programlisting>
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
36
doc/reference/src/preface.xml
Normal 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>
|
||||
78
doc/reference/src/psa-intro.xml
Normal 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>
|
||||
646
doc/reference/src/quickstarts.xml
Normal 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><?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="spring">
|
||||
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
|
||||
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
<spring>
|
||||
<context>
|
||||
<resource uri="config://spring/objects"/>
|
||||
</context>
|
||||
<objects xmlns="http://www.springframework.net">
|
||||
<description>An example that demonstrates simple IoC features.</description>
|
||||
</objects>
|
||||
</spring>
|
||||
</configuration></programlisting>
|
||||
|
||||
<para>The objects that will be used in the example application will be
|
||||
configured as XML <literal><object/></literal> elements nested
|
||||
inside the <literal><objects/></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><objects/></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><objects xmlns="http://www.springframework.net">
|
||||
<object name="MyMovieLister"
|
||||
type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder">
|
||||
</object>
|
||||
</objects></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><objects xmlns="http://www.springframework.net">
|
||||
<object name="MyMovieFinder"
|
||||
type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/>
|
||||
</object>
|
||||
</objects></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><objects xmlns="http://www.springframework.net">
|
||||
<object name="MyMovieLister"
|
||||
type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder">
|
||||
<!-- using setter injection... -->
|
||||
<property name="movieFinder" ref="MyMovieFinder"/>
|
||||
</object>
|
||||
<object name="MyMovieFinder"
|
||||
type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/>
|
||||
</object>
|
||||
</objects></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>...
|
||||
<object name="AnotherMovieFinder"
|
||||
type="Spring.Examples.MovieFinder.ColonDelimitedMovieFinder, Spring.Examples.MovieFinder">
|
||||
</object>
|
||||
...</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>...
|
||||
<object name="AnotherMovieFinder"
|
||||
type="Spring.Examples.MovieFinder.ColonDelimitedMovieFinder, Spring.Examples.MovieFinder">
|
||||
<constructor-arg index="0" value="movies.txt"/>
|
||||
</object>
|
||||
...</programlisting></para>
|
||||
|
||||
<para>Unsurprisingly, the <constructor-arg/> 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>...
|
||||
<object name="MyMovieLister"
|
||||
type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder">
|
||||
<!-- lets use the colon delimited implementation instead -->
|
||||
<property name="movieFinder" ref="AnotherMovieFinder"/>
|
||||
</object>
|
||||
<object name="MyMovieFinder"
|
||||
type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/>
|
||||
</object>
|
||||
<object name="AnotherMovieFinder"
|
||||
type="Spring.Examples.MovieFinder.ColonDelimitedMovieFinder, Spring.Examples.MovieFinder">
|
||||
<constructor-arg index="0" value="movies.txt"/>
|
||||
</object>
|
||||
...</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><section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" /></programlisting>
|
||||
The corresponding configuration section looks like this <programlisting>
|
||||
<log4net>
|
||||
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
|
||||
<layout type="log4net.Layout.PatternLayout">
|
||||
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<!-- Set default logging level to DEBUG -->
|
||||
<root>
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="ConsoleAppender" />
|
||||
</root>
|
||||
|
||||
<!-- Set logging for Spring to INFO. Logger names in Spring correspond to the namespace -->
|
||||
<logger name="Spring">
|
||||
<level value="INFO" />
|
||||
</logger>
|
||||
</log4net>
|
||||
</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>...
|
||||
<object name="messageSource" type="Spring.Context.Support.ResourceSetMessageSource, Spring.Core">
|
||||
<property name="resourceManagers">
|
||||
<list>
|
||||
<value>Spring.Examples.AppContext.Images, Spring.Examples.AppContext</value>
|
||||
<ref object="myResourceManager"/>
|
||||
</list>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
<object name="myResourceManager" type="Spring.Objects.Factory.Config.ResourceManagerFactoryObject, Spring.Core">
|
||||
<property name="baseName">
|
||||
<value>Spring.Examples.AppContext.MyResource</value>
|
||||
</property>
|
||||
<property name="assemblyName">
|
||||
<value>Spring.Examples.AppContext</value>
|
||||
</property>
|
||||
</object>
|
||||
...</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.<PropertyName>. 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>
|
||||
817
doc/reference/src/remoting-quickstart.xml
Normal 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 <return> 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><system.runtime.remoting>
|
||||
<application>
|
||||
<channels>
|
||||
<channel ref="tcp" port="8005" />
|
||||
</channels>
|
||||
</application>
|
||||
</system.runtime.remoting></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> <configSections>
|
||||
<sectionGroup name="spring">
|
||||
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
|
||||
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
|
||||
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
|
||||
</sectionGroup>
|
||||
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
|
||||
</configSections>
|
||||
|
||||
<spring>
|
||||
<parsers>
|
||||
<parser type="Spring.Remoting.Config.RemotingNamespaceParser, Spring.Services" />
|
||||
</parsers>
|
||||
<context>
|
||||
<resource uri="config://spring/objects" />
|
||||
<resource uri="assembly://RemoteServer/RemoteServer.Config/cao.xml" />
|
||||
<resource uri="assembly://RemoteServer/RemoteServer.Config/saoSingleCall.xml" />
|
||||
<resource uri="assembly://RemoteServer/RemoteServer.Config/saoSingleCall-aop.xml" />
|
||||
<resource uri="assembly://RemoteServer/RemoteServer.Config/saoSingleton.xml" />
|
||||
<resource uri="assembly://RemoteServer/RemoteServer.Config/saoSingleton-aop.xml" />
|
||||
</context>
|
||||
<objects xmlns="http://www.springframework.net">
|
||||
<description>Definitions of objects to be exported.</description>
|
||||
|
||||
<object type="Spring.Remoting.RemotingConfigurer, Spring.Services">
|
||||
<property name="Filename" value="Spring.Calculator.RemoteApp.exe.config" />
|
||||
</object>
|
||||
|
||||
<object id="Log4NetLoggingAroundAdvice" type="Spring.Aspects.Logging.Log4NetLoggingAroundAdvice, Spring.Aspects">
|
||||
<property name="Level" value="Debug" />
|
||||
</object>
|
||||
|
||||
<object id="singletonCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services">
|
||||
<constructor-arg type="int" value="217"/>
|
||||
</object>
|
||||
|
||||
<object id="singletonCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
|
||||
<property name="target" ref="singletonCalculator"/>
|
||||
<property name="interceptorNames">
|
||||
<list>
|
||||
<value>Log4NetLoggingAroundAdvice</value>
|
||||
</list>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
<object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services" singleton="false">
|
||||
<constructor-arg type="int" value="217"/>
|
||||
</object>
|
||||
|
||||
<object id="prototypeCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
|
||||
<property name="targetSource">
|
||||
<object type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop">
|
||||
<property name="TargetObjectName" value="prototypeCalculator"/>
|
||||
</object>
|
||||
</property>
|
||||
<property name="interceptorNames">
|
||||
<list>
|
||||
<value>Log4NetLoggingAroundAdvice</value>
|
||||
</list>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
</objects>
|
||||
</spring></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><objects
|
||||
xmlns="http://www.springframework.net"
|
||||
xmlns:r="http://www.springframework.net/remoting">
|
||||
|
||||
<description>Registers the calculator service as a SAO in 'Singleton' mode.</description>
|
||||
|
||||
<r:saoExporter
|
||||
targetName="singletonCalculator"
|
||||
serviceName="RemotedSaoSingletonCalculator" />
|
||||
</objects></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><object name="saoSingletonCalculator" type="Spring.Remoting.SaoExporter, Spring.Services">
|
||||
<property name="TargetName" value="singletonCalculator" />
|
||||
<property name="ServiceName" value="RemotedSaoSingletonCalculator" />
|
||||
</object></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><system.runtime.remoting>
|
||||
<application>
|
||||
<channels>
|
||||
<channel ref="tcp"/>
|
||||
</channels>
|
||||
</application>
|
||||
</system.runtime.remoting></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>
|
||||
<spring>
|
||||
<context>
|
||||
<resource uri="config://spring/objects" />
|
||||
|
||||
<!-- Only one at a time ! -->
|
||||
|
||||
<!-- ================================== -->
|
||||
<!-- In process (local) implementations -->
|
||||
<!-- ================================== -->
|
||||
<resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.InProcess/inProcess.xml" />
|
||||
|
||||
<!-- ======================== -->
|
||||
<!-- Remoting implementations -->
|
||||
<!-- ======================== -->
|
||||
<!-- Make sure 'RemoteApp' console application is running and listening. -->
|
||||
<!-- <resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.Remoting/cao.xml" /> -->
|
||||
<!-- <resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.Remoting/cao-ctor.xml" /> -->
|
||||
<!-- <resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.Remoting/saoSingleton.xml" /> -->
|
||||
<!-- <resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.Remoting/saoSingleton-aop.xml" /> -->
|
||||
<!-- <resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.Remoting/saoSingleCall.xml" /> -->
|
||||
<!-- <resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.Remoting/saoSingleCall-aop.xml" /> -->
|
||||
|
||||
<!-- =========================== -->
|
||||
<!-- Web Service implementations -->
|
||||
<!-- =========================== -->
|
||||
<!-- Make sure 'http://localhost/SpringCalculator/' web application is running -->
|
||||
<!-- <resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.WebServices/webServices.xml" /> -->
|
||||
<!-- <resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.WebServices/webServices-aop.xml" /> -->
|
||||
|
||||
<!-- ================================= -->
|
||||
<!-- EnterpriseService implementations -->
|
||||
<!-- ================================= -->
|
||||
<!-- Make sure you register components with 'RegisterComponentServices' console application. -->
|
||||
<!-- <resource uri="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.EnterpriseServices/enterpriseServices.xml" /> -->
|
||||
</context>
|
||||
</spring>
|
||||
</programlisting>
|
||||
|
||||
<para>The inProcess.xml configuration file creates an instance of
|
||||
AdvancedCalculator directly <programlisting>
|
||||
<objects xmlns="http://www.springframework.net">
|
||||
|
||||
<description>inProcess</description>
|
||||
|
||||
<object id="calculatorService" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services" />
|
||||
|
||||
</objects>
|
||||
</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><objects xmlns="http://www.springframework.net">
|
||||
|
||||
<description>saoSingleton</description>
|
||||
|
||||
<object id="calculatorService" type="Spring.Remoting.SaoFactoryObject, Spring.Services">
|
||||
<property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract" />
|
||||
<property name="ServiceUrl" value="tcp://localhost:8005/RemotedSaoSingletonCalculator" />
|
||||
</object>
|
||||
|
||||
</objects>
|
||||
</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><property name="ServiceUrl" value="${protocol}://${host}:${port}/RemotedSaoSingletonCalculator" /></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><objects xmlns="http://www.springframework.net">
|
||||
|
||||
<description>cao</description>
|
||||
|
||||
<object id="calculatorService" type="Spring.Remoting.CaoFactoryObject, Spring.Services">
|
||||
<property name="RemoteTargetName" value="prototypeCalculator" />
|
||||
<property name="ServiceUrl" value="tcp://localhost:8005" />
|
||||
</object>
|
||||
|
||||
</objects>
|
||||
</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 <return> to quit ---
|
||||
|
||||
|
||||
CLIENT WINDOW
|
||||
|
||||
--- Press <return> to continue --- (hit return...)
|
||||
Get Calculator...
|
||||
Divide(11, 2) : Quotient: '5'; Rest: '1'
|
||||
Memory = 0
|
||||
Memory + 2 = 2
|
||||
Get Calculator...
|
||||
Memory = 2
|
||||
--- Press <return> 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><!-- Calculator definitions -->
|
||||
<object id="singletonCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services">
|
||||
<constructor-arg type="int" value="217" />
|
||||
</object>
|
||||
|
||||
<object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services" singleton="false">
|
||||
<constructor-arg type="int" value="217" />
|
||||
</object>
|
||||
|
||||
<!-- CAO object -->
|
||||
<r:caoExporter targetName="prototypeCalculator" infinite="false">
|
||||
<r:lifeTime initialLeaseTime="2m" renewOnCallTime="1m"/>
|
||||
</r:caoExporter>
|
||||
|
||||
<!-- SAO Single Call -->
|
||||
<r:saoExporter
|
||||
targetName="prototypeCalculator"
|
||||
serviceName="RemotedSaoSingleCallCalculator"/>
|
||||
|
||||
<!-- SAO Singleton -->
|
||||
<r:saoExporter
|
||||
targetName="singletonCalculator"
|
||||
serviceName="RemotedSaoSingletonCalculator" /></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> <spring>
|
||||
|
||||
<context>
|
||||
<resource uri="config://spring/objects" />
|
||||
<resource uri="assembly://Spring.Calculator.RegisterComponentServices/Spring.Calculator.RegisterComponentServices.Config/enterpriseServices.xml" />
|
||||
</context>
|
||||
|
||||
<objects xmlns="http://www.springframework.net">
|
||||
<description>Definitions of objects to be registered.</description>
|
||||
|
||||
<object id="calculatorService" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services" />
|
||||
|
||||
</objects>
|
||||
|
||||
</spring></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><objects xmlns="http://www.springframework.net">
|
||||
|
||||
<description>enterpriseService</description>
|
||||
|
||||
<object id="calculatorComponent" type="Spring.EnterpriseServices.ServicedComponentExporter, Spring.Services">
|
||||
<property name="TargetName" value="calculatorService" />
|
||||
<property name="TypeAttributes">
|
||||
<list>
|
||||
<object type="System.EnterpriseServices.TransactionAttribute, System.EnterpriseServices" />
|
||||
</list>
|
||||
</property>
|
||||
<property name="MemberAttributes">
|
||||
<dictionary>
|
||||
<entry key="*">
|
||||
<list>
|
||||
<object type="System.EnterpriseServices.AutoCompleteAttribute, System.EnterpriseServices" />
|
||||
</list>
|
||||
</entry>
|
||||
</dictionary>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
<object type="Spring.EnterpriseServices.EnterpriseServicesExporter, Spring.Services">
|
||||
<property name="ApplicationName">
|
||||
<value>Spring Calculator Application</value>
|
||||
</property>
|
||||
<property name="Description">
|
||||
<value>Spring Calculator application.</value>
|
||||
</property>
|
||||
<property name="AccessControl">
|
||||
<object type="System.EnterpriseServices.ApplicationAccessControlAttribute, System.EnterpriseServices">
|
||||
<property name="AccessChecksLevel">
|
||||
<value>ApplicationComponent</value>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="Roles">
|
||||
<list>
|
||||
<value>Admin : Administrator role</value>
|
||||
<value>User : User role</value>
|
||||
<value>Manager : Administrator role</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="Components">
|
||||
<list>
|
||||
<ref object="calculatorComponent" />
|
||||
</list>
|
||||
</property>
|
||||
<property name="Assembly">
|
||||
<value>Spring.Calculator.EnterpriseServices</value>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
</objects></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> <context>
|
||||
<resource uri="config://spring/objects"/>
|
||||
<resource uri="~/Config/webServices.xml"/>
|
||||
<resource uri="~/Config/webServices-aop.xml"/>
|
||||
</context></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> <objects xmlns="http://www.springframework.net">
|
||||
|
||||
<!-- Aspect -->
|
||||
|
||||
<object id="CommonLoggingAroundAdvice" type="Spring.Aspects.Logging.CommonLoggingAroundAdvice, Spring.Aspects">
|
||||
<property name="Level" value="Debug"/>
|
||||
</object>
|
||||
|
||||
<!-- Service -->
|
||||
|
||||
<!-- 'plain object' for AdvancedCalculator -->
|
||||
<object id="calculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"/>
|
||||
|
||||
<!-- AdvancedCalculator object with AOP logging advice applied. -->
|
||||
<object id="calculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
|
||||
<property name="target" ref="calculator"/>
|
||||
<property name="interceptorNames">
|
||||
<list>
|
||||
<value>CommonLoggingAroundAdvice</value>
|
||||
</list>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
</objects></programlisting>The configuration file webService.xml
|
||||
simply exports the named calculator object</para>
|
||||
|
||||
<programlisting> <object id="calculatorService" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
|
||||
<property name="TargetName" value="calculator" />
|
||||
<property name="Namespace" value="http://SpringCalculator/WebServices" />
|
||||
<property name="Description" value="Spring Calculator Web Services" />
|
||||
</object></programlisting>
|
||||
|
||||
<para>Whereas the webService-aop.xml exports the calculator instance that
|
||||
has AOP advice applied to it.</para>
|
||||
|
||||
<programlisting> <object id="calculatorServiceWeaved" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
|
||||
<property name="TargetName" value="calculatorWeaved" />
|
||||
<property name="Namespace" value="http://SpringCalculator/WebServices" />
|
||||
<property name="Description" value="Spring Calculator Web Services" />
|
||||
</object>
|
||||
</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>
|
||||
470
doc/reference/src/remoting.xml
Normal 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><object id="singletonCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services">
|
||||
<constructor-arg type="int" value="217"/>
|
||||
</object>
|
||||
|
||||
<!-- Registers the calculator service as a SAO in 'Singleton' mode. -->
|
||||
<object name="saoSingletonCalculator" type="Spring.Remoting.SaoExporter, Spring.Services">
|
||||
<property name="TargetName" value="singletonCalculator" />
|
||||
<property name="ServiceName" value="RemotedSaoSingletonCalculator" />
|
||||
</object></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><objects xmlns="http://www.springframework.net"
|
||||
xmlns:r="http://www.springframework.net/remoting">
|
||||
|
||||
<r:saoExporter targetName="singletonCalculator"
|
||||
serviceName="RemotedSaoSingletonCalculator" />
|
||||
|
||||
... other object definitions
|
||||
|
||||
</objects></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><object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"
|
||||
singleton="false">
|
||||
<constructor-arg type="int" value="217"/>
|
||||
</object>
|
||||
|
||||
<object name="saoSingleCallCalculator" type="Spring.Remoting.SaoExporter, Spring.Services">
|
||||
<property name="TargetName" value="prototypeCalculator" />
|
||||
<property name="ServiceName" value="RemotedSaoSingleCallCalculator" />
|
||||
</object></programlisting></para>
|
||||
|
||||
<para>Note that we change the singleton attribute of the plain .NET object
|
||||
as configured by Spring in the <object> 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><object id="singletonCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
|
||||
<property name="target" ref="singletonCalculator"/>
|
||||
<property name="interceptorNames">
|
||||
<list>
|
||||
<value>Log4NetLoggingAroundAdvice</value>
|
||||
</list>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
<object name="saoSingletonCalculatorWeaved" type="Spring.Remoting.SaoExporter, Spring.Services">
|
||||
<property name="TargetName" value="singletonCalculatorWeaved" />
|
||||
<property name="ServiceName" value="RemotedSaoSingletonCalculatorWeaved" />
|
||||
</object></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><system.runtime.remoting>
|
||||
<application>
|
||||
<channels>
|
||||
<channel ref="tcp" port="8005" />
|
||||
</channels>
|
||||
</application>
|
||||
</system.runtime.remoting></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><objects xmlns="http://www.springframework.net"
|
||||
xmlns:r="http://www.springframework.net/remoting">
|
||||
|
||||
<r:configurer filename="Spring.Calculator.RemoteApp.exe.config" />
|
||||
|
||||
</objects></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
|
||||
<system.runtime.remoting> 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><object id="calculatorService" type="Spring.Remoting.SaoFactoryObject, Spring.Services">
|
||||
<property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract" />
|
||||
<property name="ServiceUrl" value="tcp://localhost:8005/RemotedSaoSingletonCalculator" />
|
||||
</object></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><object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"
|
||||
singleton="false">
|
||||
<constructor-arg type="int" value="217" />
|
||||
</object></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><object id="caoCalculator" type="Spring.Remoting.CaoExporter, Spring.Services">
|
||||
<property name="TargetName" value="prototypeCalculator" />
|
||||
<property name="Infinite" value="false" />
|
||||
<property name="InitialLeaseTime" value="2m" />
|
||||
<property name="RenewOnCallTime" value="1m" />
|
||||
</object></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><r:caoExporter targetName="prototypeCalculator" infinite="false">
|
||||
<r:lifeTime initialLeaseTime="2m" renewOnCallTime="1m" />
|
||||
</r:caoExporter></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><object id="prototypeCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
|
||||
<property name="targetSource">
|
||||
<object type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop">
|
||||
<property name="TargetObjectName" value="prototypeCalculator" />
|
||||
</object>
|
||||
</property>
|
||||
<property name="interceptorNames">
|
||||
<list>
|
||||
<value>ConsoleLoggingAroundAdvice</value>
|
||||
</list>
|
||||
</property>
|
||||
</object></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><r:caoExporter targetName="prototypeCalculatorWeaved" infinite="false">
|
||||
<r:lifeTime initialLeaseTime="2m" renewOnCallTime="1m" />
|
||||
</r:caoExporter></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><object id="calculatorService" type="Spring.Remoting.CaoFactoryObject, Spring.Services">
|
||||
<property name="RemoteTargetName" value="prototypeCalculator" />
|
||||
<property name="ServiceUrl" value="tcp://localhost:8005" />
|
||||
</object></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><r:caoFactory id="calculatorService"
|
||||
remoteTargetName="prototypeCalculator"
|
||||
serviceUrl="tcp://localhost:8005" /></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>
|
||||
362
doc/reference/src/resources.xml
Normal 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://<AssemblyName>/<NameSpace>/<ResourceName></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://<path to section></literal>
|
||||
|
||||
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
|
||||
|
||||
<literal>FileSystemResource</literal>
|
||||
|
||||
accesses file system data. Uri syntax is
|
||||
|
||||
<literal>file://<filename></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><configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="spring">
|
||||
|
||||
<section name='context' type='Spring.Context.Support.ContextHandler, Spring.Core'/>
|
||||
|
||||
<emphasis role="bold"> <section name="resourceHandlers"
|
||||
type="Spring.Context.Support.ResourceHandlersSectionHandler, Spring.Core"/></emphasis>
|
||||
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<spring>
|
||||
|
||||
<emphasis role="bold"> <resourceHandlers>
|
||||
<handler protocol="db" type="MyCompany.MyApp.Resources.MyDbResource, MyAssembly"/>
|
||||
</resourceHandlers></emphasis>
|
||||
|
||||
<context>
|
||||
<resource uri="db://user:pass@dbName/MyDefinitionsTable"/>
|
||||
</context>
|
||||
|
||||
</spring>
|
||||
</configuration></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>
|
||||
209
doc/reference/src/services.xml
Normal 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><object id="userManager" type="MyApp.Services.SimpleUserManager">
|
||||
<property name="UserDao" ref="userDao"/>
|
||||
</object></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><object id="MyApp.EnterpriseServices.UserManager" type="Spring.Enterprise.ServicedComponentExporter, Spring.Services">
|
||||
<property name="TargetName" value="userManager"/>
|
||||
<property name="TypeAttributes">
|
||||
<list>
|
||||
<object type="System.EnterpriseServices.TransactionAttribute, System.EnterpriseServices"/>
|
||||
</list>
|
||||
</property>
|
||||
<property name="MemberAttributes">
|
||||
<dictionary>
|
||||
<entry key="*">
|
||||
<list>
|
||||
<object type="System.EnterpriseServices.AutoCompleteAttribute, System.EnterpriseServices"/>
|
||||
</list>
|
||||
</entry>
|
||||
</dictionary>
|
||||
</property>
|
||||
</object></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><object id="MyComponentExporter" type="Spring.Enterprise.EnterpriseServicesExporter, Spring.Services">
|
||||
<property name="ApplicationName" value="My COM+ Application"/>
|
||||
<property name="Description" value="My enterprise services application."/>
|
||||
<property name="AccessControl">
|
||||
<object type="System.EnterpriseServices.ApplicationAccessControlAttribute, System.EnterpriseServices">
|
||||
<property name="AccessChecksLevel" value="ApplicationComponent"/>
|
||||
</object>
|
||||
</property>
|
||||
<property name="Roles">
|
||||
<list>
|
||||
<value>Admin : Administrator role</value>
|
||||
<value>User : User role</value>
|
||||
<value>Manager : Administrator role</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="Components">
|
||||
<list>
|
||||
<ref object="MyApp.EnterpriseServices.UserManager"/>
|
||||
</list>
|
||||
</property>
|
||||
<property name="Assembly" value="MyComPlusApp"/>
|
||||
</object></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><object id="enterpriseUserManager" type="Spring.Enterprise.ServicedComponentFactory, Spring.Services">
|
||||
<property name="Name" value="MyApp.EnterpriseServices.UserManager"/>
|
||||
<property name="Template" value="userManager"/>
|
||||
</object></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>
|
||||
321
doc/reference/src/springair.xml
Normal 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><spring>
|
||||
<parsers>
|
||||
<parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
|
||||
</parsers>
|
||||
|
||||
<context>
|
||||
<resource uri="~/Config/Aspects.xml"/>
|
||||
<resource uri="~/Config/Web.xml"/>
|
||||
<resource uri="~/Config/Services.xml"/>
|
||||
|
||||
<!-- TEST CONFIGURATION -->
|
||||
|
||||
<resource uri="~/Config/Test/Services.xml"/>
|
||||
<resource uri="~/Config/Test/Dao.xml"/>
|
||||
|
||||
<!-- PRODUCTION CONFIGURATION -->
|
||||
|
||||
<!--
|
||||
<resource uri="~/Config/Production/Services.xml"/>
|
||||
<resource uri="~/Config/Production/Dao.xml"/>
|
||||
-->
|
||||
</context>
|
||||
</spring></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> <object type="TripForm.aspx" parent="standardPage">
|
||||
<property name="BookingAgent" ref="bookingAgent" />
|
||||
<property name="AirportDao" ref="airportDao" />
|
||||
<property name="TripValidator" ref="tripValidator" />
|
||||
<property name="Results">
|
||||
<dictionary>
|
||||
<entry key="displaySuggestedFlights" value="redirect:SuggestedFlights.aspx" />
|
||||
</dictionary>
|
||||
</property>
|
||||
</object></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> <v:group id="tripValidator">
|
||||
|
||||
<v:required id="departureAirportValidator" test="StartingFrom.AirportCode">
|
||||
<v:message id="error.departureAirport.required" providers="departureAirportErrors, validationSummary"/>
|
||||
</v:required>
|
||||
|
||||
<v:group id="destinationAirportValidator">
|
||||
<v:required test="ReturningFrom.AirportCode">
|
||||
<v:message id="error.destinationAirport.required" providers="destinationAirportErrors, validationSummary"/>
|
||||
</v:required>
|
||||
<v:condition test="ReturningFrom.AirportCode != StartingFrom.AirportCode" when="ReturningFrom.AirportCode != ''">
|
||||
<v:message id="error.destinationAirport.sameAsDeparture" providers="destinationAirportErrors, validationSummary"/>
|
||||
</v:condition>
|
||||
</v:group>
|
||||
|
||||
<v:group id="departureDateValidator">
|
||||
<v:required test="StartingFrom.Date">
|
||||
<v:message id="error.departureDate.required" providers="departureDateErrors, validationSummary"/>
|
||||
</v:required>
|
||||
<v:condition test="StartingFrom.Date >= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue">
|
||||
<v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"/>
|
||||
</v:condition>
|
||||
</v:group>
|
||||
|
||||
<v:group id="returnDateValidator" when="Mode == 'RoundTrip'">
|
||||
<v:required test="ReturningFrom.Date">
|
||||
<v:message id="error.returnDate.required" providers="returnDateErrors, validationSummary"/>
|
||||
</v:required>
|
||||
<v:condition test="ReturningFrom.Date >= StartingFrom.Date" when="ReturningFrom.Date != DateTime.MinValue">
|
||||
<v:message id="error.returnDate.beforeDeparture" providers="returnDateErrors, validationSummary"/>
|
||||
</v:condition>
|
||||
</v:group>
|
||||
|
||||
</v:group></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> <object id="bookingAgentWebService" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
|
||||
<property name="TargetName" value="bookingAgent"/>
|
||||
<property name="Name" value="BookingAgent"/>
|
||||
<property name="Namespace" value="http://SpringAir/WebServices"/>
|
||||
<property name="Description" value="SpringAir Booking Agent Web Service"/>
|
||||
<property name="MemberAttributes">
|
||||
<dictionary>
|
||||
<entry key="SuggestFlights">
|
||||
<object type="System.Web.Services.WebMethodAttribute, System.Web.Services">
|
||||
<property name="Description" value="Gets those flight suggestions that are applicable for the supplied trip."/>
|
||||
</object>
|
||||
</entry>
|
||||
<entry key="Book">
|
||||
<object type="System.Web.Services.WebMethodAttribute, System.Web.Services">
|
||||
<property name="Description" value="Goes ahead and actually books what up until this point has been a transient reservation."/>
|
||||
</object>
|
||||
</entry>
|
||||
<entry key="GetAirportList">
|
||||
<object type="System.Web.Services.WebMethodAttribute, System.Web.Services">
|
||||
<property name="Description" value="Return a collection of all those airports that can be used for the purposes of booking."/>
|
||||
</object>
|
||||
</entry>
|
||||
</dictionary>
|
||||
</property>
|
||||
</object></programlisting>
|
||||
|
||||
<para></para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
BIN
doc/reference/src/templated/AgileDocs.Core.dll
Normal file
162
doc/reference/src/templated/misc.xml
Normal 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>
|
||||
158
doc/reference/src/templated/pooling-example.xml
Normal 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>
|
||||
|
||||
37
doc/reference/src/templated/properties.props
Normal 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>
|
||||
441
doc/reference/src/templated/windows-service.xml
Normal 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>
|
||||
414
doc/reference/src/testing.xml
Normal 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><runtime>
|
||||
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="nunit.framework"
|
||||
publicKeyToken="96d09a1eb7f44a77"
|
||||
culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535
|
||||
newVersion="2.4.6.0"/>
|
||||
|
||||
</dependentAssembly>
|
||||
|
||||
</assemblyBinding>
|
||||
|
||||
</runtime></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><?xml version="1.0" encoding="utf-8" ?>
|
||||
<objects xmlns="http://www.springframework.net">
|
||||
|
||||
<lineannotation><!-- this object will be injected into the <classname>HibernateTitleDaoTests</classname> class --></lineannotation>
|
||||
<object id="titleDao" type="Spring.Samples.HibernateTitleDao, Spring.Samples">
|
||||
<property name="sessionFactory" ref="sessionFactory"/>
|
||||
</object>
|
||||
|
||||
<object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate">
|
||||
<lineannotation><!-- dependencies elided for clarity --></lineannotation>
|
||||
</object>
|
||||
|
||||
</objects></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>
|
||||
247
doc/reference/src/threading.xml
Normal 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&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<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>
|
||||
2006
doc/reference/src/transaction.xml
Normal file
553
doc/reference/src/tx-quickstart.xml
Normal 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 > maxTransferAmount || debitAmount > 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><objects xmlns='http://www.springframework.net'>
|
||||
|
||||
<!-- DAO Implementations -->
|
||||
<object id="accountCreditDao" type="Spring.TxQuickStart.Dao.Ado.AccountCreditDao, Spring.TxQuickStart">
|
||||
<property name="DbProvider" ref="CreditDbProvider"/>
|
||||
</object>
|
||||
|
||||
<object id="accountDebitDao" type="Spring.TxQuickStart.Dao.Ado.AccountDebitDao, Spring.TxQuickStart">
|
||||
<property name="DbProvider" ref="DebitDbProvider"/>
|
||||
</object>
|
||||
|
||||
|
||||
<!-- The service that performs multiple data access operations -->
|
||||
<object id="accountManager"
|
||||
type="Spring.TxQuickStart.Services.AccountManager, Spring.TxQuickStart">
|
||||
<constructor-arg name="accountCreditDao" ref="accountCreditDao"/>
|
||||
<constructor-arg name="accountDebitDao" ref="accountDebitDao"/>
|
||||
</object>
|
||||
|
||||
</objects></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><objects xmlns="http://www.springframework.net"
|
||||
xmlns:db="http://www.springframework.net/database"
|
||||
xmlns:tx="http://www.springframework.net/tx">
|
||||
|
||||
|
||||
<!-- Imports application configuration -->
|
||||
<import resource="assembly://Spring.TxQuickStart/Spring.TxQuickStart/application-config.xml"/>
|
||||
|
||||
<!-- Imports additional aspects -->
|
||||
<!--
|
||||
<import resource="assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/aspects-config.xml"/>
|
||||
-->
|
||||
|
||||
|
||||
<!-- Database Providers -->
|
||||
|
||||
<db:provider id="DebitDbProvider"
|
||||
provider="System.Data.SqlClient"
|
||||
connectionString="Data Source=MARKT60\SQL2005;Initial Catalog=CreditsAndDebits;User ID=springqa; Password=springqa"/>
|
||||
|
||||
<db:provider id="CreditDbProvider"
|
||||
provider="System.Data.SqlClient"
|
||||
connectionString="Data Source=MARKT60\SQL2005;Initial Catalog=CreditsAndDebits;User ID=springqa; Password=springqa"/>
|
||||
|
||||
<alias name="DebitDbProvider" alias="CreditDbProvider"/>
|
||||
|
||||
<!-- Transaction Manager if using a single database that contain both credit and debit tables -->
|
||||
<object id="transactionManager"
|
||||
type="Spring.Data.Core.AdoPlatformTransactionManager, Spring.Data">
|
||||
<property name="DbProvider" ref="DebitDbProvider"/>
|
||||
</object>
|
||||
|
||||
<!-- Transaction aspect -->
|
||||
|
||||
<tx:attribute-driven/>
|
||||
|
||||
</objects></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">
|
||||
|
||||
|
||||
<!-- Imports application configuration -->
|
||||
<import resource="assembly://Spring.TxQuickStart/Spring.TxQuickStart/application-config.xml"/>
|
||||
|
||||
<!-- Imports additional aspects -->
|
||||
<!--
|
||||
<import resource="assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/aspects-config.xml"/>
|
||||
-->
|
||||
|
||||
<db:provider id="DebitDbProvider"
|
||||
provider="System.Data.SqlClient"
|
||||
connectionString="Data Source=MARKT60\SQL2005;Initial Catalog=Debits;User ID=springqa; Password=springqa"/>
|
||||
|
||||
|
||||
<db:provider id="CreditDbProvider"
|
||||
provider="System.Data.SqlClient"
|
||||
connectionString="Data Source=MARKT60\SQL2005;Initial Catalog=Credits;User ID=springqa; Password=springqa"/>
|
||||
|
||||
|
||||
<!-- Transaction Manager if using two databases, one containing the credit table and the other a debit table -->
|
||||
|
||||
<object id="transactionManager"
|
||||
type="Spring.Data.Core.TxScopeTransactionManager, Spring.Data">
|
||||
</object>
|
||||
|
||||
|
||||
<!-- Transaction aspect -->
|
||||
<tx:attribute-driven/>
|
||||
|
||||
</objects></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 > maxTransferAmount || debitAmount > 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> <import resource="assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/aspects-config.xml"/></programlisting>
|
||||
|
||||
<para>in either system-test-dtc-config.xml or system-test-local-config.xml
|
||||
The aspect configuration file is shown below</para>
|
||||
|
||||
<programlisting><objects xmlns='http://www.springframework.net'
|
||||
xmlns:aop="http://www.springframework.net/aop">
|
||||
|
||||
|
||||
|
||||
<object name="exceptionAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop">
|
||||
<property name="exceptionHandlers">
|
||||
<list>
|
||||
<value>on exception name ArithmeticException log 'Logging an exception thrown from method ' + #method.Name </value>
|
||||
</list>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
<object name="loggingAdvice" type="Spring.Aspects.Logging.SimpleLoggingAdvice, Spring.Aop">
|
||||
<property name="logUniqueIdentifier" value="true"/>
|
||||
<property name="logExecutionTime" value="true"/>
|
||||
<property name="logMethodArguments" value="true"/>
|
||||
<property name="Separator" value=";"/>
|
||||
|
||||
<property name="HideProxyTypeNames" value="true"/>
|
||||
<property name="UseDynamicLogger" value="true"/>
|
||||
|
||||
<property name="LogLevel" value="Info"/>
|
||||
</object>
|
||||
|
||||
|
||||
<object id="txAttributePointcut" type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop">
|
||||
<property name="Attribute" value="Spring.Transaction.Interceptor.TransactionAttribute, Spring.Data"/>
|
||||
</object>
|
||||
|
||||
<aop:config>
|
||||
|
||||
<aop:advisor id="exceptionProcessAdvisor" order="1"
|
||||
advice-ref="exceptionAdvice"
|
||||
pointcut-ref="txAttributePointcut"/>
|
||||
|
||||
<aop:advisor id="loggingAdvisor" order="2"
|
||||
advice-ref="loggingAdvice"
|
||||
pointcut-ref="txAttributePointcut"/>
|
||||
|
||||
</aop:config>
|
||||
|
||||
</objects></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, <tx:attribute-driven/> 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>
|
||||
866
doc/reference/src/validation.xml
Normal 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><objects xmlns="http://www.springframework.net" xmlns:v="http://www.springframework.net/validation">
|
||||
|
||||
<object type="TripForm.aspx" parent="standardPage">
|
||||
<property name="TripValidator" ref="tripValidator" />
|
||||
</object>
|
||||
|
||||
<v:group id="tripValidator">
|
||||
|
||||
<v:required id="departureAirportValidator" test="StartingFrom.AirportCode">
|
||||
<v:message id="error.departureAirport.required" providers="departureAirportErrors, validationSummary"/>
|
||||
</v:required>
|
||||
|
||||
<v:group id="destinationAirportValidator">
|
||||
<v:required test="ReturningFrom.AirportCode">
|
||||
<v:message id="error.destinationAirport.required" providers="destinationAirportErrors, validationSummary"/>
|
||||
</v:required>
|
||||
<v:condition test="ReturningFrom.AirportCode != StartingFrom.AirportCode" when="ReturningFrom.AirportCode != ''">
|
||||
<v:message id="error.destinationAirport.sameAsDeparture" providers="destinationAirportErrors, validationSummary"/>
|
||||
</v:condition>
|
||||
</v:group>
|
||||
|
||||
<v:group id="departureDateValidator">
|
||||
<v:required test="StartingFrom.Date">
|
||||
<v:message id="error.departureDate.required" providers="departureDateErrors, validationSummary"/>
|
||||
</v:required>
|
||||
<v:condition test="StartingFrom.Date >= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue">
|
||||
<v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"/>
|
||||
</v:condition>
|
||||
</v:group>
|
||||
|
||||
<v:group id="returnDateValidator" when="Mode == 'RoundTrip'">
|
||||
<v:required test="ReturningFrom.Date">
|
||||
<v:message id="error.returnDate.required" providers="returnDateErrors, validationSummary"/>
|
||||
</v:required>
|
||||
<v:condition test="ReturningFrom.Date >= StartingFrom.Date" when="ReturningFrom.Date != DateTime.MinValue">
|
||||
<v:message id="error.returnDate.beforeDeparture" providers="returnDateErrors, validationSummary"/>
|
||||
</v:condition>
|
||||
</v:group>
|
||||
|
||||
</v:group>
|
||||
|
||||
</objects></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 <object> 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><v:<emphasis role="bold">condition</emphasis> id="id" test="testCondition" when="applicabilityCondition" parent="parentValidator">
|
||||
actions
|
||||
</v:<emphasis role="bold">condition</emphasis>></programlisting>
|
||||
|
||||
<para>An example is shown below</para>
|
||||
|
||||
<programlisting><v:condition test="StartingFrom.Date >= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue">
|
||||
<v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"/>
|
||||
</v:condition></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><v:<emphasis role="bold">required</emphasis> id="id" test="requiredValue" when="applicabilityCondition" parent="parentValidator">
|
||||
actions
|
||||
</v:<emphasis role="bold">required</emphasis>></programlisting>
|
||||
|
||||
<para>An example is shown below</para>
|
||||
|
||||
<programlisting><v:required test="ReturningFrom.AirportCode">
|
||||
<v:message id="error.destinationAirport.required" providers="destinationAirportErrors, validationSummary"/>
|
||||
</v:required></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><v:<emphasis role="bold">regex</emphasis> id="id" test="valueToEvaluate" when="applicabilityCondition" parent="parentValidator">
|
||||
<<emphasis role="bold">v:property name="Expression" value="regularExpressionToMatch"</emphasis>/>
|
||||
<v:property name="Options" value="regexOptions"/>
|
||||
actions
|
||||
</v:<emphasis role="bold">regex</emphasis>></programlisting>
|
||||
|
||||
<para>An example is shown below</para>
|
||||
|
||||
<programlisting><v:regex test="ReturningFrom.AirportCode">
|
||||
<v:property name="Expression" value="[A-Z][A-Z][A-Z]"/>
|
||||
<v:message id="error.destinationAirport.threeCharacters" providers="destinationAirportErrors, validationSummary"/>
|
||||
</v:regex></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><v:<emphasis role="bold">validator</emphasis> id="id" test="requiredValue" when="applicabilityCondition" type="validatorType" parent="parentValidator">
|
||||
actions
|
||||
</v:<emphasis role="bold">validator</emphasis>></programlisting>
|
||||
|
||||
<para>An example is shown below</para>
|
||||
|
||||
<programlisting><v:validator test="ReturningFrom.AirportCode" type="MyNamespace.MyAirportCodeValidator, MyAssembly">
|
||||
<v:message id="error.destinationAirport.invalid" providers="destinationAirportErrors, validationSummary"/>
|
||||
</v:required></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><v:group id="returnDateValidator" when="Mode == 'RoundTrip'">
|
||||
// nested validators
|
||||
</v:group></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><v:message id="messageId" providers="errorProviderList" when="messageApplicabilityCondition">
|
||||
<v:param value="paramExpression"/>
|
||||
</v:message></programlisting>
|
||||
|
||||
<para>An example is shown below</para>
|
||||
|
||||
<programlisting><v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary">
|
||||
<v:param value="StartingFrom.Date.ToString('D')"/>
|
||||
<v:param value="DateTime.Today.ToString('D')"/>
|
||||
</v:message></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><v:action type="actionType" when="actionApplicabilityCondition">
|
||||
properties
|
||||
</v:action></programlisting>
|
||||
|
||||
<para>An example is shown below</para>
|
||||
|
||||
<programlisting><v:action type="Spring.Validation.Actions.ExpressionAction, Spring.Core" when="#page != null">
|
||||
<v:property name="Valid" value="#page.myPanel.Visible = true"/>
|
||||
<v:property name="Invalid" value="#page.myPanel.Visible = false"/>
|
||||
</v:action></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
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the action.
|
||||
/// </summary>
|
||||
/// <param name="isValid">Whether associated validator is valid or not.</param>
|
||||
/// <param name="validationContext">Validation context.</param>
|
||||
/// <param name="contextParams">Additional context parameters.</param>
|
||||
/// <param name="errors">Validation errors container.</param>
|
||||
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><v:ref name="referencedValidatorId" context="validationContextForTheReferencedValidator"/></programlisting>
|
||||
|
||||
<para>An example is shown below</para>
|
||||
|
||||
<programlisting><v:group id="objectA.validator">
|
||||
<v:ref name="objectC.validator" context="MyObjectC"/>
|
||||
// other validators for ObjectA
|
||||
</v:group>
|
||||
|
||||
<v:group id="objectB.validator">
|
||||
<v:ref name="objectC.validator" context="ObjectCProperty"/>
|
||||
// other validators for ObjectB
|
||||
</v:group>
|
||||
|
||||
<v:group id="objectC.Validator">
|
||||
// validators for ObjectC
|
||||
</v:group></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><objects xmlns="http://www.springframework.net" xmlns:v="http://www.springframework.net/validation">
|
||||
|
||||
<object type="TripForm.aspx" parent="standardPage">
|
||||
<property name="TripValidator" ref="tripValidator" />
|
||||
</object>
|
||||
|
||||
<v:group id="tripValidator">
|
||||
// our validation rules
|
||||
</v:group>
|
||||
|
||||
</objects></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><spring:validationError/></literal> and
|
||||
<literal><spring:validationSummary/></literal> controls to the
|
||||
ASP.NET form:</para>
|
||||
|
||||
<programlisting><%@ Page Language="c#" MasterPageFile="~/Web/StandardTemplate.master" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %>
|
||||
<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %>
|
||||
|
||||
<asp:Content ID="head" ContentPlaceHolderID="head" runat="server">
|
||||
|
||||
<script language="javascript" type="text/javascript">
|
||||
<!--
|
||||
function showReturnCalendar(isVisible)
|
||||
{
|
||||
document.getElementById('<%= returningOnDate.ClientID %>').style.visibility = isVisible? '': 'hidden';
|
||||
document.getElementById('returningOnCalendar').style.visibility = isVisible? '': 'hidden';
|
||||
}
|
||||
-->
|
||||
</script>
|
||||
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="body" ContentPlaceHolderID="body" runat="server">
|
||||
<div style="text-align: center">
|
||||
<h4><asp:Label ID="caption" runat="server"></asp:Label></h4>
|
||||
<emphasis role="bold"><spring:ValidationSummary ID="validationSummary" runat="server" /></emphasis>
|
||||
<table>
|
||||
<tr class="formLabel">
|
||||
<td>&nbsp;</td>
|
||||
<td colspan="3">
|
||||
<spring:RadioButtonGroup ID="tripMode" runat="server">
|
||||
<asp:RadioButton ID="OneWay" onclick="showReturnCalendar(false);" runat="server" />
|
||||
<asp:RadioButton ID="RoundTrip" onclick="showReturnCalendar(true);" runat="server" />
|
||||
</spring:RadioButtonGroup>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="formLabel" align="right">
|
||||
<asp:Label ID="leavingFrom" runat="server" /></td>
|
||||
<td nowrap="nowrap">
|
||||
<asp:DropDownList ID="leavingFromAirportCode" AutoCallBack="true" runat="server" />
|
||||
<emphasis role="bold"><spring:ValidationError id="departureAirportErrors" runat="server" /></emphasis>
|
||||
</td>
|
||||
<td class="formLabel" align="right">
|
||||
<asp:Label ID="goingTo" runat="server" /></td>
|
||||
<td nowrap="nowrap">
|
||||
<asp:DropDownList ID="goingToAirportCode" AutoCallBack="true" runat="server" />
|
||||
<emphasis role="bold"><spring:ValidationError id="destinationAirportErrors" runat="server" /></emphasis>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="formLabel" align="right">
|
||||
<asp:Label ID="leavingOn" runat="server" /></td>
|
||||
<td nowrap="nowrap">
|
||||
<spring:Calendar ID="leavingFromDate" runat="server" Width="75px" AllowEditing="true" Skin="system" />
|
||||
<emphasis role="bold"><spring:ValidationError id="departureDateErrors" runat="server" /></emphasis>
|
||||
</td>
|
||||
<td class="formLabel" align="right">
|
||||
<asp:Label ID="returningOn" runat="server" /></td>
|
||||
<td nowrap="nowrap">
|
||||
<div id="returningOnCalendar">
|
||||
<spring:Calendar ID="returningOnDate" runat="server" Width="75px" AllowEditing="true" Skin="system" />
|
||||
<emphasis role="bold"><spring:ValidationError id="returnDateErrors" runat="server" /></emphasis>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="buttonBar" colspan="4">
|
||||
<br/>
|
||||
<asp:Button ID="findFlights" runat="server"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script language="javascript" type="text/javascript">
|
||||
if (document.getElementById('<%= tripMode.ClientID %>').value == 'OneWay')
|
||||
showReturnCalendar(false);
|
||||
else
|
||||
showReturnCalendar(true);
|
||||
</script>
|
||||
|
||||
</asp:Content></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><div></literal> tag. Default renderer for
|
||||
<literal><spring:validationSummary></literal>
|
||||
control.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Inline</entry>
|
||||
|
||||
<entry><literal>Spring.Web.Validation.SpanValidationErrorsRenderer
|
||||
</literal></entry>
|
||||
|
||||
<entry>Renders validation errors within a
|
||||
<literal><span></literal> tag. Default renderer for
|
||||
<literal><spring:validationError></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
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface should be implemented by all validation errors renderers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Validation errors renderers are used to decouple rendering behavior from the
|
||||
/// validation errors controls such as <see cref="ValidationError"/> and
|
||||
/// <see cref="ValidationSummary"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// 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.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public interface IValidationErrorsRenderer
|
||||
{
|
||||
/// <summary>
|
||||
/// Renders validation errors using specified <see cref="HtmlTextWriter"/>.
|
||||
/// </summary>
|
||||
/// <param name="page">Web form instance.</param>
|
||||
/// <param name="writer">An HTML writer to use.</param>
|
||||
/// <param name="errors">The list of validation errors.</param>
|
||||
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><spring:validationSummary></literal> and
|
||||
<literal><spring:validationError></literal> controls:</para>
|
||||
|
||||
<programlisting><!-- Validation errors renderer configuration -->
|
||||
<object id="Spring.Web.UI.Controls.ValidationError" abstract="true">
|
||||
<property name="Renderer">
|
||||
<object type="Spring.Web.Validation.IconValidationErrorsRenderer, Spring.Web">
|
||||
<property name="IconSrc" value="validation-error.gif"/>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
<object id="Spring.Web.UI.Controls.ValidationSummary" abstract="true">
|
||||
<property name="Renderer">
|
||||
<object type="Spring.Web.Validation.DivValidationErrorsRenderer, Spring.Web">
|
||||
<property name="CssClass" value="validationError"/>
|
||||
</object>
|
||||
</property>
|
||||
</object></programlisting>
|
||||
|
||||
<para>It's as simple as that!</para>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
204
doc/reference/src/vsnet.xml
Normal 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><spring>
|
||||
|
||||
<context>
|
||||
<resource uri="file://objects.xml"/>
|
||||
</context>
|
||||
|
||||
</spring></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><?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
<object id="..." type="...">
|
||||
...
|
||||
</object>
|
||||
<object id="..." type="...">
|
||||
...
|
||||
</object>
|
||||
...
|
||||
</objects></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><?xml version="1.0" encoding="UTF-8"?>
|
||||
<objects xmlns="http://www.springframework.net">
|
||||
<object id="..." type="...">
|
||||
...
|
||||
</object>
|
||||
<object id="..." type="...">
|
||||
...
|
||||
</object>
|
||||
...
|
||||
</objects></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>
|
||||
<configuration>
|
||||
|
||||
<configSections>
|
||||
<sectionGroup name="spring">
|
||||
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
|
||||
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<spring>
|
||||
|
||||
<context>
|
||||
<resource uri="config://spring/objects"/>
|
||||
</context>
|
||||
|
||||
<objects xmlns="http://www.springframework.net">
|
||||
...
|
||||
</objects>
|
||||
|
||||
</spring>
|
||||
|
||||
</configuration>
|
||||
</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><spring></literal> and
|
||||
<literal><context></literal> section in addition to the
|
||||
<literal><objects></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>
|
||||
13
doc/reference/src/web-quickstart.xml
Normal 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
536
doc/reference/src/webservices.xml
Normal 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>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
<add verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/>
|
||||
</httpHandlers>
|
||||
</system.web>
|
||||
</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><system.webServer>
|
||||
<validation validateIntegratedModeConfiguration="false"/>
|
||||
<handlers>
|
||||
<add name="SpringWebServiceSupport" verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/>
|
||||
</handlers>
|
||||
</system.webServer></programlisting>
|
||||
|
||||
<para><emphasis>2. Create an object definition for one's web
|
||||
service.</emphasis></para>
|
||||
|
||||
<programlisting><object name="HelloWorld" type="MyComany.MyApp.Services.HelloWorldService, MyAssembly" abstract="true"/></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>
|
||||
<object id="HelloWorld" type="MyApp.Services.HelloWorldService, MyApp">
|
||||
<property name="Message" value="Hello, World!"/>
|
||||
</object>
|
||||
|
||||
<object id="HelloWorldExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
|
||||
<property name="TargetName" value="HelloWorld"/>
|
||||
</object>
|
||||
</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>
|
||||
<object id="HelloWorldExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
|
||||
<property name="TargetName" value="HelloWorld"/>
|
||||
<property name="Namespace" value="http://myCompany/services"/>
|
||||
<property name="Description" value="My exported HelloWorld web service"/>
|
||||
<property name="MemberAttributes">
|
||||
<dictionary>
|
||||
<entry key="HelloWorld">
|
||||
<object type="System.Web.Services.WebMethodAttribute, System.Web.Services">
|
||||
<property name="Description" value="My Spring-configured HelloWorld method."/>
|
||||
<property name="MessageName" value="ZdravoSvete"/>
|
||||
</object>
|
||||
</entry>
|
||||
</dictionary>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
// or, once configuration improvements are implemented...
|
||||
<web:service targetName="HelloWorld" namespace="http://myCompany/services">
|
||||
<description>My exported HelloWorld web service.</description>
|
||||
<methods>
|
||||
<method name="HelloWorld" messageName="ZdravoSvete">
|
||||
<description>My Spring-configured HelloWorld method.</description>
|
||||
</method>
|
||||
</methods>
|
||||
</web:service>
|
||||
</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>
|
||||
<object id="DebugAdvice" type="MyApp.AOP.DebugAdvice, MyApp"/>
|
||||
|
||||
<object id="TimerAdvice" type="MyApp.AOP.TimerAdvice, MyApp"/>
|
||||
|
||||
<object id="MyService" type="MyApp.Services.MyService, MyApp"/>
|
||||
|
||||
<object id="MyServiceProxy" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
|
||||
<property name="TargetName" value="MyService"/>
|
||||
<property name="IsSingleton" value="true"/>
|
||||
<property name="InterceptorNames">
|
||||
<list>
|
||||
<value>DebugAdvice</value>
|
||||
<value>TimerAdvice</value>
|
||||
</list>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
<object id="MyServiceExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
|
||||
<property name="TargetName" value="MyServiceProxy"/>
|
||||
<property name="Name" value="MyService"/>
|
||||
<property name="Namespace" value="http://myApp/webservices"/>
|
||||
<property name="Description" value="My web service"/>
|
||||
</object>
|
||||
</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>
|
||||
<object id="HelloWorld" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services">
|
||||
<property name="ProxyType" value="MyCompany.WebServices.HelloWorld, MyClientApp"/>
|
||||
<property name="ServiceInterface" value="MyCompany.Services.IHelloWorld, MyServices"/>
|
||||
</object>
|
||||
</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>
|
||||
<object id="calculatorService" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services">
|
||||
<property name="ServiceUri" value="http://myServer/Calculator/calculatorService.asmx"/>
|
||||
<!--<property name="ServiceUri" value="file://~/calculatorService.wsdl"/>-->
|
||||
<property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract"/>
|
||||
<!-- Dependency injection on Factory's product : the proxy instance of type SoapHttpClientProtocol -->
|
||||
<property name="ProductTemplate">
|
||||
<object>
|
||||
<property name="Timeout" value="10000" /> <!-- 10s -->
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
</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>
|
||||
<object id="PublicarAltasWebService" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services">
|
||||
<property name="ProxyType" value="My.WebService" />
|
||||
<property name="ServiceInterface" value="My.IWebServiceInterface" />
|
||||
<emphasis role="bold"><property name="ProductTemplate"> </emphasis>
|
||||
<object>
|
||||
<!-- Configure the web service URL -->
|
||||
<property name="Url" value="https://localhost/MyApp/webservice.jws" />
|
||||
<emphasis role="bold"> <!-- Configure the Username and password for the web service --> </emphasis>
|
||||
<property name="Credentials">
|
||||
<object type="System.Net.NetworkCredential, System">
|
||||
<property name="UserName" value="user"/>
|
||||
<property name="Password" value="password"/>
|
||||
</object>
|
||||
</property>
|
||||
<emphasis role="bold"><!-- Configure client certificate for the web service --> </emphasis>
|
||||
<property name="ClientCertificates">
|
||||
<list>
|
||||
<object id="MyCertificate" type="System.Security.Cryptography.X509Certificates.X509Certificate2, System">
|
||||
<constructor-arg name="fileName" value="Certificate.p12" />
|
||||
<constructor-arg name="password" value="notgoingtotellyou" />
|
||||
</object>
|
||||
</list>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</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>
|
||||
643
doc/reference/src/windows-service.xml
Normal 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'>
|
||||
<configuration>
|
||||
|
||||
<configSections>
|
||||
<section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
|
||||
<sectionGroup name="spring">
|
||||
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
|
||||
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<spring>
|
||||
<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
|
||||
<resource uri="file://~/service-process-definition.xml" />
|
||||
</context>
|
||||
</spring>
|
||||
|
||||
<system.runtime.remoting>
|
||||
<application>
|
||||
<channels>
|
||||
<channel ref="http" port="1234" />
|
||||
</channels>
|
||||
</application>
|
||||
</system.runtime.remoting>
|
||||
|
||||
<log4net>
|
||||
<!-- see http://logging.apache.org/log4net/release/manual/introduction.html -->
|
||||
<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
|
||||
<layout type="log4net.Layout.PatternLayout">
|
||||
<conversionPattern value="%d [%t] %-5p %c{1} - %m%n" />
|
||||
</layout>
|
||||
<file value="logs/Spring.Service.Process.log" />
|
||||
<appendToFile value="true" />
|
||||
<maximumFileSize value="500KB" />
|
||||
<maxSizeRollBackups value="5" />
|
||||
</appender>
|
||||
<appender name="OutputDebugString" type="log4net.Appender.OutputDebugStringAppender">
|
||||
<layout type="log4net.Layout.PatternLayout">
|
||||
<conversionPattern value="%d{HH:mm:ss,fff} %-5p %c{2}(line:%L) - %m%n" />
|
||||
</layout>
|
||||
</appender>
|
||||
<root>
|
||||
<level value="OFF" />
|
||||
</root>
|
||||
<logger name="Spring.Services">
|
||||
<level value="ALL" />
|
||||
<appender-ref ref="RollingFile" />
|
||||
<appender-ref ref="OutputDebugString" />
|
||||
</logger>
|
||||
</log4net>
|
||||
|
||||
</configuration></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'>
|
||||
<!-- provides access to the ${spring.services.process.base.dir} property -->
|
||||
<object
|
||||
name="localizer"
|
||||
type="Spring.Services.WindowsService.Common.Localizer+ForProcess, Spring.Services.WindowsService.Common">
|
||||
<!-- change this to access the property with another prefix, for example ${foo.process.base.dir}
|
||||
<property name="prefix" value="foo"/>
|
||||
-->
|
||||
</object></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
In that object definition you can customize the prefix for the following string
|
||||
<programlisting format='linespecific'>
|
||||
public static readonly string SpringServicesProcessBaseDirFormat = "{0}.process.base.dir";</programlisting>
|
||||
but you usually won't need it; the default value is
|
||||
<programlisting format='linespecific'>
|
||||
public static readonly string DefaultPrefix = "spring.services";</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'>
|
||||
<object
|
||||
name="service"
|
||||
type="Spring.Services.WindowsService.Common.DefaultService, Spring.Services.WindowsService.Common"
|
||||
init-method="Start"
|
||||
destroy-method="Stop">
|
||||
<property name="DeployPath" value="${spring.services.process.base.dir}/deploy"/>
|
||||
</object></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'>
|
||||
<object name="remoted.service" type="Spring.Remoting.SaoExporter, Spring.Services">
|
||||
<property name="TargetName" value="service"/>
|
||||
<property name="ServiceName" value="SpringWindowsService.rem"/>
|
||||
</object></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'>
|
||||
<configuration>
|
||||
|
||||
<configSections>
|
||||
<sectionGroup name="spring">
|
||||
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
|
||||
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<appSettings>
|
||||
<add key="port" value="10"/>
|
||||
</appSettings>
|
||||
|
||||
<spring>
|
||||
<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
|
||||
<resource uri="file://~/service.xml" />
|
||||
</context>
|
||||
</spring>
|
||||
|
||||
</configuration></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'>
|
||||
<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">
|
||||
|
||||
<object name="echo"
|
||||
type="Spring.Services.WindowsService.Samples.Echo, Spring.Services.WindowsService.Tests"
|
||||
init-method="Start" destroy-method="Stop">
|
||||
<property name="port"><value>${port}</value></property>
|
||||
</object>
|
||||
|
||||
<object id="configurer" type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core">
|
||||
<property name="locations">
|
||||
<list>
|
||||
<value>file://~/service.config</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="configSections">
|
||||
<list>
|
||||
<value>appSettings</value>
|
||||
</list>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
</objects></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'>
|
||||
<!-- provides access to the ${spring.services.application.*} properties -->
|
||||
<object
|
||||
name="localizer"
|
||||
type="Spring.Services.WindowsService.Common.Localizer+ForApplication, Spring.Services.WindowsService.Common">
|
||||
<!-- change this to access the property with another prefix, for example ${foo.application.base.dir}
|
||||
-->
|
||||
<property name="prefix" value="myPrefix"/>
|
||||
</object></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'>
|
||||
<object name="simple"
|
||||
type="Spring.Services.WindowsService.Samples.Simple, Spring.Services.WindowsService.Tests"
|
||||
init-method="Start" destroy-method="Stop">
|
||||
<constructor-arg index="0" value="${myPrefix.application.name},${myPrefix.application.fullPath}"/>
|
||||
<property name="AppName">
|
||||
<value>${myPrefix.application.name}</value>
|
||||
</property>
|
||||
<property name="AppFullPath">
|
||||
<value>${myPrefix.application.fullpath}</value>
|
||||
</property>
|
||||
</object></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'>
|
||||
/// <summary>
|
||||
Interface defining the contract for an application watcher.
|
||||
<p>An application watcher is responsible to dispatch an
|
||||
<see cref="IApplicationWatcherFactory">event</see> whenever it thinks the
|
||||
application has been updated.</p>
|
||||
<p>Usually it should not raise other kind of events
|
||||
as they are usually raised by the <see cref="FileSystemApplicationWatcher"/>
|
||||
that creates the watcher itself</p>
|
||||
</summary>
|
||||
<remarks>Usually instances of this interface need to be disposed</remarks>
|
||||
<seealso cref="DeployEventArgs"/>
|
||||
<seealso cref="IDeployLocation"/>
|
||||
<seealso cref="DeployEventType.ApplicationUpdated"/>
|
||||
<seealso cref="DeployEventAggregator"/>
|
||||
<seealso cref="IDeployLocation"/>
|
||||
<seealso cref="DeployEventType"/>
|
||||
public interface IApplicationWatcher : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The watched application
|
||||
/// </summary>
|
||||
IApplication Application {get; }
|
||||
|
||||
/// <summary>
|
||||
/// Start to watch the application, using the given dispatcher to
|
||||
/// dispatch deply events
|
||||
/// </summary>
|
||||
/// <param name="dispatcher">the dispatcher used to raise deploy events</param>
|
||||
void StartWatching (IDeployEventDispatcher dispatcher);
|
||||
|
||||
/// <summary>
|
||||
/// Stop to watch the application.
|
||||
/// </summary>
|
||||
void StopWatching ();
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="allows">the list of allowing filters</param>
|
||||
/// <param name="disallows">the list of disallowing filters</param>
|
||||
/// <seealso cref="FilteringSupport"/>
|
||||
/// <seealso cref="RegularExpressionFilter"/>
|
||||
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'>
|
||||
<objects>
|
||||
|
||||
<object name='watcher'
|
||||
type='Spring.Services.WindowsService.Common.Deploy.FileSystem.FileSystemApplicationWatcher'>
|
||||
<!--
|
||||
we can get access to the IApplication we are asked to monitor
|
||||
using a reference like the following
|
||||
-->
|
||||
<constructor-arg ref='.injected.application'/>
|
||||
|
||||
<!-- 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
|
||||
<property name="ignoreCase" value="false"/>
|
||||
-->
|
||||
|
||||
<property name="includes">
|
||||
<list>
|
||||
<value>wwwroot/bin/*.*</value>
|
||||
<value>service.config</value>
|
||||
<value>service.xml</value>
|
||||
</list>
|
||||
</property>
|
||||
|
||||
<!--
|
||||
<property name="excludes">
|
||||
<list>
|
||||
<value>Db/**/*.*</value>
|
||||
<value>Jobs</value>
|
||||
<value>Jobs</value>
|
||||
<value>**/*.log</value>
|
||||
</list>
|
||||
</property>
|
||||
-->
|
||||
|
||||
</object>
|
||||
|
||||
</objects></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'>
|
||||
/// <summary>
|
||||
/// Interface defining how a deploy location should look like
|
||||
/// </summary>
|
||||
public interface IDeployLocation : IDeployEventSource, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of applications deployed at this location
|
||||
/// Usually non-valid applications are not listed
|
||||
/// </summary>
|
||||
/// <seealso cref="Application"/>
|
||||
IList Applications { get; }
|
||||
}</programlisting>
|
||||
<programlisting format='linespecific'>
|
||||
/// <summary>
|
||||
/// Interface defining the contract for an object acting as the source of
|
||||
/// deploy events (application added, removed, updated)
|
||||
/// </summary>
|
||||
/// <seealso cref="DeployEventArgs"/>
|
||||
/// <seealso cref="DeployEventHandler"/>
|
||||
public interface IDeployEventSource
|
||||
{
|
||||
/// <summary>
|
||||
/// The multicaster for deploy events
|
||||
/// </summary>
|
||||
event DeployEventHandler DeployEvent;
|
||||
}</programlisting>
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
609
doc/reference/src/xml-config-reference.xml
Normal 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><objects/></literal>, these objects have zero or more
|
||||
<literal><constructor-arg/></literal> or
|
||||
<literal><property/></literal> elements that are generally
|
||||
<literal><ref/>erences</literal> to other
|
||||
<literal><object/>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 <object/> 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><object name="service" <co
|
||||
id="xcf-plain-vanilla-object-name" />
|
||||
type="Example.Foo, FooAssembly"/> <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><object id="anException" type="System.ArgumentException, Mscorlib"/></programlisting>
|
||||
|
||||
<programlisting><object id="anEmptyList" type="System.Collections.ArrayList, Mscorlib"/></programlisting>
|
||||
|
||||
<programlisting><object id="anSqlCommand" type="System.Data.SqlClient.SqlCommand, System.Data"/></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><object id="aGauge" type="Example.Gauge, FooAssembly">
|
||||
<property name="setting" value="213"/>
|
||||
</object></programlisting>
|
||||
|
||||
<para>We can also use any of the normal supported conventions (such
|
||||
as hexadecimal) to set values, as shown below.</para>
|
||||
|
||||
<programlisting><object id="aGauge" type="Example.Gauge, FooAssembly">
|
||||
<property name="setting" value="0x10"/>
|
||||
</object></programlisting>
|
||||
|
||||
<programlisting><object id="aGauge" type="Example.Gauge, FooAssembly">
|
||||
<property name="sensitivity" value="31000.00"/>
|
||||
</object></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><object id="aGauge" type="Example.Gauge, FooAssembly">
|
||||
<property name="lastChecked" value=""/>
|
||||
</object></programlisting>
|
||||
|
||||
<programlisting><object id="aGauge" type="Example.Gauge, FooAssembly">
|
||||
<property name="lastChecked" value=""/>
|
||||
</object></programlisting>
|
||||
|
||||
<programlisting><object id="aGauge" type="Example.Gauge, FooAssembly">
|
||||
<property name="lastChecked" value=""/>
|
||||
</object></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><value/></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><object id="aGauge" type="Example.Gauge, FooAssembly">
|
||||
<property name="IsSwitchedOn" value="true"/>
|
||||
</object></programlisting>
|
||||
|
||||
<programlisting><object id="aGauge" type="Example.Gauge, FooAssembly">
|
||||
<property name="IsSwitchedOn" value="false"/>
|
||||
</object></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><object id="supportTeamEmail" type="string">
|
||||
<constructor-arg index="0" value="support@my.company.com"/>
|
||||
</object></programlisting>
|
||||
|
||||
<programlisting><object id="projectManagerEmail" type="string">
|
||||
<constructor-arg index="0" value="projectManager@my.company.com"/>
|
||||
</object></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><object id="aGauge" type="Example.Gauge, FooAssembly">
|
||||
<property name="RunMode" value="Starting"/>
|
||||
</object></programlisting>
|
||||
|
||||
<programlisting><object id="aGauge" type="Example.Gauge, FooAssembly">
|
||||
<property name="RunMode" value="SwitchingOff"/>
|
||||
</object></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><objects xmlns="http://www.springframework.net">
|
||||
<object id="gauge" type="Example.Gauge, FooAssembly">
|
||||
<property name="callback">
|
||||
<object type="Spring.Objects.Factory.Config.DelegateFactoryObject">
|
||||
<property name="delegateType" value="Example.GaugeCallback, FooAssembly"/>
|
||||
<property name="targetObject">
|
||||
<object type="Example.MyGaugeCallback, FooAssembly"/>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</objects></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>
|
||||
388
doc/reference/src/xml-custom.xml
Normal 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><myns:regex id="regex"
|
||||
pattern="(^\d{5}$)|(^\d{5}-\d{4}$)"
|
||||
options="Compiled"/>
|
||||
</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><?xml version="1.0" encoding="utf-8" ?>
|
||||
<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=""
|
||||
>
|
||||
|
||||
<xsd:import namespace="http://www.springframework.net"/>
|
||||
|
||||
<xsd:element name="regex">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<emphasis role="bold"><xsd:extension base="objects:identifiedType"></emphasis>
|
||||
<xsd:attribute name="pattern" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="options" type="xsd:string" use="optional"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
</xsd:schema> </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><myns:regex/></literal>
|
||||
element.</para>
|
||||
|
||||
<programlisting><myns:regex id="usZipCodeRegex"
|
||||
pattern="(^\d{5}$)|(^\d{5}-\d{4}$)"
|
||||
options="Compiled"/></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> <object id="usZipCodeRegex" type="System.Text.RegularExpressions.Regex, System">
|
||||
<constructor-arg name="pattern" value="(^\d{5}$)|(^\d{5}-\d{4}$)"/>
|
||||
<constructor-arg name="options" value="Compiled"/>
|
||||
</object></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><myns:regex/></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><configuration>
|
||||
|
||||
<configSections>
|
||||
<sectionGroup name="spring">
|
||||
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<spring>
|
||||
<parsers>
|
||||
<parser type="CustomNamespace.MyNamespaceParser, CustomNamespace" />
|
||||
</parsers>
|
||||
</spring>
|
||||
|
||||
</configuration></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><regex/></literal> element developed in the previous steps
|
||||
in a Spring XML configuration file.</para>
|
||||
|
||||
<programlisting><?xml version="1.0" encoding="utf-8" ?>
|
||||
<objects xmlns="http://www.springframework.net"
|
||||
xmlns:myns="http://www.mycompany.com/schema/myns">
|
||||
|
||||
<!-- as a top level object definition -->
|
||||
<myns:regex id="usZipCodeRegex"
|
||||
pattern="(^\d{5}$)|(^\d{5}-\d{4}$)"/>
|
||||
|
||||
<object id="jobDetailTemplate" abstract="true">
|
||||
<property name="regex">
|
||||
<!-- as an inner object definition -->
|
||||
<myns:regex pattern="(^\d{5}$)|(^\d{5}-\d{4}$)"
|
||||
options="Compiled"/>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
</objects></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>
|
||||
225
doc/reference/src/xsd-configuration.xml
Normal 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><object/></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>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
|
||||
<lineannotation> <!-- <literal><object/></literal> definitions here --></lineannotation>
|
||||
|
||||
</objects></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><object/></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><?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>>
|
||||
|
||||
<lineannotation><!-- <literal><object/></literal> definitions here --></lineannotation>
|
||||
|
||||
</object></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><?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>>
|
||||
|
||||
<lineannotation><!-- <literal><object/></literal> definitions here --></lineannotation>
|
||||
|
||||
</objects></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><?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>>
|
||||
|
||||
<lineannotation><!-- <literal><object/></literal> definitions here --></lineannotation>
|
||||
|
||||
</objects></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><?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>>
|
||||
|
||||
<lineannotation><!-- <literal><object/></literal> definitions here --></lineannotation>
|
||||
|
||||
</objects></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><?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>>
|
||||
|
||||
<lineannotation><!-- <literal><object/></literal> definitions here --></lineannotation>
|
||||
|
||||
</objects></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><?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
|
||||
<object id="foo" class="X.Y.Foo, X">
|
||||
<property name="name" value="Rick"/>
|
||||
</object>
|
||||
|
||||
</objects></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>
|
||||
4
doc/reference/src/xsd-template.xml
Normal 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
@@ -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 & 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>
|
||||