diff --git a/doc/reference/docbook.build b/doc/reference/docbook.build new file mode 100644 index 00000000..d33af336 --- /dev/null +++ b/doc/reference/docbook.build @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/reference/images/admons/Thumbs.db b/doc/reference/images/admons/Thumbs.db deleted file mode 100644 index 29ceb9f7..00000000 Binary files a/doc/reference/images/admons/Thumbs.db and /dev/null differ diff --git a/doc/reference/images/admons/blank.png b/doc/reference/images/admons/blank.png deleted file mode 100644 index 764bf4f0..00000000 Binary files a/doc/reference/images/admons/blank.png and /dev/null differ diff --git a/doc/reference/images/admons/caution.gif b/doc/reference/images/admons/caution.gif deleted file mode 100644 index d9f5e5b1..00000000 Binary files a/doc/reference/images/admons/caution.gif and /dev/null differ diff --git a/doc/reference/images/admons/caution.png b/doc/reference/images/admons/caution.png deleted file mode 100644 index 5b7809ca..00000000 Binary files a/doc/reference/images/admons/caution.png and /dev/null differ diff --git a/doc/reference/images/admons/caution.tif b/doc/reference/images/admons/caution.tif deleted file mode 100644 index 4a282948..00000000 Binary files a/doc/reference/images/admons/caution.tif and /dev/null differ diff --git a/doc/reference/images/admons/draft.png b/doc/reference/images/admons/draft.png deleted file mode 100644 index 0084708c..00000000 Binary files a/doc/reference/images/admons/draft.png and /dev/null differ diff --git a/doc/reference/images/admons/home.gif b/doc/reference/images/admons/home.gif deleted file mode 100644 index 6784f5bb..00000000 Binary files a/doc/reference/images/admons/home.gif and /dev/null differ diff --git a/doc/reference/images/admons/home.png b/doc/reference/images/admons/home.png deleted file mode 100644 index cbb711de..00000000 Binary files a/doc/reference/images/admons/home.png and /dev/null differ diff --git a/doc/reference/images/admons/important.gif b/doc/reference/images/admons/important.gif deleted file mode 100644 index 6795d9a8..00000000 Binary files a/doc/reference/images/admons/important.gif and /dev/null differ diff --git a/doc/reference/images/admons/important.png b/doc/reference/images/admons/important.png deleted file mode 100644 index ad57f6f7..00000000 Binary files a/doc/reference/images/admons/important.png and /dev/null differ diff --git a/doc/reference/images/admons/important.tif b/doc/reference/images/admons/important.tif deleted file mode 100644 index 184de637..00000000 Binary files a/doc/reference/images/admons/important.tif and /dev/null differ diff --git a/doc/reference/images/admons/next.gif b/doc/reference/images/admons/next.gif deleted file mode 100644 index aa1516e6..00000000 Binary files a/doc/reference/images/admons/next.gif and /dev/null differ diff --git a/doc/reference/images/admons/next.png b/doc/reference/images/admons/next.png deleted file mode 100644 index 45835bf8..00000000 Binary files a/doc/reference/images/admons/next.png and /dev/null differ diff --git a/doc/reference/images/admons/note.gif b/doc/reference/images/admons/note.gif deleted file mode 100644 index f329d359..00000000 Binary files a/doc/reference/images/admons/note.gif and /dev/null differ diff --git a/doc/reference/images/admons/note.png b/doc/reference/images/admons/note.png deleted file mode 100644 index ad57f6f7..00000000 Binary files a/doc/reference/images/admons/note.png and /dev/null differ diff --git a/doc/reference/images/admons/note.tif b/doc/reference/images/admons/note.tif deleted file mode 100644 index 08644d6b..00000000 Binary files a/doc/reference/images/admons/note.tif and /dev/null differ diff --git a/doc/reference/images/admons/prev.gif b/doc/reference/images/admons/prev.gif deleted file mode 100644 index 64ca8f3c..00000000 Binary files a/doc/reference/images/admons/prev.gif and /dev/null differ diff --git a/doc/reference/images/admons/prev.png b/doc/reference/images/admons/prev.png deleted file mode 100644 index cf24654f..00000000 Binary files a/doc/reference/images/admons/prev.png and /dev/null differ diff --git a/doc/reference/images/admons/tip.gif b/doc/reference/images/admons/tip.gif deleted file mode 100644 index 823f2b41..00000000 Binary files a/doc/reference/images/admons/tip.gif and /dev/null differ diff --git a/doc/reference/images/admons/tip.png b/doc/reference/images/admons/tip.png deleted file mode 100644 index ad57f6f7..00000000 Binary files a/doc/reference/images/admons/tip.png and /dev/null differ diff --git a/doc/reference/images/admons/tip.tif b/doc/reference/images/admons/tip.tif deleted file mode 100644 index 4a3d8c75..00000000 Binary files a/doc/reference/images/admons/tip.tif and /dev/null differ diff --git a/doc/reference/images/admons/toc-blank.png b/doc/reference/images/admons/toc-blank.png deleted file mode 100644 index 6ffad17a..00000000 Binary files a/doc/reference/images/admons/toc-blank.png and /dev/null differ diff --git a/doc/reference/images/admons/toc-minus.png b/doc/reference/images/admons/toc-minus.png deleted file mode 100644 index abbb020c..00000000 Binary files a/doc/reference/images/admons/toc-minus.png and /dev/null differ diff --git a/doc/reference/images/admons/toc-plus.png b/doc/reference/images/admons/toc-plus.png deleted file mode 100644 index 941312ce..00000000 Binary files a/doc/reference/images/admons/toc-plus.png and /dev/null differ diff --git a/doc/reference/images/admons/up.gif b/doc/reference/images/admons/up.gif deleted file mode 100644 index aabc2d01..00000000 Binary files a/doc/reference/images/admons/up.gif and /dev/null differ diff --git a/doc/reference/images/admons/up.png b/doc/reference/images/admons/up.png deleted file mode 100644 index 07634de2..00000000 Binary files a/doc/reference/images/admons/up.png and /dev/null differ diff --git a/doc/reference/images/admons/warning.gif b/doc/reference/images/admons/warning.gif deleted file mode 100644 index 3adf1912..00000000 Binary files a/doc/reference/images/admons/warning.gif and /dev/null differ diff --git a/doc/reference/images/admons/warning.png b/doc/reference/images/admons/warning.png deleted file mode 100644 index 1c33db8f..00000000 Binary files a/doc/reference/images/admons/warning.png and /dev/null differ diff --git a/doc/reference/images/admons/warning.tif b/doc/reference/images/admons/warning.tif deleted file mode 100644 index 7b6611ec..00000000 Binary files a/doc/reference/images/admons/warning.tif and /dev/null differ diff --git a/doc/reference/images/callouts/1.gif b/doc/reference/images/callouts/1.gif deleted file mode 100644 index 0d669771..00000000 Binary files a/doc/reference/images/callouts/1.gif and /dev/null differ diff --git a/doc/reference/images/callouts/1.png b/doc/reference/images/callouts/1.png deleted file mode 100644 index 7d473430..00000000 Binary files a/doc/reference/images/callouts/1.png and /dev/null differ diff --git a/doc/reference/images/callouts/10.gif b/doc/reference/images/callouts/10.gif deleted file mode 100644 index fb50b06d..00000000 Binary files a/doc/reference/images/callouts/10.gif and /dev/null differ diff --git a/doc/reference/images/callouts/10.png b/doc/reference/images/callouts/10.png deleted file mode 100644 index 997bbc82..00000000 Binary files a/doc/reference/images/callouts/10.png and /dev/null differ diff --git a/doc/reference/images/callouts/11.gif b/doc/reference/images/callouts/11.gif deleted file mode 100644 index 9f5dba4f..00000000 Binary files a/doc/reference/images/callouts/11.gif and /dev/null differ diff --git a/doc/reference/images/callouts/11.png b/doc/reference/images/callouts/11.png deleted file mode 100644 index ce47dac3..00000000 Binary files a/doc/reference/images/callouts/11.png and /dev/null differ diff --git a/doc/reference/images/callouts/12.gif b/doc/reference/images/callouts/12.gif deleted file mode 100644 index a373d0b4..00000000 Binary files a/doc/reference/images/callouts/12.gif and /dev/null differ diff --git a/doc/reference/images/callouts/12.png b/doc/reference/images/callouts/12.png deleted file mode 100644 index 31daf4e2..00000000 Binary files a/doc/reference/images/callouts/12.png and /dev/null differ diff --git a/doc/reference/images/callouts/13.gif b/doc/reference/images/callouts/13.gif deleted file mode 100644 index b00b1637..00000000 Binary files a/doc/reference/images/callouts/13.gif and /dev/null differ diff --git a/doc/reference/images/callouts/13.png b/doc/reference/images/callouts/13.png deleted file mode 100644 index 14021a89..00000000 Binary files a/doc/reference/images/callouts/13.png and /dev/null differ diff --git a/doc/reference/images/callouts/14.gif b/doc/reference/images/callouts/14.gif deleted file mode 100644 index 6d6642ee..00000000 Binary files a/doc/reference/images/callouts/14.gif and /dev/null differ diff --git a/doc/reference/images/callouts/14.png b/doc/reference/images/callouts/14.png deleted file mode 100644 index 64014b75..00000000 Binary files a/doc/reference/images/callouts/14.png and /dev/null differ diff --git a/doc/reference/images/callouts/15.gif b/doc/reference/images/callouts/15.gif deleted file mode 100644 index cdd7072d..00000000 Binary files a/doc/reference/images/callouts/15.gif and /dev/null differ diff --git a/doc/reference/images/callouts/15.png b/doc/reference/images/callouts/15.png deleted file mode 100644 index 0d65765f..00000000 Binary files a/doc/reference/images/callouts/15.png and /dev/null differ diff --git a/doc/reference/images/callouts/2.gif b/doc/reference/images/callouts/2.gif deleted file mode 100644 index 100ff79f..00000000 Binary files a/doc/reference/images/callouts/2.gif and /dev/null differ diff --git a/doc/reference/images/callouts/2.png b/doc/reference/images/callouts/2.png deleted file mode 100644 index 5d09341b..00000000 Binary files a/doc/reference/images/callouts/2.png and /dev/null differ diff --git a/doc/reference/images/callouts/3.gif b/doc/reference/images/callouts/3.gif deleted file mode 100644 index 5008ca7d..00000000 Binary files a/doc/reference/images/callouts/3.gif and /dev/null differ diff --git a/doc/reference/images/callouts/3.png b/doc/reference/images/callouts/3.png deleted file mode 100644 index ef7b7004..00000000 Binary files a/doc/reference/images/callouts/3.png and /dev/null differ diff --git a/doc/reference/images/callouts/4.gif b/doc/reference/images/callouts/4.gif deleted file mode 100644 index 0e5617d2..00000000 Binary files a/doc/reference/images/callouts/4.gif and /dev/null differ diff --git a/doc/reference/images/callouts/4.png b/doc/reference/images/callouts/4.png deleted file mode 100644 index adb8364e..00000000 Binary files a/doc/reference/images/callouts/4.png and /dev/null differ diff --git a/doc/reference/images/callouts/5.gif b/doc/reference/images/callouts/5.gif deleted file mode 100644 index 9bc75ada..00000000 Binary files a/doc/reference/images/callouts/5.gif and /dev/null differ diff --git a/doc/reference/images/callouts/5.png b/doc/reference/images/callouts/5.png deleted file mode 100644 index 4d7eb460..00000000 Binary files a/doc/reference/images/callouts/5.png and /dev/null differ diff --git a/doc/reference/images/callouts/6.gif b/doc/reference/images/callouts/6.gif deleted file mode 100644 index d3964070..00000000 Binary files a/doc/reference/images/callouts/6.gif and /dev/null differ diff --git a/doc/reference/images/callouts/6.png b/doc/reference/images/callouts/6.png deleted file mode 100644 index 0ba694af..00000000 Binary files a/doc/reference/images/callouts/6.png and /dev/null differ diff --git a/doc/reference/images/callouts/7.gif b/doc/reference/images/callouts/7.gif deleted file mode 100644 index c90b2f3d..00000000 Binary files a/doc/reference/images/callouts/7.gif and /dev/null differ diff --git a/doc/reference/images/callouts/7.png b/doc/reference/images/callouts/7.png deleted file mode 100644 index 472e96f8..00000000 Binary files a/doc/reference/images/callouts/7.png and /dev/null differ diff --git a/doc/reference/images/callouts/8.gif b/doc/reference/images/callouts/8.gif deleted file mode 100644 index 6fe3287d..00000000 Binary files a/doc/reference/images/callouts/8.gif and /dev/null differ diff --git a/doc/reference/images/callouts/8.png b/doc/reference/images/callouts/8.png deleted file mode 100644 index 5e60973c..00000000 Binary files a/doc/reference/images/callouts/8.png and /dev/null differ diff --git a/doc/reference/images/callouts/9.gif b/doc/reference/images/callouts/9.gif deleted file mode 100644 index bc5c8125..00000000 Binary files a/doc/reference/images/callouts/9.gif and /dev/null differ diff --git a/doc/reference/images/callouts/9.png b/doc/reference/images/callouts/9.png deleted file mode 100644 index a0676d26..00000000 Binary files a/doc/reference/images/callouts/9.png and /dev/null differ diff --git a/doc/reference/images/callouts/Thumbs.db b/doc/reference/images/callouts/Thumbs.db deleted file mode 100644 index fc135009..00000000 Binary files a/doc/reference/images/callouts/Thumbs.db and /dev/null differ diff --git a/doc/reference/src/ado.xml b/doc/reference/src/ado.xml index 4be10ed1..4b245fb6 100644 --- a/doc/reference/src/ado.xml +++ b/doc/reference/src/ado.xml @@ -1,8 +1,25 @@ - + + Data access using ADO.NET - + Introduction Spring provides an abstraction for data access via ADO.NET that @@ -113,7 +130,7 @@ - + Motivations There are a variety of motivations to create a higher level ADO.NET @@ -249,7 +266,7 @@ well as mixing orm/ado.net operations within the same transaction. - + Provider Abstraction Before you get started executing queries against the database you @@ -260,7 +277,7 @@ ADO.NET interfaces, such as IDbCommand or IDbParameter in your code. However, In the .NET 1.1 BCL the only means to obtain references to instances of these interfaces is to directly instantiate the classes, i.e. - for SqlServer this would be IDbCommand command = new SqlCommand(); + for SqlServer this would be IDbCommand command = new SqlCommand(); One of the classic creational patterns in the GoF Design Patterns book addresses this situation directly, the Abstract Factory pattern. This approach was applied in the .NET BCL with the introduction of the @@ -288,7 +305,7 @@ For more information on configuring a Spring database provider refer to - + Creating an instance of IDbProvider Each database vendor is associated with a particular @@ -298,14 +315,14 @@ configuration for database that is not yet provided. The programmatic way to create an IDbProvider is shown below - IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient"); + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient"); Please refer to the for information on how to create a IDbProvider in Spring's XML configuration file. - + Namespaces The ADO.NET framework consists of a few namespaces, namely @@ -331,25 +348,25 @@ thread safe, reusable objects. Finally the Spring.Data.Support namespace is - where you find the IAdoExceptionTransactor + where you find the IAdoExceptionTransactor translation functionality and some utility classes. - + Approaches to Data Access Spring provides two styles to interact with ADO.NET. The first is a 'template' based approach in which you create an single instance of - AdoTemplate to be used by all your DAO + AdoTemplate to be used by all your DAO implementations. Your DAO methods are frequently implemented as a single method call on the template class as described in detail in the following section. The other approach a more object-oriented manner that models database operations as objects. For example, one can encapsulate the - functionality of a database query via an AdoQuery + functionality of a database query via an AdoQuery class and a create/update/delete operation as a - AdoNonQuery class. Stored procedures are also + AdoNonQuery class. Stored procedures are also modelled in this manner via the class - StoredProcedure. To use these classes you inherit + StoredProcedure. To use these classes you inherit from them and define the details of the operation in the constructor and implement an abstract method. This reads very cleanly when looking at DAO method implementation as you can generally see all the details of what is @@ -366,34 +383,34 @@ benefit for a particular situation. - + Introduction to AdoTemplate - The class AdoTemplate is at the heart of + The class AdoTemplate is at the heart of Spring's ADO.NET support. It is based on an Inversion of Control (i.e. callback) design with the central method 'Execute' - handing you a IDbCommand instance that has + handing you a IDbCommand instance that has its Connection and Transaction properties set based on the transaction context of the calling code. All resource management is handled by the framework, you only need to focus on dealing with the - IDbCommand object. The other methods in + IDbCommand object. The other methods in this class build upon this central 'Execute' method to provide you a quick means to execute common data access scenarios. - There are two implementations of AdoTemplate. + There are two implementations of AdoTemplate. The one that uses Generics and is in the namespace - Spring.Data.Generic and the other non-generic + Spring.Data.Generic and the other non-generic version in Spring.Data. In either case you create an - instance of an AdoTemplate by passing it a - IDbProvider instance as shown below + instance of an AdoTemplate by passing it a + IDbProvider instance as shown below - AdoTemplate adoTemplate = new AdoTemplate(dbProvider); + AdoTemplate adoTemplate = new AdoTemplate(dbProvider); - AdoTemplate is a thread-safe class and as + AdoTemplate is a thread-safe class and as such a single instance can be used for all data access operations in you - applications DAOs. AdoTemplate implements an - IAdoOperations interface. Although the - IAdoOperations interface is more commonly + applications DAOs. AdoTemplate implements an + IAdoOperations interface. Although the + IAdoOperations interface is more commonly used for testing scenarios you may prefer to code against it instead of the direct class instance. @@ -401,33 +418,33 @@ the non-generic version via the property ClassicAdoTemplate. The following two sections show basic usage of the - AdoTemplate 'Execute' API for .NET 1.1 and + AdoTemplate 'Execute' API for .NET 1.1 and 2.0. - + Execute Callback The Execute method and its associated callback function/inteface is the basic method upon which all the other - methods in AdoTemplate delegate their work. If + methods in AdoTemplate delegate their work. If you can not find a suitable 'one-liner' method in - AdoTemplate for your purpose you can always fall + AdoTemplate for your purpose you can always fall back to the Execute method to perform any database operation while benefiting from ADO.NET resource management and transaction enlistment. This is commonly the case when you are using special provider specific features, such as XML or BLOB support. - + Execute Callback in .NET 2.0 In this example a simple query against the 'Northwind' database is done to determine the number of customers who have a particular postal code. - public int FindCountWithPostalCode(string postalCode) + public int FindCountWithPostalCode(string postalCode) { return adoTemplate.Execute<int>(delegate(DbCommand command) { @@ -443,11 +460,11 @@ }); -}The DbCommand that is passed into the +}The DbCommand that is passed into the anonymous delegate is already has it Connection property set to the corresponding value of the dbProvider instance used to create the template. Furthermore, the Transaction property - of the DbCommand is set based on the + of the DbCommand is set based on the transactional calling context of the code as based on the use of Spring's transaction management features. Also note the feature of anonymous delegates to access the variable 'postalCode' which is defined @@ -456,20 +473,20 @@ data access code. If you find that your callback implementation is getting very long, it may improve code clarity to use an interface based version of the callback function, i.e. an - ICommandCallback shown below. + ICommandCallback shown below. As you can see, only the most relevant portions of the data access task at hand need to be coded. (Note that in this simple example you would be better off using AdoTemplate's ExecuteScalar method directly. This method is described in the following sections). As mentioned before, the typical usage scenario for the Execute callback would - involve downcasting the passed in DbCommand + involve downcasting the passed in DbCommand object to access specific provider API features. There is also an interface based version of the execute method. The signatures for the delegate and interface are shown below - public delegate T CommandDelegate<T>(DbCommand command); + public delegate T CommandDelegate<T>(DbCommand command); public interface ICommandCallback @@ -482,7 +499,7 @@ public interface ICommandCallback on Spring.Data.Generic.AdoTemplate are shown below - public class AdoTemplate : AdoAccessor, IAdoOperations + public class AdoTemplate : AdoAccessor, IAdoOperations { ... @@ -502,7 +519,7 @@ public interface ICommandCallback IDbCommand and not DbCommand as callback arguments. The following listing shows these methods on AdoTemplate. - public class AdoTemplate : AdoAccessor, IAdoOperations + public class AdoTemplate : AdoAccessor, IAdoOperations { ... @@ -516,7 +533,7 @@ public interface ICommandCallback where the signatures for the delegate and interface are shown below - public delegate T IDbCommandDelegate<T>(IDbCommand command); + public delegate T IDbCommandDelegate<T>(IDbCommand command); public interface IDbCommandCallback<T> @@ -524,25 +541,25 @@ public interface IDbCommandCallback<T> T DoInCommand(IDbCommand command); } - Internally the AdoTemplate implementation + Internally the AdoTemplate implementation delegates to implementations of - IDbCommandCallback so that the 'lowest common + IDbCommandCallback so that the 'lowest common denominator' API is used to have maximum portability. If you accidentally call Execute<T>(ICommandCallback action)and the command does not inherit from - DbCommand, an - InvalidDataAccessApiUsageException will be + DbCommand, an + InvalidDataAccessApiUsageException will be thrown. Depending on how portable you would like your code to be, you can choose among the two callback styles. The one based on - DbCommand has the advantage of access to the more - user friendly DbParameter class as compared to - IDbParameter obtained from - IDbCommand. + DbCommand has the advantage of access to the more + user friendly DbParameter class as compared to + IDbParameter obtained from + IDbCommand. - + > Execute Callback in .NET 1.1 @@ -550,19 +567,19 @@ public interface IDbCommandCallback<T> AdoTemplate differs from its .NET 2.0 generic counterpart in that - it exposes the interface IDbCommand in + it exposes the interface IDbCommand in its 'Execute' callback methods and delegate as compared to the abstract - base class DbProvider. Also, since anonymous + base class DbProvider. Also, since anonymous delegates are not available in .NET 1.1, the typical usage pattern requires you to create a explicitly delegate and/or class that - implements the ICommandCallback + implements the ICommandCallback interface. Example code to query In .NET 1.1 the 'Northwind' database is done to determine the number of customers who have a particular postal code is shown below. - public virtual int FindCountWithPostalCode(string postalCode) + public virtual int FindCountWithPostalCode(string postalCode) { return (int) AdoTemplate.Execute(new PostalCodeCommandCallback(postalCode)); } @@ -573,7 +590,7 @@ public interface IDbCommandCallback<T> - private class PostalCodeCommandCallback : ICommandCallback + private class PostalCodeCommandCallback : ICommandCallback { private string cmdText = "select count(*) from Customer where PostalCode = @PostalCode"; @@ -610,7 +627,7 @@ public interface IDbCommandCallback<T> - public delegate object CommandDelegate(IDbCommand command); + public delegate object CommandDelegate(IDbCommand command); public interface ICommandCallback { @@ -624,7 +641,7 @@ public interface ICommandCallback - public class AdoTemplate : AdoAccessor, IAdoOperations + public class AdoTemplate : AdoAccessor, IAdoOperations { ... @@ -644,7 +661,7 @@ public interface ICommandCallback - + Quick Guide to AdoTemplate Methods There are many methods in AdoTemplate so it is easy to feel a bit @@ -690,41 +707,41 @@ public interface ICommandCallback QueryWithResultSetExtractor - Execute a query mapping a result set to an object with an implementation of - the IResultSetExtractor + the IResultSetExtractor interface. QueryWithResultSetExtractorDelegate - Same as QueryWithResultSetExtractor but using a - ResultSetExtractorDelegate to perform + ResultSetExtractorDelegate to perform result set mapping. QueryWithRowCallback - Execute a query calling an implementation of - IRowCallback for each row in the + IRowCallback for each row in the result set. QueryWithRowCallbackDelegate - Same as QueryWithRowCallback but calling a - RowCallbackDelegate for each + RowCallbackDelegate for each row. QueryWithRowMapper - Execute a query mapping a result set on a row by row basis with an implementation of - the IRowMapper interface. + the IRowMapper interface. QueryWithRowMapperDelegate - Same as QueryWithRowMapper but using a - RowMapperDelegate to perform result + RowMapperDelegate to perform result set row to object mapping. @@ -735,7 +752,7 @@ public interface ICommandCallback QueryForObject - Execute a query mapping the result set to an object using a - IRowMapper. Exception is thrown if + IRowMapper. Exception is thrown if the query does not return exactly one object. @@ -748,7 +765,7 @@ public interface ICommandCallback QueryWithCommandCreator - Execute a query with a callback to - IDbCommandCreator to create a + IDbCommandCreator to create a IDbCommand object and using either a IRowMapper or IResultSetExtractor to map the result set to an object. One variation lets multiple result set 'processors' be specified to act @@ -898,7 +915,7 @@ public interface ICommandCallback type-safety. - + Quick Guide to AdoTemplate Properties AdoTemplate has the following properties that you can @@ -907,39 +924,39 @@ public interface ICommandCallback LazyInit - Indicates if the - IAdoExceptionTranslator should be created on + IAdoExceptionTranslator should be created on first encounter of an exception from the data provider or when - AdoTemplate is created. Default is true, i.e. + AdoTemplate is created. Default is true, i.e. to lazily instantiate. ExceptionTranslator - Gets or sets the - implementation of IAdoExceptionTranslator to + implementation of IAdoExceptionTranslator to use. If no custom translator is provided, a default - ErrorCodeExceptionTranslator is used. + ErrorCodeExceptionTranslator is used. DbProvider - Gets or sets the - IDbProvider instance to use. + IDbProvider instance to use. DataReaderWrapperType - Gets or set the System.Type to use to create an instance of - IDataReaderWrapper for the purpose of + IDataReaderWrapper for the purpose of providing extended mapping functionality. Spring provides an implementation to use as the basis for a mapping strategy that will - map DBNull values to default values based on - the standard IDataReader interface. See the + map DBNull values to default values based on + the standard IDataReader interface. See the section custom IDataReader implementations for more information. CommandTimeout - Gets or sets the command - timeout for IDbCommands that this AdoTemplate + timeout for IDbCommands that this AdoTemplate executes. Default is 0, indicating to use the database provider's default. @@ -947,11 +964,11 @@ public interface ICommandCallback - + Transaction Management The AdoTemplate is used in conjunction with an implementation of a - IPlatformTransactionManager, which is Spring's + IPlatformTransactionManager, which is Spring's portable transaction management API. This section gives a brief overview of the transaction managers you can use with AdoTemplate and the details of how you can retrieve the connection/transaction ADO.NET objects that @@ -962,10 +979,10 @@ public interface ICommandCallback To use local transactions, those with only one transactional resource (i.e. the database) you will typically use - AdoPlatformTransactionManager. If you need to mix + AdoPlatformTransactionManager. If you need to mix Hibernate and ADO.NET data access operations within the same local transaction you should use - HibernatePlatformTransaction manager which is + HibernatePlatformTransaction manager which is described more in the section on ORM transaction management. @@ -977,7 +994,7 @@ public interface ICommandCallback some integration with other data access APIs. The can be done using the utility class ConnectionUtils as shown below. - IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient"); + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient"); ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(dbProvider); @@ -990,13 +1007,13 @@ command.Transaction = connectionTxPairToUse.Transaction; conjunction with Spring's transaction management features. If you are using - ServiceDomainPlatformTransactionManager or - TxScopePlatformTransactionManager then you can + ServiceDomainPlatformTransactionManager or + TxScopePlatformTransactionManager then you can retrieve the currently executing transaction object via the standard .NET APIs. - + Exception Translation AdoTemplate's methods throw exceptions within a Data Access Object @@ -1009,7 +1026,7 @@ command.Transaction = connectionTxPairToUse.Transaction; diagnose the issue. - + Parameter Management A fair amount of the code in ADO.NET applications is related to the @@ -1020,19 +1037,19 @@ command.Transaction = connectionTxPairToUse.Transaction; collection. Spring provides two ways to make this mundane task easier and more portable across providers. - + IDbParametersBuilder Instead of creating a parameter on one line of code, then setting its type on another and size on another, a builder and parameter - interface, IDbParametersBuilder and - IDbParameter respectfully, are provided + interface, IDbParametersBuilder and + IDbParameter respectfully, are provided so that this declaration process can be condensed. The IDbParameter support chaining calls to its methods, in effect a simple language-constrained domain specific language, to be fancy about it. Here is an example of it in use. - IDbParametersBuilder builder = CreateDbParametersBuilder(); + IDbParametersBuilder builder = CreateDbParametersBuilder(); builder.Create().Name("Country").Type(DbType.String).Size(15).Value(country); builder.Create().Name("City").Type(DbType.String).Size(15).Value(city); @@ -1042,8 +1059,8 @@ builder.Create().Name("City").Type(DbType.String).Size(15).Value(city); IDbParameters parameters = builder.GetParameters(); - Please note that IDbParameters and - IDbParameter are not part of the BCL, but part of + Please note that IDbParameters and + IDbParameter are not part of the BCL, but part of the Spring.Data.Common namespace. The IDbParameters collection is a frequent argument to the overloaded methods of AdoTemplate. @@ -1063,7 +1080,7 @@ IDbParameters parameters = builder.GetParameters(); database providers just by a change in configuration files. - + IDbParameters This class is similar to the parameter collection class you find @@ -1109,7 +1126,7 @@ IDbParameters parameters = builder.GetParameters(); Here a simple usage example - // inside method has has local variable country and city... + // inside method has has local variable country and city... IDbParameters parameters = CreateDbParameters(); parameters.AddWithValue("Country", country).DbType = DbType.String; @@ -1125,12 +1142,12 @@ parameters.Add("City", DbType.String).Value = city; - + Custom IDataReader implementations - The passed in implementation of IDataReader + The passed in implementation of IDataReader can be customized. This lets you add a strategy for handling null values - to the standard methods in the IDataReader + to the standard methods in the IDataReader interface or to provide sub-interface of IDataReader that contains extended functionality, for example support for default values. In callback code, i.e. IRowMapper and associated delegate, you would downcast @@ -1138,21 +1155,21 @@ parameters.Add("City", DbType.String).Value = city; Spring provides a class to map DBNull values to default values. When reading from a IDataReader there is often the need to - map DBNull values to some default values, i.e. null + map DBNull values to some default values, i.e. null or say a magic number such as -1. This is usually done via a ternary operator which decreases readability and also increases the likelihood of mistakes. Spring provides an - IDataReaderWrapper interface (which - inherits from the standard IDataReader) so + IDataReaderWrapper interface (which + inherits from the standard IDataReader) so that you can provide your own implementation of a IDataReader that will perform DBNull mapping for you in a consistent and non invasive manner to your result set reading code. A default implementation, - NullMappingDataReader is provided which you can + NullMappingDataReader is provided which you can subclass to customize or simply implement the - IDataReaderWrapper interface directly. This + IDataReaderWrapper interface directly. This interface is shown below - public interface IDataReaderWrapper : IDataReader + public interface IDataReaderWrapper : IDataReader { IDataReader WrappedReader { @@ -1163,28 +1180,28 @@ parameters.Add("City", DbType.String).Value = city; } All of AdoTemplates callback interfaces/delegates that have an - IDataReader as an argument are wrapped with - a IDataReaderWrapper if the AdoTemplate has + IDataReader as an argument are wrapped with + a IDataReaderWrapper if the AdoTemplate has been configured with one via its DataReaderWrapperType property. Your implementation should support a zero-arg constructor. Frequently you will use a common mapper for DBNull across your - application so only one instance of AdoTemplate and - IDataReaderWrapper in required. If you need to use + application so only one instance of AdoTemplate and + IDataReaderWrapper in required. If you need to use multiple null mapping strategies you will need to create multiple - instances of AdoTemplate and configure them + instances of AdoTemplate and configure them appropriately in the DAO objects. - + Basic data access operations The 'ExecuteNonQuery' and 'ExecuteScalar' methods of - AdoTemplate have the same functionality as the same + AdoTemplate have the same functionality as the same named methods on the DbCommand object - + ExecuteNonQuery ExecuteNonQuery is used to perform create, update, and delete @@ -1193,7 +1210,7 @@ parameters.Add("City", DbType.String).Value = city; An example of using this method is shown below - public void CreateCredit(float creditAmount) + public void CreateCredit(float creditAmount) { AdoTemplate.ExecuteNonQuery(CommandType.Text, String.Format("insert into Credits(creditAmount) VALUES ({0})", @@ -1201,17 +1218,17 @@ parameters.Add("City", DbType.String).Value = city; } - + ExecuteScalar An example of using this method is shown below - int iCount = (int)adoTemplate.ExecuteScalar(CommandType.Text, "SELECT COUNT(*) FROM TestObjects"); + int iCount = (int)adoTemplate.ExecuteScalar(CommandType.Text, "SELECT COUNT(*) FROM TestObjects"); - + Queries and Lightweight Object Mapping A common ADO.NET development task is reading in a result set and @@ -1258,7 +1275,7 @@ parameters.Add("City", DbType.String).Value = city; The following sections describe in more detail how to use Spring's lightweight object mapping framework. - + ResultSetExtractor The ResultSetExtractor gives you control to iterate over the @@ -1272,7 +1289,7 @@ parameters.Add("City", DbType.String).Value = city; shown below for the generic version in the Spring.Data.Generic namespace - public interface IResultSetExtractor<T> + public interface IResultSetExtractor<T> { T ExtractData(IDataReader reader); } @@ -1282,7 +1299,7 @@ public delegate T ResultSetExtractorDelegate<T>(IDataReader reader); The definition for the non-generic version is shown below - public interface IResultSetExtractor + public interface IResultSetExtractor { object ExtractData(IDataReader reader); } @@ -1295,7 +1312,7 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); - public virtual IList<string> GetCustomerNameByCountryAndCityWithParamsBuilder(string country, string city) + public virtual IList<string> GetCustomerNameByCountryAndCityWithParamsBuilder(string country, string city) { IDbParametersBuilder builder = CreateDbParametersBuilder(); @@ -1311,7 +1328,7 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader);The implementation of the ResultSetExtractor is shown below. - internal class CustomerNameResultSetExtractor<T> : IResultSetExtractor<T> where T : IList<string>, new() + internal class CustomerNameResultSetExtractor<T> : IResultSetExtractor<T> where T : IList<string>, new() { public T ExtractData(IDataReader reader) @@ -1336,14 +1353,14 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); - + RowCallback The RowCallback is usually a stateful object itself or populates another stateful object that is accessible to the calling code. Here is a sample take from the Data QuickStart - public class RowCallbackDao : AdoDaoSupport + public class RowCallbackDao : AdoDaoSupport { private string cmdText = "select ContactName, PostalCode from Customers"; @@ -1363,7 +1380,7 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); - internal class PostalCodeRowCallback : IRowCallback + internal class PostalCodeRowCallback : IRowCallback { private IDictionary<string, IList<string>> postalCodeMultimap = new Dictionary<string, IList<string>>(); @@ -1391,7 +1408,7 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); - + RowMapper The RowMapper lets you focus on just the logic to map a row of @@ -1400,7 +1417,7 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); - public class RowMapperDao : AdoDaoSupport + public class RowMapperDao : AdoDaoSupport { private string cmdText = "select Address, City, CompanyName, ContactName, " + "ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " + @@ -1416,7 +1433,7 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader);where the implementation of the RowMapper is - public class CustomerRowMapper<T> : IRowMapper<T> where T : Customer, new() + public class CustomerRowMapper<T> : IRowMapper<T> where T : Customer, new() { public T MapRow(IDataReader dataReader, int rowNum) { @@ -1440,7 +1457,7 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); - public virtual IList<Customer> GetCustomersWithDelegate() + public virtual IList<Customer> GetCustomersWithDelegate() { return AdoTemplate.QueryWithRowMapperDelegate<Customer>(CommandType.Text, cmdText, delegate(IDataReader dataReader, int rowNum) @@ -1462,7 +1479,7 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); - + Query for a single object The QueryForObject method is used when you expect there to be @@ -1470,7 +1487,7 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); - public class QueryForObjectDao : AdoDaoSupport + public class QueryForObjectDao : AdoDaoSupport { private string cmdText = "select Address, City, CompanyName, ContactName, " + "ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " + @@ -1485,12 +1502,12 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); - + Query using a CommandCreator There is a family of overloaded methods that allows you to encapsulate and reuse a particular configuration of a - IDbCommand object. These methods also allow for + IDbCommand object. These methods also allow for access to returned out parameters as well as a method that allows processing of multiple result sets. These methods are used internally to support the classes in the Spring.Data.Objects @@ -1539,7 +1556,7 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader);The IDbCommandCreator callback interface is shown below - public interface IDbCommandCreator + public interface IDbCommandCreator { IDbCommand CreateDbCommand(); } @@ -1548,9 +1565,9 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); To process multiple result sets specify a list of named result set - processors,( i.e. IResultSetExtractor, - IRowCallback, or IRowMapper). - This method is shown below + processors,( i.e. IResultSetExtractor, + IRowCallback, or IRowMapper). + This method is shown below @@ -1560,11 +1577,11 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); The list must contain objects of the type - Spring.Data.Support.NamedResultSetProcessor. This + Spring.Data.Support.NamedResultSetProcessor. This is the class responsible for associating a name with a result set processor. The constructors are listed below. - public class NamedResultSetProcessor { + public class NamedResultSetProcessor { public NamedResultSetProcessor(string name, IRowMapper rowMapper) { ... } @@ -1651,7 +1668,7 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); - + DataTable and DataSet AdoTemplate contains several 'families' of methods to help remove @@ -1677,22 +1694,22 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader); - Where IDataAdapterCallback is defined + Where IDataAdapterCallback is defined as - public interface IDataAdapterCallback + public interface IDataAdapterCallback { object DoInDataAdapter(IDbDataAdapter dataAdapter); } - The passed in IDbDataAdapter will have its + The passed in IDbDataAdapter will have its SelectCommand property created and set with its Connection and Transaction values based on the calling transaction context. The return value is the result of processing or null. There are type-safe versions of this method in - Spring.Data.Generic.AdoTemplate + Spring.Data.Generic.AdoTemplate @@ -1711,20 +1728,20 @@ public delegate object ResultSetExtractorDelegate(IDataReader reader);Where IDataAdapterCallback<T> and DataAdapterDelegate<T> are defined as - public interface IDataAdapterCallback<T> + public interface IDataAdapterCallback<T> { T DoInDataAdapter(IDbDataAdapter dataAdapter); } public delegate T DataAdapterDelegate<T>(IDbDataAdapter dataAdapter); - + DataTables DataTable operations are available on the class - Spring.Data.Core.AdoTemplate. If you are using + Spring.Data.Core.AdoTemplate. If you are using the generic version, - Spring.Data.Generic.AdoTemplate, you can access + Spring.Data.Generic.AdoTemplate, you can access these methods through the property ClassicAdoTemplate, which returns the non-generic version of AdoTemplate. DataTable operations available fall into the @@ -1764,13 +1781,13 @@ public delegate T DataAdapterDelegate<T>(IDbDataAdapter dataAdapter); - + DataSets DataSet operations are available on the class - Spring.Data.Core.AdoTemplate. If you are using + Spring.Data.Core.AdoTemplate. If you are using the generic version, - Spring.Data.Generic.AdoTemplate, you can access + Spring.Data.Generic.AdoTemplate, you can access these methods through the property ClassicAdoTemplate, which returns the non-generic version of AdoTemplate. DataSet operations available fall into the @@ -1813,7 +1830,7 @@ public delegate T DataAdapterDelegate<T>(IDbDataAdapter dataAdapter); - public class DataSetDemo : AdoDaoSupport + public class DataSetDemo : AdoDaoSupport { private string selectAll = @"select Address, City, CompanyName, ContactName, " + "ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " + @@ -1857,7 +1874,7 @@ public delegate T DataAdapterDelegate<T>(IDbDataAdapter dataAdapter); - public class DataSetDemo : AdoDaoSupport + public class DataSetDemo : AdoDaoSupport { private string selectAll = @"select Address, City, CompanyName, ContactName, " + "ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " + @@ -1925,7 +1942,7 @@ public delegate T DataAdapterDelegate<T>(IDbDataAdapter dataAdapter); - + TableAdapters and participation in transactional context Typed DataSets need to have commands in their internal DataAdapters @@ -1939,12 +1956,12 @@ public delegate T DataAdapterDelegate<T>(IDbDataAdapter dataAdapter);Spring.Data.Support.TypedDataSetUtils and is named + Spring.Data.Support.TypedDataSetUtils and is named ApplyConnectionAndTx. Here is sample usage of a DAO method that uses a VS.NET 2005 generated typed dataset for a PrintGroupMapping table. - public PrintGroupMappingDataSet FindAll() + public PrintGroupMappingDataSet FindAll() { PrintGroupMappingTableAdapter adapter = new PrintGroupMappingTableAdapter(); @@ -1971,7 +1988,7 @@ public delegate T DataAdapterDelegate<T>(IDbDataAdapter dataAdapter); - public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbCommand sourceCommand) + public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbCommand sourceCommand) public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider dbProvider) @@ -1987,7 +2004,7 @@ public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider techniques or via a service locator style lookup. - + Database operations as Objects The Spring.Data.Objects and Spring.Data.Objects.Generic @@ -2003,9 +2020,9 @@ public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider amongst some of the Spring developers that the various RDBMS operation classes described below (with the exception of the StoredProcedure class) can often be - replaced with straight AdoTemplate calls... + replaced with straight AdoTemplate calls... often it is simpler to use and plain easier to read a DAO method that - simply calls a method on a AdoTemplate direct + simply calls a method on a AdoTemplate direct (as opposed to encapsulating a query as a full-blown class). It must be stressed however that this is just a @@ -2014,29 +2031,29 @@ public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider continue using these classes. - + AdoQuery - AdoQuery is a reusable, threadsafe class + AdoQuery is a reusable, threadsafe class that encapsulates an SQL query. Subclasses must implement the NewRowMapper(..) method to provide a - IRowMapper instance that can create one + IRowMapper instance that can create one object per row obtained from iterating over the - IDataReader that is created during the - execution of the query. The AdoQuery class is - rarely used directly since the MappingAdoQuery + IDataReader that is created during the + execution of the query. The AdoQuery class is + rarely used directly since the MappingAdoQuery subclass provides a much more convenient implementation for mapping rows to .NET classes. Another implementations that extends - AdoQuery is - MappingadoQueryWithParameters (See SDK docs for + AdoQuery is + MappingadoQueryWithParameters (See SDK docs for details). - The AdoNonQuery class encapsulates an + The AdoNonQuery class encapsulates an IDbCommand 's ExecuteNonQuery method functionality. Like the - AdoQuery object, an - AdoNonQuery object is reusable, and like all - AdoOperation classes, an - AdoNonQuery can have parameters and is defined in + AdoQuery object, an + AdoNonQuery object is reusable, and like all + AdoOperation classes, an + AdoNonQuery can have parameters and is defined in SQL. This class provides two execute methods @@ -2059,7 +2076,7 @@ public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider statement for a 'TestObject' (consisting only name and age columns) is shown below - public class CreateTestObjectNonQuery : AdoNonQuery + public class CreateTestObjectNonQuery : AdoNonQuery { private static string sql = "insert into TestObjects(Age,Name) values (@Age,@Name)"; @@ -2078,18 +2095,18 @@ public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider } - + MappingAdoQuery - MappingAdoQuery is a reusable query in + MappingAdoQuery is a reusable query in which concrete subclasses must implement the abstract MapRow(..) method to convert each row of the - supplied IDataReader into an object. Find + supplied IDataReader into an object. Find below a brief example of a custom query that maps the data from a - relation to an instance of the Customer + relation to an instance of the Customer class. - public class TestObjectQuery : MappingAdoQuery + public class TestObjectQuery : MappingAdoQuery { private static string sql = "select TestObjectNo, Age, Name from TestObjects"; @@ -2110,15 +2127,15 @@ public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider } - + AdoNonQuery - The AdoNonQuery class encapsulates an + The AdoNonQuery class encapsulates an IDbCommand 's ExecuteNonQuery method functionality. Like the - AdoQuery object, an - AdoNonQuery object is reusable, and like all - AdoOperation classes, an - AdoNonQuery can have parameters and is defined in + AdoQuery object, an + AdoNonQuery object is reusable, and like all + AdoOperation classes, an + AdoNonQuery can have parameters and is defined in SQL. This class provides two execute methods @@ -2137,7 +2154,7 @@ public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider to add a custom update method) it can easily be parameterized by setting SQL and declaring parameters. - public class CreateTestObjectNonQuery : AdoNonQuery + public class CreateTestObjectNonQuery : AdoNonQuery { private static string sql = "insert into TestObjects(Age,Name) values (@Age,@Name)"; @@ -2156,8 +2173,8 @@ public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider } - - Stored Procedure + + Stored Procedure The StoredProcedure class is designed to make it as simple as possible to call a stored procedure. It takes advantage of metadata @@ -2206,7 +2223,7 @@ public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider Each of these methods returns an - IDictionary that contains the output parameters + IDictionary that contains the output parameters and/or any results from Spring's object mapping framework. The arguments to these methods can be a variable length argument list, in which case the order must match the parameter order of the stored procedure. If the @@ -2218,18 +2235,18 @@ public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider programmatically by adding to the parameter collection exposed by the property DeclaredParameters. For each result sets that is returned by the stored procedures you can registering either an - IResultSetExtractor, - IRowCallback, or - IRowMapper by name, which is used later to + IResultSetExtractor, + IRowCallback, or + IRowMapper by name, which is used later to extract the mapped results from the returned - IDictionary. + IDictionary. Lets take a look at an example. The following stored procedure class will call the CustOrdersDetail stored procedure in the Northwind database, passing in the OrderID as a stored procedure argument and returning a collection of OrderDetails business objects. - public class CustOrdersDetailStoredProc : StoredProcedure + public class CustOrdersDetailStoredProc : StoredProcedure { private static string procedureName = "CustOrdersDetail"; @@ -2262,15 +2279,15 @@ public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider DeriveParameters(). - The StoredProcedure class is threadsafe + The StoredProcedure class is threadsafe once 'compiled', an act which is usually done in the constructor. This sets up the cache of database parameters that can be used on each call to Query or QueryByNamedParam. The implementation of - IRowMapper that is used to extract the business + IRowMapper that is used to extract the business objects is 'registered' with the class and then later retrieved by name as a fictional output parameter. You may also register - IRowCallback and - IResultSetExtractor callback interfaces via the + IRowCallback and + IResultSetExtractor callback interfaces via the AddRowCallback and AddResultSetExtractor methods. @@ -2279,7 +2296,7 @@ public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider type parameters that will be used to process result sets returned from the stored procedure. An example is shown below - public class CustOrdersDetailStoredProc : StoredProcedure + public class CustOrdersDetailStoredProc : StoredProcedure { private static string procedureName = "CustOrdersDetail"; diff --git a/doc/reference/src/ajax.xml b/doc/reference/src/ajax.xml index 71ee9a06..76e0e16c 100644 --- a/doc/reference/src/ajax.xml +++ b/doc/reference/src/ajax.xml @@ -1,7 +1,7 @@ - + ASP.NET AJAX - + Introduction Spring's ASP.NET AJAX integration allows for a plain .NET object @@ -29,40 +29,40 @@ JavaScript. - + Web Services Spring.NET, and particularly Spring.Web, improved support for web services in .NET with the - WebServiceExporter. Exporting of an ordinary plain + WebServiceExporter. Exporting of an ordinary plain .NET object as a web service is achieved by registering a custom - implementation of the WebServiceHandlerFactory + implementation of the WebServiceHandlerFactory class as the HTTP handler for *.asmx requests. Microsoft ASP.NET AJAX introduced a new HTTP handler - System.Web.Script.Services.ScriptHandlerFactory to + System.Web.Script.Services.ScriptHandlerFactory to allow a Web Service to be invoked from the browser by using JavaScript. Spring's integration allows for both Spring.Web and ASP.NET AJAX functionality to be used together by creating a new HTTP handler. - + Exposing Web Services - The WebServiceExporter combined with the + The WebServiceExporter combined with the new HTTP handler exposes PONOs as Web Services in your ASP.NET AJAX application. In order for a Web service to be accessed from script, the - WebServiceExporter should decorate the Web - Service class with the ScriptServiceAttribute. + WebServiceExporter should decorate the Web + Service class with the ScriptServiceAttribute. The code below is taken from the sample application Spring.Web.Extensions.Sample, aka the 'AJAX' shortcut in the - installation. : + installation. : <object id="ContactWebService" type="Spring.Web.Services.WebServiceExporter, Spring.Web"> <property name="TargetName" value="ContactService"/> <property name="Namespace" value="http://Spring.Examples.Atlas/ContactService"/> @@ -77,15 +77,17 @@ All that one needs to do in order to use the - WebServiceExporter is: + WebServiceExporter is: 1. Configure the Web.config file of your ASP.NET AJAX - application as a Spring.Web application. + application as a Spring.Web application. + <sectionGroup name="spring"> <section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/> </sectionGroup> - + + <spring> <context> <resource uri="~/Spring.config"/> @@ -96,7 +98,7 @@ 2. Register the HTTP handler and the Spring HttpModule under the system.web section. - + <httpHandlers> <remove verb="*" path="*.asmx"/> <add verb="*" path="*.asmx" validate="false" type="Spring.Web.Script.Services.ScriptHandlerFactory, Spring.Web.Extensions"/> @@ -113,7 +115,7 @@ 3. Register the HTTP handler and the Spring HttpModule under system.webServer section. - + <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"/> @@ -133,14 +135,14 @@ this integration. - + Calling Web Services by using JavaScript A proxy class is generated for each Web Service. Calls to Web Services methods are made by using this proxy class. When using the - WebServiceExporter, the name of the proxy class - is equal to the WebServiceExporter's id. - + WebServiceExporter, the name of the proxy class + is equal to the WebServiceExporter's id. + // This function calls the Contact Web service method // passing simple type parameters and the callback function function GetEmails(prefix, count) diff --git a/doc/reference/src/aop-aspect-library.xml b/doc/reference/src/aop-aspect-library.xml index 845addff..a7d9a9b2 100644 --- a/doc/reference/src/aop-aspect-library.xml +++ b/doc/reference/src/aop-aspect-library.xml @@ -1,8 +1,25 @@ - + + Aspect Library - + Introduction Spring provides several aspects in the distribution. The most @@ -15,7 +32,7 @@ release. - + Caching Caching the return value of a method or the value of a method @@ -34,12 +51,12 @@ functionality and its configuration. The base cache interface that any cache implementation should - implement is Spring.Caching.ICache located in - Spring.Core. Two implementations are provided, - Spring.Caching.AspNetCache located in - Spring.Web which stores cache entries within an + implement is Spring.Caching.ICache located in + Spring.Core. Two implementations are provided, + Spring.Caching.AspNetCache located in + Spring.Web which stores cache entries within an ASP.NET cache and a simple implementation, - Spring.Caching.NonExpiringCache that stores cache + Spring.Caching.NonExpiringCache 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 ICache @@ -59,18 +76,18 @@ - CacheResult - used to cache the return - value + CacheResult - used to cache the return + value - CacheResultItems - used when returning a collection as - a return value + CacheResultItems - used when returning a collection as + a return value - CacheParameter - used to cache a method - parameter + CacheParameter - used to cache a method + parameter @@ -79,9 +96,9 @@ - Each CacheResult, - CacheResultItems, and - CacheParameter attributes define the following + Each CacheResult, + CacheResultItems, and + CacheParameter attributes define the following properties. @@ -108,13 +125,13 @@ - The InvalidateCache attribute has properties + The InvalidateCache attribute has properties for the CacheName, the Key as well as the Condition, with the same meanings as listed previously. - Each ICache implementation will have + Each ICache implementation will have properties that are specific to a caching technology. In the case of - AspNetCache, the two important properties to + AspNetCache, the two important properties to configure are: @@ -166,12 +183,11 @@ sample application of the AirportDao implementation that implements an interface with the method GetAirport(long id). - [CacheResult("AspNetCache", "'Airport.Id=' + #id", TimeToLive = "0:1:0")] - public Airport GetAirport(long id) - { - // implementation not shown... - } - + [CacheResult("AspNetCache", "'Airport.Id=' + #id", TimeToLive = "0:1:0")] +public Airport GetAirport(long id) +{ + // implementation not shown... +} The first parameter is the cache name. The second string parameter is the cache key and is a string expression that incorporates the argument @@ -186,35 +202,35 @@ The configuration to enable the caching aspect is shown below - <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="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> + + + + + *Dao + + + + + CacheAspect + + +]]> - in this example an ObjectNameAutoProxyCreator + in this example an ObjectNameAutoProxyCreator 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. - + Exception Handling In some cases existing code can be easily adopted to a simple error @@ -252,13 +268,13 @@ could be referred to as a Domain Specific Language (DSL). Here is a simple example, which should hopefully be self explanatory. - <object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop"> - <property name="exceptionHandlers"> - <list> - <value>on exception name ArithmeticException wrap System.InvalidOperationException</value> - </list> - </property> -</object>What this is instructing the advice to do is + + + + on exception name ArithmeticException wrap System.InvalidOperationException + + +]]>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 @@ -283,7 +299,7 @@ on exception name ArithmeticException replace System.InvalidOperationException ' on exception name ArithmeticException translate new System.InvalidOperationException('My Message, Method Name ' + #method.Name, #e)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 expression language for more + on the expression language 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 @@ -340,7 +356,7 @@ on exception name ArithmeticException return 12 action, i.e. log(Debug,"LoggerName"). Multiple exception handling statements can be specified within the - <list> shown above. The processing flow is on exception, the name of + 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 @@ -383,17 +399,17 @@ on exception name ArithmeticException return 12 for example setting the logging level and pass the exception into the logging subsystem - <object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop"> - <property name="exceptionHandlers"> - <list> - <object type="Spring.Aspects.Exceptions.LogExceptionHandler"> - <property name="LogName" value="Cms.Session.ExceptionHandler" /> - <property name="ConstraintExpressionText" value="#e is T(System.Threading.ThreadAbortException)" /> - <property name="ActionExpressionText" value="#log.Fatal('Request Timeout occured', #e)" /> - </object> - </list> - </property> -</object> + + + + + + + + + + +]]> The configuration of the logger name, level, and weather or not to pass the thrown exception as the second argument to the log method will be @@ -455,12 +471,12 @@ on exception name ArithmeticException return 12 - + Logging 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, Common.Logging, that provides + logging library, Common.Logging, that provides portability across different logging libraries. There are a number of configuration options available, listed below @@ -492,21 +508,21 @@ on exception name ArithmeticException return 12 You declare the logging advice in IoC container with the following XML fragment. Alternatively, you can use the class - SimpleLoggingAdvice programatically. + SimpleLoggingAdvice programatically. - <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> + + +]]> The default values for LogUniqueIdentifier, LogExecutionTime, LogMethodArguments and LogReturnValue are false. The default separator @@ -526,8 +542,8 @@ on exception name ArithmeticException return 12 true target type and not the proxy type. To further extend the functionality of the - SimpleLoggingAdvice you can subclass - SimpleLoggingAdvice and override the methods + SimpleLoggingAdvice you can subclass + SimpleLoggingAdvice and override the methods @@ -551,19 +567,19 @@ on exception name ArithmeticException return 12 The default implementation to calculate a unique identifier is to use a GUID. You can alter this behavior by overriding the method string CreateUniqueIdentifier(). The - SimpleLoggingAdvice class inherits from - AbstractLoggingAdvice, which has the abstract + SimpleLoggingAdvice class inherits from + AbstractLoggingAdvice, which has the abstract method object InvokeUnderLog(IMethodInvocation invocation, ILog log) and you can also override the method ILog GetLoggerForInvocation(IMethodInvocation invocation) to customize the logger instance used for logging. Refer to the SDK documentation for more details on subclassing - AbstractLoggingAdvice. + AbstractLoggingAdvice. As an example of the Logging advice's output, adding the advice to the method - public int Bark(string message, int[] luckyNumbers) + public int Bark(string message, int[] luckyNumbers) { return 4; } @@ -586,7 +602,7 @@ Exiting Bark, 5d2bad47-62cd-435b-8de7-91f12b7f433e, 30453.125 ms, return=4 - + Retry When making a distributed call it is often a common requirement to @@ -657,11 +673,11 @@ on exception (#e is T(System.ArithmeticException)) retry 3x rate (1*#n + 0.5)

You declare the advice in IoC container with the following XML fragment. Alternatively, you can use the - RetryAdvice class programatically.
+ RetryAdvice class programatically. - <object name="exceptionHandlingAdvice" type="Spring.Aspects.RetryAdvice, Spring.Aop"> - <property name="retryExpression" value="on exception name ArithmeticException retry 3x delay 1s"/> -</object> + + +]]> Language Reference @@ -679,14 +695,14 @@ on exception (#e is T(System.ArithmeticException)) retry 3x rate (1*#n + 0.5)

- + Transactions The transaction aspect is more fully described in the section on transaction management. - + Parameter Validation Spring provides a UI-agnostic validation @@ -705,15 +721,15 @@ on exception (#e is T(System.ArithmeticException)) retry 3x rate (1*#n + 0.5)

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 - ParameterValidationAdvice is used in conjunction - with the Validated attribute to specify which + ParameterValidationAdvice is used in conjunction + with the Validated 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 SpringAir sample - application, you would apply the Validated + application, you would apply the Validated attribute to the method parameters as shown below.
- public FlightSuggestions SuggestFlights( [Validated("tripValidator")] Trip trip) + public FlightSuggestions SuggestFlights( [Validated("tripValidator")] Trip trip) { // unmodified implementation goes here } @@ -721,34 +737,34 @@ on exception (#e is T(System.ArithmeticException)) retry 3x rate (1*#n + 0.5)

The Validated 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 - Validated attribute is located in the namespace + Validated attribute is located in the namespace Spring.Validation of the Spring.Core assembly. The configuration of the advice is to simply define the an instance of the ParameterValidationAdvice class and apply the advice, for example based on object names using an - ObjectNameAutoProxyCreator, as shown below, + ObjectNameAutoProxyCreator, as shown below, - <object id="validationAdvice" 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>validationAdvice</value> - </list> - </property> - </object> + + + + bookingAgent + + + + + validationAdvice + + +]]> 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 - ValidationException is thrown and you can retrieve + ValidationException is thrown and you can retrieve errors information from its property ValidationErrors. See the SDK documentation for details.
diff --git a/doc/reference/src/aop-quickstart.xml b/doc/reference/src/aop-quickstart.xml index 90e107ae..dd7a6b11 100644 --- a/doc/reference/src/aop-quickstart.xml +++ b/doc/reference/src/aop-quickstart.xml @@ -1,8 +1,25 @@ - + + AOP Guide - + Introduction This is an introductory guide to Aspect Oriented Programming (AOP) @@ -37,13 +54,13 @@ linkend="springair" />). - + The basics This initial section introduces the basics of defining and then applying some simple advice. - + Applying advice Lets see (a very basic) example of using Spring.NET AOP. The @@ -59,27 +76,27 @@ terminology, an instance of the following class is going to be the advised object. - public interface ICommand + public interface ICommand +{ + object Execute(object context); +} + +public class ServiceCommand : ICommand +{ + public object Execute(object context) { - object Execute(object context); + Console.Out.WriteLine("Service implementation : [{0}]", context); + return null; } - - public class ServiceCommand : ICommand - { - public object Execute(object context) - { - Console.Out.WriteLine("Service implementation : [{0}]", context); - return null; - } - } +} Find below the advice that is going to be applied to the object Execute(object context) method of the - ServiceCommand class. As you can see, this is an + ServiceCommand class. As you can see, this is an example of around advice (see ). - public class ConsoleLoggingAroundAdvice : IMethodInterceptor + public class ConsoleLoggingAroundAdvice : IMethodInterceptor { public object Invoke(IMethodInvocation invocation) { @@ -129,16 +146,16 @@ So thus far we have three artifacts: an interface - (ICommand); an implementation of said interface - (ServiceCommand); and some (trivial) advice - (encapsulated by the ConsoleLoggingAroundAdvice + (ICommand); an implementation of said interface + (ServiceCommand); and some (trivial) advice + (encapsulated by the ConsoleLoggingAroundAdvice class). All that remains is to actually apply the - ConsoleLoggingAroundAdvice advice to the + ConsoleLoggingAroundAdvice advice to the invocation of the Execute() method of the - ServiceCommand class. Lets look at how to effect + ServiceCommand class. Lets look at how to effect this programmatically... - ProxyFactory factory = new ProxyFactory(new ServiceCommand()); + ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvice(new ConsoleLoggingAroundAdvice()); ICommand command = (ICommand) factory.GetProxy(); command.Execute("This is the argument"); @@ -152,23 +169,23 @@ The output shows that the advice (the Console.Out statements from the - ConsoleLoggingAroundAdvice was applied + ConsoleLoggingAroundAdvice was applied around the invocation of the advised method. So what is happening here? The fact that the preceding code used a - class called ProxyFactory may have clued you in. - The constructor for the ProxyFactory class took + class called ProxyFactory may have clued you in. + The constructor for the ProxyFactory class took as an argument the object that we wanted to advise (in this case, an - instance of the ServiceCommand class). We then - added some advice (a ConsoleLoggingAroundAdvice + instance of the ServiceCommand class). We then + added some advice (a ConsoleLoggingAroundAdvice instance) using the AddAdvice() method of the - ProxyFactory instance. We then called the + ProxyFactory instance. We then called the GetProxy() method of the - ProxyFactory instance which gave us a proxy... an + ProxyFactory instance which gave us a proxy... an (AOP) proxy that proxied the target object (the - ServiceCommand instance), and called the advice + ServiceCommand instance), and called the advice (a single instance of the - ConsoleLoggingAroundAdvice in this case). When we + ConsoleLoggingAroundAdvice in this case). When we invoked the Execute(object context) method of the proxy, the advice was 'applied' (executed), as can be seen from the attendant output. @@ -184,9 +201,9 @@ One thing to note here is that the AOP proxy that was returned from the call to the GetProxy() method of the - ProxyFactory instance was cast to the - ICommand interface that the - ServiceCommand target object implemented. This is + ProxyFactory instance was cast to the + ICommand interface that the + ServiceCommand target object implemented. This is very important... currently, Spring.NET's AOP implementation mandates the use of an interface for advised objects. In short, this means that in order for your classes to leverage Spring.NET's AOP support, those @@ -208,7 +225,7 @@ should also be added that this declarative style approach to Spring.NET AOP is preferred to the programmatic style. - <object id="consoleLoggingAroundAdvice" + <object id="consoleLoggingAroundAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingAroundAdvice"/> <object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject"> <property name="target"> @@ -222,50 +239,50 @@ </property> </object> - ICommand command = (ICommand) ctx["myServiceObject"]; + ICommand command = (ICommand) ctx["myServiceObject"]; command.Execute(); Some comments are warranted concerning the above XML configuration snippet. Firstly, note that the - ConsoleLoggingAroundAdvice is itself a plain + ConsoleLoggingAroundAdvice is itself a plain vanilla object, and is eligible for configuration just like any other class... if the advice itself needed to be injected with any dependencies, any such dependencies could be injected as normal. Secondly, notice that the object definition corresponding to the object that is retrieved from the IoC container is a - ProxyFactoryObject. The - ProxyFactoryObject class is an implementation of - the IFactoryObject interface; - IFactoryObject implementations are treated + ProxyFactoryObject. The + ProxyFactoryObject class is an implementation of + the IFactoryObject interface; + IFactoryObject implementations are treated specially by the Spring.NET IoC container... in this specific case, it - is not a reference to the ProxyFactoryObject + is not a reference to the ProxyFactoryObject instance itself that is returned, but rather the object that the - ProxyFactoryObject produces. In this case, it - will be an advised instance of the ServiceCommand + ProxyFactoryObject produces. In this case, it + will be an advised instance of the ServiceCommand class. Thirdly, notice that the target of the - ProxyFactoryObject is an instance of the - ServiceCommand class; this is the object that is + ProxyFactoryObject is an instance of the + ServiceCommand class; this is the object that is going to be advised (i.e. invocations of its methods are going to be intercepted). This object instance is defined as an inner object definition... this is the preferred idiom for using the - ProxyFactoryObject, as it means that other + ProxyFactoryObject, as it means that other objects cannot acquire a reference to the raw object, but rather only the advised object. Finally, notice that the advice that is to be applied to the target object is referred to by its object name in the list of the names - of interceptors for the ProxyFactoryObject's + of interceptors for the ProxyFactoryObject's interceptorNames property. In this particular case, there is only one instance of advice being applied... the - ConsoleLoggingAroundAdvice defined in an object + ConsoleLoggingAroundAdvice defined in an object definition of the same name. The reason for using a list of object names as opposed to references to the advice objects themselves is explained in the reference documentation... - '... if the ProxyFactoryObject's + '... if the ProxyFactoryObject's singleton property is set to false, it must be able to return independent proxy instances. If any of the advisors is itself a prototype, an independent instance would need to be returned, so it is @@ -273,12 +290,12 @@ context; holding a reference isn't sufficient.' - + Using Pointcuts - the basics The advice that was applied in the previous section was rather indiscriminate with regard to which methods on the advised object were - to be advised... the ConsoleLoggingAroundAdvice + to be advised... the ConsoleLoggingAroundAdvice simply intercepted all methods (that were part of an interface implementation) on the target object. @@ -292,22 +309,22 @@ The mechanism that Spring.NET AOP uses to discriminate about where advice is applied (i.e. which method invocations are intercepted) is - encapsulated by the IPointcut interface (see + encapsulated by the IPointcut interface (see ). Spring.NET provides many - out-of-the-box implementations of the IPointcut + out-of-the-box implementations of the IPointcut interface... the implementation that is used if none is explicitly supplied (as was the case with the first example) is the canonical - TruePointcut : as the name suggests, this + TruePointcut : as the name suggests, this pointcut always matches, and hence all methods that can be advised will be advised. So let's change the configuration of the advice such that it is only applied to methods that contain the letters 'Do'. We'll change the - ICommand interface (and it's attendant + ICommand interface (and it's attendant implementation) to accommodate this... - public interface ICommand + public interface ICommand { void Execute(); @@ -328,7 +345,7 @@ } Please note that the advice itself (encapsulated within the - ConsoleLoggingAroundAdvice class) does not need + ConsoleLoggingAroundAdvice class) does not need to change; we are changing where this advice is applied, and not the advice itself. @@ -336,7 +353,7 @@ fact that we only want methods that contain the letters 'Do' to be advised, looks like this... - ProxyFactory factory = new ProxyFactory(new ServiceCommand()); + ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvisor(new DefaultPointcutAdvisor( new SdkRegularExpressionMethodPointcut("Do"), new ConsoleLoggingAroundAdvice())); @@ -346,7 +363,7 @@ The result of executing the above snippet of code will look something like this... - Intercepted call : about to invoke next item in chain... + Intercepted call : about to invoke next item in chain... Service implementation... Intercepted call : returned @@ -356,7 +373,7 @@ the pertinent code snippet to invoke the Execute() method, like so... - ProxyFactory factory = new ProxyFactory(new ServiceCommand()); + ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvisor( new DefaultPointcutAdvisor( new SdkRegularExpressionMethodPointcut("Do"), @@ -376,7 +393,7 @@ XML configuration that accomplishes exactly the same thing as the previous programmatic configuration example can be seen below... - <object id="consoleLoggingAroundAdvice" + <object id="consoleLoggingAroundAdvice" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor"> <property name="pattern" value="Do"/> <property name="advice"> @@ -407,12 +424,12 @@ that match the pattern 'Do' (the pointcut). The pattern to match against is supplied as a simple string value to the pattern property of the - RegularExpressionMethodPointcutAdvisor + RegularExpressionMethodPointcutAdvisor class. - + Going deeper The first section should (hopefully) have demonstrated the basics of @@ -423,18 +440,18 @@ describes the various advice and pointcuts that are available for you to use (yes, there is more than one type of advice and pointcut). - + Other types of Advice The advice that was demonstrated and explained in the preceding section is what is termed 'around advice'. The name 'around advice' is used because the advice is applied around the target method invocation. In the - specific case of the ConsoleLoggingAroundAdvice + specific case of the ConsoleLoggingAroundAdvice advice that was defined previously, the target was made available to the - advice as an IMethodInvocation object... a call - was made to the Console class before the target - was invoked, and a call was made to the Console + advice as an IMethodInvocation object... a call + was made to the Console class before the target + was invoked, and a call was made to the Console class after the target method invocation was invoked. The advice surrounded the target, one could even say that the advice was totally 'around' the target... hence the name, 'around @@ -448,14 +465,14 @@ value. Sometimes you don't need all that power though. If we stick with - the example of the ConsoleLoggingAroundAdvice + the example of the ConsoleLoggingAroundAdvice advice, what if one just wants to log the fact that a method was called? In that case one doesn't need to do anything after the target method invocation is to be invoked, nor do you need access to the return value of the target method invocation. In fact, you only want to do something before the target is to be invoked (in this case, print out a message to the system - Console detailing the name of the method). In the + Console detailing the name of the method). In the tradition of good programming that says one should use only what one needs and no more, Spring.NET has another type of advice that one can use... if one only wants to do something before the @@ -463,7 +480,7 @@ call the Proceed() method? The most expedient solution simply is to use 'before advice'. - + Before advice 'before advice' is just that... it is @@ -479,13 +496,13 @@ advice' is just what you need. 'before advice' in Spring.NET is defined by - the IMethodBeforeAdvice interface in the + the IMethodBeforeAdvice interface in the Spring.Aop namespace. Lets just dive in with an example... we'll use the same scenario as before to keep things simple. Let's define the 'before advice' implementation first. - public class ConsoleLoggingBeforeAdvice : IMethodBeforeAdvice + public class ConsoleLoggingBeforeAdvice : IMethodBeforeAdvice { public void Before(MethodInfo method, object[] args, object target) { @@ -503,15 +520,15 @@ } Let's apply a single instance of the - ConsoleLoggingBeforeAdvice advice to the + ConsoleLoggingBeforeAdvice advice to the invocation of the Execute() method of the - ServiceCommand. What follows is programmatic + ServiceCommand. What follows is programmatic configuration; as you can see, its pretty much identical to the previous version... the only difference is that we're using our new 'before advice' (encapsulated as an instance of - the ConsoleLoggingBeforeAdvice class). + the ConsoleLoggingBeforeAdvice class). - ProxyFactory factory = new ProxyFactory(new ServiceCommand()); + ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvice(new ConsoleLoggingBeforeAdvice()); ICommand command = (ICommand) factory.GetProxy(); command.Execute(); @@ -529,7 +546,7 @@ advice', with 'before advice' there is no chance of forgetting to call the Proceed() method on the target, because one does not have access to the - IMethodInvocation (as is the case with + IMethodInvocation (as is the case with 'around advice')... similarly, you cannot forget to return the return value either. @@ -541,7 +558,7 @@ Here is the Spring.NET XML configuration for applying our 'before advice' declaratively... - <object id="beforeAdvice" + <object id="beforeAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingBeforeAdvice"/> <object id="myServiceObject" @@ -558,7 +575,7 @@ </object> - + After advice Just as 'before advice' defines advice that @@ -567,12 +584,12 @@ role="bold">after a target has been executed. 'after advice' in Spring.NET is defined by - the IAfterReturningAdvice interface in the + the IAfterReturningAdvice interface in the Spring.Aop namespace. Again, lets just fire on ahead with an example... again, we'll use the same scenario as before to keep things simple. - public class ConsoleLoggingAfterAdvice : IAfterReturningAdvice + public class ConsoleLoggingAfterAdvice : IAfterReturningAdvice { public void AfterReturning( object returnValue, MethodInfo method, object[] args, object target) @@ -592,17 +609,17 @@ } Let's apply a single instance of the - ConsoleLoggingAfterAdvice advice to the + ConsoleLoggingAfterAdvice advice to the invocation of the Execute() method of the - ServiceCommand. What follows is programmatic + ServiceCommand. What follows is programmatic configuration; as you can, its pretty much identical to the 'before advice' version (which in turn was pretty much identical to the original 'around advice' version)... the only real difference is that we're using our new 'after advice' (encapsulated as an instance of - the ConsoleLoggingAfterAdvice class). + the ConsoleLoggingAfterAdvice class). - ProxyFactory factory = new ProxyFactory(new ServiceCommand()); + ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvice(new ConsoleLoggingAfterAdvice()); ICommand command = (ICommand) factory.GetProxy(); command.Execute(); @@ -624,7 +641,7 @@ advice' there is no chance of forgetting to call the Proceed() method on the target, because just like 'before advice' you don't have access to the - IMethodInvocation... similarly, although you + IMethodInvocation... similarly, although you get access to the return value of the target, you cannot forget to return the return value either. You can however change the state of the return value, typically by setting some of its properties, or by @@ -652,7 +669,7 @@ Here is the Spring.NET XML configuration for applying the 'after advice' declaratively... - <object id="afterAdvice" + <object id="afterAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingAfterAdvice"/> <object id="myServiceObject" @@ -669,7 +686,7 @@ </object> - + Throws advice So far we've covered 'around advice', @@ -697,13 +714,13 @@ possible uses cases is of course endless. The 'throws advice' type in Spring.NET is - defined by the IThrowsAdvice interface in the + defined by the IThrowsAdvice interface in the Spring.Aop namespace... basically, one defines on one's 'throws advice' implementation class what types of exception are going to be handled. Lets take a quick look at - the IThrowsAdvice interface... + the IThrowsAdvice interface... - public interface IThrowsAdvice : IAdvice + public interface IThrowsAdvice : IAdvice { } @@ -714,7 +731,7 @@ point, so here is some simple Spring.NET style 'throws advice'... - public class ConsoleLoggingThrowsAdvice : IThrowsAdvice + public class ConsoleLoggingThrowsAdvice : IThrowsAdvice { public void AfterThrowing(Exception ex) { @@ -724,11 +741,11 @@ Lets also change the implementation of the Execute() method of the - ServiceCommand class such that it throws an + ServiceCommand class such that it throws an exception. This will allow the advice encapsulated by the above - ConsoleLoggingThrowsAdvice to kick in. + ConsoleLoggingThrowsAdvice to kick in. - public class ServiceCommand : ICommand + public class ServiceCommand : ICommand { public void Execute() { @@ -738,11 +755,11 @@ Let's programmatically apply the 'throws advice' (an instance of our - ConsoleLoggingThrowsAdvice) to the invocation + ConsoleLoggingThrowsAdvice) to the invocation of the Execute() method of the above - ServiceCommand class; to wit... + ServiceCommand class; to wit... - ProxyFactory factory = new ProxyFactory(new ServiceCommand()); + ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvice(new ConsoleLoggingThrowsAdvice()); ICommand command = (ICommand) factory.GetProxy(); command.Execute(); @@ -754,19 +771,19 @@ Attempted to perform an unauthorized operation. As can be seen from the output, the - ConsoleLoggingThrowsAdvice kicked in when the + ConsoleLoggingThrowsAdvice kicked in when the advised method invocation threw an exception. There are a number of things to note about the - ConsoleLoggingThrowsAdvice advice class, so + ConsoleLoggingThrowsAdvice advice class, so lets take them each in turn. In Spring.NET, 'throws advice' means that you have to define a class that implements the - IThrowsAdvice interface. Then, for each type of + IThrowsAdvice interface. Then, for each type of exception that your 'throws advice' is going to handle, you have to define a method with this signature... - void AfterThrowing(Exception ex) + void AfterThrowing(Exception ex) Basically, your exception handling method has to be named AfterThrowing. This name is important... your @@ -779,19 +796,19 @@ in the future). Your exception handling method must (at the very least) declare - a parameter that is an Exception type... this - parameter can be the root Exception class (as + a parameter that is an Exception type... this + parameter can be the root Exception class (as in the case of the above example), or it can be an - Exception subclass if you only want to handle + Exception subclass if you only want to handle certain types of exception. It is good practice to always make your - exception handling methods have an Exception + exception handling methods have an Exception parameter that is the most specialized - Exception type possible... i.e. if you are + Exception type possible... i.e. if you are applying 'throws advice' to a method that could - only ever throw ArgumentExceptions, then + only ever throw ArgumentExceptions, then declare the parameter of your exception handling method as... - void AfterThrowing(ArgumentException ex) + void AfterThrowing(ArgumentException ex) Note that your exception handling method can have any return type, but returning any value from a Spring.NET 'throws @@ -803,7 +820,7 @@ Finally, here is the Spring.NET XML configuration for applying the 'throws advice' declaratively... - <object id="throwsAdvice" + <object id="throwsAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingThrowsAdvice"/> <object id="myServiceObject" @@ -830,14 +847,14 @@ wrapped exception in the body of one's exception handling method. One can use this to implement some sort of exception translation or exception scrubbing policy, in which implementation specific - exceptions (such as SqlException or - OracleException exceptions being thrown by an + exceptions (such as SqlException or + OracleException exceptions being thrown by an advised data access object) get replaced with a business exception that has meaning to the service objects in one's business layer. A toy example of this type of 'throws advice' can be seen below. - public class DataAccessExceptionScrubbingThrowsAdvice : IThrowsAdvice + public class DataAccessExceptionScrubbingThrowsAdvice : IThrowsAdvice { public void AfterThrowing (SqlException ex) { @@ -862,11 +879,11 @@ reference documentation, which describes how to declare an exception handling method that gives one access to the above extra objects, and how to declare multiple exception handling methods on the same - IThrowsAdvice implementation class (see IThrowsAdvice implementation class (see ). - + Introductions (mixins) In a nutshell, introductions are all about adding new state and @@ -877,7 +894,7 @@ don't share the same inheritance hierarchy. - + Layering advice The examples shown so far have all demonstrated the application @@ -898,18 +915,18 @@ Please do consult the class definitions for the following previously defined advice types to see exactly what each advice type implementation does... we're going to be using single instances of the - ConsoleLoggingAroundAdvice, - ConsoleLoggingBeforeAdvice, - ConsoleLoggingAfterAdvice, and - ConsoleLoggingThrowsAdvice advice to advise a - single instance of the ServiceCommand + ConsoleLoggingAroundAdvice, + ConsoleLoggingBeforeAdvice, + ConsoleLoggingAfterAdvice, and + ConsoleLoggingThrowsAdvice advice to advise a + single instance of the ServiceCommand class. You can find the following listing and executable application in the AopQuickStart solution in the project Spring.AopQuickStart.Step1. - ProxyFactory factory = new ProxyFactory(new ServiceCommand()); + ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvice(new ConsoleLoggingBeforeAdvice()); factory.AddAdvice(new ConsoleLoggingAfterAdvice()); factory.AddAdvice(new ConsoleLoggingThrowsAdvice()); @@ -924,7 +941,7 @@ the AopQuickStart solution in the project Spring.AopQuickStart.Step2. - <object id="throwsAdvice" + <object id="throwsAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingThrowsAdvice"/> <object id="afterAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingAfterAdvice"/> @@ -950,7 +967,7 @@ </object> - + Configuring advice In case it is not immediately apparent, remember that advice is @@ -978,14 +995,14 @@ - + Using Attributes to define Pointcuts - + The Spring.NET AOP Cookbook The preceding treatment of Spring.NET AOP has (quite intentionally) @@ -993,7 +1010,7 @@ Spring.NET AOP... this section of the Spring.NET AOP guide contains a number of real world examples of the application of Spring.NET AOP. - + Caching This example illustrates one of the more common usages of AOP... @@ -1005,26 +1022,26 @@ it exists only in the database to satisfy referential integrity amongst the various relations in the database schema. An example of such static (and typically immutable) reference data would be a collection of - Country objects (comprising a country name and a + Country objects (comprising a country name and a code). What we would like to do is suck in the collection of - Country objects and then pin them in a cache. + Country objects and then pin them in a cache. This saves us having to hit the back end database again and again every time we need to reference a country in our application (for example, to populate dropdown controls in a Windows Forms desktop application). The Data Access Object (DAO) that will load the collection of - Country objects is called - AdoCountryDao (it is an implementation of the + Country objects is called + AdoCountryDao (it is an implementation of the data-access-technology agnostic DAO interface called - ICountryDao). The implementation of the - AdoCountryDao is quite simple, in that every time + ICountryDao). The implementation of the + AdoCountryDao is quite simple, in that every time the FindAllCountries instance method is called, an instance will query the database for an - IDataReader and hydrate zero or more - Country objects using the returned data. + IDataReader and hydrate zero or more + Country objects using the returned data. - public class AdoCountryDao : ICountryDao + public class AdoCountryDao : ICountryDao { public IList FindAllCountries () { @@ -1044,15 +1061,15 @@ The mechanism that this example is going to use to identify (or pick out) areas in our application that we would like to apply caching - to is a .NET Attribute. Spring.NET ships with a - number of useful custom .NET Attribute + to is a .NET Attribute. Spring.NET ships with a + number of useful custom .NET Attribute implementations, one of which is the cunningly named - CacheAttribute. In the specific case of this + CacheAttribute. In the specific case of this example, we are simply going to decorate the definition of the FindAllCountries instance method with the - CacheAttribute. + CacheAttribute. - public class AdoCountryDao : ICountryDao + public class AdoCountryDao : ICountryDao { [Cache] public IList FindAllCountries () @@ -1067,7 +1084,7 @@ applied using Spring.NET AOP (see ). - + Performance Monitoring This recipe show how easy it is to instrument the classes and @@ -1076,7 +1093,7 @@ counters to display and track the performance data. - + Retry Rules This final recipe describes a simple (but really quite useful) @@ -1087,7 +1104,7 @@ - + Spring.NET AOP Best Practices Spring.NET AOP is an 80% AOP solution, in that it only tries to diff --git a/doc/reference/src/aop.xml b/doc/reference/src/aop.xml index 4880569f..83234c43 100644 --- a/doc/reference/src/aop.xml +++ b/doc/reference/src/aop.xml @@ -1,8 +1,25 @@ - + + Aspect Oriented Programming with Spring.NET - + Introduction Aspect-Oriented Programming @@ -46,7 +63,7 @@ exploring how to use Spring's AOP functionality, head on over to . - + AOP concepts Let us begin by defining some central AOP concepts. These terms @@ -88,7 +105,7 @@ Introduction: Adding methods or fields to an advised class. Spring.NET allows you to introduce new interfaces to any advised object. For example, you could use an introduction to - make any object implement an IAuditable + make any object implement an IAuditable interface, to simplify the tracking of changes to an object's state. @@ -155,7 +172,7 @@ accomplish the same thing. Using the most specific advice type provides a simpler programming model with less potential for errors. For example, you don't need to invoke the proceed() method on the - IMethodInvocation used for around advice, and + IMethodInvocation used for around advice, and hence can't fail to invoke it. The pointcut concept is the key to AOP, distinguishing AOP from @@ -166,7 +183,7 @@ structural element of AOP. - + Spring.NET AOP capabilities Spring.NET AOP is implemented in pure C#. There is no need for a @@ -189,21 +206,21 @@ pointcut targeting it to specific joinpoints. Different advice types are - IMethodInterceptor (from the AOP Alliance + IMethodInterceptor (from the AOP Alliance interception API); and the advice interfaces defined in the Spring.Aop namespace. All advices must implement the - AopAlliance.Aop.IAdvice tag interface. Advices - supported out the box are IMethodInterceptor ; - IThrowsAdvice; - IBeforeAdvice; and - IAfterReturningAdvice. We'll discuss advice types + AopAlliance.Aop.IAdvice tag interface. Advices + supported out the box are IMethodInterceptor ; + IThrowsAdvice; + IBeforeAdvice; and + IAfterReturningAdvice. We'll discuss advice types in detail below. Spring.NET provides a .NET translation of the Java interfaces defined by the AOP Alliance. Around advice must implement the AOP Alliance - AopAlliance.Interceptr.IMethodInterceptor + AopAlliance.Interceptr.IMethodInterceptor interface. Whilst there is wide support for the AOP Alliance in Java, Spring.NET is currently the only .NET AOP framework that makes use of these interfaces. In the short term, this will provide a consistent @@ -223,7 +240,7 @@ managed by Spring.NET IoC. - + AOP Proxies in Spring.NET Spring.NET generates AOP proxies at runtime using classes from the @@ -272,49 +289,49 @@ - + Pointcut API in Spring.NET Let's look at how Spring.NET handles the crucial pointcut concept. - + Concepts Spring.NET's pointcut model enables pointcut reuse independent of advice types. It's possible to target different advice using the same pointcut. - The Spring.Aop.IPointcut interface is the + The Spring.Aop.IPointcut interface is the central interface, used to target advices to particular types and methods. The complete interface is shown below: - public interface IPointcut + public interface IPointcut { ITypeFilter TypeFilter { get; } IMethodMatcher MethodMatcher { get; } } - Splitting the IPointcut interface into two + Splitting the IPointcut interface into two parts allows reuse of type and method matching parts, and fine-grained composition operations (such as performing a "union" with another method matcher). - The ITypeFilter interface is used to + The ITypeFilter interface is used to restrict the pointcut to a given set of target classes. If the Matches() method always returns true, all target types will be matched: - public interface ITypeFilter + public interface ITypeFilter { bool Matches(Type type); } - The IMethodMatcher interface is normally + The IMethodMatcher interface is normally more important. The complete interface is shown below: - public interface IMethodMatcher + public interface IMethodMatcher { bool IsRuntime { get; } @@ -329,7 +346,7 @@ avoid the need for a test on every method invocation. If the 2-argument matches method returns true for a given method, and the IsRuntime property for the - IMethodMatcher returns true, the 3-argument + IMethodMatcher returns true, the 3-argument matches method will be invoked on every method invocation. This enables a pointcut to look at the arguments passed to the method invocation immediately before the target advice is to execute. @@ -344,7 +361,7 @@ AOP proxy is created. - + Operations on pointcuts Spring.NET supports operations on pointcuts: notably, @@ -362,14 +379,14 @@ namespace. - + Convenience pointcut implementations Spring.NET provides several convenient pointcut implementations. Some can be used out of the box; others are intended to be subclassed in application-specific pointcuts. - + Static pointcuts Static pointcuts are based on method and target class, and @@ -382,7 +399,7 @@ Let's consider some static pointcut implementations included with Spring.NET. - + Regular expression pointcuts @@ -392,7 +409,7 @@ One obvious way to specify static pointcuts is using regular expressions. Several AOP frameworks besides Spring.NET make this possible. The - Spring.Aop.Support.SdkRegularExpressionMethodPointcut + Spring.Aop.Support.SdkRegularExpressionMethodPointcut class is a generic regular expression pointcut, that uses the regular expression classes from the .NET BCL. @@ -411,7 +428,7 @@ - <object id="settersAndAbsquatulatePointcut" + <object id="settersAndAbsquatulatePointcut" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop"> <property name="patterns"> <list> @@ -424,17 +441,17 @@ As a convenience, Spring provides the - RegularExpressionMethodPointcutAdvisor class - that allows us to reference an IAdvice + RegularExpressionMethodPointcutAdvisor class + that allows us to reference an IAdvice instance as well as defining the pointcut rules (remember that an - IAdvice instance can be an interceptor, + IAdvice instance can be an interceptor, before advice, throws advice etc.) This simplifies wiring, as the one object serves as both pointcut and advisor, as shown below: - <object id="settersAndAbsquatulateAdvisor" + <object id="settersAndAbsquatulateAdvisor" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop"> <property name="advice"> <ref local="objectNameOfAopAllianceInterceptor"/> @@ -450,8 +467,8 @@ The - RegularExpressionMethodPointcutAdvisor class - can be used with any Advice type. + RegularExpressionMethodPointcutAdvisor class + can be used with any Advice type. If you only have one pattern you can use the property name @@ -463,16 +480,16 @@ and specifying a list. - You may also specify a Regex object + You may also specify a Regex object from the System.Text.RegularExpressions - namespace. The built in RegexConverter class + namespace. The built in RegexConverter class will perform the conversion. See for more information on Spring's build in type converters. The Regex object is created as any other object within the IoC container. Using an inner-object definition for the Regex object is a handy way to keep the definition close to the PointcutAdvisor declaration. Note that the - class SdkRegularExpressionMethodPointcut has + class SdkRegularExpressionMethodPointcut has a DefaultOptions property to set the regular expression options if they are not explicitly specified in the constructor. @@ -480,24 +497,24 @@ - + Attribute pointcuts Pointcuts can be specified by matching an attribute type that is associated with a method. Advice associated with this pointcut can then read the metadata associated with the attribute to configure itself. The class - AttributeMatchMethodPointcut provides this + AttributeMatchMethodPointcut provides this functionality. Sample usage that will match all methods that have the attribute - Spring.Attributes.CacheAttribute is shown - below. <object id="cachePointcut" type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop"> + Spring.Attributes.CacheAttribute is shown + below. <object id="cachePointcut" type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop"> <property name="Attribute" value="Spring.Attributes.CacheAttribute, Spring.Core"/> </object> This can be used with a - DefaultPointcutAdvisor as shown - below<object id="cacheAspect" type="Spring.Aop.Support.DefaultPointcutAdvisor, Spring.Aop"> + DefaultPointcutAdvisor as shown + below<object id="cacheAspect" type="Spring.Aop.Support.DefaultPointcutAdvisor, Spring.Aop"> <property name="Pointcut"> <object type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop"> <property name="Attribute" value="Spring.Attributes.CacheAttribute, Spring.Core"/> @@ -505,16 +522,16 @@ </property> <property name="Advice" ref="aspNetCacheAdvice"/> </object> where aspNetCacheAdvice is an implementation - of an IMethodInterceptor that caches method + of an IMethodInterceptor that caches method return values. See the SDK docs for - Spring.Aop.Advice.CacheAdvice for more + Spring.Aop.Advice.CacheAdvice for more information on this particular advice. As a convenience the class - AttributeMatchMethodPointcutAdvisor is + AttributeMatchMethodPointcutAdvisor is provided to defining an attribute based Advisor as a somewhat shorter alternative to using the generic DefaultPointcutAdvisor. An - example is shown below.<object id="AspNetCacheAdvice" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop"> + example is shown below.<object id="AspNetCacheAdvice" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop"> <property name="advice"> <object type="Aspect.AspNetCacheAdvice, Aspect"/> </property> @@ -523,7 +540,7 @@ - + Dynamic Pointcuts Dynamic pointcuts are costlier to evaluate than static @@ -590,7 +607,7 @@ it will not match a control flow pointcut for the method "GetAge". - public int GetAge(IPerson person) + public int GetAge(IPerson person) { return person.GetAge(); } @@ -598,7 +615,7 @@ However, applying the attributes as shown below will prevent the method from being inlined even in a release build. - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoInlining)] public int GetAge(IPerson person) { return person.GetAge(); @@ -622,11 +639,11 @@ public int GetAge(IPerson person) Because static pointcuts are the most common and generally useful pointcut type, you'll probably subclass - StaticMethodMatcherPointcut, as shown below. This + StaticMethodMatcherPointcut, as shown below. This requires you to implement just one abstract method (although it is possible to override other methods to customize behaviour): - public class TestStaticPointcut : StaticMethodMatcherPointcut { + public class TestStaticPointcut : StaticMethodMatcherPointcut { public override bool Matches(MethodInfo method, Type targetType) { // return true if custom criteria match @@ -635,12 +652,12 @@ public int GetAge(IPerson person) - + Advice API in Spring.NET Let's now look at how Spring.NET AOP handles advice. - + Advice Lifecycle Spring.NET advices can be shared across all advised objects, or @@ -661,7 +678,7 @@ public int GetAge(IPerson person) the same AOP proxy. - + Advice types Spring.NET provides several advice types out of the box, and is @@ -678,7 +695,7 @@ public int GetAge(IPerson person) around advice using method interception. Around advice is implemented using the following interface: - public interface IMethodInterceptor : IInterceptor + public interface IMethodInterceptor : IInterceptor { object Invoke(IMethodInvocation invocation); } @@ -692,7 +709,7 @@ public int GetAge(IPerson person) A simple IMethodInterceptor implementation looks as follows: - public class DebugInterceptor : IMethodInterceptor { + public class DebugInterceptor : IMethodInterceptor { public object Invoke(IMethodInvocation invocation) { Console.WriteLine("Before: invocation=[{0}]", invocation); @@ -725,10 +742,10 @@ public int GetAge(IPerson person) possibility of inadvertently failing to proceed down the interceptor chain. - The IMethodBeforeAdvice interface is + The IMethodBeforeAdvice interface is shown below. - public interface IMethodBeforeAdvice : IBeforeAdvice + public interface IMethodBeforeAdvice : IBeforeAdvice { void Before(MethodInfo method, object[] args, object target); } @@ -745,7 +762,7 @@ public int GetAge(IPerson person) An example of a before advice in Spring.NET, which counts all methods that return normally: - public class CountingBeforeAdvice : IMethodBeforeAdvice { + public class CountingBeforeAdvice : IMethodBeforeAdvice { private int count; @@ -761,7 +778,7 @@ public int GetAge(IPerson person) Before advice can be used with any pointcut. - + Throws advice Throws advice is invoked after the return of the joinpoint if @@ -771,7 +788,7 @@ public int GetAge(IPerson person) advice object implements one or more typed throws advice methods. These throws advice methods must be of the form: - AfterThrowing([MethodInfo method, Object[] args, Object target], Exception subclass) + AfterThrowing([MethodInfo method, Object[] args, Object target], Exception subclass) Throws-advice methods must be named 'AfterThrowing'. The return value will be ignored @@ -786,10 +803,10 @@ public int GetAge(IPerson person) advice. This advice will be invoked if a - RemotingException is thrown (including + RemotingException is thrown (including subclasses): - public class RemoteThrowsAdvice : IThrowsAdvice { + public class RemoteThrowsAdvice : IThrowsAdvice { public void AfterThrowing(RemotingException ex) { // Do something with remoting exception @@ -797,11 +814,11 @@ public int GetAge(IPerson person) } The following advice is invoked if a - SqlException is thrown. Unlike the above + SqlException is thrown. Unlike the above advice, it declares 4 arguments, so that it has access to the invoked method, method arguments and target object: - public class SqlExceptionThrowsAdviceWithArguments : IThrowsAdvice { + public class SqlExceptionThrowsAdviceWithArguments : IThrowsAdvice { public void AfterThrowing(MethodInfo method, object[] args, object target, SqlException ex) { // Do something will all arguments @@ -810,12 +827,12 @@ public int GetAge(IPerson person) The final example illustrates how these two methods could be used in a single class, which handles both - RemotingException and - SqlException. Any number of throws advice + RemotingException and + SqlException. Any number of throws advice methods can be combined in a single class, as can be seen in the following example. - public class CombinedThrowsAdvice : IThrowsAdvice { + public class CombinedThrowsAdvice : IThrowsAdvice { public void AfterThrowing(RemotingException ex) { // Do something with remoting exception @@ -829,30 +846,30 @@ public int GetAge(IPerson person) Finally, it is worth stating that throws advice is only applied to the actual exception being thrown. What does this mean? Well, it means that if you have defined some throws advice that handles - RemotingExceptions, the applicable + RemotingExceptions, the applicable AfterThrowing method will only be invoked if the type of the thrown - exception is RemotingException... if a - RemotingException has been thrown and + exception is RemotingException... if a + RemotingException has been thrown and subsequently wrapped inside another exception before the exception bubbles up to the throws advice interceptor, then the throws advice - that handles RemotingExceptions will RemotingExceptions will never be called. Consider a business method that is advised by throws advice that handles - RemotingExceptions; if during the course of a + RemotingExceptions; if during the course of a method invocation said business method throws a RemoteException... and - subsequently wraps said RemotingException + subsequently wraps said RemotingException inside a business-specific - BadConnectionException (see the code snippet + BadConnectionException (see the code snippet below) before throwing the exception, then the throws advice will never be able to respond to the - RemotingException... because all the throws - advice sees is a BadConnectionException. The - fact that the RemotingException is wrapped up - inside the BadConnectionException is + RemotingException... because all the throws + advice sees is a BadConnectionException. The + fact that the RemotingException is wrapped up + inside the BadConnectionException is immaterial. - public void BusinessMethod() + public void BusinessMethod() { try { @@ -873,10 +890,10 @@ public int GetAge(IPerson person) After Returning advice An after returning advice in Spring.NET must implement the - Spring.Aop.IAfterReturningAdvice interface, + Spring.Aop.IAfterReturningAdvice interface, shown below: - public interface IAfterReturningAdvice : IAdvice + public interface IAfterReturningAdvice : IAdvice { void AfterReturning(object returnValue, MethodBase method, object[] args, object target); } @@ -888,7 +905,7 @@ public int GetAge(IPerson person) The following after returning advice counts all successful method invocations that have not thrown exceptions: - public class CountingAfterReturningAdvice : IAfterReturningAdvice { + public class CountingAfterReturningAdvice : IAfterReturningAdvice { private int count; public void AfterReturning(object returnValue, MethodBase m, object[] args, object target) { @@ -909,7 +926,7 @@ public int GetAge(IPerson person) - + Advice Ordering When multiple pieces of advice want to run on the same joinpoint @@ -937,14 +954,14 @@ public int GetAge(IPerson person) Introduction advice is defined by using a normal interface declaration that implements the tag interface - IAdvice. The need for implementing this + IAdvice. The need for implementing this marker interface will likely be removed in future versions. As - an example, consider the interface IAuditable + an example, consider the interface IAuditable that describes the last modified time of an object. - public interface IAuditable : IAdvice + public interface IAuditable : IAdvice { DateTime LastModifiedDate { @@ -955,22 +972,22 @@ public int GetAge(IPerson person) where - public interface IAdvice + public interface IAdvice { } Access to the advised object can be obtained by implementing the - interface ITargetAware public interface ITargetAware + interface ITargetAware public interface ITargetAware { IAopProxy TargetProxy { set; } -} with the IAopProxy reference +} with the IAopProxy reference providing a layer of indirection through which the advised object can - be accessed. public interface IAopProxy + be accessed. public interface IAopProxy { object GetProxy(); } @@ -978,7 +995,7 @@ public int GetAge(IPerson person) A simple class that demonstrates this functionality is shown - below. public interface IAuditable : IAdvice, ITargetAware + below. public interface IAuditable : IAdvice, ITargetAware { DateTime LastModifiedDate { @@ -993,7 +1010,7 @@ public int GetAge(IPerson person) - public class AuditableMixin : IAuditable + public class AuditableMixin : IAuditable { private DateTime date; private IAopProxy targetProxy; @@ -1020,13 +1037,13 @@ public int GetAge(IPerson person) Introduction advice is not associated with a pointcut, since it applies at the class and not the method level. As such, introductions use their own subclass of the interface - IAdvisor, namely - IIntroductionAdvisor, to specify the types that + IAdvisor, namely + IIntroductionAdvisor, to specify the types that the introduction can be applied to. - public interface IIntroductionAdvisor : IAdvisor + public interface IIntroductionAdvisor : IAdvisor { ITypeFilter TypeFilter { get; } @@ -1055,7 +1072,7 @@ public int GetAge(IPerson person) Spring.NET provides a default implementation of this interface - (the DefaultIntroductionAdvisor class) that + (the DefaultIntroductionAdvisor class) that should be sufficient for the majority of situations when you need to use introductions. The most simple implementation of an introduction advisor is a subclass that simply passes a new instance the base @@ -1064,7 +1081,7 @@ public int GetAge(IPerson person) - public class AuditableAdvisor : DefaultIntroductionAdvisor + public class AuditableAdvisor : DefaultIntroductionAdvisor { public AuditableAdvisor() : base(new AuditableMixin()) { @@ -1083,7 +1100,7 @@ public int GetAge(IPerson person) IAdvised.AddIntroduction(), method, or (the recommended way) in XML configuration using the IntroductionNames property on - ProxyFactoryObject, which will be discussed + ProxyFactoryObject, which will be discussed later. @@ -1108,7 +1125,7 @@ public int GetAge(IPerson person) - + Advisor API in Spring.NET In Spring.NET, an advisor is a modularization of an aspect. Advisors @@ -1116,17 +1133,17 @@ public int GetAge(IPerson person) Apart from the special case of introductions, any advisor can be used with any advice. The - Spring.Aop.Support.DefaultPointcutAdvisor class is + Spring.Aop.Support.DefaultPointcutAdvisor class is the most commonly used advisor implementation. For example, it can be used - with a IMethodInterceptor, - IBeforeAdvice or - IThrowsAdvice and any pointcut definition. + with a IMethodInterceptor, + IBeforeAdvice or + IThrowsAdvice and any pointcut definition. Other convenience implementations provided are: - AttributeMatchMethodPointcutAdvisor shown in usage + AttributeMatchMethodPointcutAdvisor shown in usage previously in for use with attribute based pointcuts. - RegularExpressionMethodPointcutAdvisor that will + RegularExpressionMethodPointcutAdvisor that will apply pointcuts based on the matching a regular expression to method names. @@ -1136,39 +1153,39 @@ public int GetAge(IPerson person) will automatically create the necessary interceptor chain. - - Using the ProxyFactoryObject to create + <sect1 xml:id="aop-proxyfactoryobject"> + <title>Using the ProxyFactoryObject to create AOP proxies If you're using the Spring.NET IoC container for your business objects - generally a good idea - you will want to use one of Spring.NET's - AOP-specific IFactoryObject implementations + AOP-specific IFactoryObject implementations (remember that a factory object introduces a layer of indirection, enabling it to create objects of a different type - ). The basic way to create an AOP proxy in Spring.NET is to use the - Spring.Aop.Framework.ProxyFactoryObject class. This + Spring.Aop.Framework.ProxyFactoryObject class. This gives complete control over ordering and application of the pointcuts and advice that will apply to your business objects. However, there are simpler options that are preferable if you don't need such control. - + Basics - The ProxyFactoryObject, like other - Spring.NET IFactoryObject implementations, + The ProxyFactoryObject, like other + Spring.NET IFactoryObject implementations, introduces a level of indirection. If you define a - ProxyFactoryObject with name + ProxyFactoryObject with name foo, what objects referencing foo - see is not the ProxyFactoryObject instance + see is not the ProxyFactoryObject instance itself, but an object created by the - ProxyFactoryObject's implementation of the + ProxyFactoryObject's implementation of the GetObject() method. This method will create an AOP proxy wrapping a target object. One of the most important benefits of using a - ProxyFactoryObject or other IoC-aware classes + ProxyFactoryObject or other IoC-aware classes that create AOP proxies, is that it means that advice and pointcuts can also be managed by IoC. This is a powerful feature, enabling certain approaches that are hard to achieve with other AOP frameworks. For @@ -1177,11 +1194,11 @@ public int GetAge(IPerson person) all the pluggability provided by Dependency Injection. - + ProxyFactoryObject Properties - Like most IFactoryObject implementations - provided with Spring.NET, the ProxyFactoryObject + Like most IFactoryObject implementations + provided with Spring.NET, the ProxyFactoryObject is itself a Spring.NET configurable object. Its properties are used to: @@ -1196,7 +1213,7 @@ public int GetAge(IPerson person) Some key properties are inherited from the - Spring.Aop.Framework.ProxyConfig class: this + Spring.Aop.Framework.ProxyConfig class: this class is the superclass for all AOP proxy factories in Spring.NET. Some of the key properties include: @@ -1247,7 +1264,7 @@ public int GetAge(IPerson person) Other properties specific to the - ProxyFactoryObject class include: + ProxyFactoryObject class include: @@ -1266,7 +1283,7 @@ public int GetAge(IPerson person) The names are object names in the current container, including objectnames from container hierarchies. You can't mention object references here since doing so would result in the - ProxyFactoryObject ignoring the singleton + ProxyFactoryObject ignoring the singleton setting of the advise. @@ -1301,7 +1318,7 @@ public int GetAge(IPerson person) - + Proxying Interfaces Let's look at a simple example of @@ -1324,7 +1341,7 @@ public int GetAge(IPerson person) - <object id="personTarget" type="MyCompany.MyApp.Person, MyCompany"> + <object id="personTarget" type="MyCompany.MyApp.Person, MyCompany"> <property name="name" value="Tony"/> <property name="age" value="51"/> </object> @@ -1369,13 +1386,13 @@ public int GetAge(IPerson person) The "person" object definition above can be used in place of an IPerson implementation, as follows: - IPerson person = (IPerson) factory.GetObject("person"); + IPerson person = (IPerson) factory.GetObject("person"); Other objects in the same IoC context can express a strongly typed dependency on it, as with an ordinary .NET object: - <object id="personUser" type="MyCompany.MyApp.PersonUser, MyCompany"> + <object id="personUser" type="MyCompany.MyApp.PersonUser, MyCompany"> <property name="person" ref="person"/> </object> @@ -1394,7 +1411,7 @@ public int GetAge(IPerson person) ProxyFactoryObject definition is different; the advice is included only for completeness: - <object id="myCustomInterceptor" type="MyCompany.MyApp.MyCustomInterceptor, MyCompany"> + <object id="myCustomInterceptor" type="MyCompany.MyApp.MyCustomInterceptor, MyCompany"> <property name="customProperty" value="configuration string"/> </object> @@ -1439,31 +1456,37 @@ public int GetAge(IPerson person) Let's look at an example of configuring the proxy objects retrieved from ProxyFactoryObject. - -<!-- create the object to reference --> -<object id="RealObjectTarget" type="MyRealObject" singleton="false"/> -<!-- create the proxied object for everyone to use--> -<object id="MyObject" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"> - <property name="proxyInterfaces" value="MyInterface" /> - <property name="isSingleton" value="false"/> - <property name="targetName" value="RealObjectTarget" /> -</object> - If you are using a prototype as the target you must set the - TargetName property with the name/object id of your - object and not use the property Target with a - reference to that object. This will then allow a new proxy to be created - around a new prototype target instance. Consider the above - Spring.Net object configuration. Notice that the - IsSingleton property of the - ProxyFactoryObject instance is set to false. This - means that each proxy object will be unique. Thus, you can configure - each proxy object with its' own individual advice(s) using the following - syntax MyInterface myProxyObject1 = (MyInterface)ctx.GetObject("MyObject"); // Will return un-advised instance of proxy object + + + <!-- create the object to reference --> + <object id="RealObjectTarget" type="MyRealObject" singleton="false"/> + <!-- create the proxied object for everyone to use--> + <object id="MyObject" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"> + <property name="proxyInterfaces" value="MyInterface" /> + <property name="isSingleton" value="false"/> + <property name="targetName" value="RealObjectTarget" /> + </object> + If you are using a prototype as the target you must set the + TargetName property with the name/object id of your + object and not use the property Target with a + reference to that object. This will then allow a new proxy to be created + around a new prototype target instance. + + + Consider the above Spring.Net object configuration. Notice that the + IsSingleton property of the ProxyFactoryObject + instance is set to false. This means that each proxy object will be unique. + Thus, you can configure each proxy object with its' own individual advice(s) + using the following syntax +// Will return un-advised instance of proxy object +MyInterface myProxyObject1 = (MyInterface)ctx.GetObject("MyObject"); +// myProxyObject1 instance now has an advice attached to it. IAdvised advised = (IAdvised)myProxyObject1; -advised.AddAdvice( new DebugAdvice() ); // myProxyObject1 instance now has an advice attached to it. +advised.AddAdvice( new DebugAdvice() ); -MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject"); // Will return a new, un-advised instance of proxy object +// Will return a new, un-advised instance of proxy object +MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject"); @@ -1510,7 +1533,7 @@ MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject"); // Will ret First a parent, template, object definition is created for the proxy: - <object id="txProxyTemplate" abstract="true" + <object id="txProxyTemplate" abstract="true" type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"> <property name="PlatformTransactionManager" ref="adoTransactionManager"/> @@ -1527,7 +1550,7 @@ MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject"); // Will ret object definition, since the target will never be used on its own anyway. - <object name="testObjectManager" parent="txProxyTemplate"> + <object name="testObjectManager" parent="txProxyTemplate"> <property name="Target"> <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> <property name="TestObjectDao" ref="testObjectDao"/> @@ -1539,7 +1562,7 @@ MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject"); // Will ret template, such as in this case, the transaction propagation settings: - <object name="testObjectManager" parent="txProxyTemplate"> + <object name="testObjectManager" parent="txProxyTemplate"> <property name="Target"> <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> <property name="TestObjectDao" ref="testObjectDao"/> @@ -1567,7 +1590,7 @@ MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject"); // Will ret - + Proxying mechanisms Spring creates AOP proxies built at runtime through the use of the @@ -1590,9 +1613,9 @@ MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject"); // Will ret if needed. Please note that in both cases a target method implementation that calls other methods on the target object will not be advised. To force inheritance based proxies you should either set the - ProxyTargetType to true property of a ProxyFactory - or set the XML namespace element proxy-target-type = - true when using an AOP schema based configuration. + ProxyTargetType to true property of a ProxyFactory + or set the XML namespace element proxy-target-type = + true when using an AOP schema based configuration. An important alternative approach to inheritance based proxies is @@ -1607,7 +1630,7 @@ MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject"); // Will ret InternalsVisibleTo("Spring.DynamicReflection")] to your to AssemblyInfo file. - + InheritanceBasedAopConfigurer There is an important limitation in the inheritance based proxy as @@ -1625,7 +1648,7 @@ MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject"); // Will ret IObjectFactoryPostProcessor, in yoru configuraiton file. Here is an example. - <object type="Spring.Aop.Framework.AutoProxy.InheritanceBasedAopConfigurer, Spring.Aop"> + <object type="Spring.Aop.Framework.AutoProxy.InheritanceBasedAopConfigurer, Spring.Aop"> <property name="ObjectNames"> <list> <value>Form*</value> @@ -1648,7 +1671,7 @@ MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject"); // Will ret - + Creating AOP Proxies Programatically with the ProxyFactory It's easy to create AOP proxies Programatically using Spring.NET. @@ -1659,7 +1682,7 @@ MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject"); // Will ret with one interceptor and one advisor. The interfaces implemented by the target object will automatically be proxied: - ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); + ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); factory.AddAdvice(myMethodInterceptor); factory.AddAdvisor(myAdvisor); IBusinessInterface tb = (IBusinessInterface) factory.GetProxy(); @@ -1684,7 +1707,7 @@ IBusinessInterface tb = (IBusinessInterface) factory.GetProxy(); - + Manipulating Advised Objects However you create AOP proxies, you can manipulate them using the @@ -1692,7 +1715,7 @@ IBusinessInterface tb = (IBusinessInterface) factory.GetProxy(); - public interface IAdvised + public interface IAdvised { IAdvisor[] Advisors { get; } @@ -1773,7 +1796,7 @@ IBusinessInterface tb = (IBusinessInterface) factory.GetProxy(); - + Using the "autoproxy" facility So far we've considered explicit creation of AOP proxies using a @@ -1815,7 +1838,7 @@ IBusinessInterface tb = (IBusinessInterface) factory.GetProxy(); also offers this benefit.) - + Autoproxy object definitions The namespace Spring.Aop.Framework.AutoProxy @@ -1826,7 +1849,7 @@ IBusinessInterface tb = (IBusinessInterface) factory.GetProxy();DefaultAdvisorAutoProxyCreator. These are discussed in the following sections. - + ObjectNameAutoProxyCreator The ObjectNameAutoProxyCreator automatically @@ -1836,7 +1859,7 @@ IBusinessInterface tb = (IBusinessInterface) factory.GetProxy(); - public enum Language + public enum Language { English = 1, Portuguese = 2, @@ -1894,7 +1917,7 @@ public class DebugInterceptor : IMethodInterceptor and apply a Debug interceptor to object definitions whose names match "English*" and "PortugueseSpeaker". - <object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop"> + <object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop"> <property name="ObjectNames"> <list> <value>English*</value> @@ -1939,7 +1962,7 @@ public class DebugInterceptor : IMethodInterceptor Running the following simple program demonstrates the application of the AOP interceptor. - + IApplicationContext ctx = ContextRegistry.GetContext(); IDictionary speakerDictionary = ctx.GetObjectsOfType(typeof(IHelloWorldSpeaker)); foreach (DictionaryEntry entry in speakerDictionary) @@ -1953,7 +1976,7 @@ foreach (DictionaryEntry entry in speakerDictionary) The output is shown below - ItalianSpeakerOne says; Ciao Mondo! + ItalianSpeakerOne says; Ciao Mondo! EnglishSpeakerTwo says; Before: Void SayHello() Hello World! After: Void SayHello() @@ -1965,7 +1988,7 @@ Hello World! After: Void SayHello() - + DefaultAdvisorAutoProxyCreator @@ -2034,7 +2057,7 @@ After: Void SayHello() ObjectNameAutoProxyCreator we will add a new class, SpeakerDao, that acts as a Data Access Object to find and store IHelloWorldSpeaker objects. - + public interface ISpeakerDao { IList FindAll(); @@ -2073,11 +2096,11 @@ public class SpeakerDao : ISpeakerDao is used as a convenience to specify the pointcut as a regular expression that matches methods names. Other pointcuts of your own creation could be used, in which case a - DefaultPointcutAdvisor + DefaultPointcutAdvisor would be used to define the Advisor. The object definitions for these advisors, advice, and SpeakerDao object are shown below - <object id="SpeachAdvisor" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop"> + <object id="SpeachAdvisor" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop"> <property name="advice" ref="debugInterceptor"/> <property name="patterns"> @@ -2115,7 +2138,7 @@ public class SpeakerDao : ISpeakerDao Adding an instance of DefaultAdvisorAutoProxyCreator to the configuration - file <object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator, Spring.Aop"/> + file <object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator, Spring.Aop"/> will apply the debug interceptor on all objects in the context that have a method that contains the text "Say" and apply the timing interceptor on objects in the context that have a method that contains @@ -2125,7 +2148,7 @@ public class SpeakerDao : ISpeakerDao - + IApplicationContext ctx = ContextRegistry.GetContext(); IDictionary speakerDictionary = ctx.GetObjectsOfType(typeof(IHelloWorldSpeaker)); foreach (DictionaryEntry entry in speakerDictionary) @@ -2146,7 +2169,7 @@ IHelloWorldSpeaker speaker = dao.Save(new HelloWorldSpeaker()); - + ItalianSpeakerOne says; Before: Void SayHello() Ciao Mondo! After: Void SayHello() @@ -2188,7 +2211,7 @@ Saving speaker... - + Using attribute-driven auto-proxying A particularly important type of autoproxying is driven by @@ -2206,18 +2229,18 @@ Saving speaker... Several of the aspect provided with Spring use attribute driven autoproxying. The most prominent example is Transaction support. + linkend="transaction">Transaction support. - + Using AOP Namespace The AOP namespace allows you to define an advisor, i.e pointcut + 1 piece of advice, in a more declarative manner. Under the covers the DefaultAdvisorAutoProxyCreator is being used. Here is an example, - <objects xmlns="http://www.springframework.net" + <objects xmlns="http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.net/aop"> @@ -2256,7 +2279,7 @@ Saving speaker... context configuration for more extensive information.. - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> @@ -2264,16 +2287,16 @@ Saving speaker... <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"/> + <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/> </sectionGroup> </configSections> <spring> - <parsers> + <parsers> <parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" /> - </parsers> + </parsers> <context> @@ -2290,7 +2313,7 @@ Saving speaker... - + Using TargetSources Spring.NET offers the concept of a @@ -2317,7 +2340,7 @@ Saving speaker... to be a prototype rather than a singleton object definition. This allows Spring.NET to create a new target instance when required. - + Hot swappable target sources The @@ -2331,13 +2354,13 @@ Saving speaker... You can change the target via the swap() method on HotSwappableTargetSource as follows: - HotSwappableTargetSource swapper = + HotSwappableTargetSource swapper = (HotSwappableTargetSource) objectFactory.GetObject("swapper"); object oldTarget = swapper.swap(newTarget); The XML definitions required look as follows: - <object id="initialTarget" type="MyCompany.OldTarget, MyCompany"> + <object id="initialTarget" type="MyCompany.OldTarget, MyCompany"> </object> <object id="swapper" @@ -2364,7 +2387,7 @@ object oldTarget = swapper.swap(newTarget); with arbitrary advice. - + Pooling target sources Using a pooling target source provides a programming model in @@ -2384,7 +2407,7 @@ object oldTarget = swapper.swap(newTarget); Sample configuration is shown below: - <object id="businessObjectTarget" type="MyCompany.MyBusinessObject, MyCompany" singleton="false"> + <object id="businessObjectTarget" type="MyCompany.MyBusinessObject, MyCompany" singleton="false"> ... properties omitted </object> @@ -2418,22 +2441,22 @@ object oldTarget = swapper.swap(newTarget); size of the pool through an introduction. You'll need to define an advisor like this: - <object id="poolConfigAdvisor" + <object id="poolConfigAdvisor" type="Spring.Object.Factory.Config.MethodInvokingFactoryObject, Spring.Aop"> <property name="target" ref="poolTargetSource" /> <property name="targetMethod" value="getPoolingConfigMixin" /> </object> This advisor is obtained by calling a convenience method on the - AbstractPoolingTargetSource class, hence the use - of MethodInvokingFactoryObject. This advisor's + AbstractPoolingTargetSource class, hence the use + of MethodInvokingFactoryObject. This advisor's name ('poolConfigAdvisor' here) must be in the list - of interceptor names in the ProxyFactoryObject + of interceptor names in the ProxyFactoryObject exposing the pooled object. The cast will look as follows: - PoolingConfig conf = (PoolingConfig) objectFactory.GetObject("businessObject"); + PoolingConfig conf = (PoolingConfig) objectFactory.GetObject("businessObject"); Console.WriteLine("Max pool size is " + conf.getMaxSize()); Pooling stateless service objects is not usually necessary. We @@ -2445,7 +2468,7 @@ Console.WriteLine("Max pool size is " + conf.getMaxSize()); set the TargetSources used by any autoproxy creator. - + Prototype target sources Setting up a "prototype" target source is similar to a pooling @@ -2459,7 +2482,7 @@ Console.WriteLine("Max pool size is " + conf.getMaxSize()); poolTargetSource definition shown above as follows. (the name of the definition has also been changed, for clarity.) - <object id="prototypeTargetSource" + <object id="prototypeTargetSource" type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop"> <property name="targetObjectName" value="businessObject" /> </object> @@ -2480,14 +2503,14 @@ Console.WriteLine("Max pool size is " + conf.getMaxSize()); alongside a thread. Setting up a ThreadLocalTargetSource is pretty much the same as was explained for the other types of target source: - <object id="threadlocalTargetSource" + <object id="threadlocalTargetSource" type="Spring.Aop.Target.ThreadLocalTargetSource, Spring.Aop"> <property name="targetObjectName" value="businessObject" /> </object> - + Defining new Advice types Spring.NET AOP is designed to be extensible. While the interception @@ -2500,13 +2523,13 @@ Console.WriteLine("Max pool size is " + conf.getMaxSize()); SPI (Service Provider Interface) package allowing support for new custom advice types to be added without changing the core framework. The only constraint on a custom Advice type is that it must implement the - AopAlliance.Aop.IAdvice tag interface. + AopAlliance.Aop.IAdvice tag interface. Please refer to the Spring.Aop.Framework.Adapter namespace documentation for further information. - + Further reading and resources The Spring.NET team recommends the excellent AspectJ in diff --git a/doc/reference/src/background.xml b/doc/reference/src/background.xml index c401cab5..028bf0ae 100644 --- a/doc/reference/src/background.xml +++ b/doc/reference/src/background.xml @@ -1,8 +1,25 @@ - + + Background information - + Inversion of Control In early 2004, Martin Fowler asked the readers of his site: when diff --git a/doc/reference/src/dao.xml b/doc/reference/src/dao.xml index 30d7ad00..5a2c5a57 100644 --- a/doc/reference/src/dao.xml +++ b/doc/reference/src/dao.xml @@ -1,8 +1,25 @@ - + + DAO support -
+
Introduction Spring promotes the use of data access interfaces in your @@ -35,17 +52,17 @@ catching exceptions that are specific to each technology.
-
+
Consistent exception hierarchy 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 - System.Data.SqlClient.SqlException or - System.Data.OracleClient.OracleException. The .NET + System.Data.SqlClient.SqlException or + System.Data.OracleClient.OracleException. The .NET 2.0 BCL improved in this regard by introducing a common base class for - exceptions, System.Data.Common.DbException. However + exceptions, System.Data.Common.DbException. 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 @@ -54,10 +71,10 @@ To promote writing portable and descriptive exception handling code Spring provides a convenient translation from technology specific - exceptions like System.Data.SqlClient.SqlException - or System.Data.OracleClient.OracleException to its + exceptions like System.Data.SqlClient.SqlException + or System.Data.OracleClient.OracleException to its own exception hierarchy with the - Spring.Dao.DataAccessException as the root + Spring.Dao.DataAccessException 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. @@ -83,13 +100,13 @@ (Please note that the class hierarchy detailed in the above image shows only a subset of the whole, rich, - DataAccessException hierarchy.) + DataAccessException hierarchy.) The exception translation functionality is in the namespace Spring.Data.Support and is based on the interface IAdoExceptionTranslator shown below. - public interface IAdoExceptionTranslator + public interface IAdoExceptionTranslator { DataAccessException Translate( string task, string sql, Exception exception ); } @@ -131,7 +148,7 @@ codes that map to a DataIntegrityViolationException. - <objects xmlns='http://www.springframework.net'> + <objects xmlns='http://www.springframework.net'> <alias name='SqlServer-2.0' alias='SqlServer2005'/> @@ -153,7 +170,7 @@ periods in the name is a workaround. Another way to customize the mappings of error codes to exceptions - is to subclass ErrorCodeExceptionTranslator and + is to subclass ErrorCodeExceptionTranslator and override the method, DataAccessException TranslateException(string task, string sql, string errorCode, Exception exception). This will be called before referencing the metadata @@ -297,26 +314,26 @@ - AdoDaoSupport - super class for ADO.NET + AdoDaoSupport - super class for ADO.NET data access objects. Requires a - DbProvider to be provided; in turn, - this class provides a AdoTemplate instance + DbProvider to be provided; in turn, + this class provides a AdoTemplate instance initialized from the supplied - DbProvider to subclasses. See the + DbProvider to subclasses. See the documentation for AdoTemplate for more information. - HibernateDaoSupport - super class for + HibernateDaoSupport - super class for NHibernate data access objects. Requires a - ISessionFactory to be provided; in - turn, this class provides a HibernateTemplate + ISessionFactory to be provided; in + turn, this class provides a HibernateTemplate instance initialized from the supplied - SessionFactory to subclasses. Can + SessionFactory to subclasses. Can alternatively be initialized directly via a - HibernateTemplate, to reuse the latter's - settings like SessionFactory, flush + HibernateTemplate, to reuse the latter's + settings like SessionFactory, flush mode, exception translator, etc. This is contained in a download separate from the main Spring.NET distribution. diff --git a/doc/reference/src/data-quickstart.xml b/doc/reference/src/data-quickstart.xml index 4395c959..be95892a 100644 --- a/doc/reference/src/data-quickstart.xml +++ b/doc/reference/src/data-quickstart.xml @@ -1,5 +1,22 @@ - + + Data Access QuickStart
@@ -14,40 +31,39 @@ 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 TestDriven.NET, TestDriven.NET, ReSharper, or an equivalent . The listing of DAO classes and the parts of Spring.Data that they demonstrate is shown below. - CommandCallbackDao - Use of the + CommandCallbackDao - Use of the ICommandCallback and CommandCallbackDelegate - ResultSetExtractorDao - Use of + ResultSetExtractorDao - Use of IResultSetExtractor and ResultSetExtractorDelegate - RowCallbackDao - Use of IRowCallback and + RowCallbackDao - Use of IRowCallback and RowCallbackDelegate - RowMapperDao - Use of IRowMapper and + RowMapperDao - Use of IRowMapper and RowMapperDelegate - QueryForObject - Use of QueryForObject + QueryForObject - Use of QueryForObject method. - StoredProcDao - Use of + StoredProcDao - Use of Spring.Data.Objects.StoredProcedure @@ -63,7 +79,7 @@ database connection string. The listing in DataQuickStart.GenericTemplate.ExampleTests.xml is shown below - <objects xmlns="http://www.springframework.net" + <objects xmlns="http://www.springframework.net" xmlns:db="http://www.springframework.net/database"> <db:provider id="dbProvider" @@ -93,7 +109,7 @@ which is responsible for performing data access operations. This is declared in ExampleTest.xml as shown below - <object id="adoTemplate" type="Spring.Data.Generic.AdoTemplate, Spring.Data"> + <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> @@ -103,7 +119,7 @@ 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, Data + more about AdoTemplate, refer to the chapter, Data access using ADO.NET.
@@ -114,7 +130,7 @@ The code that exercises the use of a CommandCallback is shown below - [Test] + [Test] public void CallbackDaoTest() { CommandCallbackDao commandCallbackDao = ctx["commandCallbackDao"] as CommandCallbackDao; @@ -124,7 +140,7 @@ The configuration of the CommandCallbackDao is shown below - <object id="commandCallbackDao" type="Spring.DataQuickStart.Dao.GenericTemplate.CommandCallbackDao, Spring.DataQuickStart"> + <object id="commandCallbackDao" type="Spring.DataQuickStart.Dao.GenericTemplate.CommandCallbackDao, Spring.DataQuickStart"> <property name="AdoTemplate" ref="adoTemplate"/> </object> @@ -134,7 +150,7 @@ size of the result set returned etc. The implementation of the FindCountWithPostalCode is shown below - public virtual int FindCountWithPostalCodeWithDelegate(string postalCode) + public virtual int FindCountWithPostalCodeWithDelegate(string postalCode) { // Using anonymous delegates allows you to easily reference the // surrounding parameters for use with the DbCommand processing. diff --git a/doc/reference/src/dbprovider.xml b/doc/reference/src/dbprovider.xml index 4611fe7a..e03ddb47 100644 --- a/doc/reference/src/dbprovider.xml +++ b/doc/reference/src/dbprovider.xml @@ -1,13 +1,30 @@ - + + DbProvider -
+
Introduction Spring provides a generic factory for creating ADO.NET API artifacts - such as IDbConnection and - IDbCommand. The factory API is very + such as IDbConnection and + IDbCommand. 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 @@ -39,15 +56,15 @@ calling context.
-
+
IDbProvider and DbProviderFactory - The IDbProvider API is + The IDbProvider 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 - public interface IDbProvider + public interface IDbProvider { IDbCommand CreateCommand(); @@ -89,7 +106,7 @@ create the string for a IDataParameter.ParameterName, typically contained inside a IDataParameterCollection. - The class DbProviderFactory creates + The class DbProviderFactory 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 @@ -242,17 +259,17 @@ An example using DbProviderFactory is shown below - IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient"); + IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient"); The default definitions of the providers are contained in the assembly resource assembly://Spring.Data/Spring.Data.Common/dbproviders.xml. Future additions to round out the database coverage are forthcoming. The current crude mechanism to add additional providers, or to apply any - standard Spring IApplicationContext + standard Spring IApplicationContext functionality, such as applying AOP advice, is to set the public static property DBPROVIDER_ADDITIONAL_RESOURCE_NAME in - DbProviderFactory to a Spring resource location. + DbProviderFactory to a Spring resource location. The default value is file://dbProviders.xml. (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 @@ -264,7 +281,7 @@ application, you should add an assembly redirect of the form shown below. - <dependentAssembly> + <dependentAssembly> <assemblyIdentity name="MySql.Data" publicKeyToken="c5687fc88969c44d" culture="neutral"/> @@ -283,7 +300,7 @@ below in the typical case of using it to specify the DbProvider property on an AdoTemplate. - <objects xmlns='http://www.springframework.net' + <objects xmlns='http://www.springframework.net' xmlns:db="http://www.springframework.net/database"> <db:provider id="DbProvider" @@ -302,7 +319,7 @@ the rest of the Spring configuration locations as described in previous chapters. - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> @@ -335,7 +352,7 @@ An example of such a setting is shown below - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> <section name='context' type='Spring.Context.Support.ContextHandler, Spring.Core'/> @@ -367,7 +384,7 @@ Where Dao.xml has a connection string as shown below - <objects xmlns='http://www.springframework.net' + <objects xmlns='http://www.springframework.net' xmlns:db="http://www.springframework.net/database"> <db:provider id="DbProvider" @@ -391,17 +408,17 @@ information.
-
+
Additional IDbProvider implementations Spring provides some convenient implementations of the IDbProvider interface that add addtional behavior on top of the standard implementation. -
+
UserCredentialsDbProvider - This UserCredentialsDbProvider will allow + This UserCredentialsDbProvider will allow you to change the username and password of a database connection at runtime. The API contains the properties Username and Password which are used as the default strings @@ -416,7 +433,7 @@ You may retrieve the user information from an HTTP session for example. Example configuration and usage is shown below - <object id="DbProvider" type="Spring.Data.Common.UserCredentialsDbProvider, Spring.Data"> + <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"/> @@ -431,7 +448,7 @@ of the type UserCredentialsDbProvider instead of IDbProvider. - userCredentialsDbProvider.SetCredentialsForCurrentThread("User ID=springqa", "Password=springqa"); + userCredentialsDbProvider.SetCredentialsForCurrentThread("User ID=springqa", "Password=springqa"); UserCredentialsDbProvider's has a base class, DelegatingDbProvider, and is intended for you to use @@ -442,21 +459,21 @@ to the target IDbProvider.
-
+
MultiDelegatingDbProvider 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 - MultiDelegatingDbProvider implements the - IDbProvider interface and provides an abstraction + MultiDelegatingDbProvider implements the + IDbProvider 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. - MultiDelegatingDbProvider does its job by looking + MultiDelegatingDbProvider 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. MultiDelegatingDbProvider + processing the request. MultiDelegatingDbProvider is configured using the dictionary property TargetDbProviders. The key of this dictionary contains the name of a dbProvider and its value is a dbProvider object. diff --git a/doc/reference/src/expressions.xml b/doc/reference/src/expressions.xml index f29ce019..9d3f1a08 100644 --- a/doc/reference/src/expressions.xml +++ b/doc/reference/src/expressions.xml @@ -1,8 +1,25 @@ - + + Expression Evaluation - + Introduction The Spring.Expressions namespace provides a powerful expression @@ -33,12 +50,12 @@ additional example usage. - + Evaluating Expressions The simplest, but not the most efficient way to perform expression evaluation is by using one of the static convenience methods of the - ExpressionEvaluator class:public static object GetValue(object root, string expression); + ExpressionEvaluator class:public static object GetValue(object root, string expression); public static object GetValue(object root, string expression, IDictionary variables) @@ -49,8 +66,8 @@ public static void SetValue(object root, string expression, IDictionary variable 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 - Inventor class. You can find the class listing in - section . Inventor tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + Inventor class. You can find the class listing in + section . Inventor tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); tesla.PlaceOfBirth.City = "Smiljan"; @@ -61,39 +78,39 @@ string evaluatedCity = (string) ExpressionEvaluator.GetValue(tesla, "PlaceOfBirt 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 ExpressionEvaluator.SetValue(tesla, "PlaceOfBirth.City", "Novi Sad"); + following line ExpressionEvaluator.SetValue(tesla, "PlaceOfBirth.City", "Novi Sad"); A much better way to evaluate expressions is to parse them once and then evaluate as many times as you want - usingExpressionclass. Unlike - ExpressionEvaluator, which parses expression every - time you invoke one of its methods, Expression + usingExpressionclass. Unlike + ExpressionEvaluator, which parses expression every + time you invoke one of its methods, Expression class will cache the parsed expression for increased performance. The - methods of this class are listed below: public static IExpression Parse(string expression) + methods of this class are listed below: public static IExpression Parse(string expression) public override object Get(object context, IDictionary variables) public override void Set(object context, IDictionary variables, object newValue) The retrieval of the Name property in the previous example using the - Expression class is shown below IExpression exp = Expression.Parse("Name"); + Expression class is shown below IExpression exp = Expression.Parse("Name"); string evaluatedName = (string) exp.GetValue(tesla, null); 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 - ExpressionEvaluator class when you are doing + ExpressionEvaluator 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. There are a few exception classes to be aware of when using the - ExpressionEvaluator. These are - InvalidPropertyException, when you refer to a + ExpressionEvaluator. These are + InvalidPropertyException, when you refer to a property that doesn't exist, - NullValueInNestedPathException, when a null value + NullValueInNestedPathException, when a null value is encountered when traversing through the nested property list, and - ArgumentException and - NotSupportedException when you pass in values that + ArgumentException and + NotSupportedException when you pass in values that are in error in some other manner. The expression language is based on a grammar and uses - + Language Reference - + Literal expressions The types of literal expressions supported are strings, dates, @@ -121,7 +138,7 @@ string evaluatedName = (string) exp.GetValue(tesla, null);string helloWorld = (string) ExpressionEvaluator.GetValue(null, "'Hello World'"); // evals to "Hello World" + side of a logical comparison operator. 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" @@ -140,29 +157,29 @@ object nullValue = ExpressionEvaluator.GetValue(null, "null"); 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 - Double.Parse unless the format character "M" or - "F" is supplied, in which case Decimal.Parse and - Single.Parse would be used respectfully. As shown + Double.Parse unless the format character "M" or + "F" is supplied, in which case Decimal.Parse and + Single.Parse would be used respectfully. As shown above, if two arguments are given to the date literal then - DateTime.ParseExact will be used. Note that all + DateTime.ParseExact will be used. Note that all parse methods of classes that are used internally reference the - CultureInfo.InvariantCulture. + CultureInfo.InvariantCulture. - + Properties, Arrays, Lists, Dictionaries, Indexers As shown in the previous example in , navigating through properties is easy, just use a period to indicate a nested property value. The - instances of Inventor class, + instances of Inventor class, pupin and tesla, were populated with data listed in section . To navigate "down" and get Tesla's year of birth and Pupin's city of birth the following expressions are - used int year = (int) ExpressionEvaluator.GetValue(tesla, "DOB.Year")); // 1856 + used int year = (int) ExpressionEvaluator.GetValue(tesla, "DOB.Year")); // 1856 string city = (string) ExpressionEvaluator.GetValue(pupin, "PlaCeOfBirTh.CiTy"); // "Idvor" For the sharp-eyed, that isn't a typo in the property name for place of @@ -170,7 +187,7 @@ string city = (string) ExpressionEvaluator.GetValue(pupin, "PlaCeOfBirTh.CiTy"); evaluation is case insensitive. The contents of arrays and lists are obtained using square bracket - notation. // Inventions Array + notation. // Inventions Array string invention = (string) ExpressionEvaluator.GetValue(tesla, "Inventions[3]"); // "Induction motor" // Members List @@ -182,7 +199,7 @@ string invention = (string) ExpressionEvaluator.GetValue(ieee, "Members[0].Inven The contents of dictionaries are obtained by specifying the literal key value within the brackets. In this case, because keys for the Officers dictionary are strings, we can specify - string literal.// Officer's Dictionary + string literal.// Officer's Dictionary Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers['president']"; string city = (string) ExpressionEvaluator.GetValue(ieee, "Officers['president'].PlaceOfBirth.City"); // "Idvor" @@ -196,7 +213,7 @@ ExpressionEvaluator.SetValue(ieee, "Officers['advisors'][0].PlaceOfBirth.Country Indexers are similarly referenced using square brackets. The following is a small example that shows the use of indexers. - Multidimensional indexers are also supported. public class Bar + Multidimensional indexers are also supported. public class Bar { private int[] numbers = new int[] {1, 2, 3}; @@ -223,7 +240,7 @@ ExpressionEvaluator.SetValue(bar, "[1]", 3); // set value to 3 items with curly brackets:{1, 2, 3, 4, 5} {'abc', 'xyz'} If you want to ensure that a strongly typed array is initialized instead of a weakly typed list, you can use array - initializer instead: new int[] {1, 2, 3, 4, 5} + initializer instead: new int[] {1, 2, 3, 4, 5} new string[] {'abc', 'xyz'} Dictionary definition syntax is a bit different: you need to use @@ -243,13 +260,13 @@ new string[] {'abc', 'xyz'} - + Methods Methods are invoked using typical C# programming syntax. You may also invoke methods on literals. - //string literal + //string literal char[] chars = (char[]) ExpressionEvaluator.GetValue(null, "'test'.ToCharArray(1, 2)")) // 't','e' //date literal @@ -260,23 +277,23 @@ int year = (int) ExpressionEvaluator.GetValue(null, "date('1974/08/24').AddYears ExpressionEvaluator.GetValue(ieee, "Members[0].GetAge(date('2005-01-01')") // 149 (eww..a big anniversary is coming up ;) - + Operators - + Relational operators 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 IComparable interface. + object implements the IComparable interface. Enumerations are also supported but you will need to register the enumeration type, as described in Section , in order to use an enumeration value in an expression if it is not contained in the mscorlib. - ExpressionEvaluator.GetValue(null, "2 == 2") // true + ExpressionEvaluator.GetValue(null, "2 == 2") // true ExpressionEvaluator.GetValue(null, "date('1974-08-24') != DateTime.Today") // true @@ -286,12 +303,12 @@ ExpressionEvaluator.GetValue(null, "DateTime.Today <= date('1974-08-24')") // ExpressionEvaluator.GetValue(null, "'Test' >= 'test'") // true - Enumerations can be evaluated as shown below FooColor fColor = new FooColor(); + Enumerations can be evaluated as shown below FooColor fColor = new FooColor(); ExpressionEvaluator.SetValue(fColor, "Color", KnownColor.Blue); bool trueValue = (bool) ExpressionEvaluator.GetValue(fColor, "Color == KnownColor.Blue"); //true - Where FooColor is the following class. public class FooColor + Where FooColor is the following class. public class FooColor { private KnownColor knownColor; @@ -308,7 +325,7 @@ bool trueValue = (bool) ExpressionEvaluator.GetValue(fColor, "Color == KnownColo like and between, as well as is and matches operators, which allow you to test if object is of a specific type or if the - value matches a regular expression.ExpressionEvaluator.GetValue(null, "3 in {1, 2, 3, 4, 5}") // true + value matches a regular expression.ExpressionEvaluator.GetValue(null, "3 in {1, 2, 3, 4, 5}") // true ExpressionEvaluator.GetValue(null, "'Abc' like '[A-Z]b*'") // true @@ -329,13 +346,13 @@ ExpressionEvaluator.GetValue(null, @"'5.00' matches '^-?\d+(\.\d{2})?$'") // tr like operator pattern string. - + Logical operators The logical operators that are supported are and, or, and not. Their use is demonstrated - below// AND + below// AND bool falseValue = (bool) ExpressionEvaluator.GetValue(null, "true and false"); //false string expression = @"IsMember('Nikola Tesla') and IsMember('Mihajlo Pupin')"; @@ -355,7 +372,7 @@ string expression = @"IsMember('Nikola Tesla') and !IsMember('Mihajlo Pupin')"; bool falseValue = (bool) ExpressionEvaluator.GetValue(ieee, expression); - + Mathematical operators The addition operator can be used on numbers, strings and dates. @@ -363,7 +380,7 @@ bool falseValue = (bool) ExpressionEvaluator.GetValue(ieee, expression);// Addition + // Addition int two = (int)ExpressionEvaluator.GetValue(null, "1 + 1"); // 2 String testString = (String)ExpressionEvaluator.GetValue(null, "'test' + ' ' + 'string'"); //'test string' @@ -407,7 +424,7 @@ int minusFortyFive = (int) ExpressionEvaluator.GetValue(null, "1+2-3*8^2/2/2"); - + Assignment Setting of a property is done by using the assignment operator. @@ -416,7 +433,7 @@ int minusFortyFive = (int) ExpressionEvaluator.GetValue(null, "1+2-3*8^2/2/2"); SetValue 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 Inventor inventor = new Inventor(); + shown below Inventor inventor = new Inventor(); String aleks = (String) ExpressionEvaluator.GetValue(inventor, "Name = 'Aleksandar Seovic'"); DateTime dt = (DateTime) ExpressionEvaluator.GetValue(inventor, "DOB = date('1974-08-24')"); @@ -424,14 +441,14 @@ DateTime dt = (DateTime) ExpressionEvaluator.GetValue(inventor, "DOB = date('197 Inventor tesla = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers['vp'] = Members[0]"); - + Expression lists 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 - //Perform property assignments and then return Name property. + //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 )")); @@ -439,11 +456,11 @@ String pupin = (String) ExpressionEvaluator.GetValue(ieee.Members, // pupin = "Mihajlo Pupin" - + Types In many cases, you can reference types by simply specifying type - name:ExpressionEvaluator.GetValue(null, "1 is int") + name:ExpressionEvaluator.GetValue(null, "1 is int") ExpressionEvaluator.GetValue(null, "DateTime.Today") @@ -455,7 +472,7 @@ ExpressionEvaluator.GetValue(null, "new string[] {'abc', 'efg'}") For all other types, you need to use special - T(typeName) expression:Type dateType = (Type) ExpressionEvaluator.GetValue(null, "T(System.DateTime)") + T(typeName) expression:Type dateType = (Type) ExpressionEvaluator.GetValue(null, "T(System.DateTime)") Type evalType = (Type) ExpressionEvaluator.GetValue(null, "T(Spring.Expressions.ExpressionEvaluator, Spring.Core)") @@ -463,14 +480,14 @@ bool trueValue = (bool) ExpressionEvaluator.GetValue(tesla, "T(System.DateTime) The implementation delegates to Spring's - ObjectUtils.ResolveType method for the actual + ObjectUtils.ResolveType 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. - + Type Registration To refer to a type within an expression that is not in the @@ -480,7 +497,7 @@ bool trueValue = (bool) ExpressionEvaluator.GetValue(tesla, "T(System.DateTime) used in expression that use the new operator or refer to a static properties of an object. Example usage is shown below. - TypeRegistry.RegisterType("Society", typeof(Society)); + TypeRegistry.RegisterType("Society", typeof(Society)); Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers[Society.President]"); @@ -488,13 +505,13 @@ Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers[Society typeAliases configuration section. - + Constructors 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: - // simple ctor + // 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. @@ -510,7 +527,7 @@ int three = (int) ExpressionEvaluator.GetValue(ieee.Members, "{ Add(new Inventor instantiation, similar to the way standard .NET attributes work. For example, you could create an instance of the Inventor class and set its Inventions property in a single - statement: + statement: Inventor aleks = (Inventor) ExpressionEvaluator.GetValue(null, "new Inventor('Aleksandar Seovic', date('1974-08-24'), 'Serbian', Inventions = {'SPELL'})"); The only rule you have to follow is that named arguments should be specified after standard constructor @@ -520,7 +537,7 @@ Inventor aleks = (Inventor) ExpressionEvaluator.GetValue(null, "new Inventor('Al 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: + attribute class: WebMethodAttribute webMethod = (WebMethodAttribute) ExpressionEvaluator.GetValue(null, "@[WebMethod(true, CacheDuration = 60, Description = 'My Web Method')]"); As you can see, with the exception of the @ prefix, syntax is exactly the same as in C#. @@ -533,29 +550,29 @@ WebMethodAttribute webMethod = (WebMethodAttribute) ExpressionEvaluator.GetValue Attribute suffix, just like the C# compiler. - + Variables Variables can referenced in the expression using the syntax #variableName. The variables are passed in and out of the expression using the dictionary parameter in - ExpressionEvaluator's GetValue - or SetValue methods. public static object GetValue(object root, string expression, IDictionary variables) + ExpressionEvaluator's GetValue + or SetValue methods. public static object GetValue(object root, string expression, IDictionary variables) public static void SetValue(object root, string expression, IDictionary variables, object newValue) The variable name is the key value of the dictionary. Example usage is - shown below; IDictionary vars = new Hashtable(); + shown below; IDictionary vars = new Hashtable(); vars["newName"] = "Mike Tesla"; ExpressionEvaluator.GetValue(tesla, "Name = #newName", vars)); 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; ExpressionEvaluator.GetValue(tesla, "{ #oldName = Name; Name = 'Nikola Tesla' }", vars); + Tesla's first name back again and keep the old value; ExpressionEvaluator.GetValue(tesla, "{ #oldName = Name; Name = 'Nikola Tesla' }", vars); String oldName = (String)vars["oldName"]; // Mike Tesla Variable names can also be used inside indexers or maps instead of - literal values. For example; vars["prez"] = "president"; + literal values. For example; vars["prez"] = "president"; Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers[#prez]", vars); - + The '#this' and '#root' variables There are two special variables that are always defined and can @@ -564,24 +581,24 @@ Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers[#prez]" The #this variable can be used to explicitly refer to the context for the node that is currently being - evaluated:// sets the name of the president and returns its instance + evaluated:// sets the name of the president and returns its instance ExpressionEvaluator.GetValue(ieee, "Officers['president'].( #this.Name = 'Nikola Tesla'; #this )") Similarly, the #root variable allows you to - refer to the root context for the expression:// removes president from the Officers dictionary and returns removed instance + refer to the root context for the expression:// removes president from the Officers dictionary and returns removed instance ExpressionEvaluator.GetValue(ieee, "Officers['president'].( #root.Officers.Remove('president'); #this )") - + Ternary Operator (If-Then-Else) You can use the ternary operator for performing if-then-else conditional logic inside the expression. A minimal example is; - String aTrueString = (String) ExpressionEvaluator.GetValue(null, "false ? 'trueExp' : 'falseExp'") // trueExp + String aTrueString = (String) ExpressionEvaluator.GetValue(null, "false ? 'trueExp' : 'falseExp'") // trueExp In this case, the boolean false results in returning the string value 'trueExp'. A less artificial example is shown below - ExpressionEvaluator.SetValue(ieee, "Name", "IEEE"); + ExpressionEvaluator.SetValue(ieee, "Name", "IEEE"); IDictionary vars = new Hashtable(); vars["queryName"] = "Nikola Tesla"; @@ -607,8 +624,8 @@ String queryResultString = (String) ExpressionEvaluator.GetValue(ieee, expressio 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 - PlaceOfBirth.City property: IList placesOfBirth = (IList) ExpressionEvaluator.GetValue(ieee, "Members.!{PlaceOfBirth.City}") // { 'Smiljan', 'Idvor' } -Or we can get the list of officers' names:IList officersNames = (IList) ExpressionEvaluator.GetValue(ieee, "Officers.Values.!{Name}") // { 'Nikola Tesla', 'Mihajlo Pupin' } + PlaceOfBirth.City property: IList placesOfBirth = (IList) ExpressionEvaluator.GetValue(ieee, "Members.!{PlaceOfBirth.City}") // { 'Smiljan', 'Idvor' } +Or we can get the list of officers' names:IList officersNames = (IList) ExpressionEvaluator.GetValue(ieee, "Officers.Values.!{Name}") // { 'Nikola Tesla', 'Mihajlo Pupin' } As you can see from the examples, projection uses @@ -620,11 +637,11 @@ String queryResultString = (String) ExpressionEvaluator.GetValue(ieee, expressio ?{projectionExpression} 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:IList serbianInventors = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{Nationality == 'Serbian'}") // { tesla, pupin } + easily get a list of Serbian inventors:IList serbianInventors = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{Nationality == 'Serbian'}") // { tesla, pupin } Or to get a list of inventors that invented - sonar:IList sonarInventors = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{'Sonar' in Inventions}") // { pupin } + sonar:IList sonarInventors = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{'Sonar' in Inventions}") // { pupin } Or we can combine selection and projection to get a list of - sonar inventors' names:IList sonarInventorsNames = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{'Sonar' in Inventions}.!{Name}") // { 'Mihajlo Pupin' } + sonar inventors' names:IList sonarInventorsNames = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{'Sonar' in Inventions}.!{Name}") // { 'Mihajlo Pupin' } As a convenience, Spring.NET Expression Language also supports a @@ -635,7 +652,7 @@ String queryResultString = (String) ExpressionEvaluator.GetValue(ieee, expressio elements were found. In order to return a first match you should prefix your selection expression with ^{ instead of ?{, and to return last match you should use - ${ prefix:ExpressionEvaluator.GetValue(ieee, "Members.^{Nationality == 'Serbian'}.Name") // 'Nikola Tesla' + ${ prefix:ExpressionEvaluator.GetValue(ieee, "Members.^{Nationality == 'Serbian'}.Name") // 'Nikola Tesla' ExpressionEvaluator.GetValue(ieee, "Members.${Nationality == 'Serbian'}.Name") // 'Mihajlo Pupin' Notice that we access the Name property directly on the selection result, because an actual matched instance is @@ -643,7 +660,7 @@ ExpressionEvaluator.GetValue(ieee, "Members.${Nationality == 'Serbian'}.Name") list. - + Collection Processors and Aggregators In addition to list projection and selection, Spring.NET @@ -670,9 +687,9 @@ ExpressionEvaluator.GetValue(ieee, "Members.${Nationality == 'Serbian'}.Name") Count or Length property depending on the context. Unlike its standard .NET counterparts, count aggregator can also be invoked on the null context - without throwing a NullReferenceException. It + without throwing a NullReferenceException. It will simply return zero in this case, which makes it much safer than - standard .NET properties within larger expression.ExpressionEvaluator.GetValue(null, "{1, 5, -3}.count()") // 3 + standard .NET properties within larger expression.ExpressionEvaluator.GetValue(null, "{1, 5, -3}.count()") // 3 ExpressionEvaluator.GetValue(null, "count()") // 0 @@ -685,7 +702,7 @@ ExpressionEvaluator.GetValue(null, "count()") // 0 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 - InvalidArgumentException.ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.sum()") // 13 (int) + InvalidArgumentException.ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.sum()") // 13 (int) ExpressionEvaluator.GetValue(null, "{5, 5.8, 12.2, 1}.sum()") // 24.0 (double) @@ -698,7 +715,7 @@ ExpressionEvaluator.GetValue(null, "{5, 5.8, 12.2, 1}.sum()") // 24.0 (double) 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 - InvalidArgumentException.ExpressionEvaluator.GetValue(null, "{1, 5, -4, 10}.average()") // 3 + InvalidArgumentException.ExpressionEvaluator.GetValue(null, "{1, 5, -4, 10}.average()") // 3 ExpressionEvaluator.GetValue(null, "{1, 5, -2, 10}.average()") // 3.5 @@ -710,9 +727,9 @@ ExpressionEvaluator.GetValue(null, "{1, 5, -2, 10}.average()") // 3.5 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 - IComparable interface. If that is not the case, + IComparable interface. If that is not the case, this aggregator will throw an - InvalidArgumentException.ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.min()") // -3 + InvalidArgumentException.ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.min()") // -3 ExpressionEvaluator.GetValue(null, "{'abc', 'efg', 'xyz'}.min()") // 'abc' @@ -724,9 +741,9 @@ ExpressionEvaluator.GetValue(null, "{'abc', 'efg', 'xyz'}.min()") // 'abc' 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 - IComparable interface. If that is not the case, + IComparable interface. If that is not the case, this aggregator will throw an - InvalidArgumentException.ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.max()") // 10 + InvalidArgumentException.ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.max()") // 10 ExpressionEvaluator.GetValue(null, "{'abc', 'efg', 'xyz'}.max()") // 'xyz' @@ -736,7 +753,7 @@ ExpressionEvaluator.GetValue(null, "{'abc', 'efg', 'xyz'}.max()") // 'xyz' A non-null processor is a very simple collection processor that eliminates all null values from the - collection.ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', null, 'abc', 'def', null}.nonNull()") // { 'abc', 'xyz', 'abc', 'def' } + collection.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' } @@ -749,7 +766,7 @@ ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', null, 'abc', 'def', null}.no an optional Boolean argument that will determine whether null values should be included in the results. The default is false, which means that - they will not be included. ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', 'abc', 'def', null, 'def' }.distinct(true).sort()") // { null, 'abc', 'def', 'xyz' } + they will not be included. 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' } @@ -758,9 +775,9 @@ ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', 'abc', 'def', null, 'def' }. Sort Processor The sort processor can be used to sort uniform collections of - elements that implement IComparable. + elements that implement IComparable. - ExpressionEvaluator.GetValue(null, "{1.2, 5.5, -3.3}.sort()") // { -3.3, 1.2, 5.5 } + 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' } @@ -775,7 +792,7 @@ ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', 'abc', 'def', null, 'def' }. The convert processor can be used to convert a collection of elements to a given Type. - object[] arr = new object[] { "0", 1, 1.1m, "1.1", 1.1f }; + object[] arr = new object[] { "0", 1, 1.1m, "1.1", 1.1f }; decimal[] result = (decimal[]) ExpressionEvaluator.GetValue(arr, "convert(decimal)"); @@ -786,7 +803,7 @@ decimal[] result = (decimal[]) ExpressionEvaluator.GetValue(arr, "convert(decima The reverse processor returns the reverse order of elements in the list - object[] arr = new object[] { "0", 1, 2.1m, "3", 4.1f }; + object[] arr = new object[] { "0", 1, 2.1m, "3", 4.1f }; object[] result = new ArrayList( (ICollection) ExpressionEvaluator.GetValue(arr, "reverse()") ).ToArray(); // { 4.1f, "3", 2.1m, 1, "0" } @@ -796,13 +813,13 @@ object[] result = new ArrayList( (ICollection) ExpressionEvaluator.GetValue(arr, Collections can be ordered in three ways, an expression, a SpEL lamda expreression, or a delegate. - // orderBy expression + // orderBy expression IExpression exp = Expression.Parse("orderBy('ToString()')"); object[] input = new object[] { 'b', 1, 2.0, "a" }; object[] ordered = exp.GetValue(input); // { 1, 2.0, "a", 'b' } -// SpEL lambda expressions +// SpEL lambda expressions IExpression exp = Expression.Parse("orderBy({|a,b| $a.ToString().CompareTo($b.ToString())})"); object[] input = new object[] { 'b', 1, 2.0, "a" }; object[] ordered = exp.GetValue(input); // { 1, 2.0, "a", 'b' } @@ -812,7 +829,7 @@ Expression.RegisterFunction( "compare", "{|a,b| $a.ToString().CompareTo($b.ToStr exp = Expression.Parse("orderBy(#compare)"); ordered = exp.GetValue(input, vars); // { 1, 2.0, "a", 'b' } -// .NET delegate +// .NET delegate private delegate int CompareCallback(object x, object y); private int CompareObjects(object x, object y) { @@ -837,7 +854,7 @@ object[] ordered = exp.GetValue(input); // { 1, 2.0, "a", 'b' } implementation that sums only the even numbers of an integer list - public class IntEvenSumCollectionProcessor : ICollectionProcessor + public class IntEvenSumCollectionProcessor : ICollectionProcessor { public object Process(ICollection source, object[] args) { @@ -874,7 +891,7 @@ object[] ordered = exp.GetValue(input); // { 1, 2.0, "a", 'b' } - + Spring Object References Expressions can refer to objects that are declared in Spring's @@ -884,7 +901,7 @@ object[] ordered = exp.GetValue(input); // { 1, 2.0, "a", 'b' } (Spring.RootContext) is used. Using the application context defined in the MovieFinder example from , the following expression returns the number of - movies directed by Roberto Benigni. public static void Main() + movies directed by Roberto Benigni. public static void Main() { . . . @@ -900,7 +917,7 @@ int numMovies = (int) ExpressionEvaluator.GetValue(null, example. - + Lambda Expressions A somewhat advanced, but a very powerful feature of Spring.NET @@ -916,7 +933,7 @@ int numMovies = (int) ExpressionEvaluator.GetValue(null, functionBody } For example, you could define a max function - and call it like this:ExpressionEvaluator.GetValue(null, "(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))", new Hashtable()) // 25 + and call it like this:ExpressionEvaluator.GetValue(null, "(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))", new Hashtable()) // 25 As you can see, any arguments defined for the expression can be referenced within the function body using a local @@ -927,7 +944,7 @@ int numMovies = (int) ExpressionEvaluator.GetValue(null, function name. Lambda expressions can be recursive, which means that you can - invoke the function within its own body:ExpressionEvaluator.GetValue(null, "(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))", new Hashtable()) // 120 + invoke the function within its own body:ExpressionEvaluator.GetValue(null, "(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))", new Hashtable()) // 120 Notice that in both examples above we had to specify a variables parameter for the @@ -945,13 +962,13 @@ int numMovies = (int) ExpressionEvaluator.GetValue(null, easy way to pre-register your lambda expressions by exposing a static Expression.RegisterFunction method, which takes function name, lambda expression and variables dictionary to register - function in as parameters:IDictionary vars = new Hashtable(); + function in as parameters:IDictionary vars = new Hashtable(); Expression.RegisterFunction("sqrt", "{|n| Math.Sqrt($n)}", vars); Expression.RegisterFunction("fact", "{|n| $n <= 1 ? 1 : $n * #fact($n-1)}", vars);Once the function registration is done, you can simply evaluate an expression that uses these functions, making sure that the vars dictionary is passed as a parameter to expression evaluation - engine:ExpressionEvaluator.GetValue(null, "#fact(5)", vars) // 120 + engine:ExpressionEvaluator.GetValue(null, "#fact(5)", vars) // 120 ExpressionEvaluator.GetValue(null, "#sqrt(9)", vars) // 3 Finally, because lambda expressions are treated as variables, they @@ -961,7 +978,7 @@ ExpressionEvaluator.GetValue(null, "#sqrt(9)", vars) // 3n that will be passed to function f as the second. Then we invoke the functions registered in the previous example, as well as the lambda - expression defined inline, through our delegate:Expression.RegisterFunction("delegate", "{|f, n| $f($n) }", vars); + expression defined inline, through our delegate: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) // 25While @@ -980,7 +997,7 @@ ExpressionEvaluator.GetValue(null, "#delegate({|n| $n ^ 2 }, 5)", vars) // 25For example, you can define a max delegate and call it like this - private delegate double DoubleFunctionTwoArgs(double arg1, double arg2); + private delegate double DoubleFunctionTwoArgs(double arg1, double arg2); private double Max(double arg1, double arg2) { @@ -1013,13 +1030,13 @@ public void DoWork() - + Classes used in the examples The following simple classes are used to demonstrate the functionality of the expression language. - public class Inventor + public class Inventor { public string Name; public string Nationality; @@ -1099,7 +1116,7 @@ public class Society The code listings in this chapter use instances of the data populated with the following information. - Inventor tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); + Inventor tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian"); tesla.Inventions = new string[] { "Telephone repeater", "Rotating magnetic field principle", diff --git a/doc/reference/src/images/Copy of S2-banner-rhs.png b/doc/reference/src/images/Copy of S2-banner-rhs.png deleted file mode 100644 index a9f6d959..00000000 Binary files a/doc/reference/src/images/Copy of S2-banner-rhs.png and /dev/null differ diff --git a/doc/reference/src/images/i21-banner-rhs.jpg b/doc/reference/src/images/i21-banner-rhs.jpg deleted file mode 100644 index 8b24a773..00000000 Binary files a/doc/reference/src/images/i21-banner-rhs.jpg and /dev/null differ diff --git a/doc/reference/src/index.xml b/doc/reference/src/index.xml index aaceb3de..8c7df74c 100644 --- a/doc/reference/src/index.xml +++ b/doc/reference/src/index.xml @@ -1,63 +1,63 @@ - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]> - - + + The Spring.NET Framework Reference Documentation Version 1.2.0 M1 @@ -81,7 +81,7 @@ Federico - Spinazzi + Spinazzi Rob @@ -110,328 +110,318 @@ 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. - + - + - &preface; - &overview; - &background; - &migration; - - Core Technologies - - - This initial part of the reference documentation covers - all of those technologies that are absolutely integral - to the Spring Framework. - - - 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. - - - The core functionality also includes an expression language - for lightweight scripting and a ui-agnostic validation framework. - - - 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. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - &objects; - &objects-misc; - &resources; - &threading; - &pool; - &misc; - &expressions; - &validation; - - &aop; - &aop-aspect-library; - &logging; - &testing; - - - Middle Tier Data Access - - - This part of the reference documentation is concerned - with othe middle tier, and specifically the data access - responsibilities of said tier. - - - 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. - - - - - - - - - - - - - - - - - - - - &transaction; - &dao; - &dbprovider; - &ado; - &orm; - - - The Web - - - This part of the reference documentation covers the - Spring Framework's support for the presentation tier, - specifically web-based presentation tiers. - - - - - - - - - - - &web; - &ajax; - - - Services - - - 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. - - - - - - - - - - - - - - - - - - - - &psa-intro; - &remoting; - &services; - &webservices; - &wcf; - + &preface; + &overview; + &background; + &migration; + + Core Technologies + + + This initial part of the reference documentation covers + all of those technologies that are absolutely integral + to the Spring Framework. + + + 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. + + + The core functionality also includes an expression language + for lightweight scripting and a ui-agnostic validation framework. + + + 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &objects; + &objects-misc; + &resources; + &threading; + &pool; + &misc; + &expressions; + &validation; + + &aop; + &aop-aspect-library; + &logging; + &testing; + + + Middle Tier Data Access + + + This part of the reference documentation is concerned + with othe middle tier, and specifically the data access + responsibilities of said tier. + + + 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. + + + + + + + + + + + + + + + + + + + + &transaction; + &dao; + &dbprovider; + &ado; + &orm; + + + The Web + + + This part of the reference documentation covers the + Spring Framework's support for the presentation tier, + specifically web-based presentation tiers. + + + + + + + + + + + &web; + &ajax; + + + Services + + + 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. + + + + + + + + + + + + + + + + + + + + &psa-intro; + &remoting; + &services; + &webservices; + &wcf; + + + Integration + + + This part of the reference documentation covers + the Spring Framework's integration with a number of + related enterprise .NET technologies. + + + + + + + + + + + + + + + + + + + + &messaging; + &msmq; + &scheduling; + + + VS.NET Integration + + + This part of the reference documentation covers + the Spring Framework's integration with VS.NET + + + + + + + + &vsnet; + + + Quickstart applications + + + This part of the reference documentation covers + the quickstart applications included with + Spring that demonstrate features in a code centric + manner. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &quickstarts; + &aop-quickstart; + &remoting-quickstart; + &web-quickstart; + &springair; + &data-quickstart; + &tx-quickstart; + &quartz-quickstart; + &nms-quickstart; + + &wcf-quickstart; + + + Spring.NET for Java developers + + + 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. + + + + + + + + &javadevelopers; + - - Integration - - - This part of the reference documentation covers - the Spring Framework's integration with a number of - related enterprise .NET technologies. - - - - - - - - - - - - - - &messaging; - &msmq; - &scheduling; - - - - VS.NET Integration - - - This part of the reference documentation covers - the Spring Framework's integration with VS.NET - - - - - - - - &vsnet; - - - Quickstart applications - - - This part of the reference documentation covers - the quickstart applications included with - Spring that demonstrate features in a code centric - manner. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - &quickstarts; - &aop-quickstart; - &remoting-quickstart; - &web-quickstart; - &springair; - &data-quickstart; - &tx-quickstart; - &quartz-quickstart; - &nms-quickstart; - &msmq-quickstart; - &wcf-quickstart; - - - Spring.NET for Java developers - - - 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. - - - - - - - - &javadevelopers; - - - - &xsd-configuration; - &xml-custom; - &xsd; + + &xsd-configuration; + &xml-custom; + &xsd; - - - - - - - - - - - - - - diff --git a/doc/reference/src/javadevelopers.xml b/doc/reference/src/javadevelopers.xml index dd570464..2d5cf2ba 100644 --- a/doc/reference/src/javadevelopers.xml +++ b/doc/reference/src/javadevelopers.xml @@ -1,8 +1,25 @@ - + + Spring.NET for Java Developers - + Introduction This chapter is to help Java developers get their sea legs using @@ -11,21 +28,21 @@ experience when you start to use Spring.NET. - + Beans to Objects 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 - <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> + <!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> Here is the corresponding file in Spring.NET - <objects xmlns="http://www.springframework.net" + <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" @@ -46,9 +63,8 @@ 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 -<property name="people"> + element is name <props> as shown below + <property name="people"> <props> <prop key="PennAndTeller">The magic property</prop> <prop key="GeorgeCarlin">The funny property</prop> @@ -58,8 +74,7 @@ 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 - -<property name="people"> + <property name="people"> <name-values> <add key="PennAndTeller" value="The magic property"/> <add key="GeorgeCarlin" value="The funny property"/> @@ -67,7 +82,7 @@ </property> - + PropertyEditors to TypeConverters PropertyEditors from the java.beans package provide the ability to @@ -90,11 +105,11 @@ approach. - + ResourceBundle-ResourceManager - + Exceptions Exceptions in Java can either be checked or unchecked. .NET supports @@ -104,7 +119,7 @@ of .NET - + Application Configuration In Spring.Java it is very common to create an ObjectFactory or @@ -135,7 +150,7 @@ without coding or using more verbose XML as would be required in the current version of Spring.Java - <?xml version="1.0" encoding="utf-8" ?> + <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> @@ -177,35 +192,35 @@ The following code segment is used to retrieve the IApplicationContext from the .NET application configuration file. - IApplicationContext ctx + IApplicationContext ctx = ConfigurationUtils.GetSection("spring/context") as IApplicationContext; In order to enforce the usage of the named configuration section spring/context the preferred instantiation mechanism is via the use of the registry class ContextRegistry as shown below - IApplicationContext ctx = ContextRegistry.GetContext(); + IApplicationContext ctx = ContextRegistry.GetContext(); - + AOP Framework - + Cannot specify target name at the end of interceptorNames for ProxyFactoryObject When configuring the list of interceptor names on a - ProxyFactoryObject instance (or object + ProxyFactoryObject instance (or object definition), one cannot specify the name of the target (i.e. the object being proxied) at the end of the list of interceptor names. This shortcut is valid in Spring - Java, where the ProxyFactoryBean will + Java, where the ProxyFactoryBean will automatically detect this, and use the last name in the interceptor - names list as the target of the ProxyFactoryBean. + names list as the target of the ProxyFactoryBean. The following configuration, which would be valid in Spring Java (barring the obvious element name changes), is not valid in Spring.NET (so don't do it). - <?xml version="1.0" encoding="utf-8" ?> + <?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"/> @@ -219,7 +234,7 @@ </objects> In Spring.NET, the InterceptorNames property of - the ProxyFactoryObject can + the ProxyFactoryObject can only be used to specify the names of interceptors. Use the TargetName property to specify the name of the target object that is to be proxied. diff --git a/doc/reference/src/logging.xml b/doc/reference/src/logging.xml index 8c67db25..0d1047f4 100644 --- a/doc/reference/src/logging.xml +++ b/doc/reference/src/logging.xml @@ -1,8 +1,25 @@ - + + Common Logging -
+
Introduction Spring uses a simple logging abstraction in order to provide a layer diff --git a/doc/reference/src/messaging.xml b/doc/reference/src/messaging.xml index 8b0a1c13..23a8558b 100644 --- a/doc/reference/src/messaging.xml +++ b/doc/reference/src/messaging.xml @@ -1,5 +1,22 @@ - + + Message Oriented Middleware
@@ -61,7 +78,7 @@ NmsTemplate, EmsTemplate (etc.) is used. Asynchronous message consumption is performed though a multi-threaded message listener container, - SimpleMessageListenerContainer. This message + SimpleMessageListenerContainer. This message listener container is used to create Message-Driven PONOs (MDPs) which refer to a messaging callback class that consists of just 'plain .NET object's and is devoid of any specific messaging types or other artifacts. @@ -73,7 +90,7 @@ Spring.Messaging.<Vendor>.Core contains the messing template class (e.g. NmsTemplate). The template class simplifies the use of the messaging APIs by handling the creation - and release of resources, much like the AdoTemplate + and release of resources, much like the AdoTemplate does for ADO.NET. The JMS inspired APIs are low-level API, much like ADO.NET. As such, even the simplest of operations requires 10s of lines of code with the bulk of that code related to resource management of @@ -205,35 +222,35 @@ Messaging Template overview Code that uses the messaging template classes - (NmsTemplate, EmsTemplate, + (NmsTemplate, EmsTemplate, etc) only needs to implement callback interfaces giving them a clearly - defined contract. The IMessageCreator callback + defined contract. The IMessageCreator callback interface creates a message given a Session provided by the calling code in NmsTemplate. In order to allow for more complex usage of the provider messaging API, the callback - ISessionCallback provides the user with the + ISessionCallback provides the user with the provider specific messaging Session and the callback - IProducerCallback exposes a provider specific + IProducerCallback exposes a provider specific Session and MessageProducer pair. Provider messaging APIs typically expose two types of send methods, one that takes delivery mode, priority, and time-to-live as quality of service (QOS) parameters and one that takes no QOS parameters which uses default values. Since there are many higher level send - methods in NmsTemplate, the setting of the QOS + methods in NmsTemplate, the setting of the QOS parameters have been exposed as properties on the template class to avoid duplication in the number of send methods. Similarly, the timeout value for synchronous receive calls is set using the property - ReceiveTimeout. + ReceiveTimeout. Instances of the NmsTemplate class are thread-safe once configured. This is important because it means that you can configure a single instance of a - NmsTemplate and then safely inject this shared + NmsTemplate and then safely inject this shared reference into multiple collaborators. To be clear, the - NmsTemplate is stateful, in that it maintains a - reference to a ConnectionFactory, but this + NmsTemplate is stateful, in that it maintains a + reference to a ConnectionFactory, but this state is not conversational state.
@@ -241,7 +258,7 @@
Connections - The NmsTemplate requires a reference to a + The NmsTemplate requires a reference to a ConnectionFactory. The ConnectionFactory serves as the entry point for working with the provider's messaging API. It is used by the client application as a factory to create connections to the messaging server @@ -268,7 +285,7 @@ creating many intermediate objects. To send a message the following 'API' walk is performed - IConnectionFactory->IConnection->ISession->IMessageProducer->Send + IConnectionFactory->IConnection->ISession->IMessageProducer->Send Between the ConnectionFactory and the Send operation there are three intermediate objects that are created and destroyed. To optimise @@ -280,15 +297,15 @@
SingleConnectionFactory - Spring.Messaging.Nms.Connections.SingleConnectionFactory - will return the same connection on all calls to + Spring.Messaging.Nms.Connections.SingleConnectionFactory + will return the same connection on all calls to CreateConnection and ignore calls to Close.
CachingConnectionFactory - Spring.Messaging.Nms.Connections.CachingConnectionFactory + Spring.Messaging.Nms.Connections.CachingConnectionFactory extends the functionality of SingleConnectionFactory and adds the caching of Sessions, MessageProducers, and MessageConsumers. @@ -298,14 +315,14 @@ than that number as sessions are cached based on their acknowledgment mode, so there can be up to 4 cached session instances when SessionCacheSize is set to one, one for each - AcknowledgementMode. - MessageProducers and - MessageConsumers are cached within their owning + AcknowledgementMode. + MessageProducers and + MessageConsumers are cached within their owning session and also take into account the unique properties of the producers and consumers when caching. - MessageProducers are cached based on - their destination. MessageConsumers are cached + MessageProducers are cached based on + their destination. MessageConsumers are cached based on a key composed of the destination, selector, noLocal delivery flag, and the durable subscription name (if creating durable consumers). @@ -323,7 +340,7 @@ administratively. You can use these vendor specific APIs to perform dependency injection on references to JMS Destination objects in Spring's XML configuration file by creating am implementation of - IObjectFactory or alternatively configuring the + IObjectFactory or alternatively configuring the specific concrete class implementation for a messaging provider. However, this approach of administered objects can be quite @@ -332,12 +349,12 @@ unique to the messaging provider. Examples of such advanced destination management would be the creation of dynamic destinations or support for a hierarchical namespace of destinations. The - NmsTemplate delegates the resolution of a + NmsTemplate delegates the resolution of a destination name to a destination object by delegating to an implementation of the interface - IDestinationResolver. - DynamicDestinationResolver is the default - implementation used by NmsTemplate and + IDestinationResolver. + DynamicDestinationResolver is the default + implementation used by NmsTemplate and accommodates resolving dynamic destinations. Quite often the destinations used in a messaging application are @@ -353,16 +370,16 @@ dynamic destinations varies from provider to provider since the properties associated with the destination are vendor specific. However, a simple implementation choice that is sometimes made by vendors is to - use the TopicSession method + use the TopicSession method CreateTopic(string topicName) or the - QueueSession method CreateQueue(string - queueName) to create a new destination with default + QueueSession method CreateQueue(string + queueName) to create a new destination with default destination properties. Depending on the vendor implementation, - DynamicDestinationResolver may then also create a + DynamicDestinationResolver may then also create a physical destination instead of only resolving one. The boolean property PubSubDomain is used to - configure the NmsTemplate with knowledge of what + configure the NmsTemplate with knowledge of what messaging 'domain' is being used. By default the value of this property is false, indicating that the point-to-point domain, Queues, will be used. This property is infrequently used as the provider messaging APIs @@ -370,10 +387,10 @@ referring to 'Destinations' rather than 'Queues' or 'Topics'. However, this property does influence the behavior of dynamic destination resolution via implementations of the - IDestinationResolver interface. + IDestinationResolver interface. You can also configure the NmsTemplate with a default destination - via the property DefaultDestination. The default + via the property DefaultDestination. The default destination will be used with send and receive operations that do not refer to a specific destination.
@@ -384,7 +401,7 @@ One of the most common uses of JMS is to concurrently process messages delivered asynchronously. A message listener container is used to receive messages from a message queue and drive the - IMessageListener that is injected into it. The + IMessageListener that is injected into it. The listener container is responsible for all threading of message reception and dispatches into the listener for processing. A message listener container is the intermediary between an Message-Driven PONO (MDP) and a @@ -396,11 +413,11 @@ infrastructure concerns to the framework. A subclass of - AbstractMessageListenerContainer is used to + AbstractMessageListenerContainer is used to receive messages from JMS and drive the Message-Driven PONOs (MDPs) that are injected into it. There are one subclasses of - AbstractMessageListenerContainer packaged with - Spring - SimpleMessageListenerContainer. + AbstractMessageListenerContainer packaged with + Spring - SimpleMessageListenerContainer. Additional subclasses, in particular to participate in distributed transactions (if the provider supports it), will be provided in future releases. SimpleMessageListenerContainer creates a fixed number of JMS @@ -415,13 +432,13 @@ manages transactions for a single ConnectionFactory. This allows messaging applications to leverage the managed transaction features of Spring as described in . The - NmsTransactionManager performs local resource + NmsTransactionManager performs local resource transactions, binding a Connection/Session pair from the specified - ConnectionFactory to the thread. NmsTemplate + ConnectionFactory to the thread. NmsTemplate automatically detects such transactional resources and operates on them accordingly. - Using Spring's SingleConnectionFactory will + Using Spring's SingleConnectionFactory will result in a shared Connection, with each transaction having its own independent Session.
@@ -430,7 +447,7 @@
Sending a Message - The NmsTemplate contains three convenience + The NmsTemplate contains three convenience methods to send a message. The methods are listed below. @@ -453,20 +470,20 @@ The method differ in how the destination is specified. In first case the JMS Destination object is specified directly. The second case specifies the destination using a string that is then resolved to a - messaging Destination object using the - IDestinationResolver associated with the template. + messaging Destination object using the + IDestinationResolver associated with the template. The last method sends the message to the destination specified by - NmsTemplate''s - DefaultDestination property. + NmsTemplate''s + DefaultDestination property. All methods take as an argument an instance of - IMessageCreator which defines the API contract for + IMessageCreator which defines the API contract for you to create the JMS message. The interface is show below - public interface IMessageCreator { + public interface IMessageCreator { IMessage CreateMessage(ISession session); }Intermediate Sessions and MessageProducers needed to send - the message are managed by NmsTemplate. The session + the message are managed by NmsTemplate. The session passed in to the method is never null. There is a similar set methods that use a delegate instead of the interface, which can be convenient when writing small implementation in .NET 2.0 using anonymous delegates. @@ -492,7 +509,7 @@ The declaration of the delegate is - public delegate IMessage MessageCreatorDelegate(ISession session); + public delegate IMessage MessageCreatorDelegate(ISession session); The following class shows how to use the SendWithDelegate method with an anonymous delegate to create a MapMessage from the supplied @@ -501,7 +518,7 @@ NmsTemplate is constructed by passing a reference to a ConnectionFactory. - public class SimplePublisher + public class SimplePublisher { private NmsTemplate template; @@ -537,15 +554,15 @@ Using MessageConverters In order to facilitate the sending of domain model objects, the - NmsTemplate has various send methods that take a + NmsTemplate has various send methods that take a .NET object as an argument for a message's data content. The overloaded - methods ConvertAndSend and - ReceiveAndConvert in - NmsTemplate delegate the conversion process to an - instance of the IMessageConverter + methods ConvertAndSend and + ReceiveAndConvert in + NmsTemplate delegate the conversion process to an + instance of the IMessageConverter interface. This interface defines a simple contract to convert between .NET objects and JMS messages. The default implementation - SimpleMessageConverter supports conversion + SimpleMessageConverter supports conversion between String and TextMessage, byte[] and BytesMesssage, and System.Collections.IDictionary and MapMessage. By using the converter, you and your application code can focus on the business object that is @@ -556,7 +573,7 @@ converts objects to an XML string and vice-versa for sending via a TextMessage. - The family of ConvertAndSend messages are + The family of ConvertAndSend messages are similar to that of the Send method with the additional argument of type IMessagePostProcessor. These methods are listed below. @@ -594,7 +611,7 @@ The example below uses the default message converter to send a Hashtable as a message to the destination "APP.STOCK". - public void PublishUsingDict(string ticker, double price) + public void PublishUsingDict(string ticker, double price) { IDictionary marketData = new Hashtable(); marketData.Add("TICKER", ticker); @@ -602,12 +619,12 @@ template.ConvertAndSend("APP.STOCK.MARKETDATA", marketData); }To accommodate the setting of message's properties, headers, and body that can not be generally encapsulated inside a converter - class, the IMessageConverterPostProcessor + class, the IMessageConverterPostProcessor interface gives you access to the message after it has been converted but before it is sent. The example below demonstrates how to modify a message header and a property after a Hashtable is converted to a message using the IMessagePostProcessor. The methods - ConvertAndSendUsingDelegate allow for the use of + ConvertAndSendUsingDelegate allow for the use of a delegate to perform message post processing. This family of methods is listed below @@ -632,11 +649,11 @@ The declaration of the delegate is - public delegate IMessage MessagePostProcessorDelegate(IMessage message); + public delegate IMessage MessagePostProcessorDelegate(IMessage message); The following code shows this in action. - public void PublishUsingDict(string ticker, double price) + public void PublishUsingDict(string ticker, double price) { IDictionary marketData = new Hashtable(); marketData.Add("TICKER", ticker); @@ -685,12 +702,12 @@ Where ISessionCallback and IProducerCallback are - public interface IProducerCallback + public interface IProducerCallback { object DoInJms(Session session, MessageProducer producer); }and - public interface ISessionCallback + public interface ISessionCallback { object DoInJms(Session session); } @@ -698,7 +715,7 @@ The delegate signatures are listed below and mirror the interface method signature - public delegate object SessionDelegate(ISession session); + public delegate object SessionDelegate(ISession session); public delegate object ProducerDelegate(ISession session, IMessageProducer producer);
@@ -712,12 +729,12 @@ public delegate object ProducerDelegate(ISession session, IMessageProducer produ While messaging middleware is typically associated with asynchronous processing, it is possible to consume messages synchronously. The overloaded Receive(..) methods on - NmsTemplate provide this functionality. During a + NmsTemplate provide this functionality. During a synchronous receive, the calling thread blocks until a message becomes available. This can be a dangerous operation since the calling thread can potentially be blocked indefinitely. The property ReceiveTimeout on - NmsTemplate specifies how long the receiver + NmsTemplate specifies how long the receiver should wait before giving up waiting for a message. The Receive methods are listed @@ -757,14 +774,14 @@ public delegate object ProducerDelegate(ISession session, IMessageProducer produ The Receive method without arguments will use the DefaultDestination. The ReceiveSelected methods apply the provided - message selector string to the MessageConsumer + message selector string to the MessageConsumer that is created. The ReceiveAndConvert methods apply the template's message converter when receiving a message. The message converter to use is set using the property - MessageConverter and is the - SimpleMessageConverter implementation by default. + MessageConverter and is the + SimpleMessageConverter implementation by default. These methods are listed below. @@ -807,7 +824,7 @@ public delegate object ProducerDelegate(ISession session, IMessageProducer produ such as the IMessageListener interface shown below, taken from the TIBCO EMS provider. - public interface IMessageListener + public interface IMessageListener { void OnMessage(Message message); } @@ -816,14 +833,14 @@ public delegate object ProducerDelegate(ISession session, IMessageProducer produ callback or even both a delegate and interface options. Apache ActiveMQ supports only the use of delegates for message reception callbacks. As a programming convenience in - Spring.Messaging.Nms.Core is an interface - IMessageListener that can be used with + Spring.Messaging.Nms.Core is an interface + IMessageListener that can be used with NMS.
Below is a simple implementation of the IMessageListener interface that processing a message. - using Spring.Messaging.Nms.Core; + using Spring.Messaging.Nms.Core; using Apache.NMS; using Common.Logging; @@ -862,15 +879,15 @@ namespace MyApp specifies various messaging configuration parameters, such as the ConnectionFactory, and the number of concurrent consumers to create. There is an abstract base class for message listener containers, - AbstractMessageListenerContainer, and one + AbstractMessageListenerContainer, and one concrete implementation, - SimpleMessageListenerContainer. - SimpleMessageListenerContainer creates a fixed + SimpleMessageListenerContainer. + SimpleMessageListenerContainer creates a fixed number of JMS Sessions/MessageConsumer pairs as set by the property ConcurrentConsumers. Here is a sample configuration - + <object id="ConnectionFactory" type="Apache.NMS.ActiveMQ.ConnectionFactory, Apache.NMS.ActiveMQ"> <constructor-arg index="0" value="tcp://localhost:61616"/> </object> @@ -895,14 +912,14 @@ namespace MyApp via the properties SubscriptionDurable and DurableSubscriptionName. You may also register an exception listener using the property - ExceptionListener. + ExceptionListener. A custom schema to create the - SimpleMessageListener container is also provided. + SimpleMessageListener container is also provided. Using this schema the configuration above looks like the following - <objects xmlns="http://www.springframework.net" + <objects xmlns="http://www.springframework.net" xmlns:nms="http://www.springframework.net/nms"> <!-- other object definitions --> @@ -913,35 +930,35 @@ namespace MyApp </objects> Exceptions that are thrown during message processing can be passed - to an implementation of IExceptionHandler and + to an implementation of IExceptionHandler and registered with the container via the property - ExceptionListener. The registered - IExceptionHandler will be invoked if the - exception is of the type NMSException (or the + ExceptionListener. The registered + IExceptionHandler will be invoked if the + exception is of the type NMSException (or the equivalent root exception type for other providers). The SimpleMessageListenerContainer will logs the exception at error level and not propagate the exception to the provider. All handling of acknowledgement and/or transactions is done by the listener container. You can override the method - HandleListenerException to change this + HandleListenerException to change this behavior. Please refer to the Spring SDK documentation for additional description of the features and properties of - SimpleMessageListenerContainer. + SimpleMessageListenerContainer.
The ISessionAwareMessageListener interface - The ISessionAwareMessageListener interface + The ISessionAwareMessageListener interface is a Spring-specific interface that provides a similar contract to the - messaging provider's IMessageListener interface + messaging provider's IMessageListener interface or Listener delegate/event, but also provides the message handling method with access to the Session from which the Message was received. - public interface ISessionAwareMessageListener + public interface ISessionAwareMessageListener { void OnMessage(IMessage message, ISession session); } @@ -950,7 +967,7 @@ namespace MyApp with the message listener container
-
+
MessageListenerAdapater The MessageListenerAdapter class is the final component in @@ -960,15 +977,15 @@ namespace MyApp Consider the following interface definition. Notice that although the interface extends neither the - IMessageListener nor - ISessionAwareMessageListener interfaces, it can + IMessageListener nor + ISessionAwareMessageListener interfaces, it can still be used as a Message-Driven PONOs (MDP) via the use of the - MessageListenerAdapter class. Notice also how the + MessageListenerAdapter class. Notice also how the various message handling methods are strongly typed according to the contents of the various Message types that they can receive and handle. - public interface MessageHandler { + public interface MessageHandler { void HandleMessage(string message); @@ -980,7 +997,7 @@ namespace MyApp and a class that implements this interface... - public class DefaultMessageHandler : IMessageHandler { + public class DefaultMessageHandler : IMessageHandler { // stub implementations elided for bevity... } @@ -989,16 +1006,16 @@ namespace MyApp messaging provider API dependencies at all. It truly is a PONO that we will make into an MDP via the following configuration. - <object id="MessagleHandler" type="MyApp.DefaultMessageHandler, MyApp"/> + <object id="MessagleHandler" type="MyApp.DefaultMessageHandler, MyApp"/> <object id="MessageListenerAdapter" type="Spring.Messaging.Nms.Listener.Adapter.MessageListenerAdapter, Spring.Messaging.Nms"> - <property name="HandlerObject" ref="MessagleHandler"/> + <property name="HandlerObject" ref="MessagleHandler"/> </object> <object id="MessageListenerContainer" type="Spring.Messaging.Nms.Listener.SimpleMessageListenerContainer, Spring.Messaging.Nms"> <property name="ConnectionFactory" ref="ConnectionFactory"/> <property name="DestinationName" value="APP.REQUEST"/> - <property name="MessageListener" ref="MessageListenerAdapter"/> + <property name="MessageListener" ref="MessageListenerAdapter"/> </object> The previous examples relies on the fact that the default @@ -1015,23 +1032,23 @@ namespace MyApp 'Receive(..)' method is strongly typed to receive and respond only to NMS ITextMessage messages. - public interface TextMessageHandler { + public interface TextMessageHandler { void Receive(ITextMessage message); } - public class TextMessageHandler implements ITextMessageHandler { + public class TextMessageHandler implements ITextMessageHandler { // implementation elided for clarity... } The configuration of the attendant - MessageListenerAdapter would look like + MessageListenerAdapter would look like this - <object id="MessagleHandler" type="MyApp.DefaultMessageHandler, MyApp"/> + <object id="MessagleHandler" type="MyApp.DefaultMessageHandler, MyApp"/> <object id="MessageListenerAdapter" type="Spring.Messaging.Nms.Listener.Adapter.MessageListenerAdapter, Spring.Messaging.Nms"> - <property name="HandlerObject" ref="TextMessagleHandler"/> + <property name="HandlerObject" ref="TextMessagleHandler"/> <property name="DefaultHandlerMethod" value="Receive"/> <!-- we don't want automatic message context extraction --> <property name="MessageConverter"> @@ -1041,18 +1058,18 @@ namespace MyApp Please note that if the above 'MessageListener' receives a Message of a type other than ITextMessage, a - ListenerExecutionFailedException will be thrown + ListenerExecutionFailedException will be thrown (and subsequently handled by the container by logging the exception). - If your IMessageConverter implementation + If your IMessageConverter implementation will return multiple object types, overloading the handler method is perfectly acceptable, the most specific matching method will be used. A method with an object signature would be consider a 'catch-all' method of last resort. For example, you can have an handler interface as shown below. - public interface IMyHandler + public interface IMyHandler { void DoWork(string text); void DoWork(OrderRequest orderRequest); @@ -1068,7 +1085,7 @@ namespace MyApp property of the original Message (if one exists) , or the default Destination set on the MessageListenerAdapter (if one has been configured). If no Destination is found then an - InvalidDestinationException will be thrown (and + InvalidDestinationException will be thrown (and please note that this exception will not be swallowed and will propagate up the call stack). @@ -1076,7 +1093,7 @@ namespace MyApp that supports multiple object types and has return values is shown below. - public interface IMyHandler + public interface IMyHandler { string DoWork(string text); OrderResponse DoWork(OrderRequest orderRequest); @@ -1120,7 +1137,7 @@ namespace MyApp more <listener/> child elements. Here is an example of a basic configuration for two listeners. - <nms:listener-container> + <nms:listener-container> <nms:listener destination="queue.orders" ref="OrderService" method="PlaceOrder"/> @@ -1166,7 +1183,7 @@ namespace MyApp role="bold">(required) The destination name for this listener, resolved - through the IDestinationResolver + through the IDestinationResolver strategy. @@ -1182,8 +1199,8 @@ namespace MyApp The name of the handler method to invoke. If the ref points to a - IMessageListener or Spring - ISessionAwareMessageListener, + IMessageListener or Spring + ISessionAwareMessageListener, this attribute may be omitted. @@ -1234,7 +1251,7 @@ namespace MyApp to define highly-customized listener containers while still benefiting from the convenience of the namespace. - <jms:listener-container connection-factory="MyConnectionFactory" + <jms:listener-container connection-factory="MyConnectionFactory" destination-resolver="MyDestinationResolver" concurrency="10"> @@ -1246,8 +1263,8 @@ namespace MyApp The following table describes all available attributes. Consult the class-level SDK documentation of the - AbstractMessageListenerContainer and its subclass - SimpleMessageListenerContainer for more detail on + AbstractMessageListenerContainer and its subclass + SimpleMessageListenerContainer for more detail on the individual properties. @@ -1272,7 +1289,7 @@ namespace MyApp connection-factoryA reference to the NMS - ConnectionFactory object (the + ConnectionFactory object (the default object name is 'ConnectionFactory'). @@ -1281,18 +1298,18 @@ namespace MyApp destination-resolverA reference to the - IDestinationResolver strategy for + IDestinationResolver strategy for resolving JMS - Destinations. + Destinations. message-converter A reference to the - IMessageConverter strategy for + IMessageConverter strategy for converting NMS Messages to listener method arguments. Default is - a SimpleMessageConverter. + a SimpleMessageConverter. @@ -1319,7 +1336,7 @@ namespace MyApp auto, client, dups-ok or transacted. A value of transacted activates a locally - transacted Session. As an + transacted Session. As an alternative, specify the transaction-manager attribute described below. Default is auto. diff --git a/doc/reference/src/migration.xml b/doc/reference/src/migration.xml index 15e714bf..383d4736 100644 --- a/doc/reference/src/migration.xml +++ b/doc/reference/src/migration.xml @@ -1,8 +1,25 @@ - + + Migrating from 1.1 M2 - + Introduction Several API changes were made after 1.1 M2 (before 1.1 RC1)due @@ -19,7 +36,7 @@ and higher - + Important Changes This section covers the common areas were you will need to make @@ -35,39 +52,39 @@ The names of the section handlers to register custom schemas has changed, from ConfigParsersSectionHandler to - NamespaceParsersSectionHandler. + NamespaceParsersSectionHandler. 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 - http://www.springframework.net/tx. + http://www.springframework.net/tx. A typical declaration to use custom schemas within your configuration file looks like this - <objects xmlns='http://www.springframework.net' + <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"> - The class XmlParserRegistry was renamed to - NamespaceParserRegistry. + The class XmlParserRegistry was renamed to + NamespaceParserRegistry. Renamed - Spring.Validation.ValidationConfigParser to - Spring.Validation.Config.ValidationNamespaceParser + Spring.Validation.ValidationConfigParser to + Spring.Validation.Config.ValidationNamespaceParser - Renamed from DatabaseConfigParser to - DatabaseNamespaceParser + Renamed from DatabaseConfigParser to + DatabaseNamespaceParser - Renamed/Moved Remoting.RemotingConfigParser + Renamed/Moved Remoting.RemotingConfigParser to - Remoting.Config.RemotingNamespaceParser + Remoting.Config.RemotingNamespaceParser A typical registration of custom parsers within your configuration file looks like this - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> @@ -85,7 +102,7 @@ A manual registration would look like this - NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser)); + NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser)); NamespaceParserRegistry.RegisterParser(typeof(DatabaseNamespaceParser)); NamespaceParserRegistry.RegisterParser(typeof(TxNamespaceParser)); diff --git a/doc/reference/src/misc.xml b/doc/reference/src/misc.xml index 2dd0779a..77546dca 100644 --- a/doc/reference/src/misc.xml +++ b/doc/reference/src/misc.xml @@ -1,4 +1,22 @@ - + + + Spring.NET miscellanea Introduction @@ -23,12 +41,10 @@ features. To do the match, you use the method: - -tatic bool Match(string pattern, string path) + static bool Match(string pattern, string path) If you want to decide if case is important or not use the method: - -tatic bool Match(string pattern, string path, bool ignoreCase) + static bool Match(string pattern, string path, bool ignoreCase) General rules @@ -60,35 +76,29 @@ tatic bool Match(string pattern, string path, bool ignoreCase) A file name can be matched using the following notation: - -foo?bar.* + foo?bar.* matches: - -fooAbar.txt + fooAbar.txt foo1bar.txt foo_bar.txt foo-bar.txt does not match: - -foo.bar.txt + foo.bar.txt foo/bar.txt foo\bar.txt The classical all files pattern: - -*.* + *.* matches: - -foo.db + foo.db .db foo foo.bar.db foo.db.db db.db.db does not match: - -c:/ + c:/ c:/foo.db c:/foo c:/.db @@ -102,48 +112,39 @@ c:/foo.foo.db A directory name can be matched at any depth level using the following notation: - -**/db/** + **/db/** That pattern matches the following paths: - -/db + /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 but does not match these: - -c:/spring/app/db-v1/foo.db + c:/spring/app/db-v1/foo.db /home/spring/spaced dir/db-v1/foo.db You can compose subdirectories to match like this: - -**/bin/**/tmp/** + **/bin/**/tmp/** That pattern matches the following paths: - -c:/spring/foo/bin/bar/tmp/a + c:/spring/foo/bin/bar/tmp/a c:/spring/foo/bin/tmp/a/b.c but does not match these: - -c:/spring/foo/bin/bar/temp/a + c:/spring/foo/bin/bar/temp/a c:/tmp/foo/bin/bar/a/b.c You can use more advanced patterns: - -**/.spring-assemblies*/** + **/.spring-assemblies*/** matches: - -c:/.spring-assemblies + c:/.spring-assemblies c:/.spring-assembliesabcd73xs c:/app/.spring-assembliesabcd73xs c:/app/.spring-assembliesabcd73xs/foo.dll //server/app/.spring-assembliesabcd73xs does not match: - -c:/app/.spring-assemblie + c:/app/.spring-assemblie @@ -153,14 +154,11 @@ c:/app/.spring-assemblie .NET is expected to be a cross-platform development ... platform. So, PathMatcher will match taking care of the case of the pattern and the case of the path. For example: - -**/db/**/*.DB + **/db/**/*.DB matches: - -c:/spring/service/deploy/app/db/foo.DB + c:/spring/service/deploy/app/db/foo.DB but does not match: - -c:/spring/service/deploy/app/DB/foo.DB + 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 @@ -169,11 +167,9 @@ c:spring/service/deploy/app/spaced dir/DB/foo.DB Back and forward slashes, in the very same cross-platform spirit, are not important: - -spring/foo.bar + spring/foo.bar matches all the following paths: - -c:\spring\foo.bar + c:\spring\foo.bar c:/spring\foo.bar c:/spring/foo.bar /spring/foo.bar diff --git a/doc/reference/src/msmq.xml b/doc/reference/src/msmq.xml index 842a1696..2d7dc409 100644 --- a/doc/reference/src/msmq.xml +++ b/doc/reference/src/msmq.xml @@ -1,5 +1,22 @@ - + + Message Oriented Middleware - MSMQ
@@ -29,29 +46,29 @@ reference to a particular middleware technology. Spring provides the 'adapter' classes that converts between the middleware world, in this case MSMQ, and the oo-world of your business processing. This is done through - the use of Spring's MessageListenerAdapter class - and IMessageConverters. + the use of Spring's MessageListenerAdapter class + and IMessageConverters. The namespace Spring.Messaging provides the core functionality for messaging. It contains the class - MessageQueueTemplate that simplifies the use of - System.Messaging.MessageQueue by handling the lack + MessageQueueTemplate that simplifies the use of + System.Messaging.MessageQueue by handling the lack of thread-safety in most of System.Messaging.MessageQueue's methods (for example Send). A single instance of - MessageQueueTemplate can be used throughout your + MessageQueueTemplate can be used throughout your application and Spring will ensure that a different instance of a - MessageQueue class is used per thread when using - MessageQueueTemplate's methods. This per-thread - instance of a System.Messaging.MessageQueue is also - available via its property MessageQueue. The - MessageQueueTemplate class is also aware of the + MessageQueue class is used per thread when using + MessageQueueTemplate's methods. This per-thread + instance of a System.Messaging.MessageQueue is also + available via its property MessageQueue. The + MessageQueueTemplate class is also aware of the presence of either an 'ambient' System.Transaction's transaction or a local - System.Messaging.MessageQueueTransaction. As such - if you use MessageQueueTemplate's send and receive + System.Messaging.MessageQueueTransaction. As such + if you use MessageQueueTemplate's send and receive methods, unlike with plain use of - System.Messaging.MessageQueue, you do not need to + System.Messaging.MessageQueue, you do not need to keep track of this information yourself and call the correct overloaded System.Messaging.MessageQueue method for a specific transaction environment. When using a @@ -79,9 +96,9 @@ configuring message listener containers and writing a callback function for message processing. On the sending side, it involves you learning how to use - MessageQueueTemplate. In both cases you will quite + MessageQueueTemplate. In both cases you will quite likely want to take advantage of using - MessageListenerConverters so you can better + MessageListenerConverters so you can better structure the translation from the System.Messaging.Message data structure to your business objects. After the initial learning hurdle, you should find that you will be much more productive leveraging Spring's helper @@ -103,12 +120,12 @@ demonstrated). On the client side you create an instance of the - MessageQueueTemplate class and configure it to use - a MessageQueue. This can be done programmatically + MessageQueueTemplate class and configure it to use + a MessageQueue. This can be done programmatically but it is common to use dependency injection and Spring's XML configuration file to configure your client class as shown below. - <object id='questionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'> + <object id='questionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'> <property name='Path' value='.\Private$\questionTxQueue'/> <property name='MessageReadPropertyFilterSetAll' value='true'/> </object> @@ -117,20 +134,20 @@ <property name="MessageQueueObjectName" value="questionTxQueue"/> </object> - <!-- Class you write --> + <!-- Class you write --> <object id="questionService" type="MyNamespace.QuestionService, MyAssembly"> <property name="MessageQueueTemplate" ref="messageQueueTemplate"/> <object> - The MessageQueue object is created via an - instance of MessageQueueFactoryObject and the - MessageQueueTemplate refers to this factory object - by name and not by reference. The SimpleSender + The MessageQueue object is created via an + instance of MessageQueueFactoryObject and the + MessageQueueTemplate refers to this factory object + by name and not by reference. The SimpleSender class looks like this - public class QuestionService : IQuestionService + public class QuestionService : IQuestionService { private MessageQueueTemplate messageQueueTemplate; @@ -146,52 +163,52 @@ } This class can be shared across multiple threads and the - MessageQueueTemplate will take care of managing + MessageQueueTemplate will take care of managing thread local access to a - System.Messaging.MessageQueue as well as any - System.Messaging.IMessageFormatter + System.Messaging.MessageQueue as well as any + System.Messaging.IMessageFormatter instances. Furthermore, since this is a transactional queue (only the name gives it away), the message will be sent using a single local messaging transaction. The conversion from the string to the underling message is - managed by an instance of the IMessageConverter + managed by an instance of the IMessageConverter class. By default an implementation that uses an - XmlMessageFormatter with a - TargetType of System.String is + XmlMessageFormatter with a + TargetType of System.String is used. You can configure the MessageQueueTemplate to use - other IMessageConveter implementations that do + other IMessageConveter implementations that do conversions above and beyond what the 'stock' - IMessageFormatters do. See the section on + IMessageFormatters do. See the section on MessageConverters for more details. On the receiving side we would like to consume the messages transactionally from the queue. Since no other database operations are being performed in our server side processing, we select the - TransactionMessageListenerContainer and configure - it to use the MessageQueueTransactionManager. The - MessageQueueTransactionManager an implementation of - Spring's IPlatformTransactionManager abstraction + TransactionMessageListenerContainer and configure + it to use the MessageQueueTransactionManager. The + MessageQueueTransactionManager an implementation of + Spring's IPlatformTransactionManager abstraction that provides a uniform API on top of various transaction manager (ADO.NET,NHibernate, MSMQ, etc). Spring's - MessageQueueTransactionManager is responsible for + MessageQueueTransactionManager is responsible for createing, committing, and rolling back a MSMQ - MessageQueueTransaction. + MessageQueueTransaction. While you can create the message listener container programmatically, we will show the declarative configuration approach below - <!-- Queue to receive from --> + <!-- Queue to receive from --> <object id='questionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'> <property name='Path' value='.\Private$\questionTxQueue'/> <property name='MessageReadPropertyFilterSetAll' value='true'/> </object> - <!-- MSMQ Transaction Manager --> + <!-- MSMQ Transaction Manager --> <object id="messageQueueTransactionManager" type="Spring.Messaging.Core.MessageQueueTransactionManager, Spring.Messaging"/> - <!-- Message Listener Container that uses MSMQ transactional for receives --> + <!-- Message Listener Container that uses MSMQ transactional for receives --> <object id="transactionalMessageListenerContainer" type="Spring.Messaging.Listener.TransactionalMessageListenerContainer, Spring.Messaging"> <property name="MessageQueueObjectName" value="questionTxQueue"/> <property name="PlatformTransactionManager" ref="messageQueueTransactionManager"/> @@ -199,23 +216,23 @@ <property name="MessageListener" ref="messageListenerAdapter"/> </object> - <!-- Adapter to call a PONO as a messaging callback --> + <!-- Adapter to call a PONO as a messaging callback --> <object id="messageListenerAdapter" type="Spring.Messaging.Listener.MessageListenerAdapter, Spring.Messaging"> <property name="HandlerObject" ref="questionHandler"/> </object> - <!-- The PONO class that you write --> + <!-- The PONO class that you write --> <object id="questionHandler" type="MyNamespace.QuestionHandler, MyAssembly"/> We have specified the queue to listen, that we want to consume the messages transactionally, process messages from the queue using 10 threads, and that our plain object that will handle the business - processing is of the type QuestionHandler. The only - class you need to write, QuestionHandler, looks + processing is of the type QuestionHandler. The only + class you need to write, QuestionHandler, looks like - public class QuestionHandler : IQuestionHandler + public class QuestionHandler : IQuestionHandler { public void HandleObject(string question) { @@ -230,7 +247,7 @@ } That is general idea. You write the sender class using - MessageQueueTemplate and the consumer class which + MessageQueueTemplate and the consumer class which does not refer to any messaging specific class. The rest is configuration of Spring provided helper classes. @@ -254,7 +271,7 @@ In the last part this 'quick tour' we will configure the message listener container to handle poison messages. This is done by creating an - instance of SendToQueueExceptionHandler and setting + instance of SendToQueueExceptionHandler and setting the property MaxRetry to be the number of exceptions or retry attempts we are willing to tolerate before taking corrective actions. In this case, the corrective action is to send the message to @@ -263,22 +280,21 @@ you will avoid automated processing of these messages and take manual corrective actions. - - <!-- The 'error' queue to send poison messages --> + + <!-- The 'error' queue to send poison messages --> <object id='errorQuestionTxQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'> <property name='Path' value='.\Private$\errorQuestionTxQueue'/> <property name='MessageReadPropertyFilterSetAll' value='true'/> </object> - <!-- Message Listener Container that uses MSMQ transactional for receives --> + <!-- Message Listener Container that uses MSMQ transactional for receives --> <object id="transactionalMessageListenerContainer" type="Spring.Messaging.Listener.TransactionalMessageListenerContainer, Spring.Messaging"> - <!-- as before but adding --> - + <!-- as before but adding --> <property name="MessageTransactionExceptionHandler" ref="messageTransactionExceptionHandler"/> </object> - <!-- Poison message handling policy --> + <!-- Poison message handling policy --> <object id="messageTransactionExceptionHandler" type="Spring.Messaging.Listener.SendToQueueExceptionHandler, Spring.Messaging"> <property name="MaxRetry" value="5"/> <property name="MessageQueueObjectName" value="errorQuestionTxQueue"/> @@ -292,8 +308,8 @@ it from the queue questionTxQueue). You can also specify that certain exceptions should commit the transaction (remove from the queue) but this is not shown here ,see below for more informatio non this functionality - The SendToQueueExceptionHandler implements the - interface IMessageTransactionExceptionHandler + The SendToQueueExceptionHandler implements the + interface IMessageTransactionExceptionHandler (discussed below) so you can write your own implementations should the provided ones not meet your needs. @@ -318,12 +334,12 @@ the MessageQueue class is available via MessageQueueTemplate's property MessageQueue. A - MessageQueueTemplate is created by passing a + MessageQueueTemplate is created by passing a reference to the name of a - MessageQueueFactoryObject, you can think of it as - a friendly name for your MessagingQueue and the + MessageQueueFactoryObject, you can think of it as + a friendly name for your MessagingQueue and the recipe of how to create an instance of it. See the following section on - MessageQueueFactoryObject for more + MessageQueueFactoryObject for more information. The MessageQueueTemplate also provides several @@ -337,25 +353,25 @@ given to the template's associated IMessageConverter implementation. This can be set using the property MessageConverter. The default implementation, - XmlMessageConverter, uses an - XmlMessageFormatter with its + XmlMessageConverter, uses an + XmlMessageFormatter with its TargetType set to - System.String. Note that - System.Messaging.IMessageFormatter classes are - also not thread safe, so MessageQueueTemplate + System.String. Note that + System.Messaging.IMessageFormatter classes are + also not thread safe, so MessageQueueTemplate ensures that thread-local instances of - IMessageConverter are used (as they generally - wrap IMessageFormatter's that are not + IMessageConverter are used (as they generally + wrap IMessageFormatter's that are not thread-safe). You can use the MessageQueueTemplate to send messages to other MessageQueues by specifying their queue 'object name', - the name of the MessageQueueFactoryObject. + the name of the MessageQueueFactoryObject. The family of overloaded ConvertAndSend and ReceiveAndConvert methods are shown below - void ConvertAndSend(object obj); + void ConvertAndSend(object obj); void ConvertAndSend(object obj, MessagePostProcessorDelegate messagePostProcessorDelegate); @@ -368,20 +384,20 @@ object ReceiveAndConvert(); object ReceiveAndConvert(string messageQueueObjectName); The transactional settings of the underlying overloaded - System.Messaging.MessageQueue Send method that + System.Messaging.MessageQueue Send method that are used are based on the following algorithm. If the message queue is transactional and there is an ambient - MessageQueueTransaction in thread local storage + MessageQueueTransaction in thread local storage (put there via the use of Spring's - MessageQueueTransactionManager or - TransactionalMessageListenerContainer), the + MessageQueueTransactionManager or + TransactionalMessageListenerContainer), the message will be sent transactionally using the - MessageQueueTransaction object in thread local + MessageQueueTransaction object in thread local storage. This lets you group together multiple messaging operations within the same transaction without having to explicitly pass around the - MessageQueueTransaction object. If the message + MessageQueueTransaction object. If the message queue is transactional but there is no ambient - MessageQueueTransaction, then a single message + MessageQueueTransaction, then a single message transaction is created on each messaging operation. (MessageQueueTransactionType = Single). If there is an ambient System.Transactions transaction then that transaction will be used @@ -389,26 +405,26 @@ object ReceiveAndConvert(string messageQueueObjectName); transactional, then a non-transactional send (MessageQueueTransactionType = None) is used. - The delegate MessagePostProcessorDelegate + The delegate MessagePostProcessorDelegate has the following signature - public delegate Message MessagePostProcessorDelegate(Message message); + public delegate Message MessagePostProcessorDelegate(Message message); This lets you modify the message after it has been converted from and object to a message using the - IMessageConverter but before it is sent. This is - useful for setting Message properties (e.g. + IMessageConverter but before it is sent. This is + useful for setting Message properties (e.g. CorrelationId, AppSpecific, TimeToReachQueue). Using anonymous delegates in .NET 2.0 makes this a very succinct coding task. If you have elaborate properties that need to be set, perhaps creating a custom - IMessageConverter would be appropriate. + IMessageConverter would be appropriate. Overloaded Send and Receive operations that use the algorithm listed above to set transactional delivery options are also available. These are listed below - Message Receive(); + Message Receive(); Message Receive(string messageQueueObjectName); @@ -419,16 +435,16 @@ void Send(string messageQueueObjectName, Message message); void Send(MessageQueue messageQueue, Message message); Note that in the last Send method that takes a - MessageQueue instance, it is the callers + MessageQueue instance, it is the callers responsibility to ensure that this instance is not accessed from multiple threads. This Send method is commonly used - when getting the MessageQueue from the + when getting the MessageQueue from the ResponseQueue property of a - Message during an asynchronous receive process. + Message during an asynchronous receive process. The receive timeout of the Receive operations is set using the ReceiveTimeout property of - MessageQueueTemplate. The default value is - MessageQueue.InfiniteTimeout (which is actually + MessageQueueTemplate. The default value is + MessageQueue.InfiniteTimeout (which is actually ~3 months). The XML configuration snippit for defining a MessageQueueTemplate @@ -439,53 +455,53 @@ void Send(MessageQueue messageQueue, Message message);
MessageQueueFactoryObject - The MessageQueueFactoryObject is - responsible for creating MessageQueue instances. + The MessageQueueFactoryObject is + responsible for creating MessageQueue instances. You configure the factory with some basic information, namely the constructor parameters you are familiar with already when creating a - standard MessageQueue instance, and then setting - MessageQueue properties, such a Label etc. Some - configuration tasks of a MessageQueue involve + standard MessageQueue instance, and then setting + MessageQueue properties, such a Label etc. Some + configuration tasks of a MessageQueue involve calling methods, for example to set which properties of the message to read. These available as properties to set on the - MessageQueueFactoryObject. An example declarative + MessageQueueFactoryObject. An example declarative configuration is shown below - <object id='testqueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'> - <!-- propeties passed to the MessageQueue constructor --> + <object id='testqueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'> + <!-- propeties passed to the MessageQueue constructor --> <property name='Path' value='.\Private$\testqueue'/> <property name='DenySharedReceive' value='true'/> <property name='AccessMode' value='Receive'/> <property name='EnableCache' value='true'/> - <!-- properties that call configuration methods on the MessageQueue --> + <!-- properties that call configuration methods on the MessageQueue --> <property name='MessageReadPropertyFilterSetAll' value='true'/> <property name='ProductTemplate'> <object> <property name='Label' value='MyLabel'/> - <!-- other MessageQueue properties can be set here --> + <!-- other MessageQueue properties can be set here --> </object> </property> </object> Whenever an object reference is made to 'testqueue' an new - instance of the MessageQueue class is created. + instance of the MessageQueue class is created. This Spring's so-called 'prototype' model, which differs from 'singleton' mode. In the singleton creation mode whenever an object reference is made to a 'testqueue' the same - MessageQueue instance would be used. So that a + MessageQueue instance would be used. So that a new instance can be retrieved based on need, the message listener containers take as an argument the name of the - MessageQueueFactoryObject and not a reference. + MessageQueueFactoryObject and not a reference. (i.e. use of 'value' instead of 'ref' in the XML). - The MessageQueueFactoryObject class is an + The MessageQueueFactoryObject class is an ideal candidate for use of a custom namespace. This will be provided in the future. This will allow you to use VS.NET IntelliSense to configure this commonly used object. An example of the potential syntax is shown below - <mq:messageQueue id="testqueue" path=".\Private$\testqueue" MessageReadPropertyFilterSetAll="true"> + <mq:messageQueue id="testqueue" path=".\Private$\testqueue" MessageReadPropertyFilterSetAll="true"> <mq:properties label="MyLabel"/> </mq:messageQueue> @@ -494,21 +510,21 @@ void Send(MessageQueue messageQueue, Message message);
MessageQueue and IMessageConverter resource management - MessageQueues and - IMessageFormatters (commonly used in - IMessageConverter implementations) are not + MessageQueues and + IMessageFormatters (commonly used in + IMessageConverter implementations) are not thread-safe. For example, only the following methods on - MessageQueue are thread-safe, + MessageQueue are thread-safe, BeginPeek, BeginReceive, EndPeek, EndReceive, GetAllMessages, Peek, and Receive. To isolate the creation logic of these classes, the factory - interface IMessageQueueFactory is used. The + interface IMessageQueueFactory is used. The interface is shown below - public interface IMessageQueueFactory + public interface IMessageQueueFactory { MessageQueue CreateMessageQueue(string messageQueueObjectName); @@ -516,26 +532,26 @@ void Send(MessageQueue messageQueue, Message message); } A provided implementation, - DefaultMessageQueueFactory will create an + DefaultMessageQueueFactory will create an instance of each class per-thread. It delegates the creation of the - MessageQueue instance to the Spring container. + MessageQueue instance to the Spring container. The argument, messageConverterObjectName, must be the id/name of a - MessageQueueFactoryObject defined in the Spring + MessageQueueFactoryObject defined in the Spring container. - DefaultMessageQueueFactory leverages + DefaultMessageQueueFactory leverages Spring's local thread storage support so it will work correctly in stand alone and web applications. - You can use the DefaultMessageQueueFactory + You can use the DefaultMessageQueueFactory independent of the rest of Spring's MSMQ support should you need only - the functionality it offers. MessageQueueTemplate + the functionality it offers. MessageQueueTemplate and the listener containers create an instance of - DefaultMessageQueueFactory by default. Should you + DefaultMessageQueueFactory by default. Should you want to share the same instance across these two classes, or provide your own custom implementation, use the property - MessageQueueFactory on either - MessageQueueTemplate or the message listener + MessageQueueFactory on either + MessageQueueTemplate or the message listener classe.s
@@ -545,8 +561,8 @@ void Send(MessageQueue messageQueue, Message message); One of the most common uses of MSMQ is to concurrently process messages delivered asynchronously. This support is provided in Spring by message listener containers. A message listener container is the - intermediary between an IMessageListener and a - MessageQueue. (Note, message listener containers + intermediary between an IMessageListener and a + MessageQueue. (Note, message listener containers are conceptually different than Spring's Inversion of Control container, though it integrates and leverages the IoC container.) The message listener container takes care of registering to receive messages, @@ -557,26 +573,26 @@ void Send(MessageQueue messageQueue, Message message); boilerplate MSMQ infrastructure concerns to the framework. A subclass of - AbstractMessageListenerContainer is used to - receive messages from a MessageQueue. Which + AbstractMessageListenerContainer is used to + receive messages from a MessageQueue. Which subclass you pick depends on your transaction processing requirements. The following subclasses are available in the namespace Spring.Messaging.Listener - NonTransactionalMessageListenerContainer + NonTransactionalMessageListenerContainer - does not surround the receive operation with a transaction - TransactionalMessageListenerContainer - + TransactionalMessageListenerContainer - surrounds the receive operation with local (non-DTC) based transaction(s). - DistributedTxMessageListenerContainer - + DistributedTxMessageListenerContainer - surrounds the receive operation with a distributed (DTC) transaction @@ -604,25 +620,25 @@ void Send(MessageQueue messageQueue, Message message); NonTransactionalMessageListenerContainer This container performs a Receive operation on the - MessageQueue without any transactional + MessageQueue without any transactional settings. As such messages will not be redelivered if an exception is thrown during message processing. Exceptions during message processing can be handled via an implementation of the interface - IExceptionHandler. This can be set via the + IExceptionHandler. This can be set via the property ExceptionHandler on the listener. The IExceptionHandler interface is shown below - public interface IExceptionHandler + public interface IExceptionHandler { void OnException(Exception exception, Message message); } An example of configuring a - NonTransactionalMessageListenerContainer with - an IExceptionHandler is shown below + NonTransactionalMessageListenerContainer with + an IExceptionHandler is shown below
- - <!-- Queue to receive from --> + + <!-- Queue to receive from --> <object id='msmqTestQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'> <property name='Path' value='.\Private$\testqueue'/> <property name='MessageReadPropertyFilterSetAll' value='true'/> @@ -633,7 +649,7 @@ void Send(MessageQueue messageQueue, Message message); </property> </object> - <!-- Queue to respond to --> + <!-- Queue to respond to --> <object id='msmqTestResponseQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'> <property name='Path' value='.\Private$\testresponsequeue'/> <property name='MessageReadPropertyFilterSetAll' value='true'/> @@ -644,7 +660,7 @@ void Send(MessageQueue messageQueue, Message message); </property> </object> - <!-- Listener container --> + <!-- Listener container --> <object id="nonTransactionalMessageListenerContainer" type="Spring.Messaging.Listener.NonTransactionalMessageListenerContainer, Spring.Messaging"> <property name="MessageQueueObjectName" value="msmqTestQueue"/> <property name="MaxConcurrentListeners" value="2"/> @@ -653,13 +669,13 @@ void Send(MessageQueue messageQueue, Message message); <property name="ExceptionHandler" ref="exceptionHandler"/> </object> - <!-- Delegate to plain .NET object for message handling --> + <!-- Delegate to plain .NET object for message handling --> <object id="messageListenerAdapter" type="Spring.Messaging.Listener.MessageListenerAdapter, Spring.Messaging"> <property name="DefaultResponseQueueName" value="msmqTestResponseQueue"/> <property name="HandlerObject" ref="simpleHandler"/> </object> - <!-- Classes you need to write --> + <!-- Classes you need to write --> <object id="simpleHandler" type="MyNamespace.SimpleHandler, MyAssembly"/> <object id="exceptionHandler" type="MyNamespace.SimpleExceptionHandler, MyAssembly"/> @@ -667,7 +683,7 @@ void Send(MessageQueue messageQueue, Message message); The SimpleHandler class would look something like this - public class SimpleHandler : ISimpleHandler + public class SimpleHandler : ISimpleHandler { public void HandleObject(string txt) { @@ -683,38 +699,38 @@ void Send(MessageQueue messageQueue, Message message); This message listener container performs receive operations within the context of local transaction. This class requires an instance of Spring's - IPlatformTransactionManager, either - AdoPlatformTransactionManager, - HibernateTransactionManager, or - MessageQueueTransactionManager. + IPlatformTransactionManager, either + AdoPlatformTransactionManager, + HibernateTransactionManager, or + MessageQueueTransactionManager. If you specify a - MessageQueueTransactionManager then a - MessageQueueTransaction will be started before + MessageQueueTransactionManager then a + MessageQueueTransaction will be started before receiving the message and used as part of the container's receive operation. As with other - IPlatformTransactionManager implementation's, + IPlatformTransactionManager implementation's, the transactional resources (in this case an instance of the - MessageQueueTransaction class) is bound to - thread local storage. MessageQueueTemplate will + MessageQueueTransaction class) is bound to + thread local storage. MessageQueueTemplate will look in thread-local storage and use this 'ambient' transaction if found for its send and receive operations. The message listener is invoked and if no exception occurs, then the - MessageQueueTransactionManager will commit the - MessageQueueTransaction. + MessageQueueTransactionManager will commit the + MessageQueueTransaction. The message listener implementation can call into service layer classes that are made transactional using standard Spring declarative transactional techniques. In case of exceptions in the service layer, the database operation will be rolled back (nothing new here), and the - TransactionalMessageListenerContainer will call - it's IMessageTransactionExceptionHandler + TransactionalMessageListenerContainer will call + it's IMessageTransactionExceptionHandler implementation to determine if the - MessageQueueTransaction should commit (removing + MessageQueueTransaction should commit (removing the message from the queue) or rollback (leaving the message on the queue for redelivery). The use of a transactional service layer in combination with - a MessageQueueTransactionManager is a + a MessageQueueTransactionManager is a powerful combination that can be used to achieve "exactly one" transaction message processing with database operations. This requires a little extra programming effort and is a more efficient @@ -738,16 +754,16 @@ void Send(MessageQueue messageQueue, Message message); exception type and vote to commit (remove from the queue) the 'outer' messaging transaction. Spring provides an exception handler with this functionality, see - SendToQueueExceptionHandler described + SendToQueueExceptionHandler described below. An example of configuring the - TransactionalMessageListenerContainer using a + TransactionalMessageListenerContainer using a MessageQueueTransactionManager is shown below - <!-- Queue to receive from --> + <!-- Queue to receive from --> <object id='msmqTestQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'> <property name='Path' value='.\Private$\testqueue'/> <property name='MessageReadPropertyFilterSetAll' value='true'/> @@ -758,7 +774,7 @@ void Send(MessageQueue messageQueue, Message message); </property> </object> - <!-- Queue to respond to --> + <!-- Queue to respond to --> <object id='msmqTestResponseQueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'> <property name='Path' value='.\Private$\testresponsequeue'/> <property name='MessageReadPropertyFilterSetAll' value='true'/> @@ -769,7 +785,7 @@ void Send(MessageQueue messageQueue, Message message); </property> </object> - <!-- Transaction Manager for MSMQ Messaging --> + <!-- Transaction Manager for MSMQ Messaging --> <object id="messageQueueTransactionManager" type="Spring.Messaging.Core.MessageQueueTransactionManager, Spring.Messaging"/> <!-- The transaction message listener container --> @@ -782,47 +798,47 @@ void Send(MessageQueue messageQueue, Message message); <property name="MessageTransactionExceptionHandler" ref="messageTransactionExceptionHandler"/> </object> - <!-- Delegate to plain .NET object for message handling --> + <!-- Delegate to plain .NET object for message handling --> <object id="messageListenerAdapter" type="Spring.Messaging.Listener.MessageListenerAdapter, Spring.Messaging"> <property name="DefaultResponseQueueName" value="msmqTestResponseQueue"/> <property name="HandlerObject" ref="simpleHandler"/> </object> - <!-- Poison message handling --> + <!-- Poison message handling --> <object id="messageTransactionExceptionHandler" type="Spring.Messaging.Listener.SendToQueueExceptionHandler, Spring.Messaging"> <property name="MaxRetry" value="5"/> <property name="MessageQueueObjectName" value="testTxErrorQueue"/> </object> - <!-- Classes you need to write --> + <!-- Classes you need to write --> <object id="simpleHandler" type="MyNamespace.SimpleHandler, MyAssembly"/> If you specify either - AdoPlatformTransactionManager or - HibernateTransactionManager then a local + AdoPlatformTransactionManager or + HibernateTransactionManager then a local database transaction will be started before the receiving the message. By default, the container will also start a local - MessageQueueTransaction after the local + MessageQueueTransaction after the local database transaction has started, but before the receiving the - message. This MessageQueueTransaction will be + message. This MessageQueueTransaction will be used to receive the message. By default the - MessageQueueTransaction will be bound to thread - local storage so that any MessageQueueTemplate + MessageQueueTransaction will be bound to thread + local storage so that any MessageQueueTemplate send or receive operations will participate transparently in the same - MessageQueueTransaction. If you do not want + MessageQueueTransaction. If you do not want this behavior set the property ExposeContainerManagedMessageQueueTransaction to false. In case of exceptions during IMessageListener processing when using either either - AdoPlatformTransactionManager or - HibernateTransactionManager the container's - IMessageTransactionExceptionHandler will - determine if the MessageQueueTransaction should + AdoPlatformTransactionManager or + HibernateTransactionManager the container's + IMessageTransactionExceptionHandler will + determine if the MessageQueueTransaction should commit (removing it from the queue) or rollback (placing it back on the queue for redelivery). The listener exception will always trigger a rollback in the 'outer' database transaction. @@ -830,10 +846,10 @@ void Send(MessageQueue messageQueue, Message message); Poison message handing, that is, the endless redelivery of a message due to exceptions during processing, can be detected using implementations of the - IMessageTransactionExceptionHandler. This + IMessageTransactionExceptionHandler. This interface is shown below - public interface IMessageTransactionExceptionHandler + public interface IMessageTransactionExceptionHandler { TransactionAction OnException(Exception exception, Message message, MessageQueueTransaction messageQueueTransaction); } @@ -842,21 +858,21 @@ void Send(MessageQueue messageQueue, Message message); Commit and Rollback. A specific implementation is provided that will move the poison message to another queue after a maximum number of redelivery attempts. See - SendToQueueExceptionHandler described below. + SendToQueueExceptionHandler described below. You can set a specific implementation to by setting - TransactionalMessageListenerContainer's + TransactionalMessageListenerContainer's property - MessageTransactionExceptionHandler + MessageTransactionExceptionHandler The IMessageTransactionExceptionHandler - implementation SendToQueueExceptionHandler + implementation SendToQueueExceptionHandler keeps track of the Message's Id property in memory with a count of how many times an exception has occurred. If that count is greater than the handler's MaxRetry count it will be sent to another queue using the provided - MessageQueueTransaction. The queue to send the + MessageQueueTransaction. The queue to send the message to is specified via the property - MessageQueueObjectName. + MessageQueueObjectName.
@@ -878,10 +894,10 @@ void Send(MessageQueue messageQueue, Message message); Exceptions in message listener processing are handled by implementations of the - IDistributedTransactionExceptionHandler + IDistributedTransactionExceptionHandler interface. This interface is shown below - public interface IDistributedTransactionExceptionHandler + public interface IDistributedTransactionExceptionHandler { bool IsPoisonMessage(Message message); @@ -899,13 +915,13 @@ void Send(MessageQueue messageQueue, Message message); Typical implementations of HandlePoisonMessage will move the poison message to another queue (under the same distributed transaction used to receive the message). The class - SendToQueueDistributedTransactionExceptionHandler + SendToQueueDistributedTransactionExceptionHandler detects poison messages by tracking the Message Id property in memory with a count of how many times an exception has occurred. If that count is greater than the handler's MaxRetry count it will be sent to another queue. The queue to send the message to is specified via the property - MessageQueueObjectName. + MessageQueueObjectName.
@@ -917,15 +933,15 @@ void Send(MessageQueue messageQueue, Message message);
Using MessageConverters In order to facilitate the sending of business model objects, the - MessageQueueTemplate has various send methods + MessageQueueTemplate has various send methods that take a .NET object as an argument for a message's data content. The overloaded methods ConvertAndSend and ReceiveAndConvert in - MessageQueue delegate the conversion process to - an instance of the IMessageConverter + MessageQueue delegate the conversion process to + an instance of the IMessageConverter interface. This interface defines a simple contract to convert between .NET objects and JMS messages. The interface is shown below - public interface IMessageConverter : ICloneable + public interface IMessageConverter : ICloneable { Message ToMessage(object obj); @@ -933,36 +949,36 @@ void Send(MessageQueue messageQueue, Message message); } There are a standard implementations provided the simply wrap - existing IMessageFormatter + existing IMessageFormatter implementations. - XmlMessageConverter - uses a + XmlMessageConverter - uses a XmlMessageFormatter. - BinaryMessageConverter - uses a + BinaryMessageConverter - uses a BinaryMessageFormatter - ActiveXMessageConverter - uses a + ActiveXMessageConverter - uses a ActiveXMessageFormatter The default implementation used in - MessageQueueTemplate and the message listener + MessageQueueTemplate and the message listener containers is an instance of XmlMessageConverter configured with a TargetType to be System.String. You specify the types that the XmlMessageConverter can convert though either the array property - TargetTypes or - TargetTypeNames. Here is an example taken from + TargetTypes or + TargetTypeNames. Here is an example taken from the QuickStart application - <object id="xmlMessageConverter" type="Spring.Messaging.Support.Converters.XmlMessageConverter, Spring.Messaging"> + <object id="xmlMessageConverter" type="Spring.Messaging.Support.Converters.XmlMessageConverter, Spring.Messaging"> <property name="TargetTypes"> <list> <value>Spring.MsmqQuickStart.Common.Data.TradeRequest, Spring.MsmqQuickStart.Common</value> @@ -972,17 +988,17 @@ void Send(MessageQueue messageQueue, Message message); </property> </object> - You can specify other IMessageConverter + You can specify other IMessageConverter implementations using the - MessageConverterObjectName property on the - MessageQueueTemplate and - MessageListenerAdapter. + MessageConverterObjectName property on the + MessageQueueTemplate and + MessageListenerAdapter. Other implementations provided are - XmlDocumentConverter - loads and saves + XmlDocumentConverter - loads and saves an XmlDocument to the message BodyStream. This lets you manipulate directly the XML data independent of type serialization issues. This is quite useful if you use XPath expressions to pick out the @@ -1022,20 +1038,20 @@ void Send(MessageQueue messageQueue, Message message);
MessageListenerAdapater - The MessageListenerAdapter allows methods + The MessageListenerAdapter allows methods of a class that does not implement the - IMessageListener interface to be invoked upon + IMessageListener interface to be invoked upon message delivery. Lets call this class the 'message handler' class. To - achieve this goal the MessageListenerAdapter - implements the standard IMessageListener + achieve this goal the MessageListenerAdapter + implements the standard IMessageListener interface to receive a message and then delegates the processing to the message handler class. Since the message handler class does not contain methods that refer to MSMQ artifacts such as Message, the - MessageListenerAdapter uses a - IMessageConverter to bridge the MSMQ and 'plain + MessageListenerAdapter uses a + IMessageConverter to bridge the MSMQ and 'plain object' worlds. As a reminder, the default - XmlMessageConverter used in - MessageQueueTemplate and the message listener + XmlMessageConverter used in + MessageQueueTemplate and the message listener containers converts from Message to string. Once the incoming message is converted to an object (string for example) a method with the name 'HandleMessage' is invoked via reflection passing in the string as an @@ -1045,7 +1061,7 @@ void Send(MessageQueue messageQueue, Message message); message listeners, a simple string based message handler would look like this. - public class MyHandler + public class MyHandler { public void HandleMessage(string text) @@ -1059,7 +1075,7 @@ void Send(MessageQueue messageQueue, Message message); the handler method name has been changed to "DoWork", by setting the adapter's property DefaultHandlerMethod. - public interface IMyHandler + public interface IMyHandler { void DoWork(string text); } @@ -1070,7 +1086,7 @@ void Send(MessageQueue messageQueue, Message message); object signature would be consider a 'catch-all' method of last resort. - public interface IMyHandler + public interface IMyHandler { void DoWork(string text); void DoWork(OrderRequest orderRequest); @@ -1079,24 +1095,24 @@ void Send(MessageQueue messageQueue, Message message); } Another of the capabilities of the - MessageListenerAdapter class is the ability to - automatically send back a response Message if a + MessageListenerAdapter class is the ability to + automatically send back a response Message if a handler method returns a non-void value. Any non-null value that is returned from the execution of the handler method will (in the default configuration) be converted to a string. The resulting string will then be sent to the ResponseQueue defined in the Message's ResponseQueue property of the original Message, or the DefaultResponseQueueName on the - MessageListenerAdapter (if one has been + MessageListenerAdapter (if one has been configured) will be used. If not ResponseQueue is - found then an Spring MessagingException will be + found then an Spring MessagingException will be thrown. Please note that this exception will not be swallowed and will propagate up the call stack. Here is an example of Handler signatures that have various return types. - public interface IMyHandler + public interface IMyHandler { string DoWork(string text); OrderResponse DoWork(OrderRequest orderRequest); @@ -1108,7 +1124,7 @@ void Send(MessageQueue messageQueue, Message message); process incoming MSMQ messages using the default message converter. - <!-- Delegate to plain .NET object for message handling --> + <!-- Delegate to plain .NET object for message handling --> <object id="messageListenerAdapter" type="Spring.Messaging.Listener.MessageListenerAdapter, Spring.Messaging"> <property name="DefaultResponseQueueName" value="msmqTestResponseQueue"/> <property name="HandlerObject" ref="myHandler"/> diff --git a/doc/reference/src/navigation.xml b/doc/reference/src/navigation.xml index 391d9f9c..85b12478 100644 --- a/doc/reference/src/navigation.xml +++ b/doc/reference/src/navigation.xml @@ -1,6 +1,24 @@ - + + + Object Navigation - + Introduction (Available in 1.0) Spring provides an expression language that allows for the easy setting @@ -19,11 +37,11 @@ contained in the Spring.Web library uses this expression language. - + Simple Expressions Consider the simple class shown below with the - public field Name + public field Name public class Inventor { public string Name; @@ -37,21 +55,21 @@ public class Inventor } which may have been instantiated in code somewhere and had its Name - and DOB set to particular valuesInventor inventor = new Inventor(); + and DOB set to particular valuesInventor inventor = new Inventor(); inventor.Name = "Nikola Tesla"; inventor.DOB = new DateTime(1854, 10, 9); - The ObjectNavigator is the central + The ObjectNavigator is the central class used to set or retrieve the value of an object, and contains the following static methods - + 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) - To retrieve the name and year of birth we can use the following code + To retrieve the name and year of birth we can use the following code string name = (string) ObjectNavigator.GetValue(inventor, "Name"); int year = (int) ObjectNavigator.GetValue(inventor, "DOB.Year"); @@ -61,13 +79,13 @@ int year = (int) ObjectNavigator.GetValue(inventor, "DOB.Year"); evaluate a complex expression frequently, creating a NavigationExpression and reusing it will increase performance. To set the property values of this - object instance to that of another famous inventor we would write + object instance to that of another famous inventor we would write ObjectNavigator.SetValue(inventor, "Name", "Michael Pupin"); ObjectNavigator.SetValue(inventor, "DOB", new DateTime(1854, 10, 9)); - + Navigating Collections TODO. TestCase shows some example usage. Please check the Spring.NET website for the latest updates to this document. diff --git a/doc/reference/src/nms-quickstart.xml b/doc/reference/src/nms-quickstart.xml index 17fcfe0f..c8db8f1a 100644 --- a/doc/reference/src/nms-quickstart.xml +++ b/doc/reference/src/nms-quickstart.xml @@ -1,5 +1,22 @@ - + + NMS QuickStart
@@ -58,51 +75,51 @@ Queues are shown in red and topics in green.
-
+
Gateways Gateways represent the service operation to send a message. The client will send a stock request to the server based on the contract - defined by the IStockService interface . + defined by the IStockService interface . - public interface IStockService + public interface IStockService { void Send(TradeRequest tradeRequest); } The server will send market data to the clients based on the - contract defined by the IMarketDataService + contract defined by the IMarketDataService interface. - public interface IMarketDataService + public interface IMarketDataService { void SendMarketData(); } The market data gateway has no method parameters as it is assumed that implementations will manage the data to send internally. The - TradeRequest object is one of the data objects that + TradeRequest object is one of the data objects that will be exchanged in the application and is discussed in the next section. The use of interfaces allows for multiple implementations to be created. Implementations that use messaging to communicate will be based - on the Spring's NmsGateway class and will be + on the Spring's NmsGateway class and will be discussed later. stub or mock implementations can be used for testing purposes.
-
+
Message Data - The TradeRequest object shown above contains + The TradeRequest object shown above contains all the information required to process a stock order. To promote the interoperability of this data across different platforms the - TradeRequest class is generated from an XML Schema + TradeRequest class is generated from an XML Schema using Microsoft's Schema Definition Tool (xsd.exe). The schema for trade request is shown below - <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" + <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://www.springframework.net/nms/common/2008-08-05"> <xs:element name="TradeRequest"> @@ -127,7 +144,7 @@ properties for each of the element names. A partial code listing of the TradeRequest class is shown below - // This code was generated by a tool. + // This code was generated by a tool. public partial class TradeRequest { public string Ticker { @@ -152,16 +169,16 @@ } - The schema and the TradeRequest class are - located in the project Spring.NmsQuickStart.Common. + The schema and the TradeRequest class are + located in the project Spring.NmsQuickStart.Common. This common project will be shared between the server and client for convenience. When sending a response back to the client the type - TradeResponse will be used. The schema for the - TradeResponse is shown below + TradeResponse will be used. The schema for the + TradeResponse is shown below - <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" + <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://www.springframework.net/nms/common/2008-08-05"> <xs:element name="TradeResponse"> @@ -179,10 +196,10 @@ </xs:schema> - The TradeResponse type also generated from a + The TradeResponse type also generated from a schema using xsd.exe. A partial code listing is shown below - // This code was generated by a tool. + // This code was generated by a tool. public partial class TradeResponse { @@ -212,15 +229,15 @@ structure.
-
+
Message Handlers - When the TradeRequest message is received by + When the TradeRequest message is received by the server, it will be handled by the class - Spring.NmsQuickStart.Server.Handlers.StockAppHandler - shown below + Spring.NmsQuickStart.Server.Handlers.StockAppHandler + shown below - public class StockAppHandler + public class StockAppHandler { private IExecutionVenueService executionVenueService; @@ -260,7 +277,7 @@ client is the class Spring.NmsQuickStart.Client.Handlers.StockAppHandler and is shown below. - public class StockAppHandler + public class StockAppHandler { // definition of stockController omitted for brevity. @@ -292,10 +309,10 @@ Message Converters The implementation of IMessageConverter used is - Spring.NmsQuickStart.Common.Converters.XmlMessageConverter. + Spring.NmsQuickStart.Common.Converters.XmlMessageConverter. This converter adds the ability to marshal and unmarshal objects to and from XML strings. It also uses Spring's - SimpleMessageConverter to convert Hashtables, + SimpleMessageConverter to convert Hashtables, strings, and byte arrays. In order to pass information about the serialized type, type information is put in the message properties. The type information can be either the class name or an integer value @@ -305,7 +322,7 @@ The XML configuration used to configure these objects is shown below - <object name="XmlMessageConverter" type="Spring.NmsQuickStart.Common.Converters.XmlMessageConverter, Spring.NmsQuickStart.Common"> + <object name="XmlMessageConverter" type="Spring.NmsQuickStart.Common.Converters.XmlMessageConverter, Spring.NmsQuickStart.Common"> <property name="TypeMapper" ref="TypeMapper"/> </object> @@ -323,11 +340,11 @@ Messaging Infrastructure The implementations of the gateway interfaces inherit from Spring's - helper class NmsGatewaySupport in order to get easy + helper class NmsGatewaySupport in order to get easy access to a NmsTemplate for sending. The implementation of the - IStockService interface is shown below + IStockService interface is shown below - public class NmsStockServiceGateway : NmsGatewaySupport, IStockService + public class NmsStockServiceGateway : NmsGatewaySupport, IStockService { private IDestination defaultReplyToQueue; @@ -347,7 +364,7 @@ } } - The Send method is using NmsTemplate's + The Send method is using NmsTemplate's ConvertAndSendWithDelegate(object obj, MessagePostProcessorDelegate messagePostProcessorDelegate) method. The anonymous delegate allows you to modify the message @@ -357,12 +374,13 @@ logic to the converted message. The object definition for the - NmsStockServiceGateway is shown below along with + NmsStockServiceGateway is shown below along with its dependent object definitions of NmsTemplate and the ConnectionFactory. - <object name="StockServiceGateway" type="Spring.NmsQuickStart.Client.Gateways.NmsStockServiceGateway, Spring.NmsQuickStart.Client"> - <property name="NmsTemplate" ref="NmsTemplate"/> + + <object name="StockServiceGateway" type="Spring.NmsQuickStart.Client.Gateways.NmsStockServiceGateway, Spring.NmsQuickStart.Client"> + <property name="NmsTemplate" ref="NmsTemplate"/> <property name="DefaultReplyToQueue"> <object type="Apache.NMS.ActiveMQ.Commands.ActiveMQQueue, Apache.NMS.ActiveMQ"> <constructor-arg value="APP.STOCK.JOE"/> @@ -370,32 +388,32 @@ </property> </object> - <object name="NmsTemplate" type="Spring.Messaging.Nms.Core.NmsTemplate, Spring.Messaging.Nms"> - <property name="ConnectionFactory" ref="ConnectionFactory"/> + <object name="NmsTemplate" type="Spring.Messaging.Nms.Core.NmsTemplate, Spring.Messaging.Nms"> + <property name="ConnectionFactory" ref="ConnectionFactory"/> <property name="DefaultDestinationName" value="APP.STOCK.REQUEST"/> <property name="MessageConverter" ref="XmlMessageConverter"/> </object> - <object id="ConnectionFactory" type="Apache.NMS.ActiveMQ.ConnectionFactory, Apache.NMS.ActiveMQ"> + <object id="ConnectionFactory" type="Apache.NMS.ActiveMQ.ConnectionFactory, Apache.NMS.ActiveMQ"> <constructor-arg index="0" value="tcp://localhost:61616"/> </object>In this example the 'raw' - Apache.NMS.ActiveMQ.ConnectionFactory connection + Apache.NMS.ActiveMQ.ConnectionFactory connection factory was used. It would be more efficient resource wise to use Spring's - CachingConnectionFactory wrapper class so that + CachingConnectionFactory wrapper class so that connections will not be open and closed for each message send as well as allowing for the caching of other intermediate NMS API objects such as sessions and message producers. A similar configuration is used on the server to configure the class - Spring.NmsQuickStart.Server.Gateways.MarketDataServiceGateway - that implements the IMarketDataService + Spring.NmsQuickStart.Server.Gateways.MarketDataServiceGateway + that implements the IMarketDataService interface. Since the client is also a consumer of messages, on the topic APP.STOCK.MARKETDATA and the queue APP.STOCK.JOE (for Trader Joe!), two message listener containers are defined as shown below. - <nms:listener-container connection-factory="ConnectionFactory"> + <nms:listener-container connection-factory="ConnectionFactory"> <nms:listener ref="MessageListenerAdapter" destination="APP.STOCK.JOE" /> <nms:listener ref="MessageListenerAdapter" destination="APP.STOCK.MARKETDATA" pubsub-domain="true"/> </nms:listener-container> @@ -410,7 +428,7 @@ APP.STOCK.REQUEST but set the concurrency property to 10 so that 10 threads will be consuming messages from the queue. - <nms:listener-container connection-factory="ConnectionFactory" concurrency=" <nms:listener-container connection-factory="ConnectionFactory" concurrency="10"> <nms:listener ref="MessageListenerAdapter" destination="APP.STOCK.REQUEST" /> </nms:listener-container> diff --git a/doc/reference/src/objects-misc.xml b/doc/reference/src/objects-misc.xml index 00a357ee..32fea0e2 100644 --- a/doc/reference/src/objects-misc.xml +++ b/doc/reference/src/objects-misc.xml @@ -1,50 +1,67 @@ - + + The IObjectWrapper and Type conversion - + Introduction The concepts encapsulated by the - IObjectWrapper interface are fundamental to the + IObjectWrapper 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 - IObjectWrapper directly... because this is + IObjectWrapper directly... because this is reference documentation however, we felt that some explanation of this - core interface might be right. The IObjectWrapper + core interface might be right. The IObjectWrapper 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 - IObjectWrapper addresses. + IObjectWrapper addresses. - + Manipulating objects using the IObjectWrapper One quite important concept of the Spring.Objects namespace is encapsulated in the definition - IObjectWrapper interface and its corresponding - implementation, the ObjectWrapper class. The - functionality offered by the IObjectWrapper + IObjectWrapper interface and its corresponding + implementation, the ObjectWrapper class. The + functionality offered by the IObjectWrapper includes methods to set and get property values (either individually or in bulk), get property descriptors (instances of the - System.Reflection.PropertyInfo class), and to query + System.Reflection.PropertyInfo class), and to query the readability and writability of properties. The - IObjectWrapper also offers support for nested + IObjectWrapper also offers support for nested properties, enabling the setting of properties on subproperties to an - unlimited depth. The IObjectWrapper usually isn't + unlimited depth. The IObjectWrapper usually isn't used by application code directly, but by framework classes such as the - various IObjectFactory implementations. + various IObjectFactory implementations. - The way the IObjectWrapper works is partly + The way the IObjectWrapper works is partly indicated by its name: it wraps an object to perform actions on a wrapped object instance... such actions would include the setting and getting of properties exposed on the wrapped object. Note: the concepts explained in this section are not important to you if you're not planning to work with the - IObjectWrapper directly. + IObjectWrapper directly. - + Setting and getting basic and nested properties Setting and getting properties is done using the @@ -59,7 +76,7 @@ GetPropertyValue() methods have a number of conventions for indicating the path of a property. A property path is an expression that implementations of the - IObjectWrapper interface can use to look up the + IObjectWrapper interface can use to look up the properties of the wrapped object; some examples of property paths include... @@ -109,8 +126,8 @@
Below you'll find some examples of working with the - IObjectWrapper to get and set properties. - Consider the following two classes: [C#] + IObjectWrapper to get and set properties. + Consider the following two classes: [C#] public class Company { private string name; @@ -127,7 +144,7 @@ public class Company get { return this.managingDirector; } set { this.managingDirector = value; } } -} [C#] +} [C#] public class Employee { private string name; @@ -148,8 +165,8 @@ public class Employee The following code snippets show some examples of how to retrieve and manipulate some of the properties of - IObjectWrapper-wrapped Company - and Employee instances. [C#] + IObjectWrapper-wrapped Company + and Employee instances. [C#] Company c = new Company(); IObjectWrapper owComp = new ObjectWrapper(c); // setting the company name... @@ -177,7 +194,7 @@ float salary = (float)owComp.GetPropertyValue("managingDirector.salary"); - [C#] + [C#] // ok, let's create the director and bind it to the company... Employee don = new Employee(); IObjectWrapper owDon = new ObjectWrapper(don); @@ -197,7 +214,7 @@ Console.WriteLine(don.Salary); // puts 80000 practice. - + Other features worth mentioning In addition to the features described in the preceding sections @@ -214,7 +231,7 @@ Console.WriteLine(don.Salary); // puts 80000 retrieving PropertyInfo instances: using GetPropertyInfo(string) and GetPropertyInfos() you can retrieve instances - of the System.Reflection.PropertyInfo + of the System.Reflection.PropertyInfo class, that might come in handy sometimes when you need access to the property metadata specific to the object being wrapped. @@ -222,19 +239,19 @@ Console.WriteLine(don.Salary); // puts 80000 - + Type conversion - If you associate a TypeConverter with the - definition of a custom Type using the standard .NET + If you associate a TypeConverter with the + definition of a custom Type using the standard .NET mechanism (see the example code below), Spring.NET will use the associated - TypeConverter to do the conversion.[C#] + TypeConverter to do the conversion.[C#] [TypeConverter (typeof (FooTypeConverter))] public class Foo { } - The TypeConverter class from the + The TypeConverter class from the System.ComponentModel namespace of the .NET BCL is used extensively by the various classes in the Spring.Core library, as said class ... provides a unified way of converting @@ -249,36 +266,36 @@ public class Foo For example, a date can be represented in a human readable format (such as 30th August 1984), while we're still able to convert the human readable form to the original date format or (even - better) to an instance of the System.DateTime + better) to an instance of the System.DateTime class. This behavior can be achieved by using the standard .NET idiom of - decorating a class with the TypeConverterAttribute. + decorating a class with the TypeConverterAttribute. Spring.NET also offers another means of associating a - TypeConverters with a class. You might want to do + TypeConverters with a class. You might want to do this to achieve a conversion that is not possible using standard idiom... for example, the Spring.Core library contains a custom - TypeConverter that converts comma-delimited strings + TypeConverter that converts comma-delimited strings to String array instances. Registering custom converters on an - IObjectWrapper instance gives the wrapper the + IObjectWrapper instance gives the wrapper the knowledge of how to convert properties to the desired - Type. + Type. An example of where property conversion is used in Spring.NET is the setting of properties on objects, accomplished using the aforementioned TypeConverters. When mentioning - System.String as the value of a property of some + System.String 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 System.Type) use - the RuntimeTypeConverter class to try to resolve - the property value to a Type object. The example + type of the associated property is System.Type) use + the RuntimeTypeConverter class to try to resolve + the property value to a Type object. The example below demonstrates this automatic conversion of the Example.Xml.SAXParser (a string) into the corresponding - Type instance for use in this factory-style class. - <objects xmlns="http://www.springframework.net"> + Type instance for use in this factory-style class. + <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> [C#] +</objects> [C#] public class XmlParserFactory { private Type parserClass; @@ -295,35 +312,35 @@ public class XmlParserFactory } } - + Type Conversion for Enumerations The default type converter for enumerations is the - System.ComponentModel.EnumConverter class. To + System.ComponentModel.EnumConverter class. To specify the value for an enumerated property, simply use the name of the - property. For example the TestObject class has a - property of the enumerated type FileMode. One of + property. For example the TestObject class has a + property of the enumerated type FileMode. One of the values for this enumeration is named Create. The following XML fragment shows how to configure this property - <object id="rod" type="Spring.Objects.TestObject, Spring.Core.Tests"> + <object id="rod" type="Spring.Objects.TestObject, Spring.Core.Tests"> <property name="name" value="Rod"/> <property name="FileMode" value="Create"/> </object> - + Built-in TypeConverters Spring.NET has a number of built-in - TypeConverters to make life easy. Each of those is + TypeConverters to make life easy. Each of those is listed below and they are all located in the Spring.Objects.TypeConverters namespace of the Spring.Core library. - Built-in <classname>TypeConverters</classname> + Built-in <literal>TypeConverters</literal> @@ -343,8 +360,8 @@ public class XmlParserFactory RuntimeTypeConverter Parses strings representing - System.Types to actual - System.Types and the other way + System.Types to actual + System.Types and the other way around. @@ -352,7 +369,7 @@ public class XmlParserFactory FileInfoConverter Capable of resolving strings to a - System.IO.FileInfo object. + System.IO.FileInfo object. @@ -396,7 +413,7 @@ public class XmlParserFactory Capable of resolving a two part string (resource name, assembly name) to a - System.Resources.ResourceManager + System.Resources.ResourceManager object. @@ -405,7 +422,7 @@ public class XmlParserFactory Capable of resolving a comma separated list of Red, Green, Blue integer values to a - System.Drawing.Color structure. + System.Drawing.Color structure. @@ -419,7 +436,7 @@ public class XmlParserFactory
Spring.NET uses the standard .NET mechanisms for the resolution of - System.Types, including, but not limited to + System.Types, including, but not limited to checking any configuration files associated with your application, checking the Global Assembly Cache (GAC), and assembly probing. diff --git a/doc/reference/src/objects.xml b/doc/reference/src/objects.xml index 6f4f866c..a7f02cc1 100644 --- a/doc/reference/src/objects.xml +++ b/doc/reference/src/objects.xml @@ -1,8 +1,25 @@ - + + The IoC container - + Introduction This chapter covers the Spring Framework's implementation of the @@ -12,34 +29,34 @@ principle The Spring.Core assembly provides the basis for - the Spring.NET Inversion of Control container. The IObjectFactory + the Spring.NET Inversion of Control container. The IObjectFactory interface provides an advanced configuration mechanism capable of managing - objects of any nature. The IApplicationContext - interface builds on top of the IObjectFactory (it + objects of any nature. The IApplicationContext + interface builds on top of the IObjectFactory (it is a sub-interface) and adds other functionality such as easier integration with Spring.NET's Aspect Oriented Programming (AOP) features, message resource handling (for use in internationalization), event propagation and application layer-specific context such as - WebApplicationContext for use in web + WebApplicationContext for use in web applications. - In short, the IObjectFactory provides the + In short, the IObjectFactory provides the configuration framework and basic functionality, while the - IApplicationContext adds more enterprise-centric - functionality to it. The IApplicationContext is a - complete superset of the IObjectFactory, and any - description of IObjectFactory capabilities and + IApplicationContext adds more enterprise-centric + functionality to it. The IApplicationContext is a + complete superset of the IObjectFactory, and any + description of IObjectFactory capabilities and behavior should be considered to apply to IApplicationContexts as well. This chapter is divided into two parts, with the first part covering the basic principles - that apply to both the IObjectFactory and - IApplicationContext, with the IObjectFactory and + IApplicationContext, with the second part covering those features - that apply only to the IApplicationContext + that apply only to the IApplicationContext interface. If you are new to Spring.NET or IoC containers in general, you may @@ -52,13 +69,13 @@ will fill in all the fine detail. - + Basics - containers and objects - + The container - The IObjectFactory is the actual + The IObjectFactory is the actual representation of the Spring IoC container that is responsible for instantiating, configuring, and managing a number of objects. @@ -68,19 +85,19 @@ and assembling the dependencies between these objects. There are a number of implementations of the - IObjectFactory interface that come supplied + IObjectFactory interface that come supplied straight out-of-the-box with Spring. The most commonly used - IObjectFactory implementation is the - XmlObjectFactory class. This implementation + IObjectFactory implementation is the + XmlObjectFactory class. This implementation allows you to express the objects that compose your application, and the doubtless rich interdependencies between such objects, in terms of XML. - The XmlObjectFactory takes this XML configuration + The XmlObjectFactory takes this XML configuration metadata and uses it to create a fully configured system or application. - Interaction with the IObjectFactory interface is + Interaction with the IObjectFactory interface is discussed in . Additional features offered by another implementation of - IObjectFactory, the - IApplicationContext, are discussed in section + IObjectFactory, the + IApplicationContext, are discussed in section . @@ -89,7 +106,7 @@ - + Configuration metadata As can be seen in the above image, the Spring IoC container @@ -146,12 +163,12 @@ - + Instantiating a container Instantiating a Spring IoC container is straightforward. - IApplicationContext context = new XmlApplicationContext( + IApplicationContext context = new XmlApplicationContext( "file://services.xml", "assembly://MyAssembly/MyDataAccess/data-access.xml"); @@ -165,14 +182,14 @@ IObjectFactory factory = context; ASP.NET pages.
You may be wondering what the assembly URL is all about. The above - example uses Spring.NET's IResource - abstraction. The IResource interface + abstraction. The IResource interface provides a simple and uniform interface to a wide array of IO resources that can represent themselves as - System.IO.Stream. An example for a file based + System.IO.Stream. An example for a file based resource, not using the URL syntax but an implementation of the - IResource interface for file is shown below.[C#] + IResource interface for file is shown below.[C#] IResource input = new FileSystemResource ("objects.xml"); IObjectFactory factory = new XmlObjectFactory(input); @@ -188,17 +205,17 @@ IObjectFactory factory = new XmlObjectFactory(input);
assembly://<AssemblyName>/<NameSpace>/<ResourceName> To create an embedded resource using Visual Studio you must set the Build Action of the .xml configuration file to Embedded Resource in the file property editor. Also, you will need to explicitly rebuild the project containing the configuration file if it is the only change you make between successive builds. If using NAnt to build, add a <resources> section to the csc task. For example usage, look at the Spring.Core.Tests.build file included the distribution. - The IResource abstraction is explained + The IResource abstraction is explained further in . The preferred way to create an - IApplicationContext or - IObjectFactory is to use a custom configuration + IApplicationContext or + IObjectFactory is to use a custom configuration section in the standard .NET application configuration file (one of App.config or Web.config). A custom configuration section that creates the same - IApplicationContext as the previous example is - <spring> + IApplicationContext as the previous example is + <spring> <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core"> <resource uri="file://services.xml"/> <resource uri="assembly://MyAssembly/MyDataAccess/data-access.xml"/> @@ -206,9 +223,9 @@ IObjectFactory factory = new XmlObjectFactory(input); </spring>
The context type (specified as the value of the type attribute of the context element) is wholly optional, and defaults to the - Spring.Context.Support.XmlApplicationContext + Spring.Context.Support.XmlApplicationContext class, so the following XML snippet is functionally equivalent to the - first. <spring> + first. <spring> <context> <resource uri="file://services.xml"/> <resource uri="assembly://MyAssembly/MyDataAccess/data-access.xml"/> @@ -216,18 +233,18 @@ IObjectFactory factory = new XmlObjectFactory(input); </spring> To acquire a reference to an - IApplicationContext using a custom configuration - section, one simply uses the following code; IApplicationContext ctx = ContextRegistry.GetContext(); - The ContextRegistry is used to both instantiate + IApplicationContext using a custom configuration + section, one simply uses the following code; IApplicationContext ctx = ContextRegistry.GetContext(); + The ContextRegistry is used to both instantiate the application context and to perform service locator style access to other objects. (See for more information). The glue that makes this possible is an implementation of the Base Class Library (BCL) provided - IConfigurationSectionHandler interface, namely - the Spring.Context.Support.ContextHandler class. + IConfigurationSectionHandler interface, namely + the Spring.Context.Support.ContextHandler class. The handler class needs to be registered in the configSections section of the .NET configuration file - as shown below. <configSections> + as shown below. <configSections> <sectionGroup name="spring"> <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/> </sectionGroup> @@ -237,9 +254,9 @@ IObjectFactory factory = new XmlObjectFactory(input); In some usage scenarios, user code will not have to explicitly instantiate an appropriate implementation of the - IObjectFactory interface, since Spring.NET code + IObjectFactory interface, since Spring.NET code will do it. For example, the ASP.NET web layer provides support code to - load a Spring.NET IApplicationContext + load a Spring.NET IApplicationContext automatically as part of the normal startup process of an ASP.NET web application. Similar support for WinForms applications is being investigated. @@ -262,12 +279,12 @@ IObjectFactory factory = new XmlObjectFactory(input);
Your XML object definitions can also be defined within the standard .NET application configuration file by registering the - Spring.Context.Support.DefaultSectionHandler + Spring.Context.Support.DefaultSectionHandler class as the configuration section handler for inline object definitions. This allows you to completely configure one or more - IApplicationContext instances within a single + IApplicationContext instances within a single standard .NET application configuration file as shown in the following - example. <configuration> + example. <configuration> <configSections> <sectionGroup name="spring"> @@ -292,15 +309,15 @@ IObjectFactory factory = new XmlObjectFactory(input); Other options available to structure the configuration files are described in and - . + . - The IApplicationContext can be configured + The IApplicationContext can be configured to register other resource handlers, custom parsers to integrate user-contributed XML schema into the object definitions section, type converters, and define type aliases. These features are discussed in section - + Composing XML-based configuration metadata It is often useful to split up container definitions into @@ -320,7 +337,7 @@ IObjectFactory factory = new XmlObjectFactory(input);
object elements in the file doing the importing. Let's look at a sample: - <objects xmlns="http://www.springframework.net"> + <objects xmlns="http://www.springframework.net"> <import resource="services.xml"/> <import resource="resources/messageSource.xml"/> @@ -350,7 +367,7 @@ IObjectFactory factory = new XmlObjectFactory(input); - + The Objects A Spring IoC container manages one or more objects. these objects @@ -422,7 +439,7 @@ IObjectFactory factory = new XmlObjectFactory(input); singleton or prototype - + @@ -467,15 +484,15 @@ IObjectFactory factory = new XmlObjectFactory(input);
Besides object definitions which contain information on how to - create a specific object, certain IObjectFactory + create a specific object, certain IObjectFactory implementations also permit the registration of existing objects that have been created outside the factory (by user code). The - DefaultListableObjectFactory class supports this + DefaultListableObjectFactory class supports this through the RegisterSingleton(..) method. (Typical applications solely work with objects defined through metadata object definitions though.) - + Naming objects Every object has one or more ids (also called @@ -508,14 +525,13 @@ IObjectFactory factory = new XmlObjectFactory(input);
Aliasing objects - + In an object definition itself, you may supply more than one name for the object, by using a combination of the id and name attributes as discussed in . This approach to aliasing objects has some limitations when you would like to assemble the main application configuration file from - multiple files. See for more - information. This usage pattern is common when each configuration + multiple files. This usage pattern is common when each configuration file represents a logical layer or component within the application. In this case you may want to refer to a common object dependency using a name that is specific to each file. If the common object @@ -563,7 +579,7 @@ IObjectFactory factory = new XmlObjectFactory(input);
- + Object creation An object definition essentially is a recipe for creating one or @@ -592,7 +608,7 @@ IObjectFactory factory = new XmlObjectFactory(input);
the invocation of the static factory method may be the same type or another type entirely, it doesn't matter). - + Object creation via constructor invocation When creating an object using the constructor approach, all @@ -604,10 +620,10 @@ IObjectFactory factory = new XmlObjectFactory(input);
constructor (i.e. a constructor that has no parameters) in the source code definition of your class. - The XmlObjectFactory implementation of - the IObjectFactory interface can consume object + The XmlObjectFactory implementation of + the IObjectFactory interface can consume object definitions that have been defined in XML, for example... - <object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary"/> + <object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary"/> The mechanism for supplying arguments to the constructor (if required), or setting properties of the object instance after it has @@ -615,7 +631,7 @@ IObjectFactory factory = new XmlObjectFactory(input); This XML fragment describes an object definition that will be identified by the exampleObject name, instances - of which will be of the Examples.ExampleObject + of which will be of the Examples.ExampleObject type that has been compiled into the ExamplesLibrary assembly. Take special note of the structure of the type attribute's value... the @@ -645,7 +661,7 @@ IObjectFactory factory = new XmlObjectFactory(input); to reference the nested class. For example, if the class Examples.ExampleObject had a nested class Person the XML declaration would be - <object id="exampleObject" type="Examples.ExampleObject+Person, ExamplesLibrary"/> + <object id="exampleObject" type="Examples.ExampleObject+Person, ExamplesLibrary"/> If you are defining classes that have been compiled into assemblies that are available to your application (such as the @@ -661,7 +677,7 @@ IObjectFactory factory = new XmlObjectFactory(input); (and their attendant classes) from that point forward. - + Object creation via a static factory method When defining an object which is to be created using a static @@ -679,7 +695,7 @@ IObjectFactory factory = new XmlObjectFactory(input); that the definition does not specify the type (class) of the returned object, only the type containing the factory method. In this example, CreateInstance must be a static method. - <object id="exampleObject" + <object id="exampleObject" type="Examples.ExampleObjectFactory, ExamplesLibrary" factory-method="CreateInstance"/> @@ -688,7 +704,7 @@ IObjectFactory factory = new XmlObjectFactory(input); returned from the factory, will be described shortly. - + Object creation via an instance factory method In a fashion similar to instantiation using a static factory @@ -700,7 +716,7 @@ IObjectFactory factory = new XmlObjectFactory(input); an object in the current (or parent/ancestor) container that contains the instance method that is to be invoked to create the object. The name of the factory method itself should still be set via the - 'factory-method' attribute.<!-- the factory object, which contains an instance method called 'CreateInstance' --> + 'factory-method' attribute.<!-- the factory object, which contains an instance method called 'CreateInstance' --> <object id="exampleFactory" type="..."> <!-- inject any dependencies required by this object --> </object> @@ -725,19 +741,19 @@ IObjectFactory factory = new XmlObjectFactory(input); - + Object creation of generic types Generic types can also be created in much the same manner an non-generic types. - + Object creation of generic types via constructor invocation The following examples shows the definition of simple generic types and how they can be created in Spring's XML based configuration - file. namespace GenericsPlay + file. namespace GenericsPlay { public class FilterableList<T> { @@ -765,7 +781,7 @@ IObjectFactory factory = new XmlObjectFactory(input); } } The XML configuration to create and configure this object - is shown below <object id="myFilteredIntList" type="GenericsPlay.FilterableList&lt;int>, GenericsPlay"> + is shown below <object id="myFilteredIntList" type="GenericsPlay.FilterableList&lt;int>, GenericsPlay"> <property name="Name" value="My Integer List"/> </object> There are a few items to note in terms how to specify a generic type. First, the left bracket that specifies the @@ -777,28 +793,27 @@ IObjectFactory factory = new XmlObjectFactory(input); Alternative characters used to overcome the two quirks can be implemented in the future but so far, all proposals don't seem to help clarify the text. The suggested solution to improve readability is to - use type aliases as shown below -<typeAliases> + use type aliases as shown below <typeAliases> <alias name="GenericDictionary" type=" System.Collections.Generic.Dictionary&lt;,>" /> <alias name="myDictionary" type="System.Collections.Generic.Dictionary&lt;int,string>" /> </typeAliases> - So that instead of something like this <object id="myGenericObject" + So that instead of something like this <object id="myGenericObject" type="GenericsPlay.ExampleGenericObject&lt;System.Collections.Generic.Dictionary&lt;int , string>>, GenericsPlay" /> - It can be shortened to <object id="myOtherGenericObject" + It can be shortened to <object id="myOtherGenericObject" type="GenericsPlay.ExampleGenericObject&lt;GenericDictionary&lt;int , string>>, GenericsPlay" /> - or even shorter <object id="myOtherOtherGenericObject" + or even shorter <object id="myOtherOtherGenericObject" type="GenericsPlay.ExampleGenericObject&lt;MyIntStringDictionary>, GenericsPlay" /> Refer to for additional information on using type aliases. - + Object creation of generic types via static factory method The following classes are used to demonstrate the ability to create instances of generic types that themselves are created via a - static generic factory method. public class TestGenericObject<T, U> + static generic factory method. public class TestGenericObject<T, U> { public TestGenericObject() { @@ -821,7 +836,7 @@ IObjectFactory factory = new XmlObjectFactory(input); set { someStringKeyedDictionary = value; } } -} The accompanying factory class is +} The accompanying factory class is public class TestGenericObjectFactory { public static TestGenericObject<V, W> StaticCreateInstance<V, W>() @@ -836,7 +851,7 @@ public class TestGenericObjectFactory } The XML snippet to create an instance of TestGenericObject where V is a List of integers and - W is an integer is shown below <object id="myTestGenericObject" + W is an integer is shown below <object id="myTestGenericObject" type="GenericsPlay.TestGenericObjectFactory, GenericsPlay" factory-method="StaticCreateInstance&lt;System.Collections.Generic.List&lt;int>,int>" /> The StaticCreateInstance method is responsible for @@ -844,13 +859,13 @@ public class TestGenericObjectFactory 'myTestGenericObject'. - + Object creation of generic types via instance factory method Using the class from the previous example the XML snippet to create an instance of a generic type via an instance factory method is - shown below <object id="exampleFactory" type="GenericsPlay.TestGenericObject&lt;int,string>, GenericsPlay"/> + shown below <object id="exampleFactory" type="GenericsPlay.TestGenericObject&lt;int,string>, GenericsPlay"/> <object id="anotherTestGenericObject" factory-object="exampleFactory" @@ -860,24 +875,24 @@ public class TestGenericObjectFactory - + Using the container - An IApplicationContext is essentially + An IApplicationContext is essentially nothing more than the interface for an advanced factory capable of maintaining a registry of different objects and their dependencies. The - IApplicationContext enables you to read object + IApplicationContext enables you to read object definitions and access them. You create one and read in some object definition in the XML format as follows: - IApplicationContext context = new XmlApplicationContext("file://objects.xml"); + IApplicationContext context = new XmlApplicationContext("file://objects.xml"); Basically that is all there is to it. Using GetObject(string) or the indexer [string], you can retrieve instances of your object; - the client-side view of the IApplicationContext - is simple. The IApplicationContext interface has + the client-side view of the IApplicationContext + is simple. The IApplicationContext interface has just a few other methods related to finding objects in the contianer, but ideally your application code should never use them... indeed, your application code should have no calls to the @@ -886,7 +901,7 @@ public class TestGenericObjectFactory - + Dependencies Your typical enterprise application is not made up of a single @@ -898,7 +913,7 @@ public class TestGenericObjectFactory collaborate) together to achieve some goal (usually an application that does what the end-user wants). - + Injecting dependencies The basic principle behind Dependency Injection (DI) is that @@ -920,7 +935,7 @@ public class TestGenericObjectFactory in two major variants, namely Constructor Injection and Setter Injection. - + Constructor Injection Constructor-based DI is effected by invoking a constructor with @@ -932,7 +947,7 @@ public class TestGenericObjectFactory could only be dependency injected using constructor injection. Notice that there is nothing special about this class. - public class SimpleMovieLister + public class SimpleMovieLister { // the SimpleMovieLister has a dependency on a MovieFinder private IMovieFinder movieFinder; @@ -947,7 +962,7 @@ public class TestGenericObjectFactory } - + Constructor Argument Resolution Constructor argument resolution matching occurs using the @@ -958,7 +973,7 @@ public class TestGenericObjectFactory appropriate constructor when it is being instantiated. Consider the following class: - namespace X.Y + namespace X.Y { public class Foo { @@ -975,7 +990,7 @@ public class TestGenericObjectFactory and you do not need to specify the constructor argument indexes and / or types explicitly. - <object name="Foo" type="X.Y.Foo, Example"> + <object name="Foo" type="X.Y.Foo, Example"> <constructor-arg> <object type="X.Y.Bar, Example"/> </constructor-arg> @@ -991,7 +1006,7 @@ public class TestGenericObjectFactory determine the type of the value, and so cannot match by type without help. Consider the following class: - using System; + using System; namespace SimpleApp { @@ -1009,13 +1024,13 @@ namespace SimpleApp } - + Constructor Argument Type Matching The above scenario can use type matching with simple types by explicitly specifying the type of the constructor argument using the type - attribute. For example: <object name="exampleObject" type="SimpleApp.ExampleObject, SimpleApp"> + attribute. For example: <object name="exampleObject" type="SimpleApp.ExampleObject, SimpleApp"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="string" value="42"/> </object> @@ -1153,12 +1168,12 @@ namespace SimpleApp - + Constructor Argument Index Constructor arguments can have their index specified explicitly by use of the index attribute. For - example: <object name="exampleObject" type="SimpleApp.ExampleObject, SimpleApp"> + example: <object name="exampleObject" type="SimpleApp.ExampleObject, SimpleApp"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </object>As well as solving the ambiguity problem of @@ -1168,14 +1183,14 @@ namespace SimpleApp based. - + Constructor Arguments by Name Constructor arguments can also be specified by name by using the name attribute of the <constructor-arg> element. - <object name="exampleObject" type="SimpleApp.ExampleObject, SimpleApp"> + <object name="exampleObject" type="SimpleApp.ExampleObject, SimpleApp"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/> </object> @@ -1183,7 +1198,7 @@ namespace SimpleApp - + Setter Injection Setter-based DI is realized by calling setter methods on your @@ -1193,7 +1208,7 @@ namespace SimpleApp Find below an example of a class that can only be dependency injected using pure setter injection. - public class MovieLister + public class MovieLister { private IMovieFinder movieFinder; @@ -1233,12 +1248,12 @@ namespace SimpleApp only type of DI available to you. - The IObjectFactory supports both of these + The IObjectFactory supports both of these variants for injecting dependencies into objects it manages. (It in fact also supports injecting setter-based dependencies after some dependencies have already been supplied via the constructor approach.) The configuration for the dependencies comes in the form of the - IObjectDefinition class, which is used together + IObjectDefinition class, which is used together with TypeConverters to know how to convert properties from one format to another. However, most users of Spring.NET will not be dealing with these classes directly (that is @@ -1249,11 +1264,11 @@ namespace SimpleApp Object dependency resolution generally happens as follows: - The IObjectFactory is created and + The IObjectFactory is created and initialized with a configuration which describes all the objects. Most Spring.NET users use an - IObjectFactory or - IApplicationContext variant that supports + IObjectFactory or + IApplicationContext variant that supports XML format configuration files. @@ -1282,12 +1297,12 @@ namespace SimpleApp id="object-factory-collaborators-typeconverter" />Each property or constructor argument which is a value must be able to be converted from whatever format it was specified in, to the - actual System.Type of that property or + actual System.Type of that property or constructor argument. By default Spring.NET can convert a value supplied in string format to all built-in types, such as int, long, string, bool, etc. - Spring.NET uses TypeConverter definitions + Spring.NET uses TypeConverter definitions to be able to convert string values to other, arbitrary types. Refer to for more information regarding type conversion, and how you can design @@ -1301,7 +1316,7 @@ namespace SimpleApp However, the object properties themselves are not set until the object is actually created. For those object that defined as singletons and set to be pre-instantiated (such as singleton object in an - IApplicationContext), creation happens at the + IApplicationContext), creation happens at the time that the container is created, but otherwise this is only when the object is requested. When an object actually has to be created, this will potentially cause a graph of other objects to be created, as @@ -1321,7 +1336,7 @@ namespace SimpleApp constructor injection. If you configure object for classes A and B to be injected into each other, the Spring IoC container will detect this circular reference at runtime, and throw a - ObjectCurrentlyInCreationException. + ObjectCurrentlyInCreationException. One possible solution to this issue is to edit the source code of some of your classes to be configured via setters instead of via @@ -1347,11 +1362,11 @@ namespace SimpleApp that object or one of its dependencies. This could happen if the object throws an exception as a result of a missing or invalid property, for example. This potentially delayed visibility of some - configuration issues is why IApplicationContext + configuration issues is why IApplicationContext by default pre-instantiates singleton objects. At the cost of some upfront time and memory to create these objects before they are actually needed, you find out about configuration issues when the - IApplicationContext is created, not later. If + IApplicationContext is created, not later. If you wish, you can still override this default behavior and set any of these singleton objects to lazy-load (not be preinstantiated) @@ -1366,16 +1381,16 @@ namespace SimpleApp configure' to mean that the object will be instantiated (if not a pre-instantiated singleton), all of its dependencies will be set, and the relevant lifecycle methods (such as a configured init - method or the IIntializingObject callback + method or the IIntializingObject callback method) will all be invoked. - + Some examples First, an example of using XML-based configuration metadata for setter-based DI. Find below a smallpart of a Spring XML configuration - file specifying some object definitions. <object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary"> + file specifying some object definitions. <object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary"> <!-- setter injection using the ref attribute --> <property name="objectOne" ref="anotherExampleObject"/> @@ -1385,7 +1400,7 @@ namespace SimpleApp <object id="anotherExampleObject" type="Examples.AnotherObject, ExamplesLibrary"/> <object id="yetAnotherObject" type="Examples.YetAnotherObject, ExamplesLibrary"/> - [C#] + [C#] public class ExampleObject { private AnotherObject objectOne; @@ -1410,7 +1425,7 @@ public class ExampleObject As you can see, setters have been declared to match against the properties specified in the XML file. Find below an example of using - constructor-based DI.<object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary"> + constructor-based DI.<object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary"> <constructor-arg name="objectOne" ref="anotherExampleObject"/> <constructor-arg name="objectTwo" ref="yetAnotherObject"/> <constructor-arg name="IntegerProperty" value="1"/> @@ -1418,7 +1433,7 @@ public class ExampleObject <object id="anotherExampleObject" type="Examples.AnotherObject, ExamplesLibrary"/> <object id="yetAnotherObject" type="Examples.YetAnotherObject, ExamplesLibrary"/> - [Visual Basic.NET] + [Visual Basic.NET] Public Class ExampleObject Private myObjectOne As AnotherObject @@ -1436,11 +1451,11 @@ Public Class ExampleObject End Sub End ClassAs you can see, the constructor arguments specified in the object definition will be used to pass in as arguments to the - constructor of the ExampleObject. + constructor of the ExampleObject. Now consider a variant of this where instead of using a constructor, Spring is told to call a static factory method to return - an instance of the object <object id="exampleObject" type="Examples.ExampleFactoryMethodObject, ExamplesLibrary" + an instance of the object <object id="exampleObject" type="Examples.ExampleFactoryMethodObject, ExamplesLibrary" factory-method="CreateInstance"> <constructor-arg name="objectOne" ref="anotherExampleObject"/> <constructor-arg name="objectTwo" ref="yetAnotherObject"/> @@ -1449,7 +1464,7 @@ End ClassAs you can see, the constructor arguments specified <object id="anotherExampleObject" type="Examples.AnotherObject, ExamplesLibrary"/> <object id="yetAnotherObject" type="Examples.YetAnotherObject, ExamplesLibrary"/> - [C#] + [C#] public class ExampleFactoryMethodObject { private AnotherObject objectOne; @@ -1488,7 +1503,7 @@ public class ExampleFactoryMethodObject Note that Setter Injection and Constructor Injectionare not mutually exclusive. It is perfectly reasonable to use both for a single object definition, as can be seen in the following example: - <object id="exampleObject" type="Examples.MixedIocObject, ExamplesLibrary"> + <object id="exampleObject" type="Examples.MixedIocObject, ExamplesLibrary"> <constructor-arg name="objectOne" ref="anotherExampleObject"/> <property name="objectTwo" ref="yetAnotherObject"/> <property name="IntegerProperty" value="1"/> @@ -1496,7 +1511,7 @@ public class ExampleFactoryMethodObject <object id="anotherExampleObject" type="Examples.AnotherObject, ExamplesLibrary"/> <object id="yetAnotherObject" type="Examples.YetAnotherObject, ExamplesLibrary"/> - [C#] + [C#] public class MixedIocObject { private AnotherObject objectOne; @@ -1521,7 +1536,7 @@ public class MixedIocObject - + Dependencies and configuration in detail As mentioned in the previous section, object properties and @@ -1539,16 +1554,16 @@ public class MixedIocObject The <value/> element specifies a property or constructor argument as a human-readable string representation. As mentioned previously, - TypeConverter instances are used to convert - these string values from a System.String to the + TypeConverter instances are used to convert + these string values from a System.String to the actual property or argument type. Custom - TypeConverter implementations in the + TypeConverter implementations in the Spring.Objects.TypeConverters namespace are used to augment the functionality offered by the .NET BCL's default - TypeConverter implementations. + TypeConverter implementations. In the following example, we use a - SqlConnection from the + SqlConnection from the System.Data.SqlClient namespace of the BCL. This class (like many other existing classes) can easily be used in a Spring.NET object factory, as it offers a convenient public property @@ -1556,7 +1571,7 @@ public class MixedIocObject property. - objects xmlns="http://www.springframework.net"> + <objects xmlns="http://www.springframework.net"> <object id="myConnection" type="System.Data.SqlClient.SqlConnection"> <!-- results in a call to the setter of the ConnectionString property --> <property @@ -1572,7 +1587,7 @@ public class MixedIocObject An idref element is simply a shorthand and error-proof way to set a property to the String id or name of another object in the - container.<object id="theTargetObject" type="..."> + container.<object id="theTargetObject" type="..."> . . . </object> @@ -1581,7 +1596,7 @@ public class MixedIocObject <idref object="theTargetObject"/> </property> </object>This is exactly equivalent at runtime to the - following fragment:<object id="theTargetObject" type="..."> + following fragment:<object id="theTargetObject" type="..."> . . . </object> @@ -1600,13 +1615,13 @@ public class MixedIocObject actual XML file, and the object name is the object id, the local attribute may be used, which will allow the XML parser itself to validate the - object name even earlier, at parse time. <property name="targetName"> + object name even earlier, at parse time. <property name="targetName"> <idref local="theTargetObject"/> </property> - + Referring to collaborating objects The ref element is the final element allowed @@ -1614,7 +1629,7 @@ public class MixedIocObject set the value of the specified property to be a reference to another object managed by the container, a collaborator, so to speak. As you saw in the previous example to set collection properties, we used the - SqlConnection instance from the initial example + SqlConnection instance from the initial example as a collaborator and specified it using a <ref object/> element. As mentioned in a previous section, the referred-to object is considered to be a dependency of the object who's property is being @@ -1628,14 +1643,14 @@ public class MixedIocObject Specifying the target object by using the object attribute of the ref tag is the most general form, and will allow creating a reference to any - object in the same IObjectFactory / - IApplicationContext (whether or not in the same - XML file), or parent IObjectFactory / - IApplicationContext. The value of the + object in the same IObjectFactory / + IApplicationContext (whether or not in the same + XML file), or parent IObjectFactory / + IApplicationContext. The value of the object attribute may be the same as either the id attribute of the target object, or one of the values in the name attribute of the target - object.<ref object="someObject"/> + object.<ref object="someObject"/> Specifying the target object by using the local attribute leverages the ability of the XML @@ -1645,31 +1660,31 @@ public class MixedIocObject will issue an error if no matching element is found in the same file. As such, using the local variant is the best choice (in order to know about errors are early as possible) if the target object is in the - same XML file.<ref local="someObject"/> + same XML file.<ref local="someObject"/> Specifying the target object by using the parent attribute allows a reference to be created - to an object that is in a parent IObjectFactory - (orIApplicationContext) of the current - IObjectFactory (or - IApplicationContext). The value of the + to an object that is in a parent IObjectFactory + (orIApplicationContext) of the current + IObjectFactory (or + IApplicationContext). The value of the parent attribute may be the same as either the id attribute of the target object, or one of the values in the name attribute of the target object, and the target object must be in a - parent IObjectFactory or - IApplicationContext of the current one. The + parent IObjectFactory or + IApplicationContext of the current one. The main use of this object reference variant is when there is a need to wrap an existing object in a parent context with some sort of proxy (which may have the same name as the parent), and needs the original object so it may wrap it. - <ref parent="someObject"/> + <ref parent="someObject"/> - + Inline objects An object element inside the @@ -1677,7 +1692,7 @@ public class MixedIocObject inline, instead of referring to an object defined elsewhere in the container. The inline object definition does not need to have any id or name defined (indeed, if any are defined, they will be ignored). - <object id="outer" type="..."> + <object id="outer" type="..."> <!-- Instead of using a reference to target, just use an inner object --> @@ -1690,7 +1705,7 @@ public class MixedIocObject </object> - + Setting collection values The list, set, @@ -1699,7 +1714,7 @@ public class MixedIocObject IList, ISet, NameValueCollection and IDictionary, respectively, to be defined and set. - <objects xmlns="http://www.springframework.net"> + <objects xmlns="http://www.springframework.net"> <object id="moreComplexObject" type="Example.ComplexObject"> <!-- results in a call to the setter of the SomeList (System.Collections.IList) property @@ -1761,30 +1776,30 @@ public class MixedIocObject linkend="objects-shortcutforms" /> for more information. - + Setting generic collection values Spring supports setting values for classes that expose properties based on the generic collection interfaces - IList<T> and - IDictionary<TKey, TValue>. The type + IList<T> and + IDictionary<TKey, TValue>. The type parameter for these collections is specified by using the XML attribute element-type for - IList<T> and the XML attributes + IList<T> and the XML attributes key-type and value-type for - IDictionary<TKey, TValue>. The values of + IDictionary<TKey, TValue>. The values of the collection are automaticaly converted from a string to the appropriate type. If you are using your own user-defined type as a generic type parameter you will likely need to register a custom type converter. Refer to for more information. The implementations of - IList<T> and - IDictionary<TKey, TValue> that is created - are System.Collections.Generic.List and - System.Collections.Generic.Dictionary. + IList<T> and + IDictionary<TKey, TValue> that is created + are System.Collections.Generic.List and + System.Collections.Generic.Dictionary. The following class represents a lottery ticket and demonstrates - how to set the values of a generic IList. public class LotteryTicket { + how to set the values of a generic IList. public class LotteryTicket { List<int> list; @@ -1800,7 +1815,7 @@ public class MixedIocObject set { date = value; } } } The XML fragment that can be used to configure this class - is shown below <object id="MyLotteryTicket" type="GenericsPlay.Lottery.LotteryTicket, GenericsPlay"> + is shown below <object id="MyLotteryTicket" type="GenericsPlay.Lottery.LotteryTicket, GenericsPlay"> <property name="Numbers"> <list element-type="int"> <value>11</value> @@ -1816,12 +1831,12 @@ public class MixedIocObject The following shows the definition of a more complex class that demonstrates the use of generics using the - Spring.Expressions.IExpression interface as the + Spring.Expressions.IExpression interface as the generic type parameter for the IList element-type and the value-type - for IDictionary. Spring.Expressions.IExpression + for IDictionary. Spring.Expressions.IExpression has an associated type converter, - Spring.Objects.TypeConverters.ExpressionConverter - that is already pre-registered with Spring. public class GenericExpressionHolder + Spring.Objects.TypeConverters.ExpressionConverter + that is already pre-registered with Spring. public class GenericExpressionHolder { private System.Collections.Generic.IList<IExpression> expressionsList; @@ -1850,7 +1865,7 @@ public class MixedIocObject get { return this.expressionsDictionary[key]; } } } An example XML configuration of this class is shown - below <object id="genericExpressionHolder" + below <object id="genericExpressionHolder" type="Spring.Objects.Factory.Xml.GenericExpressionHolder, Spring.Core.Tests"> <property name="ExpressionsList"> @@ -1880,16 +1895,16 @@ public class MixedIocObject </object> - + Setting null values The <null> element is used to handle null values. Spring.NET treats empty arguments for properties and constructor arguments as empty - string instances. The following configuration + string instances. The following configuration demonstrates this behaviour... - <object type="Examples.ExampleObject, ExamplesLibrary"> + <object type="Examples.ExampleObject, ExamplesLibrary"> <property name="email"><value></value></property> <!-- equivalent, using value attribute as opposed to nested <value/> element... @@ -1898,17 +1913,17 @@ public class MixedIocObject This results in the email property being set to the empty string value (""), in much the same way as can be seen in - the following snippet of C# code... exampleObject.Email = ""; + the following snippet of C# code... exampleObject.Email = ""; The special <null/> element may be used to indicate a null value; to wit... - <object type="Examples.ExampleObject, ExamplesLibrary"> + <object type="Examples.ExampleObject, ExamplesLibrary"> <property name="email"><null/></property> </object> This results in the email property being set to null, again in much the same way as can be seen in - the following snippet of C# code... exampleObject.Email = null; + the following snippet of C# code... exampleObject.Email = null; @@ -1921,7 +1936,7 @@ public class MixedIocObject property expression parser described in is used to perform the type conversion of the indexer name argument from a string in the XML file to a matching target type. As an example - consider the following class public class Person + consider the following class public class Person { private IList favoriteNames = new ArrayList(); @@ -1945,7 +1960,7 @@ public class MixedIocObject set { properties.Add(keyName, value); } } } The XML configuration snippet to populate this object with - data is shown below <object id="person" type="Test.Objects.Person, Test.Objects"> + data is shown below <object id="person" type="Test.Objects.Person, Test.Objects"> <property name="[0]" value="Master Shake"/> <property name="['one']" value="uno"/> </object> @@ -1953,7 +1968,7 @@ public class MixedIocObject The use of the property expression parser in Release 1.0.2 changed how you configure indexer properties. The following section describes this usage. The older style configuration uses the - following syntax <object id="objectWithIndexer" type="Spring.Objects.TestObject, Spring.Core.Tests"> + following syntax <object id="objectWithIndexer" type="Spring.Objects.TestObject, Spring.Core.Tests"> <property name="Item[0]" value="my string value"/> </object> You can also change the name used to identify the indexer by adorning your indexer method declaration with the @@ -1967,7 +1982,7 @@ public class MixedIocObject use the IndexerName attribute. - + Value and ref shortcut forms There are also some shortcut forms that are less verbose than @@ -1976,7 +1991,7 @@ public class MixedIocObject constructor-arg, and entry elements all support a value attribute which may be used instead of embedding a full value element. - Therefore, the following: <property name="myProperty"> + Therefore, the following: <property name="myProperty"> <value>hello</value> </property> @@ -1986,7 +2001,7 @@ public class MixedIocObject <entry key="myKey"> <value>hello</value> -</entry> are equivalent to:<property name="myProperty" value="hello"/> +</entry> are equivalent to:<property name="myProperty" value="hello"/> <constructor-arg value="hello"/> @@ -1998,13 +2013,13 @@ public class MixedIocObject constructor-arg elements support a similar shortcut ref attribute which may be used instead of a full nested ref element. Therefore, the following... - <property name="myProperty"> + <property name="myProperty"> <ref object="anotherObject"/> </property> <constructor-arg index="0"> <ref object="anotherObject"/> -</constructor-arg> is equivalent to... <property name="myProperty" ref="anotherObject"/> +</constructor-arg> is equivalent to... <property name="myProperty" ref="anotherObject"/> <constructor-arg index="0" ref="anotherObject"/> @@ -2016,18 +2031,18 @@ public class MixedIocObject Finally, the entry element allows a shortcut form the specify the key and/or value of a dictionary, in the form of key/key-ref and - value/value-ref attributes. Therefore, the following <entry> + value/value-ref attributes. Therefore, the following <entry> <key> <ref object="MyKeyObject"/> </key> <ref object="MyValueObject"/> -</entry> Is equivalent to: <entry key-ref="MyKeyObject" value-ref="MyValueObject"/> +</entry> Is equivalent to: <entry key-ref="MyKeyObject" value-ref="MyValueObject"/> As mentioned previously, the equivalence is to <ref object="xxx"> and not the local or parent forms of object references. - + Compound property names and Spring expression references @@ -2039,7 +2054,7 @@ public class MixedIocObject example, in this object definition a simple nested property name is configured - <object id="foo" type="Spring.Foo, Spring.Foo"> + <object id="foo" type="Spring.Foo, Spring.Foo"> <property name="bar.baz.name" value="Bingo"/> </object> @@ -2050,7 +2065,7 @@ public class MixedIocObject use the 'expression' element to refer to a Spring expression as the value of the property. Simple examples of this are shown below - <property name=“minValue” expression=“int.MinValue” /> + <property name=“minValue” expression=“int.MinValue” /> <property name=“weekFromToday” expression="DateTime.Today + 7"/> @@ -2059,7 +2074,7 @@ public class MixedIocObject configuraiton. - + Using <literal>depends-on</literal> For most situations, the fact that an object is a dependency of @@ -2072,7 +2087,7 @@ public class MixedIocObject attribute may be used to explicitly force one or more objects to be initialized before the object using this element is initialized. Find below an example of using the 'depends-on' attribute - to express a dependency on a single object.. <object id="objectOne" type="Examples.ExampleObject, ExamplesLibrary" depends-on="manager"> + to express a dependency on a single object.. <object id="objectOne" type="Examples.ExampleObject, ExamplesLibrary" depends-on="manager"> <property name="manager" ref="manager"/> </object> @@ -2083,7 +2098,7 @@ public class MixedIocObject 'depends-on' attribute, with commas, whitespace and semicolons all valid delimiters, like so: - <object id="objectOne" type="Examples.ExampleObject, ExamplesLibrary" depends-on="manager,accountDao"> + <object id="objectOne" type="Examples.ExampleObject, ExamplesLibrary" depends-on="manager,accountDao"> <property name="manager" ref="manager" /> </object> @@ -2105,9 +2120,9 @@ public class MixedIocObject Lazily-instantiated objects The default behavior for - IApplicationContext implementations is to eagerly + IApplicationContext implementations is to eagerly pre-instantiate all singleton objects at startup. Pre-instantiation - means that an IApplicationContext will eagerly + means that an IApplicationContext will eagerly create and configure all of its singleton objects as part of its initialization process. Generally this is a good thing, because it means that any errors in the configuration or in the surrounding environment @@ -2116,7 +2131,7 @@ public class MixedIocObject However, there are times when this behavior is not what is wanted. If you do not want a singleton object to be pre-instantiated when using - an IApplicationContext, you can selectively + an IApplicationContext, you can selectively control this by marking an object definition as lazy-initialized. A lazily-initialized object indicates to the IoC container whether or not an object instance should be created at startup or when it is first @@ -2126,21 +2141,21 @@ public class MixedIocObject by the 'lazy-init'attribute on the <object/> element; for example: - <object id="lazy" type="MyCompany.ExpensiveToCreateObject, MyApp" lazy-init="true"/> + <object id="lazy" type="MyCompany.ExpensiveToCreateObject, MyApp" lazy-init="true"/> <object name="not.lazy" type="MyCompany.AnotherObject, MyApp"/> When the above configuration is consumed by an - IApplicationContext, the object named + IApplicationContext, the object named 'lazy' will not be eagerly pre-instantiated when the - IApplicationContext is starting up, whereas the + IApplicationContext is starting up, whereas the 'not.lazy' object will be eagerly pre-instantiated. One thing to understand about lazy-initialization is that even though an object definition may be marked up as being lazy-initialized, if the lazy-initialized object is the dependency of a singleton object that is not lazy-initialized, when the - IApplicationContext is eagerly pre-instantiating + IApplicationContext is eagerly pre-instantiating the singleton, it will have to satisfy all of the singletons dependencies, one of which will be the lazy-initialized object! So don't be confused if the IoC container creates one of the objects that you @@ -2153,12 +2168,12 @@ public class MixedIocObject 'default-lazy-init'attribute on the <objects/> element; for example: - <objects default-lazy-init="true"> + <objects default-lazy-init="true"> <!-- no objects will be pre-instantiated... --> </objects> - + Autowiring collaborators The Spring container is able to autowire relationships between @@ -2221,10 +2236,10 @@ public class MixedIocObject This option gives you the ability to resolve collaborators by type instead of by name. Supposing you have - an IObjectDefinition with a - collaborator typed SqlConnection, + an IObjectDefinition with a + collaborator typed SqlConnection, Spring.NET will search the entire object factory for an object - definition of type SqlConnection and + definition of type SqlConnection and use it as the collaborator. If 0 (zero) or more than 1 (one) object definitions of the desired type exist in the container, a failure will be reported and you won't be able to @@ -2322,7 +2337,7 @@ public class MixedIocObject definitions. - + Checking for dependencies Spring.NET has the ability to try to check for the existence of @@ -2392,7 +2407,7 @@ public class MixedIocObject - + Method Injection For most users, the majority of the objects in the container will @@ -2412,13 +2427,13 @@ public class MixedIocObject control. Object A can be made aware of the container by implementing the - IObjectFactoryAware interface, and IObjectFactoryAware interface, and use programmatic means to ask the container via a GetObject("B") call for (a typically new) object B every time it needs it. Find below an admittedly somewhat contrived example of this approach - using System.Collections; + using System.Collections; using Spring.Objects.Factory; namespace Fiona.Apple @@ -2454,7 +2469,7 @@ namespace Fiona.Apple Method Injection, a somewhat advanced feature of the Spring IoC container, allows this use case to be handled in a clean fashion. - + Lookup Method Injection Lookup method injection refers to the ability of the container @@ -2473,11 +2488,11 @@ namespace Fiona.Apple CommandManager class), the Spring container is going to dynamically override the implementation of the CreateCommand() method. Your - CommandManager class is not going to have any + CommandManager class is not going to have any Spring dependencies, as can be seen in this reworked example below: - using System.Collections; + using System.Collections; namespace Fiona.Apple { @@ -2500,14 +2515,14 @@ namespace Fiona.Apple CommandManager in this case) the method definition must observe the following form: - <public|protected> [abstract] <return-type> TheMethodName(no-arguments); + <public|protected> [abstract] <return-type> TheMethodName(no-arguments); If the method is abstract, the dynamically-generated subclass will implement the method. Otherwise, the dynamically-generated subclass will override the concrete method defined in the original class. Let's look at an example: - <!-- a stateful object deployed as a prototype (non-singleton) --> + <!-- a stateful object deployed as a prototype (non-singleton) --> <object id="command" class="Fiona.Apple.AsyncCommand, Fiona" singleton="false"> <!-- inject dependencies here as required --> </object> @@ -2533,7 +2548,7 @@ namespace Fiona.Apple properties on the object being constructed). - + Arbitrary method replacement A less commonly useful form of method injection than Lookup @@ -2542,13 +2557,13 @@ namespace Fiona.Apple skip the rest of this section (which describes this somewhat advanced feature), until this functionality is actually needed. - In an XmlObjectFactory, the + In an XmlObjectFactory, the replaced-method element may be used to replace an existing method implementation with another. Consider the following class, with a method ComputeValue, which we want to override: - public class MyValueCalculator { + public class MyValueCalculator { public virtual string ComputeValue(string input) { // ... some real code @@ -2558,11 +2573,11 @@ namespace Fiona.Apple } A class implementing the - Spring.Objects.Factory.Support.IMethodReplacer + Spring.Objects.Factory.Support.IMethodReplacer interface is needed to provide the new (injected) method definition. - /// <summary> + /// <summary> /// Meant to be used to override the existing ComputeValue(string) /// implementation in MyValueCalculator. /// </summary> @@ -2580,7 +2595,7 @@ public class ReplacementComputeValue : IMethodReplacer The object definition to deploy the original class and specify the method override would look like this: - <object id="myValueCalculator" type="Examples.MyValueCalculator, ExampleAssembly"> + <object id="myValueCalculator" type="Examples.MyValueCalculator, ExampleAssembly"> <!-- arbitrary method replacement --> <replaced-method name="ComputeValue" replacer="replacementComputeValue"> <arg-type match="String"/> @@ -2597,9 +2612,9 @@ public class ReplacementComputeValue : IMethodReplacer variants within the class. For convenience, the type string for an argument may be a substring of the fully qualified type name. For example, all the following would match - System.String. + System.String. - System.String + System.String String Str @@ -2609,7 +2624,7 @@ public class ReplacementComputeValue : IMethodReplacer - + Setting a reference using the members of other objects and classes. @@ -2620,14 +2635,14 @@ public class ReplacementComputeValue : IMethodReplacer change to accommodate some of Spring.NET's conventions... consider the case of a class that has a constructor argument that can only be calculated by going to say, a database. The - MethodInvokingFactoryObject handles exactly this + MethodInvokingFactoryObject handles exactly this scenario ... it will allow you to inject the result of an arbitrary method invocation into a constructor (as an argument) or as the value of a property setter. Similarly, - PropertyRetrievingFactoryObject and - FieldRetrievingFactoryObject allow you to + PropertyRetrievingFactoryObject and + FieldRetrievingFactoryObject allow you to retrieve values from another object's property or field value. These - classes implement the IFactoryObject interface + classes implement the IFactoryObject interface which indicates to Spring.NET that this object is itself a factory and the factories product, not the factory itself, is what will be associated with the object id. Factory objects are discussed further in @@ -2636,8 +2651,8 @@ public class ReplacementComputeValue : IMethodReplacer Setting a reference to the value of property. - The PropertyRetrievingFactoryObject is an - IFactoryObject that addresses the scenario of + The PropertyRetrievingFactoryObject is an + IFactoryObject that addresses the scenario of setting one of the properties and / or constructor arguments of an object to the value of a property exposed on another object or class. One can use it to get the value of any In the case of a property exposed on an instance, the target - object that a PropertyRetrievingFactoryObject + object that a PropertyRetrievingFactoryObject will evaluate can be either an object instance specified directly inline or a reference to another arbitrary object. In the case of a static property exposed on a class, the target object will be the - class (the .NET System.Type) exposing the + class (the .NET System.Type) exposing the property. The result of evaluating the property lookup may then be used in another object definition as a property value or constructor argument. Note that nested properties are supported for both instance and class - property lookups. The IFactoryObject is + property lookups. The IFactoryObject is discussed more generally in . Here's an example where a property path is used against another object instance. In this case, an inner object definition is used and - the property path is nested, i.e. spouse.age. <object name="person" type="Spring.Objects.TestObject, Spring.Core.Tests"> + the property path is nested, i.e. spouse.age. <object name="person" type="Spring.Objects.TestObject, Spring.Core.Tests"> <property name="age" value="20"/> <property name="spouse"> <object type="Spring.Objects.TestObject, Spring.Core.Tests"> @@ -2678,10 +2693,10 @@ public class ReplacementComputeValue : IMethodReplacer </object> An example of using a - PropertyRetrievingFactoryObject to evaluate a + PropertyRetrievingFactoryObject to evaluate a static property is shown below. - <object id="cultureAware" + <object id="cultureAware" type="Spring.Objects.Factory.Xml.XmlObjectFactoryTests+MyTestObject, Spring.Core.Tests"> <property name="culture" ref="cultureFactory"/> </object> @@ -2696,7 +2711,7 @@ public class ReplacementComputeValue : IMethodReplacer Similarly, an example showing the use of an instance property is shown below. - <object id="instancePropertyCultureAware" + <object id="instancePropertyCultureAware" type="Spring.Objects.Factory.Xml.XmlObjectFactoryTests+MyTestObject, Spring.Core.Tests"> <property name="Culture" ref="instancePropertyCultureFactory"/> </object> @@ -2715,21 +2730,21 @@ public class ReplacementComputeValue : IMethodReplacer Setting a reference to the value of field. - The FieldRetrievingFactoryObject class + The FieldRetrievingFactoryObject class addresses much the same area of concern as the - PropertyRetrievingFactoryObject described in + PropertyRetrievingFactoryObject described in the previous section. However, as its name might suggest, the - FieldRetrievingFactoryObject class is concerned + FieldRetrievingFactoryObject class is concerned with looking up the value of a public field exposed on either an instance or a class (and similarly, in the case of a field exposed on a class, the field must obviously be static). The following example demonstrates using a - FieldRetrievingFactoryObject to look up the + FieldRetrievingFactoryObject to look up the value of a (public, static) field exposed on a class - <object id="withTypesField" + <object id="withTypesField" type="Spring.Objects.Factory.Xml.XmlObjectFactoryTests+MyTestObject, Spring.Core.Tests"> <property name="Types" ref="emptyTypesFactory"/> </object> @@ -2744,7 +2759,7 @@ public class ReplacementComputeValue : IMethodReplacer The example in the next section demonstrates the look up of a (public) field exposed on an object instance. - <object id="instanceCultureAware" + <object id="instanceCultureAware" type="Spring.Objects.Factory.Xml.XmlObjectFactoryTests+MyTestObject, Spring.Core.Tests"> <property name="Culture" ref="instanceCultureFactory"/> </object> @@ -2760,22 +2775,22 @@ public class ReplacementComputeValue : IMethodReplacer - + Setting a property or constructor argument to the return value of a method invocation. - The MethodInvokingFactoryObject rounds + The MethodInvokingFactoryObject rounds out the trio of classes that permit the setting of properties and constructor arguments using the members of other objects and classes. - Whereas the PropertyRetrievingFactoryObject and - FieldRetrievingFactoryObject classes dealt with + Whereas the PropertyRetrievingFactoryObject and + FieldRetrievingFactoryObject classes dealt with simply looking up and returning the value of property or field on an object or class, the - MethodInvokingFactoryObject allows one to set a + MethodInvokingFactoryObject allows one to set a constructor or property to the return value of an arbitrary method invocation, - The MethodInvokingFactoryObject class + The MethodInvokingFactoryObject class handles both the case of invoking an (instance) method on another object in the container, and the case of a static method call on an arbitrary class. Additionally, it is sometimes necessary to invoke a @@ -2786,17 +2801,17 @@ public class ReplacementComputeValue : IMethodReplacer mechanisms do not permit any arguments to be passed to any initialization method, and are confined to invoking an initialization method on the object that has just been instantiated by the container. - The MethodInvokingFactoryObject allows one to + The MethodInvokingFactoryObject allows one to invoke pretty much any method on any object (or class in the case of a static method). The following example (in an XML based - IObjectFactory definition) uses the - MethodInvokingFactoryObject class to force a + IObjectFactory definition) uses the + MethodInvokingFactoryObject class to force a call to a static factory method prior to the instantiation of the object... - <object id="force-init" + <object id="force-init" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core"> <property name="StaticMethod"> <value>ExampleNamespace.ExampleInitializerClass.Initialize</value> @@ -2810,7 +2825,7 @@ public class ReplacementComputeValue : IMethodReplacer thus the calling of its configured StaticMethod static initializer method, when myService is first initialized. Please note that in order to effect this initialization, - the MethodInvokingFactoryObject object + the MethodInvokingFactoryObject object must be operating in singleton mode (the default.. see the next paragraph). @@ -2818,9 +2833,9 @@ public class ReplacementComputeValue : IMethodReplacer Note that since this class is expected to be used primarily for accessing factory methods, this factory defaults to operating in singleton mode. As such, as soon as all of the - properties for a MethodInvokingFactoryObject + properties for a MethodInvokingFactoryObject object have been set, and if the - MethodInvokingFactoryObject object is still in + MethodInvokingFactoryObject object is still in singleton mode, the method will be invoked immediately and the return value cached for later access. The first request by the container for the factory to produce an object will @@ -2835,7 +2850,7 @@ public class ReplacementComputeValue : IMethodReplacer targetMethod property to a string representing the static method name, with TargetType specifying the - Type that the static method is defined on. + Type that the static method is defined on. Alternatively, a target instance method may be specified, by setting the TargetObject property to the name of another Spring.NET managed object definition (the target object), and @@ -2849,8 +2864,8 @@ public class ReplacementComputeValue : IMethodReplacer arguments is significant... the order of the values passed to the Arguments property must be the same as the order of the arguments defined on the method signature, including the - argument Type. This is shown in the example - below <object id="myObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core"> + argument Type. This is shown in the example + below <object id="myObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core"> <property name="TargetType" value="Whatever.MyClassFactory, MyAssembly"/> <property name="TargetMethod" value="GetInstance"/> @@ -2867,11 +2882,11 @@ public class ReplacementComputeValue : IMethodReplacer The second way involves passing an arguments dictionary to the NamedArguments property... this dictionary - maps argument names (Strings) to argument + maps argument names (Strings) to argument values (any object). The argument names are not case-sensitive, and order is (obviously) not significant (since dictionaries by definition do not have an order). This is shown in the example below - <object id="myObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core"> + <object id="myObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core"> <property name="TargetObject"> <object type="Whatever.MyClassFactory, MyAssembly"/> </property> @@ -2888,8 +2903,8 @@ public class ReplacementComputeValue : IMethodReplacer </object> The following example shows how use - MethodInvokingFactoryObject to call an instance - method.<object id="myMethodObject" type="Whatever.MyClassFactory, MyAssembly" /> + MethodInvokingFactoryObject to call an instance + method.<object id="myMethodObject" type="Whatever.MyClassFactory, MyAssembly" /> <object id="myObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core"> <property name="TargetObject" ref="myMethodObject"/> @@ -2902,19 +2917,19 @@ public class ReplacementComputeValue : IMethodReplacer invoked to the surrounding factory object. Finally, if you want to use - MethodInvokingFactoryObject in conjunction with + MethodInvokingFactoryObject in conjunction with a method that has a variable length argument list, then please note that the variable arguments need to be passed (and configured) as a list. Let us consider the following method - definition that uses the params keyword (in - C#), and its attendant (XML) configuration... [C#] + definition that uses the params keyword (in + C#), and its attendant (XML) configuration... [C#] public class MyClassFactory { public object CreateObject(Type objectType, params string[] arguments) { return ... // implementation elided for clarity... } -} <object id="myMethodObject" type="Whatever.MyClassFactory, MyAssembly" /> +} <object id="myMethodObject" type="Whatever.MyClassFactory, MyAssembly" /> <object id="paramsMethodObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core"> <property name="TargetObject" ref="myMethodObject"/> @@ -2932,28 +2947,28 @@ public class MyClassFactory - + Provided IFactoryObject implementations In addition to - PropertyRetrievingFactoryObject, - MethodInvokingFactoryObject, and - FieldRetrievingFactoryObject Spring.NET comes + PropertyRetrievingFactoryObject, + MethodInvokingFactoryObject, and + FieldRetrievingFactoryObject Spring.NET comes with other useful implementations of the - IFactoryObject interface. These are discussed + IFactoryObject interface. These are discussed below. - + Common logging - The LogFactoryObject is useful when you + The LogFactoryObject is useful when you would like to share a Common.Logging log object across a number of classes instead of creating a logging instance per class or class hierarchy. Information on the Common.Logging project can be found here. In the example shown below the same logging instance, with a logging category name of "DAOLogger", is used in both the SimpleAccountDao and - SimpleProductDao data access objects. <objects xmlns="http://www.springframework.net" + SimpleProductDao data access objects. <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" > @@ -2983,7 +2998,7 @@ public class MyClassFactory - + Object Scopes When you create a object definition what you are actually creating @@ -3065,7 +3080,7 @@ public class MyClassFactory - + The singleton scope When an object is a singleton, only one shared instance of the @@ -3092,7 +3107,7 @@ public class MyClassFactory scope is the default scope in Spring. To define an object as a singleton in XML, you would write configuration like so: - <object id="accountService" type="MyApp.DefaultAccountService, MyApp"/> + <object id="accountService" type="MyApp.DefaultAccountService, MyApp"/> <!-- the following is equivalent, though redundant (singleton scope is the default) --> <object id="accountService" type="MyApp.DefaultAccountService, MyApp" singleton="true"/> @@ -3100,7 +3115,7 @@ public class MyClassFactory - + The prototype scope The non-singleton, prototype scope of object deployment results in @@ -3114,7 +3129,7 @@ public class MyClassFactory To define an object as a prototype in XML, you would write configuration like so: - <object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary" singleton="false"/> + <object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary" singleton="false"/> There is one quite important thing to be aware of when deploying an object in the prototype scope, in that the lifecycle of the object @@ -3141,7 +3156,7 @@ public class MyClassFactory callbacks”.) - + Singleton objecgts with prototype-object dependencies When using singleton-scoped objects that have dependencies on @@ -3175,17 +3190,17 @@ public class MyClassFactory - + Type conversion Type converters are responsible for converting objects from one type to another. When using the XML based file to configure the IoC container, string based property values are converted to the target property type. Spring will rely on the standard .NET support for type conversion unless - an alternative TypeConverter is registered for a + an alternative TypeConverter is registered for a given type. How to register custom TypeConverters will be described shortly. As a reminder, the standard .NET type converter support works by - associating a TypeConverter attribute with the + associating a TypeConverter attribute with the class definition by passing the type of the converter as an attribute argument. More information about creating custom @@ -3193,7 +3208,7 @@ public class MyClassFactory at Microsoft's MSDN website, by searching for Implementing a Type Converter. For example, an abbreviated class definition for the BCL - type Font is shown below. [Serializable, TypeConverter(typeof(FontConverter)), ...] + type Font is shown below. [Serializable, TypeConverter(typeof(FontConverter)), ...] public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDisposable { // Methods @@ -3201,36 +3216,36 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo ... etc .. } - + Type Conversion for Enumerations The default type converter for enumerations is the - System.ComponentModel.EnumConverter class. To + System.ComponentModel.EnumConverter class. To specify the value for an enumerated property, simply use the name of the - property. For example the TestObject class has a - property of the enumerated type FileMode. One of + property. For example the TestObject class has a + property of the enumerated type FileMode. One of the values for this enumeration is named Create. The following XML fragment shows how to configure this property - <object id="rod" type="Spring.Objects.TestObject, Spring.Core.Tests"> + <object id="rod" type="Spring.Objects.TestObject, Spring.Core.Tests"> <property name="name" value="Rod"/> <property name="FileMode" value="Create"/> </object> - + Built-in TypeConverters Spring.NET pre-registers a number of custom - TypeConverter instances (for example, to convert + TypeConverter instances (for example, to convert a type expressed as a string into a real - System.Type object). Each of those is listed + System.Type object). Each of those is listed below and they are all located in the Spring.Objects.TypeConverters namespace of the Spring.Core library. - Built-in <classname>TypeConverters</classname> + Built-in <literal>TypeConverters</literal> @@ -3250,8 +3265,8 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo RuntimeTypeConverter Parses strings representing - System.Types to actual - System.Types and the other way + System.Types to actual + System.Types and the other way around. @@ -3259,7 +3274,7 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo FileInfoConverter Capable of resolving strings to a - System.IO.FileInfo object. + System.IO.FileInfo object. @@ -3304,7 +3319,7 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo Capable of resolving a two part string (resource name, assembly name) to a - System.Resources.ResourceManager + System.Resources.ResourceManager object. @@ -3313,14 +3328,14 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo Capable of resolving a comma separated list of Red, Green, Blue integer values to a - System.Drawing.Color structure. + System.Drawing.Color structure. ExpressionConverter Capable of resolving a string into an instance of an - object that implements the IExpression + object that implements the IExpression interface. @@ -3349,28 +3364,28 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo
Spring.NET uses the standard .NET mechanisms for the resolution of - System.Types, including, but not limited to + System.Types, including, but not limited to checking any configuration files associated with your application, checking the Global Assembly Cache (GAC), and assembly probing.
- + Custom Type Conversion There are a few ways to register custom type converters. The fundamental storage area in Spring for custom type converters is the - TypeConverterRegistry class. The most + TypeConverterRegistry class. The most convenient way if using an XML based implementation of - IObjectFactory or - IApplicationContext is to use the custom + IObjectFactory or + IApplicationContext is to use the custom configuration section handler - TypeConverterSectionHandler This is demonstrated + TypeConverterSectionHandler This is demonstrated in section An alternate approach, present for legacy reasons in the port of Spring.NET from the Java code base, is to use the object factory post-processor - Spring.Objects.Factory.Config.CustomConverterConfigurer. + Spring.Objects.Factory.Config.CustomConverterConfigurer. This is described in the next section. If you are constructing your IoC container Programatically then @@ -3378,19 +3393,19 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo TypeConverter converter) method of the ConfigurableObjectFactory interface. - + Using CustomConverterConfigurer This section shows in detail how to define a custom type converter that does not use the .NET - TypeConverter attribute. The type converter + TypeConverter attribute. The type converter class is standalone and inherits from the - TypeConverter class. It uses the legacy factory + TypeConverter class. It uses the legacy factory post-processor approach. Consider a user class ExoticType, and another class DependsOnExoticType which needs - ExoticType set as a property:public class ExoticType + ExoticType set as a property:public class ExoticType { private string name; @@ -3403,7 +3418,7 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo { get { return this.name; } } -} and public class DependsOnExoticType +} and public class DependsOnExoticType { public DependsOnExoticType() {} @@ -3422,10 +3437,10 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo } When things are properly set up, we want to be able to assign the type property as a string, which a TypeConverter will convert into a real ExoticType object behind the scenes: - <object name="sample" type="SimpleApp.DependsOnExoticType, SimpleApp"> + <object name="sample" type="SimpleApp.DependsOnExoticType, SimpleApp"> <property name="exoticType" value="aNameForExoticType"/> </object> The TypeConverter looks like this: - public class ExoticTypeConverter : TypeConverter + public class ExoticTypeConverter : TypeConverter { public ExoticTypeConverter() { @@ -3454,10 +3469,10 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo return base.ConvertFrom (context, culture, value); } } Finally, we use the - CustomConverterConfigurer to register the new - TypeConverter with the - IApplicationContext, which will then be able to - use it as needed: <object id="customConverterConfigurer" + CustomConverterConfigurer to register the new + TypeConverter with the + IApplicationContext, which will then be able to + use it as needed: <object id="customConverterConfigurer" type="Spring.Objects.Factory.Config.CustomConverterConfigurer, Spring.Core"> <property name="CustomConverters"> <dictionary> @@ -3471,10 +3486,10 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo
- + Customizing the nature of an object - + Lifecycle interfaces Spring.NET uses several marker interfaces to change the behaviour @@ -3488,25 +3503,25 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo objects. Internally, Spring.NET uses implementations of the - IObjectPostProcessor interface to process any + IObjectPostProcessor interface to process any marker interfaces it can find and call the appropriate methods. If you need custom features or other lifecycle behavior Spring.NET doesn't offer out-of-the-box, you can implement an - IObjectPostProcessor yourself. More information + IObjectPostProcessor yourself. More information about this can be found in . All the different lifecycle marker interfaces are described below. - + IInitializingObject / <literal>init-method</literal> The - Spring.Objects.Factory.IInitializingObject + Spring.Objects.Factory.IInitializingObject interface gives you the ability to perform initialization work after all the necessary properties on an object are set by the container. - The IInitializingObject interface specifies + The IInitializingObject interface specifies exactly one method: void AfterPropertiesSet(): called @@ -3514,7 +3529,7 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo enables you to do checking to see if all necessary properties have been set correctly, or to perform further initialization work. You can throw any - Exception to indicate misconfiguration, + Exception to indicate misconfiguration, initialization failures, etc. @@ -3522,7 +3537,7 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo Generally, the use of the - IInitializingObject + IInitializingObject can be avoided. The @@ -3531,7 +3546,7 @@ public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDispo library provides support for a generic init-method, given to the object definition in the object configuration store (be it XML, or a database, etc). - <object id="exampleInitObject" type="Examples.ExampleObject" init-method="init"/> + <object id="exampleInitObject" type="Examples.ExampleObject" init-method="init"/> [C#] public class ExampleObject { @@ -3539,7 +3554,7 @@ public class ExampleObject { // do some initialization work } -} Is exactly the same as... <object id="exampleInitObject" type="Examples.AnotherExampleObject"/> +} Is exactly the same as... <object id="exampleInitObject" type="Examples.AnotherExampleObject"/> [C#] public class AnotherExampleObject : IInitializingObject { @@ -3570,34 +3585,34 @@ public class AnotherExampleObject : IInitializingObject - + IDisposable / <literal>destroy-method</literal> - The System.IDisposable interface provides + The System.IDisposable interface provides you with the ability to get a callback when an - IObjectFactory is destroyed. The - IDisposable interface specifies exactly one + IObjectFactory is destroyed. The + IDisposable interface specifies exactly one method: void Dispose(): and is called on destruction of the container. This allows you to release any resources you are keeping in this object (such as database connections). You can throw any - Exception here... however, any such - Exception will not stop the destruction + Exception here... however, any such + Exception will not stop the destruction of the container - it will only get logged. Note: If you choose you can avoid having your class - implement IDisposable since the + implement IDisposable since the Spring.Core library provides support for a generic destroy-method, given to the object definition in the object configuration store (be it XML, or a database, etc). - <object id="exampleInitObject" type="Examples.ExampleObject" destroy-method="cleanup"/> + <object id="exampleInitObject" type="Examples.ExampleObject" destroy-method="cleanup"/> [C#] public class ExampleObject { @@ -3605,7 +3620,7 @@ public class ExampleObject { // do some destruction work (such as closing any open connection (s)) } -} is exactly the same as: <object id="exampleInitObject" type="Examples.AnotherExampleObject"/> +} is exactly the same as: <object id="exampleInitObject" type="Examples.AnotherExampleObject"/> [C#] public class AnotherExampleObject : IDisposable { @@ -3617,16 +3632,16 @@ public class AnotherExampleObject : IDisposable - + Knowing who you are - + IObjectFactoryAware A class which implements the - Spring.Objects.Factory.IObjectFactoryAware + Spring.Objects.Factory.IObjectFactoryAware interface is provided with a reference to the - IObjectFactory that created it. The interface + IObjectFactory that created it. The interface specifies one (write-only) property: IObjectFactory ObjectFactory: the @@ -3637,8 +3652,8 @@ public class AnotherExampleObject : IDisposable This allows objects to manipulate the - IObjectFactory that created them - Programatically, through the IObjectFactory + IObjectFactory that created them + Programatically, through the IObjectFactory interface, or by casting the reference to a known subclass of this which exposes additional functionality. Primarily this would consist of programmatic retrieval of other objects. While there are cases when @@ -3648,11 +3663,11 @@ public class AnotherExampleObject : IDisposable properties. - + IObjectNameAware The - Spring.Objects.Factory.IObjectNameAware + Spring.Objects.Factory.IObjectNameAware interface gives you the ability to let the container set the name of the object definition on the object instance itself. In those cases where your object needs to know what its name is, implement this @@ -3666,7 +3681,7 @@ public class AnotherExampleObject : IDisposable - + Object definition inheritance An object definition potentially contains a large amount of @@ -3678,17 +3693,17 @@ public class AnotherExampleObject : IDisposable parent and child object definitions can potentially save a lot of typing. Effectively, this is a form of templating. - When working with an IObjectFactory + When working with an IObjectFactory Programatically, child object definitions are represented by the - ChildObjectDefinition class. Most users will never + ChildObjectDefinition class. Most users will never work with them on this level, instead configuring object definitions declaratively in something like the - XmlObjectFactory. In an - XmlObjectFactory object definition, a child object + XmlObjectFactory. In an + XmlObjectFactory object definition, a child object definition is indicated simply by using the parent attribute, specifying the parent object definition as the value of this attribute. - <object id="inheritedTestObject" type="Spring.Objects.TestObject, Spring.Core.Tests"> + <object id="inheritedTestObject" type="Spring.Objects.TestObject, Spring.Core.Tests"> <property name="name" value="parent"/> <property name="age" value="1"/> </object> @@ -3717,7 +3732,7 @@ public class AnotherExampleObject : IDisposable In the case where the parent definition does not specify a class... - <object id="inheritedTestObjectWithoutClass" abstract="true"> + <object id="inheritedTestObjectWithoutClass" abstract="true"> <property name="name" value="parent"/> <property name="age" value="1"/> </object> @@ -3751,22 +3766,22 @@ public class AnotherExampleObject : IDisposable - + Interacting with the container The Spring container is essentially nothing more than an advanced factory capable of maintaining a registry of different objects and their - dependencies. The IObjectFactory enables you to + dependencies. The IObjectFactory enables you to read object definitions and access them using the object factory. When - using just the IObjectFactory you would create an + using just the IObjectFactory you would create an instance of one and then read in some object definitions in the XML format - as follows: [C#] + as follows: [C#] IResource input = new FileSystemResource ("objects.xml"); XmlObjectFactory factory = new XmlObjectFactory(input); That is pretty much it. Using GetObject(string) (or the more concise indexer method factory ["string"]) - you can retrieve instances of your objects... [C#] + you can retrieve instances of your objects... [C#] object foo = factory.GetObject ("foo"); // gets the object defined as 'foo' object bar = factory ["bar"]; // same thing, just using the indexer @@ -3774,9 +3789,9 @@ object bar = factory ["bar"]; // same thing, just using the indexer You'll get a reference to the same object if you defined it as a singleton (the default) or you'll get a new instance each time if you set the singleton property of your object definition to - false. <object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary"/> + false. <object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary"/> <object id="anotherObject" type="Examples.ExampleObject, ExamplesLibrary" singleton="false"/> - [C#] + [C#] object one = factory ["exampleObject"]; // gets the object defined as 'exampleObject' object two = factory ["exampleObject"]; Console.WriteLine (one == two) // prints 'true' @@ -3785,13 +3800,13 @@ object four = factory ["anotherObject"]; Console.WriteLine (three == four); // prints 'false' - The client-side view of the IObjectFactory is - surprisingly simple. The IObjectFactory interface + The client-side view of the IObjectFactory is + surprisingly simple. The IObjectFactory interface has only seven methods (and the aforementioned indexer) for clients to call: bool ContainsObject(string): returns true - if the IObjectFactory contains an object + if the IObjectFactory contains an object definition that matches the given name. @@ -3799,7 +3814,7 @@ Console.WriteLine (three == four); // prints 'false' object GetObject(string): returns an instance of the object registered under the given name. Depending on how the object was configured by the - IObjectFactory configuration, either a + IObjectFactory configuration, either a singleton (and thus shared) instance or a newly created object will be returned. An ObjectsException will be thrown when either the object could not be found (in which case it'll be a @@ -3809,7 +3824,7 @@ Console.WriteLine (three == four); // prints 'false' Object this [string]: this is the indexer - for the IObjectFactory interface. It + for the IObjectFactory interface. It functions in all other respects in exactly the same way as the GetObject(string) method. The rest of this documentation will always refer to the @@ -3821,7 +3836,7 @@ Console.WriteLine (three == four); // prints 'false' Object GetObject(string, Type): returns an object, registered under the given name. The object returned will be - cast to the given Type. If the object could + cast to the given Type. If the object could not be cast, corresponding exceptions will be thrown (ObjectNotOfRequiredTypeException). Furthermore, all rules of the GetObject(string) method apply @@ -3840,7 +3855,7 @@ Console.WriteLine (three == four); // prints 'false' string[] GetAliases(string): returns the aliases for the given object name, if any were defined in the - IObjectDefinition. + IObjectDefinition. @@ -3862,8 +3877,8 @@ Console.WriteLine (three == four); // prints 'false' - A sub-interface of IObjectFactory, - IConfigurableObjectFactory adds some convenient + A sub-interface of IObjectFactory, + IConfigurableObjectFactory adds some convenient methods such as @@ -3881,27 +3896,27 @@ Console.WriteLine (three == four); // prints 'false' Check the SDK docs for additional details on IConfigurableObjectFactory methods and properties and the full - IObjectFactory class hierarchy. + IObjectFactory class hierarchy. - Obtaining an <classname>IFactoryObject</classname>, not its + <title>Obtaining an <literal>IFactoryObject</literal>, not its product Sometimes there is a need to ask an - IObjectFactory for an actual - IFactoryObject instance itself, not the object it + IObjectFactory for an actual + IFactoryObject instance itself, not the object it produces. This may be done by prepending the object id with & when calling the GetObject method of the - IObjectFactory and - IApplicationContext interfaces. So for a given - IFactoryObject with an id + IObjectFactory and + IApplicationContext interfaces. So for a given + IFactoryObject with an id myObject, invoking GetObject("myObject") on the - IObjectFactory will return the product of the - IFactoryObject, but invoking + IObjectFactory will return the product of the + IFactoryObject, but invoking GetObject("&myObject") will return the - IFactoryObject instance itself. + IFactoryObject instance itself.
@@ -3910,66 +3925,66 @@ Console.WriteLine (three == four); // prints 'false' The IoC component of the Spring Framework has been designed for extension. There is typically no need for an application developer to - subclass any of the various IObjectFactory or - IApplicationContext implementation classes. The + subclass any of the various IObjectFactory or + IApplicationContext implementation classes. The Spring IoC container can be infinitely extended by plugging in implementations of special integration interfaces. The next few sections are devoted to detailing all of these various integration interfaces. - + Customizing objects with <literal>IObjectPostProcessors</literal> The first extension point that we will look at is the - Spring.Objects.Factory.Config.IObjectPostProcessor + Spring.Objects.Factory.Config.IObjectPostProcessor interface. This interface defines a number of callback methods that you as an application developer can implement in order to provide your own (or override the containers default) instantiation logic, dependency-resolution logic, and so forth. If you want to do some custom logic after the Spring container has finished instantiating, configuring and otherwise initializing an object, you can plug in one or more - IObjectPostProcessor implementations. + IObjectPostProcessor implementations. You can configure multiple - IObjectPostProcessors if you wish. You can + IObjectPostProcessors if you wish. You can control the order in which these - IObjectPostProcessor execute by setting the + IObjectPostProcessor execute by setting the 'Order' property (you can only set this property if the - IObjectPostProcessor implements the - IOrdered interface; if you write your own - IObjectPostProcessor you should consider - implementing the IOrdered interface too); consult - the SDK docs for the IObjectPostProcessor and - IOrdered interfaces for more details. + IObjectPostProcessor implements the + IOrdered interface; if you write your own + IObjectPostProcessor you should consider + implementing the IOrdered interface too); consult + the SDK docs for the IObjectPostProcessor and + IOrdered interfaces for more details. - IObjectPostProcessor operate on object + IObjectPostProcessor operate on object instances; that is to say, the Spring IoC container will have instantiated a object instance for you, and then - IObjectPostProcessors get a chance to do their + IObjectPostProcessors get a chance to do their stuff. If you want to change the actual object definition (that is the recipe that defines the object), then you rather need to use a - IObjectFactoryPostProcessor (described below in + IObjectFactoryPostProcessor (described below in the section entitled Customizing configuration metadata with IObjectFactoryPostProcessors. - Also, IObjectPostProcessors are scoped + Also, IObjectPostProcessors are scoped per-container. This is only relevant if you are using container hierarchies. If you define a - IObjectPostProcessor in one container, it will + IObjectPostProcessor in one container, it will only do its stuff on the objects in that container. Objects that are defined in another container will not be post-processed by - IObjectPostProcessors in another container, + IObjectPostProcessors in another container, even if both containers are part of the same hierarchy. The - Spring.Objects.Factory.Config.IObjectPostProcessor + Spring.Objects.Factory.Config.IObjectPostProcessor interface, which consists of two callback methods shown below. - object PostProcessBeforeInitialization(object instance, string name); + object PostProcessBeforeInitialization(object instance, string name); object PostProcessAfterInitialization(object instance, string name);When such a class is registered as a post-processor with the container, for @@ -3978,7 +3993,7 @@ object PostProcessAfterInitialization(object instance, string name);before any initialization methods (such as the AfterPropertiesSet method of the - IInitializingObject interface and any declared + IInitializingObject interface and any declared init method) are called, and also afterwards. The post-processor is free to do what it wishes with the object, including ignoring the callback completely. An object post-processor will typically check for marker @@ -3988,9 +4003,9 @@ object PostProcessAfterInitialization(object instance, string name);Other extensions to the IObjectPostProcessors interface are - IInstantiationAwareObjectPostProcessor and - IDestructionAwareObjectPostProcessor defined - below public interface IInstantiationAwareObjectPostProcessor : IObjectPostProcessor + IInstantiationAwareObjectPostProcessor and + IDestructionAwareObjectPostProcessor defined + below public interface IInstantiationAwareObjectPostProcessor : IObjectPostProcessor { object PostProcessBeforeInstantiation(Type objectType, string objectName); @@ -4002,7 +4017,7 @@ object PostProcessAfterInitialization(object instance, string name); The PostProcessBeforeInstantiation +} The PostProcessBeforeInstantiation callback method is called right before the container creates the object. If the object returned by this method is not null then the default instantiation behavior of the container is short circuited. The returned @@ -4010,38 +4025,38 @@ public interface IDestructionAwareObjectPostProcessor : IObjectPostProcessor IObjectPostProcessor callbacks will be invoked on it. This mechanism is useful if you would like to expose a proxy to the object instead of the actual target object. The - PostProcessAfterInstantiation callback method is + PostProcessAfterInstantiation callback method is called after the object has been instantiated but before Spring performs property population based on explicit properties or autowiring. A return value of false would short circuit the standard Spring based property population. The callback method - PostProcessPropertyValues is called after Spring + PostProcessPropertyValues is called after Spring collects all the property values to apply to the object, but before they are applied. This gives you the opportunity to perform additional processing such as making sure that a property is set to a value if it - contains a [Required] attribute or to perform + contains a [Required] attribute or to perform attribute based wiring, i.e. adding the attribute - [Inject("objectName")] on a property. Both of + [Inject("objectName")] on a property. Both of these features are scheduled to be included in Spring .12. - The IDestructionAwareObjectPostProcessor + The IDestructionAwareObjectPostProcessor callback contains a single method, - PostProcessBeforeDestruction, which is called + PostProcessBeforeDestruction, which is called before a singleton's destroy method is invoked. It is important to know that the - IObjectFactory treats object post-processors + IObjectFactory treats object post-processors slightly differently than the - IApplicationContext. An - IApplicationContext will automatically detect any + IApplicationContext. An + IApplicationContext will automatically detect any objects which are deployed into it that implement the - IObjectPostProcessor interface, and register them + IObjectPostProcessor interface, and register them as post-processors, to be then called appropriately by the factory on object creation. Nothing else needs to be done other than deploying the post-processor in a similar fashion to any other object. On the other hand, when using plain IObjectFactories, object post-processors have to manually be explicitly registered, with a code - sequence such as... ConfigurableObjectFactory factory = new .....; // create an IObjectFactory + sequence such as... ConfigurableObjectFactory factory = new .....; // create an IObjectFactory ... // now register some objects // now register any needed IObjectPostProcessors MyObjectPostProcessor pp = new MyObjectPostProcessor(); @@ -4051,8 +4066,8 @@ factory.AddObjectPostProcessor(pp); This explicit registration step is not convenient, and this is one of the reasons why the various - IApplicationContext implementations are preferred - above plain IObjectFactory implementations in the + IApplicationContext implementations are preferred + above plain IObjectFactory implementations in the vast majority of Spring-backed applications, especially when using IObjectPostProcessors. @@ -4060,26 +4075,26 @@ factory.AddObjectPostProcessor(pp); IObjectPostProcessors and AOP auto-proxying Classes that implement the - IObjectPostProcessor interface are special, and + IObjectPostProcessor interface are special, and so they are treated differently by the container. All - IObjectPostProcessors and their directly + IObjectPostProcessors and their directly referenced object will be instantiated on startup, as part of the special startup phase of the IApplicationContext, then all those - IObjectPostProcessors will be registered in a + IObjectPostProcessors will be registered in a sorted fashion - and applied to all further objects. Since AOP auto-proxying is implemented as a - IObjectPostProcessor itself, no - IObjectPostProcessors or directly referenced + IObjectPostProcessor itself, no + IObjectPostProcessors or directly referenced objects are eligible for auto-proxying (and thus will not have aspects 'woven' into them). For any such object, you should see an info log message: “Object 'foo' is not eligible for getting processed by all - IObjectPostProcessors (for example: not + IObjectPostProcessors (for example: not eligible for auto-proxying)”. - + Example: Hello World, IObjectPostProcessor-style This first example is hardly compelling, but serves to @@ -4095,7 +4110,7 @@ factory.AddObjectPostProcessor(pp); Find below the custom IObjectPostProcessor implementation class definition - using System; + using System; using Spring.Objects.Factory.Config; namespace Spring.IocQuickStart.MovieFinder @@ -4115,7 +4130,7 @@ namespace Spring.IocQuickStart.MovieFinder } }And the following configuration - <?xml version="1.0" encoding="utf-8" ?> + <?xml version="1.0" encoding="utf-8" ?> <objects xmlns="http://www.springframework.net" > <description>An example that demonstrates simple IoC features.</description> @@ -4129,7 +4144,7 @@ namespace Spring.IocQuickStart.MovieFinder <!-- when the above objects are instantiated, this custom IObjectPostProcessor implementation will output the fact to the system console --> - <object type="Spring.IocQuickStart.MovieFinder.TracingObjectPostProcessor, Spring.IocQuickStart.MovieFinder"/> + <object type="Spring.IocQuickStart.MovieFinder.TracingObjectPostProcessor, Spring.IocQuickStart.MovieFinder"/> </objects> Notice how the TracingObjectPostProcessor is simply defined; it @@ -4139,7 +4154,7 @@ namespace Spring.IocQuickStart.MovieFinder Find below a small driver script to exercise the above code and configuration; - IApplicationContext ctx = + IApplicationContext ctx = new XmlApplicationContext( "assembly://Spring.IocQuickStart.MovieFinder/Spring.IocQuickStart.MovieFinder/AppContext.xml"); @@ -4162,7 +4177,7 @@ DEBUG - Movie Title = 'La vita e bella', Director = 'Roberto Benigni'. DEBUG - MovieApp Done. - + Example: the RequiredAttributeObjectPostProcessor Using callback interfaces or annotations in conjunction with a @@ -4173,13 +4188,13 @@ DEBUG - MovieApp Done. being 'required-to-be-set' (i.e. an setter property with this attribute applied must be configured to be dependency injected with a value), else an - ObjectInitializationException will be thrown by + ObjectInitializationException will be thrown by the container at runtime. The best way to illustrate the usage of this attribute is with an example. - public class MovieLister + public class MovieLister { // the MovieLister has a dependency on the MovieFinder private IMovieFinder _movieFinder; @@ -4197,13 +4212,13 @@ DEBUG - MovieApp Done. Hopefully the above class definition reads easy on the eye. Any and all IObjectDefinitions for the - MovieLister class must be provided with a + MovieLister class must be provided with a value. Let's look at an example of some XML configuraiton that will not pass validation. - <object id="MyMovieLister" + <object id="MyMovieLister" type="Spring.IocQuickStart.MovieFinder.MovieLister, Spring.IocQuickStart.MovieFinder"> <!-- whoops, no MovieFinder is set (and this property is [Required]) --> </object> @@ -4221,81 +4236,81 @@ DEBUG - MovieApp Done. appropriately. This component is the - RequiredAttributeObjectPostProcessor class. + RequiredAttributeObjectPostProcessor class. This is a special IObjectPostProcessor implementation that is [Required]-aware and actually provides the 'blow up if this required property has not been set' logic. It is very easy to configure; simply drop the following object definition into your Spring XML configuration. - <object type="Spring.Objects.Factory.Attributes.RequiredAttributeObjectPostProcessor, Spring.Core"/> + <object type="Spring.Objects.Factory.Attributes.RequiredAttributeObjectPostProcessor, Spring.Core"/> Finally, one can configure an instance of the - RequiredAttributeObjectPostProcessor class to + RequiredAttributeObjectPostProcessor class to look for another Attribute type. This is great if you already have your own [Required]-style attribute. Simply plug it into the definition of a - RequiredAttributeObjectPostProcessor and you + RequiredAttributeObjectPostProcessor and you are good to go. By way of an example, let's suppose you (or your organization / team) have defined an attribute called [Mandatory]. You - can make a RequiredAttributeObjectPostProcessor + can make a RequiredAttributeObjectPostProcessor instance [Mandatory]-aware like so: - <object type="Spring.Objects.Factory.Attributes.RequiredAttributeObjectPostProcessor, Spring.Core"> + <object type="Spring.Objects.Factory.Attributes.RequiredAttributeObjectPostProcessor, Spring.Core"> <property name="RequiredAttributeType" value="MyApp.Attributes.MandatoryAttribute, MyApp"/> </object>
- + Customizing configuration metadata with ObjectFactoryPostProcessors The next extension point that we will look at is the - Spring.Objects.Factory.Config.IObjectFactoryPostProcessor. + Spring.Objects.Factory.Config.IObjectFactoryPostProcessor. The semantics of this interface are similar to the - IObjectPostProcessor, with one major difference. - IObjectFactoryPostProcessors operate on; that is + IObjectPostProcessor, with one major difference. + IObjectFactoryPostProcessors operate on; that is to say, the Spring IoC container will allow - IObjectFactoryPostProcessors to read the + IObjectFactoryPostProcessors to read the configuration metadata and potentially change it before the container has actually instantiated any other objects. By implementing this interface, you will receive a callback after the all the object definitions have been loaded into the IoC container but before they have been instantiated. The signature of the interface is shown below - public interface IObjectFactoryPostProcessor + public interface IObjectFactoryPostProcessor { void PostProcessObjectFactory (IConfigurableListableObjectFactory factory); } You can configure multiple - IObjectFactoryPostProcessors if you wish. You can + IObjectFactoryPostProcessors if you wish. You can control the order in which these - IObjectFactoryPostProcessors execute by setting + IObjectFactoryPostProcessors execute by setting the 'Order' property (you can only set this property if the - IObjectFactoryPostProcessors implements the - IOrdered interface; if you write your own - IObjectFactoryPostProcessors you should consider - implementing the IOrdered interface too); consult - the SDK docs for the IObjectFactoryPostProcessors - and IOrdered interfaces for more details. + IObjectFactoryPostProcessors implements the + IOrdered interface; if you write your own + IObjectFactoryPostProcessors you should consider + implementing the IOrdered interface too); consult + the SDK docs for the IObjectFactoryPostProcessors + and IOrdered interfaces for more details. If you want to change the actual object instances (the objects that are created from the configuration metadata), then you rather - need to use a IObjectObjectPostProcessor + need to use a IObjectObjectPostProcessor (described above in the section entitled Customizing objects with IObjectPostProcessors. - Also, IObjectFactoryPostProcessors are + Also, IObjectFactoryPostProcessors are scoped per-container. This is only relevant if you are using container hierarchies. If you define a - IObjectFactoryPostProcessors in one container, + IObjectFactoryPostProcessors in one container, it will only do its stuff on the object definitions in that container. Object definitions in another container will not be post-processed by - IObjectFactoryPostProcessors in another + IObjectFactoryPostProcessors in another container, even if both containers are part of the same hierarchy. @@ -4311,11 +4326,11 @@ DEBUG - MovieApp Done. objects transactionally or with any other kind of proxy, as described later in this manual. - In an IObjectFactory, the process of - applying an IObjectFactoryPostProcessor is + In an IObjectFactory, the process of + applying an IObjectFactoryPostProcessor is manual, and will be similar to this: - XmlObjectFactory factory = new XmlObjectFactory(new FileSystemResource("objects.xml")); + XmlObjectFactory factory = new XmlObjectFactory(new FileSystemResource("objects.xml")); // create placeholderconfigurer to bring in some property // values from a Properties file PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); @@ -4326,13 +4341,13 @@ cfg.PostProcessObjectFactory(factory); This explicit registration step is not convenient, and this is one of the - reasons why the various IApplicationContext + reasons why the various IApplicationContext implementations are preferred above plain - IObjectFactory implementations in the vast + IObjectFactory implementations in the vast majority of Spring-backed applications, especially when using - IObjectFactoryPostProcessors. + IObjectFactoryPostProcessors. - An IApplicationContext will detect any + An IApplicationContext will detect any objects which are deployed into it that implement the ObjectFactoryPostProcessor interface, and automatically use them as object factory post-processors, at the @@ -4341,22 +4356,22 @@ cfg.PostProcessObjectFactory(factory); Just as in the case of - IObjectPostProcessors, you typically don't want - to have IObjectFactoryPostProcessors marked as + IObjectPostProcessors, you typically don't want + to have IObjectFactoryPostProcessors marked as being lazily-initialized. If they are marked as such, then the Spring container will never instantiate them, and thus they won't get a chance to apply their custom logic. If you are using the 'default-lazy-init' attribute on the declaration of your <objects/> element, be sure to mark your various - IObjectFactoryPostProcessor object definitions + IObjectFactoryPostProcessor object definitions with 'lazy-init="false"'. - + Example: The - <classname>PropertyPlaceholderConfigurer</classname> + PropertyPlaceholderConfigurer - The PropertyPlaceholderConfigurer is an + The PropertyPlaceholderConfigurer is an excellent solution when you want to externalize a few properties from a file containing object definitions. This is useful to allow the person deploying an application to customize environment specific @@ -4371,14 +4386,14 @@ cfg.PostProcessObjectFactory(factory); Note that IApplicationContexts are able to automatically recognize and apply objects deployed in them that - implement the IObjectFactoryPostProcessor + implement the IObjectFactoryPostProcessor interface. This means that as described here, applying a - PropertyPlaceholderConfigurer is much more - convenient when using an IApplicationContext. + PropertyPlaceholderConfigurer is much more + convenient when using an IApplicationContext. For this reason, it is recommended that users wishing to use this or other object factory postprocessors use an - IApplicationContext instead of an - IObjectFactory. + IApplicationContext instead of an + IObjectFactory. In the example below a data access object needs to be configured with a database connection and also a value for the maximum number of @@ -4386,7 +4401,7 @@ cfg.PostProcessObjectFactory(factory); the main Spring.NET configuration file we use place holders, in the NAnt style of ${variableName}, and obtain their values from NameValueSections in the standard .NET application configuration file. - The Spring.NET configuration file looks like: <configuration> + The Spring.NET configuration file looks like: <configuration> <configSections> <sectionGroup name="spring"> @@ -4420,18 +4435,18 @@ cfg.PostProcessObjectFactory(factory); in deployment. This Spring.NET configuration file is shown below. - <objects xmlns="http://www.springframework.net" + <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="productDao" type="DaoApp.SimpleProductDao, DaoApp "> - <property name="maxResults" value="${maxResults}"/> + <property name="maxResults" value="${maxResults}"/> <property name="dbConnection" ref="myConnection"/> </object> <object name="myConnection" type="System.Data.Odbc.OdbcConnection, System.Data"> - <property name="connectionstring" value="${connection.string}"/> + <property name="connectionstring" value="${connection.string}"/> </object> <object name="appConfigPropertyHolder" @@ -4448,17 +4463,17 @@ cfg.PostProcessObjectFactory(factory); ${connection.string} match the key names used in the two NameValueSectionHandlers DaoConfiguration and DatabaseConfiguration. The - PropertyPlaceholderConfigurer refers to these + PropertyPlaceholderConfigurer refers to these two sections via a comma delimited list of section names in the configSections property. If you are using section groups, prefix the section group name, for example myConfigSection/DaoConfiguraiton. - The PropertyPlaceholderConfigurer class + The PropertyPlaceholderConfigurer class also supports retrieving name value pairs from other - IResource locations. These can be specified + IResource locations. These can be specified using the Location and Locations - properties of the PropertyPlaceHolderConfigurer + properties of the PropertyPlaceHolderConfigurer class. If there are properties with the same name in different resource @@ -4471,11 +4486,11 @@ cfg.PostProcessObjectFactory(factory); In an ASP.NET environment you must specify the full, four-part name of the assembly when using a - NameValueFileSectionHandler + NameValueFileSectionHandler - + <section name="hibernateConfiguration" type="System.Configuration.NameValueFileSectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> @@ -4491,7 +4506,7 @@ cfg.PostProcessObjectFactory(factory); type names, which is sometimes useful when you have to pick a particular implementation class at runtime. For example: - <object id="MyMovieFinder" type="${custom.moviefinder.type}"/> + <object id="MyMovieFinder" type="${custom.moviefinder.type}"/> If the class is unable to be resolved at runtime to a valid type, resolution of the object will fail once it is about to be @@ -4501,13 +4516,13 @@ cfg.PostProcessObjectFactory(factory); Similarly you can replace 'ref' and 'expression' metadata, as shown below - <object id="TestObject" type="Simple.TestObject, MyAssembly"> + <object id="TestObject" type="Simple.TestObject, MyAssembly"> <property name="age" expression="${ageExpression}"/> <property name="spouse" ref="${spouse-ref}"/> </object> - + Replacement with Environment Variables You may also use the value environment variables to replace @@ -4515,7 +4530,7 @@ cfg.PostProcessObjectFactory(factory); controlled via the property EnvironmentVariableMode. This property is an enumeration of the type - EnvironmentVariablesMode and has three + EnvironmentVariablesMode and has three values, Never, Fallback, and Override. Fallback is the default value and will resolve a property placeholder if it was not already done so via a value from a resource location. @@ -4523,8 +4538,8 @@ cfg.PostProcessObjectFactory(factory); applying values defined from a resource location. Never will, quite appropriately, disable environment variable substitution. An example of how the - PropertyPlaceholderConfigurer XML is modified - to enable override usage is shown below <object name="appConfigPropertyHolder" + PropertyPlaceholderConfigurer XML is modified + to enable override usage is shown below <object name="appConfigPropertyHolder" type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core"> <property name="configSections" value="DaoConfiguration,DatabaseConfiguration"/> <property name="EnvironmentVariableMode" value="Override"/> @@ -4533,13 +4548,13 @@ cfg.PostProcessObjectFactory(factory);
- + Example: The - <classname>PropertyOverrideConfigurer</classname> + PropertyOverrideConfigurer - The PropertyOverrideConfigurer, another + The PropertyOverrideConfigurer, another object factory post-processor, is similar to the - PropertyPlaceholderConfigurer, but in contrast + PropertyPlaceholderConfigurer, but in contrast to the latter, the original definitions can have default values or no values at all for object properties. If an overriding configuration file does not have an entry for a certain object property, the default @@ -4549,16 +4564,16 @@ cfg.PostProcessObjectFactory(factory); not aware of being overridden, so it is not immediately obvious when looking at the XML definition file that the override configurer is being used. In case that there are multiple - PropertyOverrideConfigurer instances that + PropertyOverrideConfigurer instances that define different values for the same object property, the last one will win (due to the overriding mechanism). The example usage is similar to when using - PropertyPlaceHolderConfigurer except that the + PropertyPlaceHolderConfigurer except that the key name refers to the name given to the object in the Spring.NET configuration file and is suffixed via 'dot' notation with the name of the property For example, if the application configuration file is - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/> @@ -4578,7 +4593,7 @@ cfg.PostProcessObjectFactory(factory); </configuration> Then the value of 1000 will be used to overlay the value of 2000 set in the Spring.NET configuration file - shown below <objects xmlns="http://www.springframework.net" + shown below <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" > @@ -4607,7 +4622,7 @@ cfg.PostProcessObjectFactory(factory); </objects> - + IVariableSource The IVariableSource is the base interface for providing the @@ -4622,45 +4637,45 @@ cfg.PostProcessObjectFactory(factory); - ConfigSectionVariableSource + ConfigSectionVariableSource - PropertyFileVariableSource + PropertyFileVariableSource - EnvironmentVariableSource + EnvironmentVariableSource - CommandLineArgsVariableSource + CommandLineArgsVariableSource - RegistryVariableSource + RegistryVariableSource - SpecialFolderVariableSource + SpecialFolderVariableSource - ConnectionStringsVariableSource + ConnectionStringsVariableSource You use this by defining an instance of - Spring.Objects.Factory.Config.VariablePlaceholderConfigurer + Spring.Objects.Factory.Config.VariablePlaceholderConfigurer in your configuration and set the property VariableSource to a single - IVariableSource instance or the list property + IVariableSource instance or the list property VariableSources to a list of - IVariableSource instances. In the case of the + IVariableSource instances. In the case of the same property defined in multiple - IVariableSource implementations, the first one + IVariableSource implementations, the first one in the list that contains the property value will be used. - <object type="Spring.Objects.Factory.Config.VariablePlaceholderConfigurer, Spring.Core"> + <object type="Spring.Objects.Factory.Config.VariablePlaceholderConfigurer, Spring.Core"> <property name="VariableSources"> <list> <object type="Spring.Objects.Factory.Config.ConfigSectionVariableSource, Spring.Core"> @@ -4671,7 +4686,7 @@ cfg.PostProcessObjectFactory(factory); </object> The IVariableSource interface is shown below - public interface IVariableSource + public interface IVariableSource { string ResolveVariable(string name); } @@ -4684,11 +4699,11 @@ cfg.PostProcessObjectFactory(factory); - + Customizing instantiation logic using <literal>IFactoryObjects</literal> - The Spring.Objects.Factory.IFactoryObject + The Spring.Objects.Factory.IFactoryObject interface is to be implemented by objects that are themselves factories. @@ -4700,7 +4715,7 @@ cfg.PostProcessObjectFactory(factory); inside that class, and then plug your custom IFactoryObject into the container. - The IFactoryObject interface provides one + The IFactoryObject interface provides one method and two (read-only) properties: object GetObject(): has to return an @@ -4723,38 +4738,38 @@ cfg.PostProcessObjectFactory(factory); - IFactoryObject + IFactoryObject The IFactoryObject concept and interface is used in a number of places within the Spring Framework. Some examples of its use is described in for the - PropertyRetrievingFactoryObject and - FieldRetrievingFactoryObject. An additional use + PropertyRetrievingFactoryObject and + FieldRetrievingFactoryObject. An additional use of creating an custom IFactoryObject implementation is to retrieve an object from an embedded resource file and use it to set another objects dependency. An example of this is provided here. Finally, there is sometimes a need to ask a container for an - actual IFactoryObject instance itself, not the + actual IFactoryObject instance itself, not the object it produces. This may be achieved by prepending the object id with '&' (sans quotes) when calling the GetObject method of the - IObjectFactory (including - IApplicationContext). So for a given - IFactoryObject with an id of + IObjectFactory (including + IApplicationContext). So for a given + IFactoryObject with an id of 'myObject', invoking GetObject("myObject") on the container will return - the product of the IFactoryObject, but invoking + the product of the IFactoryObject, but invoking GetObject("&myObject") will return the - IFactoryObject instance itself. + IFactoryObject instance itself. IConfigurableFactoryObject The - Spring.Objects.Factory.IConfigurableFactoryObject - interface inherits from IFactoryObject + Spring.Objects.Factory.IConfigurableFactoryObject + interface inherits from IFactoryObject interface and adds the following property. @@ -4765,36 +4780,36 @@ cfg.PostProcessObjectFactory(factory); - IConfigurableFactoryObject implementions + IConfigurableFactoryObject implementions you already have examples of in are - WebServiceProxyFactory. + WebServiceProxyFactory. - - The <classname>IApplicationContext</classname> + + The <literal>IApplicationContext</literal> While the Spring.Objects namespace provides basic functionality for managing and manipulating objects, often in a programmatic way, the Spring.Context namespace - introduces the IApplicationContext interface, which + introduces the IApplicationContext interface, which enhances the functionality provided by the - IObjectFactory interface in a more + IObjectFactory interface in a more framework-oriented style. Many users will use ApplicationContext in a completely declarative fashion, not even having to create it manually, but instead relying on support classes such as the .NET configuration section handlers such as ContextHandler and WebContextHandler together to declaratively define the ApplicationContext and retrieve it though a ContextRegistry. (Of course it is still possible - to create an IApplicationContext + to create an IApplicationContext Programatically). The basis for the context module is the - IApplicationContext interface, located in the + IApplicationContext interface, located in the Spring.Context namespace. Deriving from the - IObjectFactory interface, it provides all the - functionality of the IObjectFactory. To be able to + IObjectFactory interface, it provides all the + functionality of the IObjectFactory. To be able to work in a more framework-oriented fashion, using layering and hierarchical contexts, the Spring.Context namespace also provides the following functionality @@ -4808,13 +4823,13 @@ cfg.PostProcessObjectFactory(factory); Access to localized resources at the application level by implementing - IMessageSource. + IMessageSource. Uniform access to resources that can be read in as an InputStream, such as URLs and files by implementing - IResourceLoader + IResourceLoader @@ -4829,36 +4844,36 @@ cfg.PostProcessObjectFactory(factory); IObjectFactory or IApplicationContext? Short version: use an - IApplicationContext unless + IApplicationContext unless you have a really good reason for not doing so. For those of you that are looking for slightly more depth as to the 'but why' of the above recommendation, keep reading. - As the IApplicationContext includes all the + As the IApplicationContext includes all the functionality the object factory via its inheritance of the - IObjectFactory interface, it is generally - recommended to be used over the IObjectFactory + IObjectFactory interface, it is generally + recommended to be used over the IObjectFactory except for a few limited situations where memory consumption might be critical. This may become more important if the .NET Compact Framework - is supported. The history of IObjectFactory comes + is supported. The history of IObjectFactory comes from the Spring Java framework, where the use of Spring in Applets was a concern to reduce memory consumption. However, for most 'typical' enterprise applications and systems, the - IApplicationContext is what you will want to use. + IApplicationContext is what you will want to use. Spring generally makes heavy use of the - IObjectPostProcessor extension point (to effect + IObjectPostProcessor extension point (to effect proxying and suchlike), and if you are using just a plain - IObjectFactory then a fair amount of support such + IObjectFactory then a fair amount of support such as transactions and AOP will not take effect (at least not without some extra steps on your part), which could be confusing because nothing will actually be wrong with the configuration. Find below a feature matrix that lists what features are provided - by the IObjectFactory and - IApplicationContext interfaces (and attendant + by the IObjectFactory and + IApplicationContext interfaces (and attendant implementations). The following sections describe functionality that - IApplicationContext adds to the basic - IObjectFactory capabilities in a lot more depth + IApplicationContext adds to the basic + IObjectFactory capabilities in a lot more depth than the said feature matrix.) FeatureIObjectFactory + align="center">IObjectFactoryIApplicationContext + align="center">IApplicationContext @@ -4891,7 +4906,7 @@ cfg.PostProcessObjectFactory(factory); Automatic - IObjectPostProcessor + IObjectPostProcessor registration No @@ -4901,7 +4916,7 @@ cfg.PostProcessObjectFactory(factory); Automatic - IObjectFactoryPostProcessor + IObjectFactoryPostProcessor registration No @@ -4911,7 +4926,7 @@ cfg.PostProcessObjectFactory(factory); Convenient - IMessageSource + IMessageSource access No @@ -4920,7 +4935,7 @@ cfg.PostProcessObjectFactory(factory); - ApplicationEvent + ApplicationEvent publication No @@ -4951,7 +4966,7 @@ cfg.PostProcessObjectFactory(factory); - + Configuration of IApplicationContext Well known locations in the .NET application configuration file are @@ -4960,7 +4975,7 @@ cfg.PostProcessObjectFactory(factory); previously. A sample .NET application configuration file showing all these features is shown below. Each section requires the use of a custom configuration section handler. Note that the types shown for resource - handlers and parsers are fictional. <configuration> + handlers and parsers are fictional. <configuration> <configSections> <sectionGroup name="spring"> @@ -4969,7 +4984,7 @@ cfg.PostProcessObjectFactory(factory); <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/> - <section name="resources" type="Spring.Context.Support.ResourceHandlersSectionHandler, Spring.Core"/> + <section name="resources" type="Spring.Context.Support.ResourceHandlersSectionHandler, Spring.Core"/> <section name="typeAliases" type="Spring.Context.Support.TypeAliasesSectionHandler, Spring.Core"/> <section name="typeConverters" type="Spring.Context.Support.TypeConvertersSectionHandler, Spring.Core"/> @@ -5013,13 +5028,13 @@ cfg.PostProcessObjectFactory(factory); </configuration> The new sections are described below. The attribute caseSensitive allows the for both - IObjectFactory and - IApplicationContext implementations to not pay + IObjectFactory and + IApplicationContext implementations to not pay attention to the case of the object names. This is important in web applications so that ASP.NET pages can be resolved in a case independent manner. The default value is true. - + Registering custom parsers Instead of using the default XML schema that is generic in nature @@ -5029,7 +5044,7 @@ cfg.PostProcessObjectFactory(factory); being used. The downside is that you need to write code that will transform this XML into Spring object definitions. One would typically implement a custom parser by deriving from the class - ObjectsNamespaceParser and overriding the methods + ObjectsNamespaceParser and overriding the methods int ParseRootElement(XmlElement root, XmlResourceReader reader) and int ParseElement(XmlElement element, XmlResourceReader reader). Registering custom parsers outside @@ -5042,7 +5057,7 @@ cfg.PostProcessObjectFactory(factory); attribute. Below is an example that registers all the namespaces provided in Spring. - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> @@ -5067,7 +5082,7 @@ cfg.PostProcessObjectFactory(factory); NamespaceParserRegistry. Here is an example taken from the code used in the Transactions Quickstart application. - NamespaceParserRegistry.RegisterParser(typeof(DatabaseNamespaceParser)); + NamespaceParserRegistry.RegisterParser(typeof(DatabaseNamespaceParser)); NamespaceParserRegistry.RegisterParser(typeof(TxNamespaceParser)); NamespaceParserRegistry.RegisterParser(typeof(AopNamespaceParser)); @@ -5075,24 +5090,24 @@ IApplicationContext context = new XmlApplicationContext("assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/system-test-local-config.xml"); - + Registering custom resource handlers Creating a custom resource handler means implementing the - IResource interface. The base class - AbstractResource is a useful starting point. Look + IResource interface. The base class + AbstractResource is a useful starting point. Look at the Spring source for classes such as - FileSystemResource or - AssemblyResource for implementation tips. You can + FileSystemResource or + AssemblyResource for implementation tips. You can register your custom resource handler either within App.config, as shown in the program listing at the start of this section using a - .ResourceHandlersSectionHandler or define an + .ResourceHandlersSectionHandler or define an object of the type - Spring.Objects.Factory.Config.ResourceHandlerConfigurer + Spring.Objects.Factory.Config.ResourceHandlerConfigurer as you would any other Spring managed object. An example of the latter is shown below: - <object id="myResourceHandlers" type="Spring.Objects.Factory.Config.ResourceHandlersSectionHandler, Spring.Core"> + <object id="myResourceHandlers" type="Spring.Objects.Factory.Config.ResourceHandlersSectionHandler, Spring.Core"> <property name="ResourceHandlers"> <dictionary> <entry key="db" value="MyCompany.MyApp.Resources.MyDbResource, MyAssembly"/> @@ -5101,7 +5116,7 @@ IApplicationContext context = </object> - + Registering Type Aliases Type aliases allow you to simplify Spring configuration file by @@ -5117,8 +5132,7 @@ IApplicationContext context = configuration listing for an example that makes an alias for the WebServiceExporter type. Once you have aliases defined, you can simply use them anywhere where you would normally specify a fully qualified - type name: -<object id="MyWebService" type="WebServiceExporter"> + type name:<object id="MyWebService" type="WebServiceExporter"> ... </object> @@ -5138,7 +5152,7 @@ IApplicationContext context = a type attribute. Below is an example that registers the alias for WebServiceExporter - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> @@ -5159,14 +5173,14 @@ IApplicationContext context = linkend="objects-creation-generic-types" />. Another way is to define an object of the type - Spring.Objects.Factory.Config.TypeAliasConfigurer + Spring.Objects.Factory.Config.TypeAliasConfigurer within the regular <objects> section of any standard Spring configuration file. This approach allows for more modularity in defining type aliases, for example if you can't access App.config/Web.config. An example of registration using a - TypeAliasConfigurer is shown below + TypeAliasConfigurer is shown below - <object id="myTypeAlias" type="Spring.Objects.Factory.Config.TypeAliasConfigurer, Spring.Core"> + <object id="myTypeAlias" type="Spring.Objects.Factory.Config.TypeAliasConfigurer, Spring.Core"> <property name="TypeAliases"> <dictionary> <entry key="WebServiceExporter" value="Spring.Web.Services.WebServiceExporter, Spring.Web"/> @@ -5177,11 +5191,11 @@ IApplicationContext context = </object> - + Registering Type Converters The standard .NET mechanism for specifying a type converter is to - add a TypeConverter attribute to a type + add a TypeConverter attribute to a type definition to specify the type of the Converter. This is the preferred way of defining type converters if you control the source code for the type that you want to define a converter for. However, this @@ -5193,11 +5207,11 @@ IApplicationContext context = You can specify the type converters in App.config by using Spring.Context.Support.TypeConvertersSectionHandler as shown before or define an object of the type - Spring.Objects.Factory.Config.CustomConverterConfigurer. + Spring.Objects.Factory.Config.CustomConverterConfigurer. An example of registration using a - CustomConverterConfigurer is shown below + CustomConverterConfigurer is shown below - <object id="myTypeConverters" type="Spring.Objects.Factory.Config.CustomConverterConfigurer, Spring.Core"> + <object id="myTypeConverters" type="Spring.Objects.Factory.Config.CustomConverterConfigurer, Spring.Core"> <property name="CustomConverters"> <dictionary> <entry key="System.Date" value="MyCompany.MyProject.MyNamespace.MyCustomDateConverter, MyAssembly"/> @@ -5207,16 +5221,16 @@ IApplicationContext context = - + Added functionality of the - <classname>IApplicationContext</classname> + IApplicationContext As already stated in the previous section, the - IApplicationContext has a couple of features that - distinguish it from the IObjectFactory. Let us + IApplicationContext has a couple of features that + distinguish it from the IObjectFactory. Let us review them one-by-one. - + Context Hierarchies You can structure the configuration information of application @@ -5224,7 +5238,7 @@ IApplicationContext context = your application. As an example, abstract object definitions may appear in a parent application context configuration file, possibly as an embedded assembly resource so as not to invite accidental changes. - <spring> + <spring> <context> <resource uri="assembly://MyAssembly/MyProject/root-objects.xml"/> <context name="mySubContext"> @@ -5237,7 +5251,7 @@ IApplicationContext context = application hierarchy. The xml file must contain the <objects> as the root name. Another example of a hierarchy, but using sections in the application configuration file is - shown below. <configSections> + shown below. <configSections> <sectionGroup name="spring"> <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/> <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> @@ -5274,17 +5288,17 @@ IApplicationContext context = As a reminder, the type attribute of the context tag is optional and defaults to - Spring.Context.Support.XmlApplicationContext. The + Spring.Context.Support.XmlApplicationContext. The name of the context can be used in conjunction with the service locator - class, ContextRegistry, discussed in ContextRegistry, discussed in - + Using <literal>IMessageSource</literal> - The IApplicationContext interface extends - an interface called IMessageSource and provides + The IApplicationContext interface extends + an interface called IMessageSource and provides localization (i18n or internationalization) services for text messages and other resource data types such as images. This functionality makes it easier to use .NET's localization features at an application level @@ -5296,21 +5310,21 @@ IApplicationContext context = string GetMessage(string name): retrieves - a message from the IMessageSource and using + a message from the IMessageSource and using CurrentUICulture. string GetMessage(string name, CultureInfo cultureInfo): retrieves a message from the - IMessageSource using a specified + IMessageSource using a specified culture. string GetMessage(string name, params object[] args): retrieves a message from the - IMessageSource using a variable list of + IMessageSource using a variable list of arguments as replacement values in the message. The CurrentUICulture is used to resolve the message. @@ -5318,7 +5332,7 @@ IApplicationContext context = string GetMessage(string name, CultureInfo cultureInfo, params object[] args): retrieves a message - from the IMessageSource using a variable + from the IMessageSource using a variable list of arguments as replacement values in the message. The specified culture is used to resolve the message. @@ -5327,7 +5341,7 @@ IApplicationContext context = string GetMessage(string name, string defaultMessage, CultureInfo culture, params object[] arguments): retrieves a message from the - IMessageSource using a variable list of + IMessageSource using a variable list of arguments as replacement values in the message. The specified culture is used to resolve the message. If no message can be resolved, the default message is used. @@ -5335,7 +5349,7 @@ IApplicationContext context = string GetMessage(IMessageSourceResolvable - resolvable, CultureInfo culture) + resolvable, CultureInfo culture) : all properties used in the methods above are also wrapped in a class - the MessageSourceResolvable, which you can use in @@ -5365,15 +5379,15 @@ IApplicationContext context = - When an IApplicationContext gets loaded, it - automatically searches for an IMessageSource + When an IApplicationContext gets loaded, it + automatically searches for an IMessageSource object defined in the context. The object has to have the name messageSource. If such an object is found, all calls to the methods described above will be delegated to the message source that was found. If no message source was found, the - IApplicationContext checks to see if it has a + IApplicationContext checks to see if it has a parent containing a similar object, with a similar name. If so, it uses - that object as the IMessageSource. If it can't + that object as the IMessageSource. If it can't find any source for messages, an empty StaticMessageSource will be instantiated in order to be able to accept calls to the methods defined above. @@ -5386,24 +5400,24 @@ IApplicationContext context = The fallback rules for localized resources seem to have a bug that is fixed by applying Service Pack 1 for .NET 1.1. This affects the use of IMessageSource.GetMessage methods that specify CultureInfo. The core of the issue in the .NET BCL is the method ResourceManager.GetObject that accepts CultureInfo. . - Spring.NET provides two IMessageSource + Spring.NET provides two IMessageSource implementations. These are - ResourceSetMessageSource and - StaticMessageSource. Both implement - IHierarchicalMessageSource to resolve messages - hierarchically. The StaticMessageSource is hardly + ResourceSetMessageSource and + StaticMessageSource. Both implement + IHierarchicalMessageSource to resolve messages + hierarchically. The StaticMessageSource is hardly ever used but provides programmatic ways to add messages to the source. - The ResourceSetMessageSource is more interesting + The ResourceSetMessageSource is more interesting and an example is provided for in the distribution and discussed more extensively in the section. The - ResourceSetMessageSource is configured by - providing a list of ResourceManagers. When a + ResourceSetMessageSource is configured by + providing a list of ResourceManagers. When a message code is to be resolved, the list of ResourceManagers is searched - to resolve the code. For each ResourceManager a - ResourceSet is retrieved and asked to resolve the + to resolve the code. For each ResourceManager a + ResourceSet is retrieved and asked to resolve the code. Note that this search does not replace the standard hub-and-spoke search for localized resources. The ResourceManagers list specifies the - multiple 'hubs' where the standard search starts. <object name="messageSource" type="Spring.Context.Support.ResourceSetMessageSource, Spring.Core"> + multiple 'hubs' where the standard search starts. <object name="messageSource" type="Spring.Context.Support.ResourceSetMessageSource, Spring.Core"> <property name="resourceManagers"> <list> <value>Spring.Examples.AppContext.MyResource, Spring.Examples.AppContext</value> @@ -5414,14 +5428,14 @@ IApplicationContext context = You can specify the arguments to construct a ResourceManager as a two part string value containing the base name of the resource and the assembly name. This will be converted to a ResourceManager via the - ResourceManagerConverter TypeConverter. This + ResourceManagerConverter TypeConverter. This converter can be similarly used to set a property on any object that is - of the type ResourceManager. You may also specify - an instance of the ResourceManager to use via an + of the type ResourceManager. You may also specify + an instance of the ResourceManager to use via an object reference. The convenience class - Spring.Objects.Factory.Config.ResourceManagerFactoryObject + Spring.Objects.Factory.Config.ResourceManagerFactoryObject can be used to conveniently create an instance of a ResourceManager. - <object name="myResourceManager" type="Spring.Objects.Factory.Config.ResourceManagerFactoryObject, Spring.Core"> + <object name="myResourceManager" type="Spring.Objects.Factory.Config.ResourceManagerFactoryObject, Spring.Core"> <property name="baseName"> <value>Spring.Examples.AppContext.MyResource</value> </property> @@ -5433,7 +5447,7 @@ IApplicationContext context = In application code, a call to GetMessage will retrieve a properly localized message string based on a code value. Any arguments present in the retrieved string are replaced using - String.Format semantics. The ResourceManagers, + String.Format semantics. The ResourceManagers, ResourceSets and retrieved strings are cached to provide quicker lookup performance. The key 'HelloMessage' is contained in the resource file with a value of Hello {0} {1}. The following call on @@ -5441,7 +5455,7 @@ IApplicationContext context = Anderson. Note that the caching of ResourceSets is via the concatenation of the ResourceManager base name and the CultureInfo string. This combination must be unique. - string msg = ctx.GetMessage("HelloMessage", + string msg = ctx.GetMessage("HelloMessage", new object[] {"Mr.", "Anderson"}, CultureInfo.CurrentCulture ); @@ -5451,13 +5465,13 @@ IApplicationContext context = flexibility in how you can structure your message resolution. This is achieved by passing as an argument a class that implements IMessageResolvable instead of a string literal. The - convenience class DefaultMessageResolvable is + convenience class DefaultMessageResolvable is available for this purpose. As an example if the resource file contains a key name error.required that has the value '{0} is required {1}' and another key name field.firstname with the value 'First name'. The following code will create the string - 'First name is required dude!' string[] codes = {"field.firstname"}; + 'First name is required dude!' string[] codes = {"field.firstname"}; DefaultMessageResolvable dmr = new DefaultMessageResolvable(codes, null); ctx.GetMessage("error.required", new object[] { dmr, "dude!" }, @@ -5468,31 +5482,31 @@ ctx.GetMessage("error.required", program, Spring.Examples.AppContext, that demonstrates usage of these features. - The IMessageSourceAware interface can also + The IMessageSourceAware interface can also be used to acquire a reference to any - IMessageSource that has been defined. Any object - that is defined in an IApplicationContext that - implements the IMessageSourceAware interface will + IMessageSource that has been defined. Any object + that is defined in an IApplicationContext that + implements the IMessageSourceAware interface will be injected with the application context's - IMessageSource when it (the object) is being + IMessageSource when it (the object) is being created and configured. - + Using resources within Spring.NET A lot of applications need to access resources. Resources here, might mean files, but also news feeds from the Internet or normal web pages. Spring.NET provides a clean and transparent way of accessing resources in a protocol independent way. The - IApplicationContext has a method + IApplicationContext has a method (GetResource(string)) to take care of this. Refer to for more information on the string - format to use and the IResource abstraction in + format to use and the IResource abstraction in general. - + Loosely coupled events The Eventing Registry allows developers to utilize a loosely @@ -5506,7 +5520,7 @@ ctx.GetMessage("error.required", one subscriber to handle all events of a certain type without regards to how many different instances of that type are created. - The Spring.Objects.Events.IEventRegistry + The Spring.Objects.Events.IEventRegistry interface represents the central registry and defines publish and subscribe methods. @@ -5527,12 +5541,12 @@ ctx.GetMessage("error.required", from a source object of a particular type for which it has matching handler methods. - IApplicationContext implements + IApplicationContext implements this interface and delegates the implementation to an instance of - Spring.Objects.Events.Support.EventRegistry. You + Spring.Objects.Events.Support.EventRegistry. You are free to create and use as many EventRegistries as you like but since it is common to use only one in an application, - IApplicationContext provides convenient access to + IApplicationContext provides convenient access to a single instance. Within the @@ -5541,10 +5555,10 @@ ctx.GetMessage("error.required", When you open up the project, the most interesting file is the EventRegistryApp.cs file. This application loads a set of object definitions from the application configuration file into an - IApplicationContext instance. From there, three + IApplicationContext instance. From there, three objects are loaded up: one publisher and two subscribers. The publisher - publishes its events to the IApplicationContext - instance: // Create the Application context using configuration file + publishes its events to the IApplicationContext + instance: // Create the Application context using configuration file IApplicationContext ctx = ContextRegistry.GetContext(); // Gets the publisher from the application context @@ -5553,8 +5567,8 @@ MyEventPublisher publisher = (MyEventPublisher)ctx.GetObject("MyEventPublisher") // Publishes events to the context. ctx.PublishEvents( publisher ); One of the two subscribers subscribes to all events - published to the IApplicationContext instance, - using the publisher type as the filter criteria.// Gets first instance of subscriber + published to the IApplicationContext instance, + using the publisher type as the filter criteria.// Gets first instance of subscriber MyEventSubscriber subscriber = (MyEventSubscriber)ctx.GetObject("MyEventSubscriber"); // Gets second instance of subscriber @@ -5570,47 +5584,47 @@ ctx.Subscribe( subscriber, typeof(MyEventPublisher) ); This prototype definition. - + Event notification from - <classname>IApplicationContext</classname> + IApplicationContext - Event handling in the IApplicationContext - is provided through the IApplicationListener + Event handling in the IApplicationContext + is provided through the IApplicationListener interface that contains the single method void OnApplicationEvent( object source, ApplicationEventArgs applicationEventArgs ). Classes that implement the - IApplicationListener interface are automatically + IApplicationListener interface are automatically registered as a listener with the - IApplicationContext. Publishing an event is done + IApplicationContext. Publishing an event is done via the context's PublishEvent( ApplicationEventArgs eventArgs ) method. This implementation is based on the traditional Observer design pattern. The event argument type, - ApplicationEventArgs, adds the time of the event + ApplicationEventArgs, adds the time of the event firing as a property. The derived class - ContextEventArgs is used to notify observers on + ContextEventArgs is used to notify observers on the lifecycle events of the application context. It contains a property ContextEvent Event that returns the enumeration Refreshed or Closed.. The Refreshed enumeration value indicated that the - IApplicationContext was either initialized or + IApplicationContext was either initialized or refreshed. Initialized here means that all objects are loaded, singletons are pre-instantiated and the - IApplicationContext is ready for use. The + IApplicationContext is ready for use. The Closed is published when the - IApplicationContext is closed using the + IApplicationContext is closed using the Dispose() method on the - IConfigurableApplicationContext interface. Closed + IConfigurableApplicationContext interface. Closed here means that singletons are destroyed. Implementing custom events can be done as well. Simply call the PublishEvent method on the - IApplicationContext, specifying a parameter which + IApplicationContext, specifying a parameter which is an instance of your custom event argument subclass. Let's have a look at an example. First, the - IApplicationContext: <object id="emailer" type="Example.EmailObject"> + IApplicationContext: <object id="emailer" type="Example.EmailObject"> <property name="blackList"> <list> <value>black@list.org</value> @@ -5625,7 +5639,7 @@ ctx.Subscribe( subscriber, typeof(MyEventPublisher) ); This <value>spam@list.org</value> </property> </object> and then, the actual objects: - public class EmailObject : IApplicationContextAware { + public class EmailObject : IApplicationContextAware { // the blacklist private IList blackList; @@ -5673,26 +5687,26 @@ public class BlackListNotifier : IApplicationListener - + Customized behavior in the ApplicationContext - The IObjectFactory already offers a number of + The IObjectFactory already offers a number of mechanisms to control the lifecycle of objects deployed in it (such as - marker interfaces like IInitializingObject and - System.IDisposable, their configuration only + marker interfaces like IInitializingObject and + System.IDisposable, their configuration only equivalents such as init-method and destroy-method) attributes in an XmlObjectFactory configuration, and object post-processors. In an - IApplicationContext, all of these still work, but + IApplicationContext, all of these still work, but additional mechanisms are added for customizing behavior of objects and the container. - + The <literal>IApplicationContextAware</literal> marker interface All marker interfaces available with ObjectFactories still work. - The IApplicationContext does add one extra marker + The IApplicationContext does add one extra marker interface which objects may implement, IApplicationContextAware. An object which implements this interface and is deployed into the context will be called back on @@ -5702,47 +5716,47 @@ public class BlackListNotifier : IApplicationListener the context. - + The <literal>IObjectPostProcessor</literal> Object post-processors are classes which implement the - Spring.Objects.Factory.Config.IObjectPostProcessor + Spring.Objects.Factory.Config.IObjectPostProcessor interface, have already been mentioned. It is worth mentioning again here though, that post-processors are much - more convenient to use in IApplicationContexts - than in plain IObjectFactory instances. In an - IApplicationContext, any deployed object which + more convenient to use in IApplicationContexts + than in plain IObjectFactory instances. In an + IApplicationContext, any deployed object which implements the above marker interface is automatically detected and registered as an object post-processor, to be called appropriately at creation time for each object in the factory. - + The <literal>IObjectFactoryPostProcessor</literal> Object factory post-processors are classes which implement the - Spring.Objects.Factory.Config.IObjectFactoryPostProcessor + Spring.Objects.Factory.Config.IObjectFactoryPostProcessor interface, have already been mentioned. It is worth mentioning again here though, that object factory post-processors are much more convenient to use in - IApplicationContexts. In an - IApplicationContext, any deployed object which + IApplicationContexts. In an + IApplicationContext, any deployed object which implements the above marker interface is automatically detected as an object factory post-processor, to be called at the appropriate time. - + The <literal>PropertyPlaceholderConfigurer</literal> The PropertyPlaceholderConfigurer has already been described in the context of its use within an - IObjectFactory. It is worth mentioning here + IObjectFactory. It is worth mentioning here though, that it is generally more convenient to use it with an - IApplicationContext, since the context will + IApplicationContext, since the context will automatically recognize and apply any object factory post-processors, such as this one, when they are simply deployed into it like any other object. There is no need for a manual step to execute it. @@ -5763,7 +5777,7 @@ public class BlackListNotifier : IApplicationListener configure the GenericApplicationContext to read from XML, just so show familiar API usage - GenericApplicationContext ctx = new GenericApplicationContext(); + GenericApplicationContext ctx = new GenericApplicationContext(); XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(ctx); reader.LoadObjectDefinitions("assembly://Spring.Core.Tests/Spring.Context.Support/contextB.xml"); reader.LoadObjectDefinitions("assembly://Spring.Core.Tests/Spring.Context.Support/contextC.xml"); @@ -5780,7 +5794,7 @@ ctx.Refresh(); MyAssembly.dll located in the runtime path, would look something like this - GenericApplicationContext ctx = new GenericApplicationContext(); + GenericApplicationContext ctx = new GenericApplicationContext(); ObjectDefinitionScanner scanner = new ObjectDefinitionScanner(ctx); scanner.scan("MyAssembly.dll"); ctx.refresh(); @@ -5788,18 +5802,18 @@ ctx.refresh(); Refer to the Spring API documentation for more information. - + Service Locator access The majority of the code inside an application is best written in a Dependency Injection (Inversion of Control) style, where that code is - served out of an IObjectFactory or - IApplicationContext container, has its own + served out of an IObjectFactory or + IApplicationContext container, has its own dependencies supplied by the container when it is created, and is completely unaware of the container. However, there is sometimes a need for singleton (or quasi-singleton) style access to an - IObjectFactory or - IApplicationContext. For example, third party code + IObjectFactory or + IApplicationContext. For example, third party code may try to construct a new object directly without the ability to force it to get these objects out of the IObjectFactory. Similarly, nested user control components in a WinForms application are created inside the @@ -5809,22 +5823,22 @@ ctx.refresh(); obtain the object it requires. (Note support for DI in WinForms is under development.) - The Spring.Context.Support.ContextRegistry + The Spring.Context.Support.ContextRegistry class allows you to obtain a reference to an - IApplicationContext via a static locator method. - The ContextRegistry is initialized when creating an - IApplicationContext through use of the - ContextHandler discussed previously. The simple + IApplicationContext via a static locator method. + The ContextRegistry is initialized when creating an + IApplicationContext through use of the + ContextHandler discussed previously. The simple static method GetContext() can then be used to retrieve the context. Alternatively, if you create an - IApplicationContext though other means you can - register it with the ContextRegistry via the method + IApplicationContext though other means you can + register it with the ContextRegistry via the method void RegisterContext(IApplicationContext context) in the start-up code of your application. Hierarchical context retrieval is also supported though the use of the GetContext(string - name) method, for example:IApplicationContex ctx = ContextRegistry.GetContext("mySubContext"); + name) method, for example:IApplicationContex ctx = ContextRegistry.GetContext("mySubContext"); This would retrieve the nested context for the context configuration shown - previously.<spring> + previously.<spring> <context> <resource uri="assembly://MyAssembly/MyProject/root-objects.xml"/> <context name="mySubContext"> diff --git a/doc/reference/src/orm.xml b/doc/reference/src/orm.xml index 05840e75..c0d8c111 100644 --- a/doc/reference/src/orm.xml +++ b/doc/reference/src/orm.xml @@ -1,8 +1,25 @@ - + + Object Relational Mapping (ORM) data access -
+
Introduction The Spring Framework provides integration with NHibernate @@ -31,8 +48,8 @@ Ease of testing. Spring's IoC approach makes it easy to swap the implementations and config locations of - Hibernate SessionFactory instances, - ADO.NET DbProvider instances, + Hibernate SessionFactory instances, + ADO.NET DbProvider 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. @@ -52,18 +69,18 @@ General resource management. Spring application contexts can handle the location and configuration of - Hibernate ISessionFactory instances, - ADO.NET DbProvider instances and other + Hibernate ISessionFactory instances, + ADO.NET DbProvider 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 Session for + to use the same NHibernate Session for efficiency and proper transaction handling. Spring makes it easy to - transparently create and bind a Session + transparently create and bind a Session to the current thread, either by using an explicit 'template' wrapper class at the code level or by exposing a current - Session through the Hibernate - SessionFactory (for DAOs based on plain + Session through the Hibernate + SessionFactory (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). @@ -110,7 +127,7 @@ value proposition.
-
+
NHibernate We will start with a coverage of -
+
Resource management Typical business applications are often cluttered with repetitive @@ -140,18 +157,18 @@ 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 AdoTemplate class mentioned in a previous + the AdoTemplate 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 DataAccessException hierarchy, including + Spring's DataAccessException 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. Spring also offers Hibernate support, consisting of a - HibernateTemplate analogous to - AdoTemplate, a - HibernateInterceptor, and a Hibernate transaction + HibernateTemplate analogous to + AdoTemplate, a + HibernateInterceptor, 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 @@ -169,7 +186,7 @@ the business services),and so on.
-
+
Transaction Management While NHibernate offers an API for transaction management you will @@ -183,8 +200,8 @@ the other the .NET 2.0 TransactionScope API. The first strategy is encapsulated in the class - Spring.Data.NHibernate.HibernateTransactionManager - in both the Spring.Data.NHibernate + Spring.Data.NHibernate.HibernateTransactionManager + in both the Spring.Data.NHibernate 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 @@ -198,10 +215,10 @@ 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 HibernateTransactionManager is + the declaration of HibernateTransactionManager is shown below. - <object id="HibernateTransactionManager" + <object id="HibernateTransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate"> <property name="DbProvider" ref="DbProvider"/> @@ -210,14 +227,14 @@ </object> The important property of - HibernateTransactionManager are the references to + HibernateTransactionManager are the references to the DbProvider and the Hibernate ISessionFactory. For more information on the DbProvider, refer to the chapter DbProvider and the following section on SessionFactory set up. The second strategy is to use the class - Sping.Data.TxScopeTransactionManager that uses + Sping.Data.TxScopeTransactionManager 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. @@ -226,7 +243,7 @@ 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 OpenSessionInViewModule in a web + using the OpenSessionInViewModule in a web application in single session mode (see ). In this case the session will be created on the start of the web request and closed on the end of the @@ -240,22 +257,22 @@ of the web request.
-
- <interfacename>SessionFactory</interfacename> set up in a Spring + <section xml:id="orm-session-factory-setup"> + <title><literal>SessionFactory</literal> set up in a Spring container To avoid tying application objects to hard-coded resource lookups, Spring allows you to define resources like a - DbProvider or a Hibernate - SessionFactory as objects in an + DbProvider or a Hibernate + SessionFactory 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 - SessionFactory on top of it: + SessionFactory on top of it: - <objects xmlns="http://www.springframework.net" + <objects xmlns="http://www.springframework.net" xmlns:db="http://www.springframework.net/database"> @@ -318,7 +335,7 @@ array so multiple configuration files are supported. There are other properties in - LocalSessionFactoryObject that relate to the + LocalSessionFactoryObject that relate to the integration of Spring with NHibernate. The property ExposeTransactionAwareSessionFactory is discussed below and allows you to use Spring's declarative transaction demarcation @@ -335,7 +352,7 @@ - Delegate to the DbProvider itself as + Delegate to the DbProvider itself as the NHibernate connection provider instead of listing it via property hibernate.connection.provider via HibernateProperties. @@ -345,9 +362,9 @@ 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 DbProvider as the + logged. If you use Spring's DbProvider as the NHibernate connection provider then you can take advantage of - IDbProvider implementations that will let you + IDbProvider implementations that will let you change the connection string at runtime such as UserCredentialsDbProvider and MultiDelegatingDbProvider 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 ISessionFactory instance. As such, they + to each ISessionFactory 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 - LocalSessionFactoryObject and override the + LocalSessionFactoryObject and override the method ISessionFactory NewSessionFactory(Configuration config) so that it returns an implementation of - ISessionFactory that selects among multiple + ISessionFactory that selects among multiple instances based on values in thread local storage, much like the implementation of - MultiDelegatingDbProvider. + MultiDelegatingDbProvider.
-
- The <classname>HibernateTemplate</classname> +
+ The <literal>HibernateTemplate</literal> 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 - SessionFactory. It can get the latter + SessionFactory. It can get the latter from anywhere, but preferably as an object reference from a Spring IoC container - via a simple SessionFactory property setter. The following snippets show a DAO definition in a Spring container, referencing the above defined - SessionFactory, and an example for a DAO + SessionFactory, and an example for a DAO method implementation. - <objects> + <objects> <object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerDao, Spring.Northwind.Dao.NHibernate"> <property name="SessionFactory" ref="MySessionFactory"/> @@ -402,7 +419,7 @@ - public class HibernateCustomerDao : ICustomerDao { + public class HibernateCustomerDao : ICustomerDao { private HibernateTemplate hibernateTemplate; @@ -418,15 +435,15 @@ } } - The HibernateTemplate class provides many + The HibernateTemplate class provides many methods that mirror the methods exposed on the Hibernate - Session interface, in addition to a + Session interface, in addition to a number of convenience methods such as the one shown above. If you need - access to the Session to invoke methods - that are not exposed on the HibernateTemplate, + access to the Session to invoke methods + that are not exposed on the HibernateTemplate, you can always drop down to a callback-based approach like so. - public class HibernateCustomerDao : ICustomerDao { + public class HibernateCustomerDao : ICustomerDao { private HibernateTemplate hibernateTemplate; @@ -454,7 +471,7 @@ generics, you can avoid the typecast and write code like the following - IList<Supplier> suppliers = HibernateTemplate.ExecuteFind<Supplier>( + IList<Supplier> suppliers = HibernateTemplate.ExecuteFind<Supplier>( delegate(ISession session) { return session.CreateQuery("from Supplier s were s.Code = ?") @@ -466,22 +483,22 @@ inside the anonymous delegate implementation. A callback implementation effectively can be used for any - Hibernate data access. HibernateTemplate will - ensure that Session instances are + Hibernate data access. HibernateTemplate will + ensure that Session 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, HibernateTemplate offers alternative + Delete call, HibernateTemplate offers alternative convenience methods that can replace such one line callback implementations. Furthermore, Spring provides a convenient - HibernateDaoSupport base class that provides a + HibernateDaoSupport base class that provides a SessionFactory property for receiving a - SessionFactory and for use by subclasses. + SessionFactory and for use by subclasses. In combination, this allows for very simple DAO implementations for typical requirements: - public class HibernateCustomerDao : HibernateDaoSupport, ICustomerDao + public class HibernateCustomerDao : HibernateDaoSupport, ICustomerDao { public Customer SaveOrUpdate(Customer customer) { @@ -491,28 +508,28 @@ }
-
+
Implementing Spring-based DAOs without callbacks As an alternative to using Spring's - HibernateTemplate to implement DAOs, data access + HibernateTemplate 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 - DataAccessException hierarchy. The - HibernateDaoSupport base class offers methods to - access the current transactional Session + DataAccessException hierarchy. The + HibernateDaoSupport base class offers methods to + access the current transactional Session and to convert exceptions in such a scenario; similar methods are also available as static helpers on the - SessionFactoryUtils class. Note that such code + SessionFactoryUtils class. Note that such code will usually pass 'false' as the value of the DoGetSession(..) method's 'allowCreate' argument, to enforce running within a transaction (which avoids the need to close the returned - Session, as its lifecycle is managed by + Session, as its lifecycle is managed by the transaction). Asking for the - public class HibernateProductDao extends HibernateDaoSupport implements ProductDao { + public class HibernateProductDao extends HibernateDaoSupport implements ProductDao { public Customer SaveOrUpdate(Customer customer) { @@ -527,18 +544,18 @@ DataAccessException.
-
+
Implementing DAOs based on plain Hibernate 1.2 API Hibernate 1.2 introduced a feature called "contextual Sessions", where Hibernate itself manages one current - ISession per transaction. This is roughly + ISession per transaction. This is roughly equivalent to Spring's synchronization of one Hibernate - Session per transaction. A corresponding + Session per transaction. A corresponding DAO implementation looks like as follows, based on the plain Hibernate API: - public class ProductDaoImpl implements IProductDao { + public class ProductDaoImpl implements IProductDao { private SessionFactory sessionFactory; @@ -575,13 +592,13 @@ public class HibernateCustomerDao : ICustomerDao { 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 HibernateTemplate. Of course, such a DAO + Spring's HibernateTemplate. Of course, such a DAO can also be set up in plain C# (for example, in unit tests): simply instantiate it and call SessionFactory property with the desired factory reference. As a Spring object definition, it would look as follows: - + <objects> <object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerDao, Spring.Northwind.Dao.NHibernate"> @@ -602,9 +619,9 @@ public class HibernateCustomerDao : ICustomerDao { The first way is shown below - <object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12"> + <object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12"> - <property name="ExposeTransactionAwareSessionFactory" value="true" /> + <property name="ExposeTransactionAwareSessionFactory" value="true" /> <!-- other configuration settings omitted --> @@ -612,7 +629,7 @@ public class HibernateCustomerDao : ICustomerDao { Which is simply a shortcut for the following configuration - <object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12"> + <object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12"> <!-- other configuration settings omitted --> @@ -621,8 +638,8 @@ public class HibernateCustomerDao : ICustomerDao { <!-- other dictionary entries omitted --> - <entry key="hibernate.current_session_context_class" - value="Spring.Data.NHibernate.SpringSessionContext, Spring.Data.NHibernate12"/> + <entry key="hibernate.current_session_context_class" + value="Spring.Data.NHibernate.SpringSessionContext, Spring.Data.NHibernate12"/> </dictionary> </property> @@ -635,7 +652,7 @@ public class HibernateCustomerDao : ICustomerDao { doubt feel more natural to Hibernate developers. However, the DAO throws plain - HibernateException which means that callers can + HibernateException 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 @@ -644,11 +661,11 @@ public class HibernateCustomerDao : ICustomerDao { special exception treatment. Fortunately, Spring's - LocalSessionFactoryObject supports Hibernate's + LocalSessionFactoryObject supports Hibernate's SessionFactory.GetCurrentSession() method for any Spring transaction strategy, returning the current Spring-managed - transactional Session even with - HibernateTransactionManager. + transactional Session even with + HibernateTransactionManager. In summary: DAOs can be implemented based on the plain Hibernate 1.2 API, while still being able to participate in Spring-managed @@ -656,29 +673,29 @@ public class HibernateCustomerDao : ICustomerDao { transaction.
-
+
Programmatic transaction demarcation 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 PlatformTransactionManager. Again, + needs a Spring PlatformTransactionManager. Again, the latter can come from anywhere, but preferably as an object reference via a TransactionManager property - just like - the productDAO should be set via a + the productDAO should be set via a setProductDao(..) 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. - <objects> + <objects> TO BE DONE </objects> - public class FulfillmentService : IFulfillmentService + public class FulfillmentService : IFulfillmentService private TransactionTemplate transactionTemplate; @@ -697,7 +714,7 @@ TO BE DONE }
-
+
Declarative transaction demarcation Alternatively, one can use Spring's declarative transaction @@ -711,7 +728,7 @@ TO BE DONE An example showing attribute driven transaction is shown below - <objects> + <objects> <object id="HibernateTransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate"> @@ -740,7 +757,7 @@ TO BE DONE expresses the intent as compared to the contents of DeclarativeServicesAttributeDriven.xml. - <objects xmlns="http://www.springframework.net" + <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 @@ -772,7 +789,7 @@ TO BE DONE The placement of the transaction attribute in the service layer method is shown below. - public class FulfillmentService : IFulfillmentService + public class FulfillmentService : IFulfillmentService { // fields and properties for dao object omitted, see above @@ -808,7 +825,7 @@ TO BE DONE boundaries, you can import a configuration file with the following XML instead of using <tx:attribute-driven/> - <object id="TxProxyConfigurationTemplate" abstract="true" + <object id="TxProxyConfigurationTemplate" abstract="true" type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"> <property name="PlatformTransactionManager" ref="HibernateTransactionManager"/> @@ -825,23 +842,23 @@ TO BE DONE configuration of other features, such as rollback rules.
-
+
Transaction management strategies - Both TransactionTemplate and - TransactionInterceptor (not yet seen explicitly + Both TransactionTemplate and + TransactionInterceptor (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 - PlatformTransactionManager instance, which can be - a HibernateTransactionManager (for a single - Hibernate SessionFactory, using a - ThreadLocal - Session under the hood) or a - TxScopeTransactionManager (delegating to MS-DTC + PlatformTransactionManager instance, which can be + a HibernateTransactionManager (for a single + Hibernate SessionFactory, using a + ThreadLocal + Session under the hood) or a + TxScopeTransactionManager (delegating to MS-DTC for distributed transaction) for Hibernate applications. You could even - use a custom PlatformTransactionManager + use a custom PlatformTransactionManager implementation. So switching from native Hibernate transaction management to TxScopeTransactionManager, such as when facing distributed transaction requirements for certain deployments of your application, is @@ -852,24 +869,24 @@ TO BE DONE For distributed transactions across multiple Hibernate session factories, simply combine - TxScopeTransactionManager as a transaction - strategy with multiple LocalSessionFactoryObject + TxScopeTransactionManager as a transaction + strategy with multiple LocalSessionFactoryObject definitions. Each of your DAOs then gets one specific - SessionFactory reference passed into it's + SessionFactory reference passed into it's respective object property. TO BE DONE - HibernateTransactionManager can export the - ADO.NET Transaction used by Hibernate to + HibernateTransactionManager can export the + ADO.NET Transaction used by Hibernate to plain ADO.NET access code, for a specific - DbProvider. (matching connection string). + DbProvider. (matching connection string). This allows for high-level transaction demarcation with mixed Hibernate/ADO.NET data access!
-
+
Web Session Management The open session in view pattern keeps the hibernate session open @@ -877,7 +894,7 @@ TO BE DONE displayed. You configure its use by adding an additional custom HTTP module declaration as shown below - <system.web> + <system.web> <httpModules> <add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewModule, Spring.Data.NHibernate"/> </httpModules> @@ -890,7 +907,7 @@ TO BE DONE will use by setting 'global' application key-value pairs as shown below. (this will change in future releases) - <appSettings> + <appSettings> <add key="Spring.Data.NHibernate.Support.OpenSessionInViewModule.SessionFactoryObjectName" value="SessionFactory"/> </appSettings> @@ -933,7 +950,7 @@ TO BE DONE you to use a single NHibernate session across multiple transactions. The usage is shown below - using (new SessionScope()) + using (new SessionScope()) { ... do multiple operations with a single session, possibly in multiple transactions. } diff --git a/doc/reference/src/overview.xml b/doc/reference/src/overview.xml index 84b29b21..e7dabb1f 100644 --- a/doc/reference/src/overview.xml +++ b/doc/reference/src/overview.xml @@ -1,8 +1,25 @@ - + + Introduction - + Overview Spring.NET is an application framework that provides comprehensive @@ -79,11 +96,12 @@ 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. + and DI, please do refer to the article : + + http://martinfowler.com/articles/injection.html. - + Modules The Spring Framework contains a lot of features, which are @@ -111,7 +129,7 @@ logging, performance monitoring, caching, method retry, and exception handling. - Spring.Data - Use this + Spring.Data - Use this module to achieve greater efficiency and consistency in writing data access functionality in ADO.NET and to perform declarative transaction management. @@ -133,7 +151,7 @@ data binding, validation, and ASP.NET page/control/module/provider configuration. - Spring.Services - Use this + Spring.Services - 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 @@ -296,10 +314,10 @@ scheduling. - + NMS - Applicatoin diff --git a/doc/reference/src/pool.xml b/doc/reference/src/pool.xml index cd363449..b0e41ae0 100644 --- a/doc/reference/src/pool.xml +++ b/doc/reference/src/pool.xml @@ -1,8 +1,25 @@ - + + Object Pooling - + Introduction The Spring.Pool namespace contains a generic API for implementing @@ -37,7 +54,7 @@ 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 - Spring.Aop.Target.SimplePoolTargetSource is more + Spring.Aop.Target.SimplePoolTargetSource 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 @@ -47,21 +64,21 @@ use of the pooling API independent of AOP functionality. - + Interfaces and Implementations The Spring.Pool namespace provides two simple interfaces to manage pools of objects. The first interface, - IObjectPool describes how to take and put back an + IObjectPool describes how to take and put back an object from the pool. The second interface - IPoolableObjectFactory is meant to be used in - conjunction with implementations of the IObjectPool + IPoolableObjectFactory is meant to be used in + conjunction with implementations of the IObjectPool 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. Spring.Pool.Support.SimplePool is a - default implementation of IObjectPool and - Spring.Aop.Target.SimplePoolTargetSource is the - implementation of IPoolableObjectFactory for use + Pool API. Spring.Pool.Support.SimplePool is a + default implementation of IObjectPool and + Spring.Aop.Target.SimplePoolTargetSource is the + implementation of IPoolableObjectFactory 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, diff --git a/doc/reference/src/pooling-example.xml b/doc/reference/src/pooling-example.xml index c4a093f6..0d7527aa 100644 --- a/doc/reference/src/pooling-example.xml +++ b/doc/reference/src/pooling-example.xml @@ -60,12 +60,10 @@ In our case, as already said, we want to to implement a pool of QueuedExecutor. Ok, here the declaration: - -public class QueuedExecutorPoolableFactory : IPoolableObjectFactory + public class QueuedExecutorPoolableFactory : IPoolableObjectFactory { the first task a factory should do is to create objects: - -object IPoolableObjectFactory.MakeObject() + object IPoolableObjectFactory.MakeObject() { // to actually make this work as a pooled executor // use a bounded queue of capacity 1. @@ -76,8 +74,7 @@ object IPoolableObjectFactory.MakeObject() return new QueuedExecutor(new BoundedBuffer(1)); } and should be also able to destroy them: - -void IPoolableObjectFactory.DestroyObject(object o) + void IPoolableObjectFactory.DestroyObject(object o) { // ah, self documenting code: // Here you can see that we decided to let the @@ -90,8 +87,7 @@ void IPoolableObjectFactory.DestroyObject(object o) 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: - -void IPoolableObjectFactory.ActivateObject(object o) + void IPoolableObjectFactory.ActivateObject(object o) { QueuedExecutor executor = o as QueuedExecutor; executor.Restart(); @@ -113,8 +109,7 @@ void IPoolableObjectFactory.ActivateObject(object o) ). Here we check that the worker thread exists: - -bool IPoolableObjectFactory.ValidateObject(object o) + bool IPoolableObjectFactory.ValidateObject(object o) { QueuedExecutor executor = o as QueuedExecutor; return executor.Thread != null; @@ -124,16 +119,14 @@ bool IPoolableObjectFactory.ValidateObject(object o) 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: - -void IPoolableObjectFactory.PassivateObject(object o) + void IPoolableObjectFactory.PassivateObject(object o) { } At this point, creating a pool is simply a matter of creating an SimplePool as in: - -pool = new SimplePool(new QueuedExecutorPoolableFactory(), size); + pool = new SimplePool(new QueuedExecutorPoolableFactory(), size); @@ -143,8 +136,7 @@ pool = new SimplePool(new QueuedExecutorPoolableFactory(), size);c# days, so we implement a very simple helper (PooledObjectHolder) that can allow us to do things like: - -using (PooledObjectHolder holder = PooledObjectHolder.UseFrom(pool)) + using (PooledObjectHolder holder = PooledObjectHolder.UseFrom(pool)) { QueuedExecutor executor = (QueuedExecutor) holder.Pooled; executor.Execute(runnable); @@ -154,8 +146,7 @@ using (PooledObjectHolder holder = PooledObjectHolder.UseFrom(pool)) Here is the implementation: - -public class PooledObjectHolder : IDisposable + public class PooledObjectHolder : IDisposable { IObjectPool pool; object pooled; @@ -204,8 +195,7 @@ public class PooledObjectHolder : IDisposable Please don't forget to destroy all the pooled istances once you have finished! How? Well using something like this in PooledQueuedExecutor: - -public void Stop () + public void Stop () { // waits for all the grep-task to have been queued ... foreach (ISync sync in syncs) @@ -221,8 +211,7 @@ public void Stop () The use of the just built executor is quite straigtforward but a little tricky if we want to really exploit the pool. - -private PooledQueuedExecutor executor; + private PooledQueuedExecutor executor; public ParallelGrep(int size) { @@ -246,9 +235,8 @@ public void Stop() executor.Stop(); } - - -public static void Main(string[] args) + + public static void Main(string[] args) { if (args.Length < 3) { diff --git a/doc/reference/src/preface.xml b/doc/reference/src/preface.xml index 67102bfa..5776bf6d 100644 --- a/doc/reference/src/preface.xml +++ b/doc/reference/src/preface.xml @@ -1,5 +1,22 @@ - + + Preface Developing software applications is hard enough even with good tools diff --git a/doc/reference/src/psa-intro.xml b/doc/reference/src/psa-intro.xml index 3793850b..19a3b009 100644 --- a/doc/reference/src/psa-intro.xml +++ b/doc/reference/src/psa-intro.xml @@ -1,5 +1,22 @@ - + + Introduction to Spring Services diff --git a/doc/reference/src/quartz-quickstart.xml b/doc/reference/src/quartz-quickstart.xml index 71f94e66..dc3f0ad1 100644 --- a/doc/reference/src/quartz-quickstart.xml +++ b/doc/reference/src/quartz-quickstart.xml @@ -1,5 +1,22 @@ - + + Quartz QuickStart
@@ -21,22 +38,22 @@ The full details of Quartz are outside the scope of this quickstart but here is 'quick tour for the impatient' of the main classes and interfaces used in Quartz so you can get your sea legs. A Quartz - IJob interface represents the task you would like + IJob interface represents the task you would like to execute. You either directly implement Quartz's - IJob interface or a convenience base class. The - Quartz Trigger controls when a job is executed, for + IJob interface or a convenience base class. The + Quartz Trigger controls when a job is executed, for example in the wee hours of the morning every weekday . This would be done - using Quartz's CronTrigger implementation. + using Quartz's CronTrigger implementation. Instances of your job are created every time the trigger fires. As such, in order to pass information between different job instances you stash data away in a hashtable that gets passed to the each Job instance upon - its creation. Quartz's JobDetail class combines the - IJob and this hashtable of data. Instead of the - standard System.Collections.Hashtable the class - JobDataMap is used. Triggers are registered with a - Quartz IScheduler implementation that manages the + its creation. Quartz's JobDetail class combines the + IJob and this hashtable of data. Instead of the + standard System.Collections.Hashtable the class + JobDataMap is used. Triggers are registered with a + Quartz IScheduler implementation that manages the overall execution of the triggers and jobs. The - StdSchedulerFactory implementation is generally + StdSchedulerFactory implementation is generally used.
@@ -44,7 +61,7 @@ Application Overview The sample application has two types of Jobs. One that inherits from - Spring's convenience base class QuartzJobObject and + Spring's convenience base class QuartzJobObject and another which does not inherit from any base class. The latter class is adapted by Spring to be a Job. Two triggers, one for each of the jobs, are created. These triggers are in turn registered with a scheduler. In each @@ -55,13 +72,13 @@
Standard job scheduling - The Spring base class QuartzJobObject - implements IJob and allows for your object's + The Spring base class QuartzJobObject + implements IJob and allows for your object's properties to be set via values that are stored inside Quartz's - JobDataMap that is passed along each time your job + JobDataMap that is passed along each time your job is instantiated due a trigger firing. This class is shown below - public class ExampleJob : QuartzJobObject + public class ExampleJob : QuartzJobObject { private string userName; @@ -79,16 +96,16 @@ } - The method ExecuteInternal is called when the + The method ExecuteInternal is called when the trigger fires and is where you would put your business logic. The - JobExecutionContext passed in lets you access + JobExecutionContext passed in lets you access various pieces of information about the current job execution, such as the JobDataMap or information on when the next time the trigger will fire. The - ExampleJob is configured by creating a - JobDetail object as shown below in the following + ExampleJob is configured by creating a + JobDetail object as shown below in the following XML snippet taken from spring-objects.xml - <object name="exampleJob" type="Spring.Scheduling.Quartz.JobDetailObject, Spring.Scheduling.Quartz"> + <object name="exampleJob" type="Spring.Scheduling.Quartz.JobDetailObject, Spring.Scheduling.Quartz"> <property name="JobType" value="Spring.Scheduling.Quartz.Example.ExampleJob, Spring.Scheduling.Quartz.Example" /> <!-- We can inject values through JobDataMap --> <property name="JobDataAsMap"> @@ -99,18 +116,18 @@ </object> The dictionary property of the - JobDetailObject, - JobDataAsMap, is used to set the values of the + JobDetailObject, + JobDataAsMap, is used to set the values of the ExampleJob's properties. This will result in the ExampleJob being instantiated with it's UserName property value set to 'Alexandre' the first time the trigger fires. We then will schedule this job to be executed on 20 second increments of every minute as shown below using Spring's - CronTriggerObject which creates a Quartz + CronTriggerObject which creates a Quartz CronTrigger. - <object id="cronTrigger" type="Spring.Scheduling.Quartz.CronTriggerObject, Spring.Scheduling.Quartz"> + <object id="cronTrigger" type="Spring.Scheduling.Quartz.CronTriggerObject, Spring.Scheduling.Quartz"> <property name="jobDetail" ref="exampleJob" /> <!-- run every 20 second of minute --> <property name="cronExpressionString" value="0/20 * * * * ?" /> @@ -119,7 +136,7 @@ Lastly, we schedule this trigger with the scheduler as shown below - <object type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz"> + <object type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz"> <property name="triggers"> <list> <ref object="cronTrigger" /> @@ -141,7 +158,7 @@ The AdminService class in the example demonstrates this functionality and is listed below. - public class AdminService + public class AdminService { private string userName; @@ -157,12 +174,12 @@ } Note that it does not inherit from any base class. To instruct - Spring to create a JobDetail object for this method + Spring to create a JobDetail object for this method we use Spring's factory object class - MethodInvokingJobDetailFactoryObject as shown + MethodInvokingJobDetailFactoryObject as shown below - <object id="adminService" type="Spring.Scheduling.Quartz.Example.AdminService, Spring.Scheduling.Quartz.Example"> + <object id="adminService" type="Spring.Scheduling.Quartz.Example.AdminService, Spring.Scheduling.Quartz.Example"> <!-- we inject straight to target object --> <property name="UserName" value="admin-service" /> </object> @@ -174,14 +191,14 @@ </object> - Note that AdminService object is configured + Note that AdminService object is configured using Spring as you would do normally, without consideration for Quartz. The trigger associated with the jobDetail object is listed below. Also note that when using MethodInvokingJobDetailFactoryObject you can't use database persistence for Jobs. See the class documentation for additional details. - <object id="simpleTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz"> + <object id="simpleTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz"> <!-- see the example of method invoking job above --> <property name="jobDetail" ref="jobDetail" /> <!-- 5 seconds --> @@ -200,7 +217,7 @@ This trigger can then be added to the scheduler's list of registered triggers as shown below. - <object type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz"> + <object type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz"> <property name="triggers"> <list> <ref object="cronTrigger" /> diff --git a/doc/reference/src/quickstarts.xml b/doc/reference/src/quickstarts.xml index 475ef060..0fb02427 100644 --- a/doc/reference/src/quickstarts.xml +++ b/doc/reference/src/quickstarts.xml @@ -1,7 +1,7 @@ - + IoC Quickstarts @@ -26,7 +26,7 @@ the Spring.NET framework. - + Movie Finder The source material for this simple demonstration of Spring.NET's @@ -65,12 +65,12 @@ - + Getting Started - Movie Finder The startup class for the MovieFinder example is the MovieApp class, which is an ordinary .NET class with - a single application entry point... using System; + a single application entry point... using System; namespace Spring.Examples.MovieFinder { public class MovieApp @@ -91,7 +91,7 @@ namespace Spring.Examples.MovieFinder custom configuration section in a standard .NET application config file... - <?xml version="1.0" encoding="utf-8" ?> + <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="spring"> @@ -115,7 +115,7 @@ namespace Spring.Examples.MovieFinder The body of the Main method in the MovieApp class can now be fleshed out a little - further... + further... using System; using Spring.Context; ... @@ -129,14 +129,14 @@ using Spring.Context; Spring.Context namespace gives the application access to the IApplicationContext class that will serve as the primary means for the application to access the IoC container. The - line of code... IApplicationContext ctx = ContextRegistry.GetContext(); + line of code... IApplicationContext ctx = ContextRegistry.GetContext(); ... retrieves a fully configured IApplicationContext implementation that has been configured using the named <objects/> section from the application config file. - + First Object Definition As yet, no objects have been defined in the application config @@ -144,7 +144,7 @@ using Spring.Context; MovieLister instance that we are going to use in the application can be seen in the following XML snippet... - <objects xmlns="http://www.springframework.net"> + <objects xmlns="http://www.springframework.net"> <object name="MyMovieLister" type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder"> </object> @@ -158,7 +158,7 @@ using Spring.Context; object so defined can be retrieved from the IApplicationContext reference like so... - ... + ... public static void Main () { IApplicationContext ctx = ContextRegistry.GetContext(); @@ -177,21 +177,21 @@ using Spring.Context; injected into the lister instance looks like this... - <objects xmlns="http://www.springframework.net"> + <objects xmlns="http://www.springframework.net"> <object name="MyMovieFinder" type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/> </object> </objects> - + Setter Injection What we want to do is inject the IMovieFinder instance identified by the MyMovieFinder id into the MovieLister instance identified by the MyMovieLister id, which can be accomplished using - Setter Injection and the following XML... <objects xmlns="http://www.springframework.net"> + Setter Injection and the following XML... <objects xmlns="http://www.springframework.net"> <object name="MyMovieLister" type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder"> <!-- using setter injection... --> @@ -210,7 +210,7 @@ using Spring.Context; MovieLister 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. - ... + ... public static void Main () { IApplicationContext ctx = ContextRegistry.GetContext(); @@ -235,12 +235,12 @@ using Spring.Context; the reference documentation. - + Constructor Injection Let's define another implementation of the IMovieFinder interface in the application config - file...... + file...... <object name="AnotherMovieFinder" type="Spring.Examples.MovieFinder.ColonDelimitedMovieFinder, Spring.Examples.MovieFinder"> </object> @@ -248,18 +248,18 @@ using Spring.Context; IMovieFinder 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 - System.IO.FileInfo as it's single constructor + System.IO.FileInfo as it's single constructor argument. As this object definition currently stands, attempting to get this object out of the IApplicationContext in the - application with a line of code like so... IMovieFinder finder = (IMovieFinder) ctx.GetObject ("AnotherMovieFinder"); + application with a line of code like so... IMovieFinder finder = (IMovieFinder) ctx.GetObject ("AnotherMovieFinder"); will result in a fatal - Spring.Objects.Factory.ObjectCreationException, + Spring.Objects.Factory.ObjectCreationException, because the - Spring.Examples.MovieFinder.ColonDelimitedMovieFinder + Spring.Examples.MovieFinder.ColonDelimitedMovieFinder class does not have a default constructor that takes no arguments. If we want to use this implementation of the IMovieFinder interface, we will have to supply an appropriate constructor - argument...... + argument...... <object name="AnotherMovieFinder" type="Spring.Examples.MovieFinder.ColonDelimitedMovieFinder, Spring.Examples.MovieFinder"> <constructor-arg index="0" value="movies.txt"/> @@ -269,11 +269,11 @@ using Spring.Context; 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 - System.ComponentModel.TypeConverter + System.ComponentModel.TypeConverter specializations to convert the movies.txt string into - an instance of the System.IO.FileInfo that is + an instance of the System.IO.FileInfo that is required by the single constructor of the - Spring.Examples.MovieFinder.ColonDelimitedMovieFinder + Spring.Examples.MovieFinder.ColonDelimitedMovieFinder (see for a more in depth treatment concerning the automatic type conversion functionality offered by Spring.NET). @@ -283,7 +283,7 @@ using Spring.Context; distinct object definitions in the config file of the example application; if we wanted to, we could switch the implementation that the MyMovieLister object uses like - so...... + so...... <object name="MyMovieLister" type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder"> <!-- lets use the colon delimited implementation instead --> @@ -306,7 +306,7 @@ using Spring.Context; MyMovieLister object. - + Summary This example application is quite simple, and admittedly it @@ -345,8 +345,8 @@ using Spring.Context; 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 <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" /> - The corresponding configuration section looks like this + as shown below <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" /> + The corresponding configuration section looks like this <log4net> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> @@ -379,7 +379,7 @@ using Spring.Context; 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. - private static readonly ILog LOG = LogManager.GetLogger(typeof (MovieApp)); + private static readonly ILog LOG = LogManager.GetLogger(typeof (MovieApp)); 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 @@ -390,7 +390,7 @@ using Spring.Context; format %logger{2}. To initialize the logging system add the following to the start of - your application XmlConfigurator.Configure(); + your application XmlConfigurator.Configure(); Note that if you are using or reading information on version 1.2.0 this used to be called DOMConfigurator.Configure(); @@ -406,9 +406,9 @@ using Spring.Context; 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. LOG.Info("Searching for movie..."); + log, i.e. LOG.Info("Searching for movie..."); Logging exceptions is another common task, which can be done using the - error level try { + error level try { //do work { catch (Exception e) @@ -418,7 +418,7 @@ catch (Exception e) - + ApplicationContext and IMessageSource @@ -453,7 +453,7 @@ catch (Exception e) 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 ... + The corresponding XML fragment is shown below ... <object name="messageSource" type="Spring.Context.Support.ResourceSetMessageSource, Spring.Core"> <property name="resourceManagers"> <list> @@ -484,7 +484,7 @@ catch (Exception e) contains a text resource, Hello {0} {1} under the key name HelloMessage (aka Keys.HELLO_MESSAGE) that can be used for string text formatting purposes. The example code - + string msg = ctx.GetMessage(Keys.HELLO_MESSAGE, CultureInfo.CurrentCulture, "Mr.", "Anderson"); @@ -492,7 +492,7 @@ string msg = ctx.GetMessage(Keys.HELLO_MESSAGE, 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 - + CultureInfo spanishCultureInfo = new CultureInfo("es"); string esMsg = ctx.GetMessage(Keys.HELLO_MESSAGE, spanishCultureInfo, @@ -510,7 +510,7 @@ string esMsg = ctx.GetMessage(Keys.HELLO_MESSAGE, into a key of its own, called FemaleGreeting (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 + convenience class DefaultMessageResolvable. The code string[] codes = {Keys.FEMALE_GREETING}; DefaultMessageResolvable dmr = new DefaultMessageResolvable(codes, null); @@ -519,7 +519,7 @@ msg = ctx.GetMessage(Keys.HELLO_MESSAGE, dmr, "Anderson"); will assign msg the value, Hello Mrs. Anderson, since the value for the key FemaleGreeting in MyResource.resx - is 'Mrs.' Similarly, the code + is 'Mrs.' Similarly, the code esMsg = ctx.GetMessage(Keys.HELLO_MESSAGE, spanishCultureInfo, dmr, "Anderson"); @@ -536,7 +536,7 @@ esMsg = ctx.GetMessage(Keys.HELLO_MESSAGE, 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 + values to an object is shown below Person p = new Person(); ctx.ApplyResources(p, "person", CultureInfo.CurrentUICulture); While you could also use the Spring itself to set the diff --git a/doc/reference/src/remoting-quickstart.xml b/doc/reference/src/remoting-quickstart.xml index b47565e4..23931b61 100644 --- a/doc/reference/src/remoting-quickstart.xml +++ b/doc/reference/src/remoting-quickstart.xml @@ -1,8 +1,25 @@ - + + Portable Service Abstraction Quick Start - + Introduction This quickstart demonstrates the basic usage of Spring.NET's @@ -12,17 +29,17 @@ shows the use of the WebServiceExporter. - + .NET Remoting Example The infrastructure classes are located in the Spring.Services assembly under the Spring.Services.Remoting namespace. The overall strategy is to export .NET objects on the server side as either CAO or SAO - objects using CaoExporter or - SaoExporter and obtain references to these objects - on the client side using CaoFactoryObject and - SaoFactoryObject. This quickstart does assume + objects using CaoExporter or + SaoExporter and obtain references to these objects + on the client side using CaoFactoryObject and + SaoFactoryObject. 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. @@ -46,23 +63,23 @@ The Spring.Calculator.Contract project contains - the interface ICalculator that defines the basic + the interface ICalculator that defines the basic operations of a calculator and another interface - IAdvancedCalculator that adds support for memory + IAdvancedCalculator that adds support for memory storage for results. (woo hoo - big feature - HP-12C beware!) These interfaces are shown below. The Spring.Calculator.Services project contains an implementation of the these interfaces, namely the classes - Calculator and - AdvancedCalculator. The purpose of the - AdvancedCalculator implementation is to demonstrate + Calculator and + AdvancedCalculator. The purpose of the + AdvancedCalculator implementation is to demonstrate the configuration of object state for SAO-singleton objects. Note that the calculator implementations do not inherit from the - MarshalByRefObject class. The + MarshalByRefObject class. The Spring.Calculator.ClientApp project contains the client application and the Spring.Calculator.RemoteApp project contains a console application that will host a Remoted instance of the - AdvancedCalculator class. The + AdvancedCalculator class. The Spring.Aspects project contains some logging advice that will be used to demonstrate the application of aspects to remoted objects. Spring.Calculator.RegisterComponentServices is @@ -70,7 +87,7 @@ quickstart. Spring.Calculator.Web is related to web services exporters and is not relevant for this quickstart. - public interface ICalculator + public interface ICalculator { int Add(int n1, int n2); @@ -103,7 +120,7 @@ public class DivisionResult An extension of this interface that supports having a slot for calculator memory is shown below - public interface IAdvancedCalculator : ICalculator + public interface IAdvancedCalculator : ICalculator { int GetMemory(); @@ -137,7 +154,7 @@ public class DivisionResult barrage of OO design ranting finished, on to the implementation! - + Implementation The implementation of the calculators contained in the @@ -147,7 +164,7 @@ public class DivisionResult using constructor injection. A subset of the implementation is shown below. - public class Calculator : ICalculator + public class Calculator : ICalculator { public int Add(int n1, int n2) @@ -197,11 +214,11 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator } - The Spring.Calculator.RemotedApp project + The Spring.Calculator.RemotedApp project hosts remoted objects inside a console application. The code is also quite simple and shown below - public static void Main(string[] args) + public static void Main(string[] args) { try { @@ -227,7 +244,7 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator (App.config). In this case we are using the tcp channel on port 8005. - <system.runtime.remoting> + <system.runtime.remoting> <application> <channels> <channel ref="tcp" port="8005" /> @@ -240,7 +257,7 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator remoting configurations. The AOP advice used in this example is a simple Log4Net based around advice. - <configSections> + <configSections> <sectionGroup name="spring"> <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" /> <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" /> @@ -310,8 +327,8 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator 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 - Spring.Remoting.CaoExporter is used for CAO objects - and Spring.Remoting.SaoExporter is used for SAO + Spring.Remoting.CaoExporter is used for CAO objects + and Spring.Remoting.SaoExporter is used for SAO objects. Both exporters require the setting of a TargetName property that refers to the name of the object in Spring's IoC container that will be remoted. The semantics of @@ -323,7 +340,7 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator property Infinite is set to true. The configuration for the exporting a SAO-Singleton is shown - below.<objects + below.<objects xmlns="http://www.springframework.net" xmlns:r="http://www.springframework.net/remoting"> @@ -334,16 +351,16 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator serviceName="RemotedSaoSingletonCalculator" /> </objects>The configuration shown above uses the Spring Remoting schema but you can also choose to use the standard 'generic' XML - configuration shown below.<object name="saoSingletonCalculator" type="Spring.Remoting.SaoExporter, Spring.Services"> + configuration shown below.<object name="saoSingletonCalculator" type="Spring.Remoting.SaoExporter, Spring.Services"> <property name="TargetName" value="singletonCalculator" /> <property name="ServiceName" value="RemotedSaoSingletonCalculator" /> </object> This will result in the remote object being identified by the URL tcp://localhost:8005/RemotedSaoSingletonCalculator. The - use of SaoExporter and - CaoExporter for other configuration are similar, + use of SaoExporter and + CaoExporter for other configuration are similar, look at the configuration files in the - Spring.Calculator.RemotedApp project files for more + Spring.Calculator.RemotedApp project files for more information. On the client side, the client application will connect a specific @@ -354,7 +371,7 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator application configuration file (App.config), as can been seen below. - <system.runtime.remoting> + <system.runtime.remoting> <application> <channels> <channel ref="tcp"/> @@ -364,7 +381,7 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator The client implementation code is shown below. - public static void Main(string[] args) + public static void Main(string[] args) { try { @@ -410,7 +427,7 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator Components (Enterprise Services) of the calculator object but are not discussed in this QuickStart. - + <spring> <context> <resource uri="config://spring/objects" /> @@ -450,7 +467,7 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator The inProcess.xml configuration file creates an instance of - AdvancedCalculator directly + AdvancedCalculator directly <objects xmlns="http://www.springframework.net"> <description>inProcess</description> @@ -462,10 +479,10 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator Factory classes are used to create a client side reference to the .NET remoting implementations. For SAO objects use the - SaoFactoryObject class and for CAO objects use the - CaoFactoryObject class. The configuration for + SaoFactoryObject class and for CAO objects use the + CaoFactoryObject class. The configuration for obtaining a reference to the previously exported SAO singleton - implementation is shown below <objects xmlns="http://www.springframework.net"> + implementation is shown below <objects xmlns="http://www.springframework.net"> <description>saoSingleton</description> @@ -486,7 +503,7 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator to easily switch between test, QA, and production (yea baby!) environments. An example of how this would be expressed is... - <property name="ServiceUrl" value="${protocol}://${host}:${port}/RemotedSaoSingletonCalculator" /> + <property name="ServiceUrl" value="${protocol}://${host}:${port}/RemotedSaoSingletonCalculator" /> The property values in this example are defined elsewhere; refer to for additional @@ -496,7 +513,7 @@ public class AdvancedCalculator : Calculator, IAdvancedCalculator making a simple change to the configuration file. The configuration for obtaining a reference to the previously - exported CAO implementation is shown below <objects xmlns="http://www.springframework.net"> + exported CAO implementation is shown below <objects xmlns="http://www.springframework.net"> <description>cao</description> @@ -558,7 +575,7 @@ Memory = 2 definitions which should give you a good feel for how to use the schema. - <!-- Calculator definitions --> + <!-- Calculator definitions --> <object id="singletonCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"> <constructor-arg type="int" value="217" /> </object> @@ -588,8 +605,8 @@ Memory = 2 method on the remoted object is invoked for the SAO case. - - .NET Enterprise Services Example + + .NET Enterprise Services Example The .NET Enterprise Services example is located in the project Spring.Calculator.RegisterComponentServices.2005.csproj or @@ -600,7 +617,7 @@ Memory = 2 Spring.Calculator.RegisterComponentServices.Config. The top level configuration is shown below - <spring> + <spring> <context> <resource uri="config://spring/objects" /> @@ -625,7 +642,7 @@ Memory = 2 AccessControl and Roles properties. The configuration file for enterpriseServices.xml is shown below - <objects xmlns="http://www.springframework.net"> + <objects xmlns="http://www.springframework.net"> <description>enterpriseService</description> @@ -681,7 +698,7 @@ Memory = 2 </objects> - + Web Services Example The WebServices example shows how to export the AdvancedCalculator @@ -689,7 +706,7 @@ Memory = 2 logging advice applied to it. The main configuration file, Web.config, includes information from three locations as shown below - <context> + <context> <resource uri="config://spring/objects"/> <resource uri="~/Config/webServices.xml"/> <resource uri="~/Config/webServices-aop.xml"/> @@ -698,7 +715,7 @@ Memory = 2 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 <objects xmlns="http://www.springframework.net"> + These definitions are shown below <objects xmlns="http://www.springframework.net"> <!-- Aspect --> @@ -724,7 +741,7 @@ Memory = 2 </objects>The configuration file webService.xml simply exports the named calculator object - <object id="calculatorService" type="Spring.Web.Services.WebServiceExporter, Spring.Web"> + <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" /> @@ -733,7 +750,7 @@ Memory = 2 Whereas the webService-aop.xml exports the calculator instance that has AOP advice applied to it. - <object id="calculatorServiceWeaved" type="Spring.Web.Services.WebServiceExporter, Spring.Web"> + <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" /> @@ -780,7 +797,7 @@ Memory = 2 2007-10-15 17:59:47,421 [DEBUG] Spring.Aspects.Logging.CommonLoggingAroundAdvice - Intercepted call : returned '4' - + Additional Resources Some introductory articles on .NET remoting can be found online at diff --git a/doc/reference/src/remoting.xml b/doc/reference/src/remoting.xml index ee81e7fa..623cebba 100644 --- a/doc/reference/src/remoting.xml +++ b/doc/reference/src/remoting.xml @@ -1,8 +1,25 @@ - + + .NET Remoting -
+
Introduction Spring's .NET Remoting support allows you to export a 'plain .NET @@ -32,7 +49,7 @@ item.
-
+
Publishing SAOs on the Server Exposing a Singleton SAO service can be done in two ways. The first @@ -45,7 +62,7 @@ RemotingServices.Marshal. 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 - AdvancedMBRCalculator calc = new AdvancedMBRCalculator(217); + AdvancedMBRCalculator calc = new AdvancedMBRCalculator(217); RemotingServices.Marshal(calc, "MyRemotedCalculator"); The class AdvancedMBRCalculator used above inherits from @@ -54,18 +71,18 @@ RemotingServices.Marshal(calc, "MyRemotedCalculator"); 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 SaoExporter class performs this + infrastructure. The SaoExporter 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 Remoting QuickStart demonstrates its usage to an SAO Singleton object -
+
SAO Singleton
- <object id="singletonCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"> + <object id="singletonCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services"> <constructor-arg type="int" value="217"/> </object> @@ -80,14 +97,14 @@ RemotingServices.Marshal(calc, "MyRemotedCalculator"); "RemotedSaoSingletonCalculator". (The fully qualified url is tcp://localhost:8005/RemotedSaoSingleCallCalculator using the standard .NET channel configuration shown further below.) - AdvancedCalculator class implements the business - interface IAdvancedCalculator. The current proxy + AdvancedCalculator class implements the business + interface IAdvancedCalculator. 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 AdvancedCalculator + constructor. The class AdvancedCalculator does not inherit from - MarshalByRefObject. Also note that the exporter + MarshalByRefObject. 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 @@ -95,7 +112,7 @@ RemotingServices.Marshal(calc, "MyRemotedCalculator"); A custom schema is provided to make the object declaration even easier and with intellisense support for the attributes. This is shown - below<objects xmlns="http://www.springframework.net" + below<objects xmlns="http://www.springframework.net" xmlns:r="http://www.springframework.net/remoting"> <r:saoExporter targetName="singletonCalculator" @@ -106,12 +123,12 @@ RemotingServices.Marshal(calc, "MyRemotedCalculator"); </objects>Refer to the end of this chapter for more information on Spring's .NET custom schema. -
+
SAO SingleCall
The following XML fragment shows how to expose the calculator - service in SAO 'SingleCall' mode. <object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services" + service in SAO 'SingleCall' mode. <object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services" singleton="false"> <constructor-arg type="int" value="217"/> </object> @@ -129,7 +146,7 @@ RemotingServices.Marshal(calc, "MyRemotedCalculator"); singleton calculator, the following standard AOP configuration is used to create the target for the SaoExporter - <object id="singletonCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"> + <object id="singletonCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"> <property name="target" ref="singletonCalculator"/> <property name="interceptorNames"> <list> @@ -146,15 +163,15 @@ RemotingServices.Marshal(calc, "MyRemotedCalculator"); As generally required with a .NET Remoting application, the arguments to your service methods should be Serializable. - + Console Application Configuration - When using SaoExporter you can still use + When using SaoExporter you can still use the standard remoting administration section in the application configuration file to register the channel. - ChannelServices as shown below + ChannelServices as shown below - <system.runtime.remoting> + <system.runtime.remoting> <application> <channels> <channel ref="tcp" port="8005" /> @@ -166,7 +183,7 @@ RemotingServices.Marshal(calc, "MyRemotedCalculator"); 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 RemotingConfiguration.Configure("RemoteApp.exe.config"); + shown below RemotingConfiguration.Configure("RemoteApp.exe.config"); IApplicationContext ctx = ContextRegistry.GetContext(); @@ -176,23 +193,23 @@ Console.ReadLine(); You can also put in the configuration file an instance of the - object Spring.Remoting.RemotingConfigurer to make + object Spring.Remoting.RemotingConfigurer to make the RemotingConfiguration call show above on your behalf during initialization of the IoC container. The - RemotingConfigurer implements the - IObjectFactoryPostProcessor interface, + RemotingConfigurer implements the + IObjectFactoryPostProcessor interface, which gets called after all object definitions have been loaded but before they have been instantiated, (See for more information). The RemotingConfigurer has two properties you can - configure. Filename, that specifies the filename + configure. Filename, that specifies the filename to load the .NET remoting configuration from (if null the default file - name is used) and EnsureSecurity which makes sure + name is used) and EnsureSecurity 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 Remoting QuickStart - <objects xmlns="http://www.springframework.net" + <objects xmlns="http://www.springframework.net" xmlns:r="http://www.springframework.net/remoting"> <r:configurer filename="Spring.Calculator.RemoteApp.exe.config" /> @@ -205,7 +222,7 @@ Console.ReadLine(); code in action. -
+
IIS Application Configuration If you are deploying a .NET remoting application inside IIS there @@ -219,7 +236,7 @@ Console.ReadLine(); Spring IoC container inside the application start method defined in Global.asax, as shown below - void Application_Start(object sender, EventArgs e) + void Application_Start(object sender, EventArgs e) { // Code that runs on application startup @@ -241,7 +258,7 @@ Console.ReadLine();
-
+
Accessing a SAO on the Client Administrative type registration on the client side lets you easily @@ -267,17 +284,17 @@ Console.ReadLine(); object is a SAO object. A call to Activator.GetObject 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 ICalculator calc = (ICalculator)Activator.GetObject ( + is shown below ICalculator calc = (ICalculator)Activator.GetObject ( typeof (ICalculator), "tcp://localhost:8005/MyRemotedCalculator"); To obtain a reference to a SAO proxy within the IoC container, you - can use the object factory SaoFactoryObject in the + can use the object factory SaoFactoryObject in the Spring configuration file. The following XML taken from the Remoting QuickStart demonstrates its usage. - <object id="calculatorService" type="Spring.Remoting.SaoFactoryObject, Spring.Services"> + <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> @@ -287,11 +304,11 @@ Console.ReadLine(); server and published object name. Other objects in the IoC container that depend on an implementation - of the interface ICalculator can now refer to the + of the interface ICalculator 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 - ICalculator. By using the IoC container changing + ICalculator. 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 @@ -304,7 +321,7 @@ Console.ReadLine(); integrate with the server implementation when it is ready.
-
+
CAO best practices Creating a client activated object (CAO) is typically done by @@ -320,7 +337,7 @@ Console.ReadLine(); 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 - CaoExporter class. On the client side a reference + CaoExporter class. On the client side a reference is obtained using CaoFactoryObject. The client side factory object supports creation of the CAO object using constructor arguments. In addition to reducing the clutter and tedium around creating @@ -334,14 +351,14 @@ Console.ReadLine(); resources.
-
+
Registering a CAO object on the Server 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 ICaoRemoteFactory is what + implementation of ICaoRemoteFactory 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 @@ -351,14 +368,14 @@ Console.ReadLine(); This is best shown using an example from the Remoting Quickstart application. Here is the definition of a simple calculator object, - <object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services" + <object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.Services" singleton="false"> <constructor-arg type="int" value="217" /> </object>To export this as a CAO object we can declare - the CaoExporter object directly in the server's XML + the CaoExporter object directly in the server's XML configuration file, as shown below - <object id="caoCalculator" type="Spring.Remoting.CaoExporter, Spring.Services"> + <object id="caoCalculator" type="Spring.Remoting.CaoExporter, Spring.Services"> <property name="TargetName" value="prototypeCalculator" /> <property name="Infinite" value="false" /> <property name="InitialLeaseTime" value="2m" /> @@ -372,7 +389,7 @@ Console.ReadLine(); Alternatively, you can use the remoting schema and declare the CAO object as shown below - <r:caoExporter targetName="prototypeCalculator" infinite="false"> + <r:caoExporter targetName="prototypeCalculator" infinite="false"> <r:lifeTime initialLeaseTime="2m" renewOnCallTime="1m" /> </r:caoExporter> @@ -386,7 +403,7 @@ Console.ReadLine(); from the Remoting QuickStart, a calculator with logging around advice is defined as shown below. - <object id="prototypeCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"> + <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" /> @@ -403,19 +420,19 @@ Console.ReadLine(); linkend="aop" /> for more information. The CAO exporter then references with the name 'prototypeCalculatorWeaved' as shown below. - <r:caoExporter targetName="prototypeCalculatorWeaved" infinite="false"> + <r:caoExporter targetName="prototypeCalculatorWeaved" infinite="false"> <r:lifeTime initialLeaseTime="2m" renewOnCallTime="1m" /> </r:caoExporter>
-
+
Accessing a CAO on the Client On the client side a CAO reference is obtained by using the - CaoFactoryObject as shown below + CaoFactoryObject as shown below - <object id="calculatorService" type="Spring.Remoting.CaoFactoryObject, Spring.Services"> + <object id="calculatorService" type="Spring.Remoting.CaoFactoryObject, Spring.Services"> <property name="RemoteTargetName" value="prototypeCalculator" /> <property name="ServiceUrl" value="tcp://localhost:8005" /> </object> @@ -424,14 +441,14 @@ Console.ReadLine(); 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 - IAdvancedCalculator interface. (As always, + IAdvancedCalculator interface. (As always, that doesn't mean the client should treat the object as if it was an in-process object). Alternatively, you can use the Remoting schema to shorten this definition and provide intellisense code completion - <r:caoFactory id="calculatorService" + <r:caoFactory id="calculatorService" remoteTargetName="prototypeCalculator" serviceUrl="tcp://localhost:8005" /> @@ -440,12 +457,12 @@ Console.ReadLine(); 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 - CaoFactoryObject as the AOP target, i.e. + CaoFactoryObject as the AOP target, i.e. 'calculatorService' in the previous example.
-
+
XML Schema for configuration Please install the XSD schemas into VS.NET as described in
-
+
Additional Resources Two articles that describe the process of creating a standard SAO diff --git a/doc/reference/src/resources.xml b/doc/reference/src/resources.xml index 9d1b34cc..3427d29d 100644 --- a/doc/reference/src/resources.xml +++ b/doc/reference/src/resources.xml @@ -1,14 +1,31 @@ - + + Resources -
+
Introduction The IResource interface contained in the Spring.Core.IO namespace provides a common interface to describe and access data from diverse resource locations. This abstraction - lets you treat the InputStream from a file and from + lets you treat the InputStream from a file and from a URL in a polymorphic and protocol-independent manner... the .NET BCL does not provide such an abstraction. The IResource interface inherits from IInputStream that provides a @@ -21,11 +38,11 @@
- The <interfacename>IResource</interfacename> interface + The <literal>IResource</literal> interface The IResource interface is shown below - public interface IResource : IInputStreamSource + public interface IResource : IInputStreamSource { bool IsOpen { get; } @@ -61,7 +78,7 @@ InputStream Inherited from IInputStream. Opens and returns a - System.IO.Stream. It is expected that each + System.IO.Stream. It is expected that each invocation returns a fresh Stream. It is the responsibility of the caller to close the stream. @@ -81,7 +98,7 @@ 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 - InputStreamResource. + InputStreamResource. @@ -100,7 +117,7 @@ File - Returns a System.IO.FileInfo for + Returns a System.IO.FileInfo for this resource if it can be resolved to an absolute file path. @@ -145,7 +162,7 @@ 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 - IApplicationContext implementations), take + IApplicationContext implementations), take a String which is used to create a Resource appropriate to that context implementation @@ -206,7 +223,7 @@ a wrapper around a raw - System.IO.Stream + System.IO.Stream . Uri syntax is not supported. @@ -227,37 +244,37 @@ Registering custom IResource implementations The configuration section handler, - ResourceHandlersSectionHandler, is used to - register any custom IResource + ResourceHandlersSectionHandler, is used to + register any custom IResource implementations you have created. In the configuration section you list - the type of IResource implementation and + the type of IResource implementation and the protocol prefix. Your custom - IResource implementation must provide a + IResource 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 - ResourceHandlersSectionHandler for more + ResourceHandlersSectionHandler for more information. An example of the - ResourceHandlersSectionHandler is shown below for - a fictional IResource implementation that + ResourceHandlersSectionHandler is shown below for + a fictional IResource implementation that interfaces with a database. - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> <section name='context' type='Spring.Context.Support.ContextHandler, Spring.Core'/> - <section name="resourceHandlers" - type="Spring.Context.Support.ResourceHandlersSectionHandler, Spring.Core"/> + <section name="resourceHandlers" + type="Spring.Context.Support.ResourceHandlersSectionHandler, Spring.Core"/> </sectionGroup> </configSections> <spring> - <resourceHandlers> + <resourceHandlers> <handler protocol="db" type="MyCompany.MyApp.Resources.MyDbResource, MyAssembly"/> - </resourceHandlers> + </resourceHandlers> <context> <resource uri="db://user:pass@dbName/MyDefinitionsTable"/> @@ -269,21 +286,21 @@
- The <interfacename>IResourceLoader</interfacename> + The <literal>IResourceLoader</literal> To load resources given their Uri syntax, an implementation of the - IResourceLoader is used. The default implementation - is ConfigurableResourceLoader. Typically you will + IResourceLoader is used. The default implementation + is ConfigurableResourceLoader. Typically you will not need to access this class directly since the - IApplicationContext implements the - IResourceLoader interface that contains the single + IApplicationContext implements the + IResourceLoader interface that contains the single method IResource GetResource(string location). The provided implementations of IApplicationContext delegate this method to an instance of - ConfigurableResourceLoader which supports the Uri + ConfigurableResourceLoader 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.IResource resource = appContext.GetResource("http://www.springframework.net/license.html"); + usage.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"); @@ -299,7 +316,7 @@ Console.WriteLine(reader.ReadToEnd()); Other protocols can be The CreateRelative 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: IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Spring/TestResource.txt"); + assembly. For example: IResource res = new AssemblyResource("assembly://Spring.Core.Tests/Spring/TestResource.txt"); IResource res2 = res.CreateRelative("./IO/TestIOResource.txt"); This loads the resource TestResource.txt and then navigates to the Spring.Core.IO namespace and loads the @@ -307,14 +324,14 @@ IResource res2 = res.CreateRelative("./IO/TestIOResource.txt");
- The <interfacename>IResourceLoaderAware</interfacename> + <title>The <literal>IResourceLoaderAware</literal> interface - The IResourceLoaderAware interface is + The IResourceLoaderAware interface is a special marker interface, identifying objects that expect to be provided - with a IResourceLoader reference. + with a IResourceLoader reference. - public interface IResourceLoaderAware + public interface IResourceLoaderAware { IResourceLoader ResourceLoader { @@ -324,29 +341,29 @@ IResource res2 = res.CreateRelative("./IO/TestIOResource.txt"); } When a class implements - IResourceLoaderAware and is deployed into + IResourceLoaderAware and is deployed into an application context (as a Spring-managed object), it is recognized as - IResourceLoaderAware by the application + IResourceLoaderAware 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 - IResourceLoader interface). + IResourceLoader interface). Of course, since an - IApplicationContext is a - IResourceLoader, the object could also - implement the IApplicationContextAware + IApplicationContext is a + IResourceLoader, the object could also + implement the IApplicationContextAware interface and use the supplied application context directly to load resources, but in general, it's better to use the specialized - IResourceLoader interface if that's all + IResourceLoader 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 IApplicationContext + Spring IApplicationContext interface.
- Application contexts and <interfacename>IResource</interfacename> + <title>Application contexts and <literal>IResource</literal> paths An application context constructor (for a specific application @@ -355,7 +372,7 @@ IResource res2 = res.CreateRelative("./IO/TestIOResource.txt"); of the context. For example, you can create an XmlApplicationContext from two resources as follows: - IApplicationContext context = new XmlApplicationContext( + IApplicationContext context = new XmlApplicationContext( "file://objects.xml", "assembly://MyAssembly/MyProject/objects-dal-layer.xml");
diff --git a/doc/reference/src/scheduling.xml b/doc/reference/src/scheduling.xml index addbeda9..a0bc0d86 100644 --- a/doc/reference/src/scheduling.xml +++ b/doc/reference/src/scheduling.xml @@ -1,41 +1,58 @@ - + + Scheduling and Thread Pooling -
+
Introduction The Spring Framework features integration classes for scheduling support. Currently, Spring supports the Quartz Scheduler (). The scheduler is set up - using a IFactoryObject with optional - references to Trigger instances, respectively. + using a IFactoryObject with optional + references to Trigger instances, respectively. Furthermore, a convenience class for both the Quartz Scheduler is available that allows you to invoke a method of an existing target object.
-
+
Using the Quartz.NET Scheduler - Quartz uses Trigger, - Job and JobDetail objects to + Quartz uses Trigger, + Job and JobDetail objects to realize scheduling of all kinds of jobs. For the basic concepts behind Quartz, have a look at . For convenience purposes, Spring offers a couple of classes that simplify the usage of Quartz within Spring-based applications. -
+
Using the JobDetailObject - JobDetail objects contain all information + JobDetail objects contain all information needed to run a job. The Spring Framework provides a - JobDetailObject that makes the - JobDetail easier to configure and with sensible + JobDetailObject that makes the + JobDetail easier to configure and with sensible defaults. Let's have a look at an example: - + <object name="ExampleJob" type="Spring.Scheduling.Quartz.JobDetailObject, Spring.Scheduling.Quartz"> <property name="JobType" value="Example.Quartz.ExampleJob, Example.Quartz" /> <property name="JobDataAsMap"> @@ -46,17 +63,17 @@ </object> The job detail object has all information it needs to run the job - (ExampleJob). The timeout is specified in the job + (ExampleJob). The timeout is specified in the job data dictionary. The job data dictonary is available through the - JobExecutionContext (passed to you at execution - time), but the JobDetailObject also maps the + JobExecutionContext (passed to you at execution + time), but the JobDetailObject also maps the properties from the job data map to properties of the actual job. So in - this case, if the ExampleJob contains a property + this case, if the ExampleJob contains a property named Timeout, the - JobDetailObject will automatically apply + JobDetailObject will automatically apply it: - namespace Example.Quartz; + namespace Example.Quartz; public class ExampleJob extends QuartzJobObject { @@ -85,15 +102,15 @@ public class ExampleJob extends QuartzJobObject { ExampleJob).
-
+
Using the - <classname>MethodInvokingJobDetailFactoryObject</classname> + MethodInvokingJobDetailFactoryObject Often you just need to invoke a method on a specific object. Using - the MethodInvokingJobDetailFactoryObject you can + the MethodInvokingJobDetailFactoryObject you can do exactly this: - <object id="JobDetail" type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz"> + <object id="JobDetail" type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz"> <property name="TargetObject" ref="ExampleBusinessObject" /> <property name="TargetMethod" value="DoIt" /> </object> @@ -102,7 +119,7 @@ public class ExampleJob extends QuartzJobObject { method being called on the exampleBusinessObject method (see below): - public class ExampleBusinessObject { + public class ExampleBusinessObject { // properties and collaborators @@ -111,28 +128,28 @@ public class ExampleJob extends QuartzJobObject { } } - + <object id="ExampleBusinessObject" type="Examples.BusinessObjects.ExampleBusinessObject, Examples.BusinessObjects"/> Using the - MethodInvokingJobDetailFactoryObject, you don't + MethodInvokingJobDetailFactoryObject, you don't need to create one-line jobs that just invoke a method, and you only need to create the actual business object and wire up the detail object. By default, Quartz Jobs are stateless, resulting in the possibility of jobs interfering with each other. If you specify two - triggers for the same JobDetail, it might be + triggers for the same JobDetail, it might be possible that before the first job has finished, the second one will - start. If JobDetail classes implement the - Stateful interface, this won't happen. + start. If JobDetail classes implement the + Stateful interface, this won't happen. The second job will not start before the first one has finished. To make jobs resulting from the - MethodInvokingJobDetailFactoryObject + MethodInvokingJobDetailFactoryObject non-concurrent, set the concurrent flag to false. - <object id="JobDetail" type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz"> + <object id="JobDetail" type="Spring.Scheduling.Quartz.MethodInvokingJobDetailFactoryObject, Spring.Scheduling.Quartz"> <property name="TargetObject" ref="ExampleBusinessObject" /> <property name="TargetMethod" value="DoIt" /> <property name="Concurrent" value="false" /> @@ -148,27 +165,27 @@ public class ExampleJob extends QuartzJobObject {
-
+
Wiring up jobs using triggers and the - <classname>SchedulerFactoryObject</classname> + SchedulerFactoryObject We've created job details and jobs. We've also reviewed the convenience class that allows to you invoke a method on a specific object. Of course, we still need to schedule the jobs themselves. This is done using triggers and a - SchedulerFactoryObject. Several triggers are + SchedulerFactoryObject. Several triggers are available within Quartz. Spring offers two subclassed triggers with - convenient defaults: CronTriggerObject and - SimpleTriggerObject + convenient defaults: CronTriggerObject and + SimpleTriggerObject Triggers need to be scheduled. Spring offers a - SchedulerFactoryObject that exposes triggers to - be set as properties. SchedulerFactoryObject + SchedulerFactoryObject that exposes triggers to + be set as properties. SchedulerFactoryObject schedules the actual jobs with those triggers. Find below a couple of examples: - <object id="SimpleTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz"> + <object id="SimpleTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz"> <!-- see the example of method invoking job above --> <property name="JobDetail" ref="ExampleJob" /> @@ -190,9 +207,9 @@ public class ExampleJob extends QuartzJobObject { Now we've set up two triggers, one running every 50 seconds with a starting delay of 10 seconds and one every morning at 6 AM. To finalize everything, we need to set up the - SchedulerFactoryObject: + SchedulerFactoryObject: - <object id="quartzSchedulerFactory" type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz"> + <object id="quartzSchedulerFactory" type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz"> <property name="triggers"> <list> <ref object="CronTrigger" /> @@ -203,7 +220,7 @@ public class ExampleJob extends QuartzJobObject { More properties are available for the - SchedulerFactoryObjecct for you to set, such as + SchedulerFactoryObjecct for you to set, such as the calendars used by the job details, properties to customize Quartz with, etc. Have a look at the SchedulerFactoryObject diff --git a/doc/reference/src/services.xml b/doc/reference/src/services.xml index 2e2a6642..a37c4b36 100644 --- a/doc/reference/src/services.xml +++ b/doc/reference/src/services.xml @@ -1,8 +1,25 @@ - + + .NET Enterprise Services - + Introduction Spring's .NET Enterprise Services support allows you to export a @@ -16,14 +33,14 @@ Programatically, as you would with any third party library. - + Serviced Components 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 - System.EnterpriseServices.ServicedComponent, adorn + System.EnterpriseServices.ServicedComponent, 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 @@ -50,7 +67,7 @@ linkend="entsvc-example">NET Enterprise Services example. - + Server Side One of the main challenges for the exporting of a serviced component @@ -64,7 +81,7 @@ - Spring.Enterprise.ServicedComponentExporter + Spring.Enterprise.ServicedComponentExporter 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. @@ -72,7 +89,7 @@ - Spring.Enterprise.EnterpriseServicesExporter + Spring.Enterprise.EnterpriseServicesExporter 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 @@ -81,7 +98,7 @@ Let's say that we have a simple service interface and implementation class, such as these: - namespace MyApp.Services + namespace MyApp.Services { public interface IUserManager { @@ -116,7 +133,7 @@ And the corresponding object definition for it in the application context config file: - <object id="userManager" type="MyApp.Services.SimpleUserManager"> + <object id="userManager" type="MyApp.Services.SimpleUserManager"> <property name="UserDao" ref="userDao"/> </object> @@ -125,7 +142,7 @@ to export our service using the exporter ServicedComponentExporter as shown below - <object id="MyApp.EnterpriseServices.UserManager" type="Spring.Enterprise.ServicedComponentExporter, Spring.Services"> + <object id="MyApp.EnterpriseServices.UserManager" type="Spring.Enterprise.ServicedComponentExporter, Spring.Services"> <property name="TargetName" value="userManager"/> <property name="TypeAttributes"> <list> @@ -152,7 +169,7 @@ The next thing we need to do is configure an exporter for the COM+ application that will host our new component: - <object id="MyComponentExporter" type="Spring.Enterprise.EnterpriseServicesExporter, Spring.Services"> + <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"> @@ -182,18 +199,18 @@ AccessControl and Roles properties. - + Client Side 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 - Spring.Enterprise.ServicedComponentFactory + Spring.Enterprise.ServicedComponentFactory 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 - <object id="enterpriseUserManager" type="Spring.Enterprise.ServicedComponentFactory, Spring.Services"> + <object id="enterpriseUserManager" type="Spring.Enterprise.ServicedComponentFactory, Spring.Services"> <property name="Name" value="MyApp.EnterpriseServices.UserManager"/> <property name="Template" value="userManager"/> </object> diff --git a/doc/reference/src/springair.xml b/doc/reference/src/springair.xml index 5e456834..6283e97d 100644 --- a/doc/reference/src/springair.xml +++ b/doc/reference/src/springair.xml @@ -1,5 +1,22 @@ - + + SpringAir - Reference Application @@ -87,7 +104,7 @@ instantiate the IoC container. The important parts of that configuration are shown below - <spring> + <spring> <parsers> <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" /> </parsers> @@ -142,7 +159,7 @@ The XML configuration to configure the TripForm form is shown below - <object type="TripForm.aspx" parent="standardPage"> + <object type="TripForm.aspx" parent="standardPage"> <property name="BookingAgent" ref="bookingAgent" /> <property name="AirportDao" ref="airportDao" /> <property name="TripValidator" ref="tripValidator" /> @@ -172,7 +189,7 @@ family of methods that are overridden to support the bi-directional data binding are listed below. - protected override void InitializeModel() + protected override void InitializeModel() { trip = new Trip(); trip.Mode = TripMode.RoundTrip; @@ -215,7 +232,7 @@ below. Notice how much cleaner and more business focused the code reads than if you were using standard ASP.NET APIs. - protected void SearchForFlights(object sender, EventArgs e) + protected void SearchForFlights(object sender, EventArgs e) { if (Validate(trip, tripValidator)) { @@ -234,7 +251,7 @@ defined declaratively in the XML configuration file and is shown below. - <v:group id="tripValidator"> + <v:group id="tripValidator"> <v:required id="departureAirportValidator" test="StartingFrom.AirportCode"> <v:message id="error.departureAirport.required" providers="departureAirportErrors, validationSummary"/> @@ -290,7 +307,7 @@ methods. Spring can expose this object as a web service by declaring the following XML defined in the top level Config/Services.xml file - <object id="bookingAgentWebService" type="Spring.Web.Services.WebServiceExporter, Spring.Web"> + <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"/> diff --git a/doc/reference/styles/html.css b/doc/reference/src/styles/html.css similarity index 87% rename from doc/reference/styles/html.css rename to doc/reference/src/styles/html.css index c9ccb50b..cfd5d178 100644 --- a/doc/reference/styles/html.css +++ b/doc/reference/src/styles/html.css @@ -264,14 +264,3 @@ div.warning * td { font-size: 100%; } -.programlisting .interfacename, -.programlisting .literal, -.programlisting .classname { - font-size: 95%; -} - -/* everything in a is displayed in a nice green, comment-like color */ -.programlisting * .lineannotation, -.programlisting * .lineannotation * { - color: green; -} diff --git a/doc/reference/src/testing.xml b/doc/reference/src/testing.xml index 6c8fcd0a..af78d1d0 100644 --- a/doc/reference/src/testing.xml +++ b/doc/reference/src/testing.xml @@ -1,8 +1,25 @@ - + + Testing -
+
Introduction The Spring team considers developer testing to be an absolutely @@ -14,7 +31,7 @@ linkend="integration-testing">integration testing.
-
+
Unit testing One of the main benefits of Dependency Injection is that your code @@ -39,7 +56,7 @@ unit tests for your IoC-based applications.
-
+
Integration testing However, it is also important to be able to perform some integration @@ -72,7 +89,7 @@ with NUnit then you should add the following to your .config file, (in the form of MyAssembly.dll.config) - <runtime> + <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> @@ -91,7 +108,7 @@ The Spring.Testing.NUnit namespace provides - valuable NUnit TestCase superclasses for + valuable NUnit TestCase 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 @@ -124,7 +141,7 @@ -
+
Context management and caching The Spring.Testing.NUnit @@ -140,11 +157,11 @@ could reduce productivity. To address this issue, the - AbstractDependencyInjectionSpringContextTests has + AbstractDependencyInjectionSpringContextTests has an protected property that subclasses must implement to provide the location of context definition files: - protected abstract string[] ConfigLocations { get; } + protected abstract string[] ConfigLocations { get; } Implementations of this method must provide an array containing the IResource locations of XML configuration metadata used to configure @@ -160,34 +177,34 @@ reloading - for example, by changing an object definition or the state of an application object - you can call the SetDirty() method on - AbstractDependencyInjectionSpringContextTests to + AbstractDependencyInjectionSpringContextTests to cause the test fixture to reload the configurations and rebuild the application context before executing the next test case.
-
+
Dependency Injection of test fixtures When - AbstractDependencyInjectionSpringContextTests + AbstractDependencyInjectionSpringContextTests (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. - AbstractDependencyInjectionSpringContextTests + AbstractDependencyInjectionSpringContextTests will automatically locate the corresponding object in the set of configuration files specified in the ConfigLocations property. Consider the scenario where we have a class, - HibernateTitleDao, that performs data access - logic for say, the Title domain object. We want + HibernateTitleDao, that performs data access + logic for say, the Title domain object. We want to write integration tests that test all of the following areas: The Spring configuration; basically, is everything related to - the configuration of the HibernateTitleDao + the configuration of the HibernateTitleDao object correct and present? @@ -197,7 +214,7 @@ - The logic of the HibernateTitleDao; + The logic of the HibernateTitleDao; does the configured instance of this class perform as anticipated? @@ -206,8 +223,8 @@ Let's look at the test class itself (we will look at the configuration immediately afterwards). - [TestFixture] -public class HibernateTitleDaoTests : AbstractDependencyInjectionSpringContextTests { + [TestFixture] +public class HibernateTitleDaoTests : AbstractDependencyInjectionSpringContextTests { // this instance will be (automatically) dependency injected private HibernateTitleDao titleDao; @@ -234,10 +251,10 @@ public class HibernateTitleDaoTests : AbstractDependencyIn ('classpath:com/foo/daos.xml') looks like this: - <?xml version="1.0" encoding="utf-8" ?> + <?xml version="1.0" encoding="utf-8" ?> <objects xmlns="http://www.springframework.net"> - <!-- this object will be injected into the HibernateTitleDaoTests class --> + <!-- this object will be injected into the HibernateTitleDaoTests class --> <object id="titleDao" type="Spring.Samples.HibernateTitleDao, Spring.Samples"> <property name="sessionFactory" ref="sessionFactory"/> </object> @@ -249,7 +266,7 @@ public class HibernateTitleDaoTests : AbstractDependencyIn </objects> The - AbstractDependencyInjectionSpringContextTests + AbstractDependencyInjectionSpringContextTests classes uses autowire by type. Thus if you have multiple object definitions of the same type, you cannot rely on this approach for those particular @@ -260,13 +277,13 @@ public class HibernateTitleDaoTests : AbstractDependencyIn If you don't want dependency injection applied to your test cases, simply don't declare any set properties. Alternatively, you can extend - the AbstractSpringContextTests - the root of the + the AbstractSpringContextTests - the root of the class hierarchy in the Spring.Testing.NUnit namespace. It merely contains convenience methods to load Spring contexts, and performs no Dependency Injection of the test fixture. -
+
Field level injection If, for whatever reason, you don't fancy having setter @@ -276,8 +293,8 @@ public class HibernateTitleDaoTests : AbstractDependencyIn Spring XML configuration does not need to change, merely the test fixture). - [TestFixture] -public class HibernateTitleDaoTests : AbstractDependencyInjectionSpringContextTests { + [TestFixture] +public class HibernateTitleDaoTests : AbstractDependencyInjectionSpringContextTests{ public HibernateTitleDaoTests() { // switch on field level injection @@ -307,7 +324,7 @@ public class HibernateTitleDaoTests : AbstractDependencyIn
-
+
Transaction management One common issue in tests that access a real database is their @@ -317,23 +334,23 @@ public class HibernateTitleDaoTests : AbstractDependencyIn data - cannot be done (or verified) outside a transaction. The - AbstractTransactionalDbProviderSpringContextTests + AbstractTransactionalDbProviderSpringContextTests 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. - AbstractTransactionalSpringContextTests - depends on a IPlatformTransactionManager object + AbstractTransactionalSpringContextTests + depends on a IPlatformTransactionManager object being defined in the application context. The name doesn't matter, due to the use of autowire by type. Typically you will extend the subclass, - AbstractTransactionalDbProviderSpringContextTests. - This also requires that a DbProvider object + AbstractTransactionalDbProviderSpringContextTests. + This also requires that a DbProvider object definition - again, with any name - be present in the configurations. It - creates an AdoTemplate instance variable that is + creates an AdoTemplate 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). @@ -341,7 +358,7 @@ public class HibernateTitleDaoTests : AbstractDependencyIn 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 SetComplete() method inherited - from AbstractTransactionalSpringContextTests. + from AbstractTransactionalSpringContextTests. This will cause the transaction to commit instead of roll back. There is also convenient ability to end a transaction before the @@ -357,27 +374,27 @@ public class HibernateTitleDaoTests : AbstractDependencyIn operation of the UI through your NUnit test suite.
-
+
Convenience variables When you extend the - AbstractTransactionalDbProviderSpringContextTests + AbstractTransactionalDbProviderSpringContextTests class you will have access to the following protected instance variables: applicationContext (a - IConfigurableApplicationContext): + IConfigurableApplicationContext): inherited from the - AbstractDependencyInjectionSpringContextTests + AbstractDependencyInjectionSpringContextTests superclass. Use this to perform explicit object lookup, or test the state of the context as a whole. adoTemplate: inherited from - AbstractTransactionalDbProviderSpringContextTests. + AbstractTransactionalDbProviderSpringContextTests. 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 @@ -385,7 +402,7 @@ public class HibernateTitleDaoTests : AbstractDependencyIn 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 Flush() method on NHibernate's - ISession interface. + ISession interface. @@ -394,10 +411,10 @@ public class HibernateTitleDaoTests : AbstractDependencyIn in many tests
-
+
-
+
Further Resources This section contains links to further resources about testing in diff --git a/doc/reference/src/threading.xml b/doc/reference/src/threading.xml index 7eb3df3c..a69bd664 100644 --- a/doc/reference/src/threading.xml +++ b/doc/reference/src/threading.xml @@ -1,11 +1,28 @@ - + + Threading and Concurrency Support - + Introduction - The purpose of the Spring.Threading namespace + The purpose of the Spring.Threading 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 @@ -26,8 +43,8 @@ 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 - HttpContext.Current. For other environments - System.Runtime.Remoting.Messaging.CallContext is + HttpContext.Current. For other environments + System.Runtime.Remoting.Messaging.CallContext is used. For more background information on the motivation behind these choices, say as compared to the attribute [ThreadStatic] refer to "Piers7"'s - The API is quite simple and shown belowpublic interface IThreadStorage + The API is quite simple and shown belowpublic interface IThreadStorage { object GetData(string name) @@ -58,27 +75,27 @@ the method FreeNamedDataSlot. In Spring.Core is the implementation, - CallContextStorage, that directly uses - CallContext and also the implementation - LogicalThreadContext which by default uses - CallContextStorage but can be configured via the + CallContextStorage, that directly uses + CallContext and also the implementation + LogicalThreadContext which by default uses + CallContextStorage but can be configured via the static method SetStorage(IThreadStorage). The methods on CallContextStorage and LogicalThreadContext are static. In Spring.Web is the implementation - HttpContextStorage which uses the - HttpContext to store thread local data and - HybridContextStorage that uses - HttpContext if within a web environment, i.e. + HttpContextStorage which uses the + HttpContext to store thread local data and + HybridContextStorage that uses + HttpContext if within a web environment, i.e. HttpContext.Current != null, and - CallContext otherwise. + CallContext otherwise. - Spring internally uses LogicalThreadContext + Spring internally uses LogicalThreadContext as this doesn't require a coupling to the System.Web namespace. In the case of Spring based web applications, Spring's - WebSupportModule sets the storage strategy of - LogicalThreadContext to be - HybridContextStorage. + WebSupportModule sets the storage strategy of + LogicalThreadContext to be + HybridContextStorage. @@ -102,7 +119,7 @@ interface which has two basic use cases. The first case is to block indefinitely until a condition is met: - void ConcurrentRun(ISync lock) { + void ConcurrentRun(ISync lock) { lock.Acquire(); // block until condition met try { // ... access shared resources @@ -116,7 +133,7 @@ The other case is to specify a maximum amount of time to block before the condition is met: - void ImpatientConcurrentRun(ISync lock) { + void ImpatientConcurrentRun(ISync lock) { // block for at most 10 milliseconds for condition if ( lock.Attempt(10) ) { try { @@ -143,7 +160,7 @@ exiting from the block. This should simplify the programming model for code using (!) an - ISync: + ISync: ISync sync = ... ... using (new SyncHolder(sync)) @@ -152,7 +169,7 @@ using (new SyncHolder(sync)) // holding the ISync lock } There is also the timed version, a little more - cumbersome as you must deal with timeouts: + cumbersome as you must deal with timeouts: ISync sync = ... long msecs = 100; ... @@ -184,7 +201,7 @@ catch (TimeoutException) unsignalled (Reset) and can only be Set(). A typical use is to act as a start signal for a group of worker threads. - class Boss { + class Boss { Latch _startPermit; void Worker() { @@ -222,7 +239,7 @@ catch (TimeoutException) keeps a count of the number available and acts accordingly. A typical use is to control access to a pool of shared objects. - class LimitedConcurrentUploader { + class LimitedConcurrentUploader { // ensure we don't exceed maxUpload simultaneous uploads Semaphore _available; public LimitedConcurrentUploader(maxUploads) { diff --git a/doc/reference/src/transaction.xml b/doc/reference/src/transaction.xml index f1a00490..b9b1d1c9 100644 --- a/doc/reference/src/transaction.xml +++ b/doc/reference/src/transaction.xml @@ -1,8 +1,25 @@ - + + Transaction management - + Introduction Spring.NET provides a consistent abstraction for transaction @@ -23,7 +40,7 @@ Provides a simple API for programmatic transaction management + linkend="transaction-programmatic">programmatic transaction management @@ -59,14 +76,14 @@ - The fourth section, entitled Programmatic + The fourth section, entitled Programmatic transaction management, covers support for programmatic transaction management. - + Motivations The data access technology landscape is a broad one, within the .NET @@ -177,16 +194,16 @@ lets move on to see the code. - + Key Abstractions The key to the Spring transaction management abstraction is the notion of a transaction strategy. A transaction strategy is defined by the - Spring.Transaction.IPlatformTransactionManager + Spring.Transaction.IPlatformTransactionManager interface, shown below: - public interface IPlatformTransactionManager { + public interface IPlatformTransactionManager { ITransactionStatus GetTransaction( ITransactionDefinition definition ); @@ -198,30 +215,30 @@ This is primarily a 'SPI' (Service Provider Interface), although it can be used Programatically. Note that in keeping with the Spring - Framework's philosophy, IPlatformTransactionManager + Framework's philosophy, IPlatformTransactionManager is an interface, and can thus be easily mocked or stubbed as necessary. - IPlatformTransactionManager implementations + IPlatformTransactionManager implementations are defined like any other object in the IoC container. The following implementations are provided - AdoPlatformTransactionManager - local + AdoPlatformTransactionManager - local ADO.NET based transactions - ServiceDomainPlatformTransactionManager - + ServiceDomainPlatformTransactionManager - distributed transaction manager from Enterprise Services - TxScopePlatformTransactionManager - + TxScopePlatformTransactionManager - local/distributed transaction manager from System.Transactions. - HibernatePlatformTransactionManager - + HibernatePlatformTransactionManager - local transaction manager for use with NHibernate or mixed ADO.NET/NHibernate data access operations. @@ -242,15 +259,15 @@ section . The GetTransaction(..) method returns a - ITransactionStatus object, depending on a - ITransactionDefinition parameters. The returned - ITransactionStatus might represent a new or + ITransactionStatus object, depending on a + ITransactionDefinition parameters. The returned + ITransactionStatus might represent a new or existing transaction (if there was a matching transaction in the current call stack - with the implication being that a - ITransactionStatus is associated with a logical + ITransactionStatus is associated with a logical thread of execution. - The ITransactionDefinition interface + The ITransactionDefinition interface specified @@ -288,31 +305,31 @@ concepts is essential to using the Spring Framework or indeed any other transaction management solution. - The ITransactionStatus interface + The ITransactionStatus interface provides a simple way for transactional code to control transaction execution and query transaction status. Regardless of whether you opt for declarative or programmatic transaction management in Spring, defining the correct - IPlatformTransactionManager implementation + IPlatformTransactionManager implementation is absolutely essential. In good Spring fashion, this important definition typically is made using via Dependency Injection. - IPlatformTransactionManager + IPlatformTransactionManager implementations normally require knowledge of the environment in which they work, ADO.NET, NHibernate, etc. The following example shows how a standard ADO.NET based - IPlatformTransactionManager can be + IPlatformTransactionManager can be defined. - We must define a Spring IDbProvider + We must define a Spring IDbProvider and then use Spring's - AdoPlatformTransactionManager, giving it a - reference to the IDbProvider. For more information - on the IDbProvider abstraction refer to the next + AdoPlatformTransactionManager, giving it a + reference to the IDbProvider. For more information + on the IDbProvider abstraction refer to the next chapter. - <objects xmlns='http://www.springframework.net' + <objects xmlns='http://www.springframework.net' xmlns:db="http://www.springframework.net/database"> <db:provider id="DbProvider" @@ -332,7 +349,7 @@ We can also use a transaction manager based on System.Transactions just as easily, as shown in the following example - <object id="TransactionManager" + <object id="TransactionManager" type="Spring.Data.TxScopeTransactionManager, Spring.Data"> </object> @@ -347,7 +364,7 @@ local to global transactions or vice versa. - + Resource synchronization with transactions How does application code participate with the resources (i.e. @@ -355,7 +372,7 @@ the different transaction managers? There are two approaches - a high-level and a low-level approach - + High-level approach The preferred approach is to use Spring's high level persistence @@ -375,19 +392,19 @@ providing specific implementations of the callback interface. - + Low-level approach A utility class can be used to directly obtain a connection/transaction pair that is aware of the transactional calling context and returns a pair suitable for that context. The class - ConnectionUtils contains the static method + ConnectionUtils contains the static method ConnectionTxPair GetConnectionTxPair(IDbProvider provider) which serves this purpose. - + Declarative transaction management Most Spring users choose declarative transaction management. It is @@ -446,7 +463,7 @@ specify which exceptions should cause automatic roll back. We specify this declaratively, in configuration, not in code. So, while we can still set RollbackOnly on the - ITransactionStatus object to roll the + ITransactionStatus object to roll the current transaction back Programatically, most often we can specify a rule that MyApplicationException must always result in rollback. This has the significant advantage that business objects don't need to depend on the @@ -455,7 +472,7 @@ rollback the transaction programmatically and you are using declarative transaction management, use the utility method - TransactionInterceptor.CurrentTransactionStatus.SetRollbackOnly(); + TransactionInterceptor.CurrentTransactionStatus.SetRollbackOnly(); Prior to Spring.NET 1.2 RC1 the API call would be @@ -463,8 +480,8 @@ true; - - Understanding + <sect2 xml:id="tx-understandingimpl"> + <title>Understanding Spring's declarative transaction implementation The aim of this section is to dispel the mystique that is @@ -490,8 +507,8 @@ proxies, and that the transactional advice is driven by metadata (currently XML- or attribute-based). The combination of a proxy with transactional metadata yields an AOP proxy that uses a - TransactionInterceptor in conjunction with an - appropriate IPlatformTransactionManager + TransactionInterceptor in conjunction with an + appropriate IPlatformTransactionManager implementation to drive transactions around method invocations. @@ -517,7 +534,7 @@ - ProxyFactoryObject. The common + ProxyFactoryObject. The common properties to set are the reference to the object to proxy (the target object) and a reference to the transaction advice. See for more details. @@ -531,14 +548,14 @@ - ObjectNameAutoProxyCreator which + ObjectNameAutoProxyCreator which specifies a collection of object names based on wildcard matching of object names. See - DefaultAdvisorAutoProxyCreator + DefaultAdvisorAutoProxyCreator which specifies one or more "advisors" i.e an object representing an aspect, including both an advice and a pointcut targeting it to specific joinpoints. See There is also a convenience subclass of - ProxyFactoryObject, namely - TransactionProxyFactoryObject, that sets some + ProxyFactoryObject, namely + TransactionProxyFactoryObject, that sets some common default values for the specific case of applying transactional advice. - The DefaultAdvisorAutoProxyCreator is very + The DefaultAdvisorAutoProxyCreator is very powerful and is the means by which Spring can be configured to use attributes to identify the pointcuts where transaction advice should be applied. The advisor that performs that task is @@ -587,19 +604,19 @@ advice. - + A First Example Consider the following interface. The intent is to convey the concepts to you so you can concentrate on the transaction usage and not have to worry about domain specific details. The - ITestObjectManager is a poor-mans + ITestObjectManager is a poor-mans business service layer - the implementation of which will make two DAO calls. Clearly this example is overly simplistic from the service layer perspective as there isn't any business logic at all!. The 'service' interface is shown below. - public interface ITestObjectManager + public interface ITestObjectManager { void SaveTwoTestObjects(TestObject to1, TestObject to2); @@ -607,9 +624,9 @@ } The implementation of - ITestObjectManager is shown below + ITestObjectManager is shown below - public class TestObjectManager : ITestObjectManager + public class TestObjectManager : ITestObjectManager { // Fields/Properties ommited @@ -642,7 +659,7 @@ update delete and find method for the 'domain' object TestObject. TestObject in turn has simple properties like name and age. - public interface ITestObjectDao + public interface ITestObjectDao { void Create(string name, int age); void Update(TestObject to); @@ -652,12 +669,12 @@ } The Create and Delete method implementation is shown below. Note - that this uses the AdoTemplate class discussed in + that this uses the AdoTemplate class discussed in the following chapter. Refer to for information on the interaction between Spring's high level persistence integration APIs and transaction management features. - public class TestObjectDao : AdoDaoSupport, ITestObjectDao + public class TestObjectDao : AdoDaoSupport, ITestObjectDao { public void Create(string name, int age) { @@ -674,19 +691,19 @@ } } - The TestObjectManager is configured with + The TestObjectManager is configured with the DAO objects by standard dependency injection techniques. The client code, which in this case directly asks the Spring IoC container for an - instance of ITestObjectManager, will + instance of ITestObjectManager, will receive a transaction proxy with transaction options based on the attribute metadata. Note that typically the - ITestObjectManager would be set on yet + ITestObjectManager would be set on yet another higher level object via dependency injection, for example a web service. The client calling code is shown below - IApplicationContext ctx = + IApplicationContext ctx = new XmlApplicationContext("assembly://Spring.Data.Integration.Tests/Spring.Data/autoDeclarativeServices.xml"); ITestObjectManager mgr = ctx["testObjectManager"] as ITestObjectManager; @@ -708,7 +725,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); The configuration of the object definitions of the DAO and manager classes is shown below. - <objects xmlns='http://www.springframework.net' + <objects xmlns='http://www.springframework.net' xmlns:db="http://www.springframework.net/database"> <db:provider id="DbProvider" @@ -745,7 +762,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); create a transactional proxy for the manager class is shown below. - <!-- The rest of the config file is common no matter how many objects you add --> + <!-- The rest of the config file is common no matter how many objects you add --> <!-- that you would like to have declarative tx management applied to --> <object id="autoProxyCreator" @@ -783,67 +800,67 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); references it can be included in its own file and then imported via the <import> element. In examples and test code this XML configuration fragment is named autoDeclarativeServices.xml See for more information. + linkend="objects-factory-xml-import" /> for more information. The classes and their roles in this configuration fragment are listed below - TransactionInterceptor is the AOP + TransactionInterceptor is the AOP advice responsible for performing transaction management functionality. - TransactionAttributeSourceAdvisor is an + TransactionAttributeSourceAdvisor is an AOP Advisor that holds the TransactionInterceptor, which is the advice, and a pointcut (where to apply the advice), in the form of a TransactionAttributeSource. - AttributesTransactionAttributeSource is + AttributesTransactionAttributeSource is an implementation of the - ITransactionAttributeSource interface that + ITransactionAttributeSource interface that defines where to get the transaction metadata defining the transaction semantics (isolation level, propagation behavior, etc) that should be applied to specific methods of specific classes. The transaction metadata is specified via implementations of the - ITransactionAttributeSource interface. This + ITransactionAttributeSource interface. This example shows the use of the implementation - Spring.Transaction.Interceptor.AttributesTransactionAttributeSource + Spring.Transaction.Interceptor.AttributesTransactionAttributeSource to obtain that information from standard .NET attributes. By the very nature of using standard .NET attributes, the attribute serves double duty in identifying the methods where the transaction semantics apply. Alternative implementations of - ITransactionAttributeSource available are - MatchAlwaysTransactionAttributeSource, - NameMatchTransactionAttributeSource, or - MethodMapTransactionAttributeSource. + ITransactionAttributeSource available are + MatchAlwaysTransactionAttributeSource, + NameMatchTransactionAttributeSource, or + MethodMapTransactionAttributeSource. - MatchAlwaysTransactionAttributeSource + MatchAlwaysTransactionAttributeSource is configured with a ITransactionAttribute instance that is applied to all methods. The shorthand string representation, i.e. PROPAGATION_REQUIRED can be used - AttributesTransactionAttributeSource + AttributesTransactionAttributeSource : Use a standard. .NET attributes to specify the transactional information. See TransactionAttribute class for more information. - NameMatchTransactionAttributeSource + NameMatchTransactionAttributeSource allows ITransactionAttributes to be matched by method name. The NameMap IDictionary property is used to specify the mapping. For example - <object name="nameMatchTxAttributeSource" type="Spring.Transaction.Interceptor.NameMatchTransactionAttributeSource, Spring.Data" + <object name="nameMatchTxAttributeSource" type="Spring.Transaction.Interceptor.NameMatchTransactionAttributeSource, Spring.Data" <property name="NameMap"> <dictionary> <entry key="Execute" value="PROPAGATION_REQUIRES_NEW, -ApplicationException"/> @@ -860,7 +877,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); - MethodMapTransactionAttributeSource + MethodMapTransactionAttributeSource : Similar to NameMatchTransactionAttributeSource but specifies that only fully qualified method names (i.e. type.method, assembly) and wildcards can be used at the start or end of the @@ -870,7 +887,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); - DefaultAdvisorAutoProxyCreator: looks + DefaultAdvisorAutoProxyCreator: looks for Advisors in the context, and automatically creates proxy objects which are the transactional wrappers @@ -881,7 +898,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); attributes. - + Declarative transactions using the transaction namespace Spring provides a custom XML schema to simplify the configuration @@ -890,7 +907,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); custom namespace parser for the transaction namespace. This can be done in the application configuration file as shown below - <?xml version="1.0" encoding="utf-8" ?> + <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> @@ -921,7 +938,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); you have not installed Spring's schema into the proper VS.NET 2005 location. See the chapter on VS.NET integration for more details. - <objects xmlns="http://www.springframework.net" + <objects xmlns="http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.net/tx" xmlns:db="http://www.springframework.net/database" @@ -955,7 +972,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); </object> - <tx:attribute-driven transaction-manager="transactionManager"/> + <tx:attribute-driven transaction-manager="transactionManager"/> </objects> @@ -964,10 +981,10 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); 'transaction-manager' attribute in the <tx:attribute-driven/> tag if the object name of the - IPlatformTransactionManager that you + IPlatformTransactionManager that you want to wire in has the name 'transactionManager'. If the - PlatformTransactionManager object + PlatformTransactionManager object that you want to dependency inject has any other name, then you have to be explicit and use the 'transaction-manager' attribute as in the example above. @@ -1015,7 +1032,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); Controls what type of transactional proxies are created for classes annotated with the - [Transaction] attribute. If + [Transaction] attribute. If "proxy-target-type" attribute is set to "true", then class-based proxies will be created (proxy inherits from target class, however calls are @@ -1054,7 +1071,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); The "proxy-target-type" attribute on the <tx:attribute-driven/> element controls what type of transactional proxies are created for classes annotated with - the Transaction attribute. If + the Transaction attribute. If "proxy-target-type" attribute is set to "true", then inheritance-based proxies will be created. If "proxy-target-type" is @@ -1074,7 +1091,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); <tx:advice> instead of <tx:attribute-driven/> in the example would look like the following - <tx:advice id="txAdvice" transaction-manager="transactionManager"> + <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="Save*"/> <tx:method name="Delete*"/> @@ -1089,7 +1106,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); Here is an example using other elements of the <tx:method/> definition - <!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> object below) --> + <!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> object below) --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- the transactional semantics... --> <tx:attributes> @@ -1113,7 +1130,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); tie together a pointcut and the above defined advice as shown below. - <object id="serviceOperation" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop"> + <object id="serviceOperation" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop"> <property name="pattern" value="Spring.TxQuickStart.Services.*"/> </object> @@ -1292,7 +1309,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill");
- + Transaction attribute settings The Transaction attribute is metadata that specifies that a class @@ -1362,7 +1379,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); TransactionPropagation enumeration, - Spring.Transaction.TransactionPropagation + Spring.Transaction.TransactionPropagation optional propagation setting. Required, Supports, Mandatory, RequiresNew, NotSupported, Never, Nested @@ -1371,7 +1388,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); Isolation - System.Data.IsolationLevel + System.Data.IsolationLevel optional isolation level @@ -1411,7 +1428,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); RollbackFor - an array of Type objects + an array of Type objects an optional array of exception classes that must cause rollback @@ -1420,7 +1437,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); NoRollbackFor - an array of Type objects + an array of Type objects an optional array of exception classes that must not cause rollback @@ -1470,7 +1487,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); transactional aspect does not affect the lifetime of your object. - + Declarative Transactions using AutoProxy if you choose not to use the transaction namespace for declarative @@ -1480,13 +1497,13 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); Spring's autoproxy functionality defines criteria to select a collection of objects to create a transactional AOP proxy. There are two AutoProxy classes that you can use, - ObjectNameAutoProxyCreator and - DefaultAdvisorAutoProxyCreator. If you are using + ObjectNameAutoProxyCreator and + DefaultAdvisorAutoProxyCreator. If you are using the new transaction namespace support you do not need to configure these objects as a DefaultAdvisorAutoProxyCreator is created 'under the covers' while parsing the transaction namespace elements - + Creating transactional proxies with ObjectNameAutoProxyCreator @@ -1499,7 +1516,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); in the section that use ProxyFactoryObject for the declaration of the transactionInterceptor. - <object name="autoProxyCreator" + <object name="autoProxyCreator" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop"> <property name="InterceptorNames" value="transactionInterceptor"/> @@ -1511,7 +1528,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); </object> - + Creating transactional proxies with DefaultAdvisorAutoProxyCreator @@ -1523,7 +1540,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); - + Declarative Transactions using TransactionProxyFactoryObject @@ -1537,7 +1554,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); naming convention for the methods of your DAOs. The example from chapter 5 is shown here using a TransactionProxyFactoryObject. - + <object id="testObjectManager" type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"> @@ -1601,7 +1618,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); Multiple rollback rules can be specified here, comma-separated. A - prefix forces rollback; a + prefix specifies commit. Under the covers the IDictionary of name value pairs will be converted to an instance of - NameMatchTransactionAttributeSource + NameMatchTransactionAttributeSource The string used for PROPAGATION_NAME are those defined on the Spring.Transaction.TransactionPropagation enumeration, namely Required, @@ -1623,7 +1640,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); than the TransactionProxyFactoryObject convenience proxy creator. - + Concise proxy definitions Using abstract object definitions in conjunction with a @@ -1636,7 +1653,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); only the configuration information that is different. An abstract object definition is shown below - <object id="txProxyTemplate" abstract="true" + <object id="txProxyTemplate" abstract="true" type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"> <property name="PlatformTransactionManager" ref="adoTransactionManager"/> @@ -1652,7 +1669,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); Subsequent definitions can refer to this 'base' configuration as shown below - <object id="testObjectManager" parent="txProxyTemplate"> + <object id="testObjectManager" parent="txProxyTemplate"> <property name="Target"> <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> <property name="TestObjectDao" ref="testObjectDao"/> @@ -1661,7 +1678,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); </object> - + Declarative Transactions using ProxyFactoryObject Using the general ProxyFactoryObject to declare transactions gives @@ -1670,7 +1687,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); example shown previously a sample configuration using ProxyFactoryObject is shown below - <object id="testObjectManagerTarget" type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> + <object id="testObjectManagerTarget" type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> <property name="TestObjectDao" ref="testObjectDao"/> </object> @@ -1692,7 +1709,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); object from the container. The interceptor name refers to the following definition. - <object id="transactionInterceptor" type="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data"> + <object id="transactionInterceptor" type="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data"> <property name="TransactionManager" ref="adoTransactionManager"/> @@ -1730,7 +1747,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); - + Programmatic transaction management Spring provides two means of programmatic transaction @@ -1738,12 +1755,12 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); - Using the TransactionTemplate + Using the TransactionTemplate Using a - IPlatformTransactionManager + IPlatformTransactionManager implementation directly @@ -1751,14 +1768,14 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); These are located in the Spring.Transaction.Support namespace. If you are going to use programmatic transaction management, the Spring team generally recommends the first approach (i.e. Using the - TransactionTemplate) + TransactionTemplate) - - Using the <classname>TransactionTemplate</classname> + + Using the <literal>TransactionTemplate</literal> The TransactionTemplate adopts the same approach as other Spring - templates such as AdoTemplate and - HibernateTemplate. It uses a callback approach, + templates such as AdoTemplate and + HibernateTemplate. It uses a callback approach, to free application code from having to do the boilerplate acquisition and release of resources, and results in code that is intention driven, in that the code that is written focuses solely on what the developer @@ -1782,10 +1799,10 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); execute in the context of a transaction. You will then pass an instance of your custom ITransactionCallback to the Execute(..) method exposed on the TransactionTemplate. Note that the - ITransactionCallback can be used to + ITransactionCallback can be used to return a value: - public class SimpleService : IService + public class SimpleService : IService { private TransactionTemplate transactionTemplate; @@ -1809,14 +1826,14 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); delegates, which provides a particularly elegant means to invoke a callback function as local variables can be referred to inside the delegate, i.e. userId. In this case the - ITransactionStatus was not exposed in the + ITransactionStatus was not exposed in the delegate (delegate can infer the signature to use), but one could also obtain a reference to the - ITransactionStatus instance and set the + ITransactionStatus instance and set the RollbackOnly property to trigger a rollback - or alternatively throw an exception. This is shown below - tt.Execute(delegate(ITransactionStatus status) + tt.Execute(delegate(ITransactionStatus status) { try { UpdateOperation1(); @@ -1829,10 +1846,10 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); If you are using .NET 1.1 then you should provide a normal delegate reference or an instance of a class that implements the - ITransactionCallback interface. This is + ITransactionCallback interface. This is shown below - tt.Execute(new TransactionRollbackTxCallback(amount)); + tt.Execute(new TransactionRollbackTxCallback(amount)); public class TransactionRollbackTxCallback : ITransactionCallback @@ -1854,24 +1871,24 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); } Application classes wishing to use the - TransactionTemplate must have access to a - IPlatformTransactionManager (which will + TransactionTemplate must have access to a + IPlatformTransactionManager (which will typically be supplied to the class via dependency injection). It is easy to unit test such classes with a mock or stub - IPlatformTransactionManager. + IPlatformTransactionManager. Specifying transaction settings Transaction settings such as the propagation mode, the isolation level, the timeout, and so forth can be set on the - TransactionTemplate either programmatically or - in configuration. TransactionTemplate instances + TransactionTemplate either programmatically or + in configuration. TransactionTemplate instances by default have the default transactional settings. Find below an example of programmatically customizing the transactional settings for - a specific TransactionTemplate. + a specific TransactionTemplate. - public class SimpleService : IService + public class SimpleService : IService { private TransactionTemplate transactionTemplate; @@ -1895,32 +1912,32 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); Find below an example of defining a - TransactionTemplate with some custom + TransactionTemplate with some custom transactional settings, using Spring XML configuration. The 'sharedTransactionTemplate' can then be injected into as many services as are required. - <object id="sharedTransactionTemplate" + <object id="sharedTransactionTemplate" type="Spring.Transaction.Support.TransactionTemplate, Sprng.Data"> <property name="TransactionIsolationLevel" value="IsolationLevel.ReadUncommitted"/> <property name="TransactionTimeout" value="30"/> </object> Finally, instances of the - TransactionTemplate class are threadsafe, in + TransactionTemplate class are threadsafe, in that instances do not maintain any conversational state. - TransactionTemplate instances do however + TransactionTemplate instances do however maintain configuration state, so while a number of classes may choose to share a single instance of a - TransactionTemplate, if a class needed to use a - TransactionTemplate with different settings + TransactionTemplate, if a class needed to use a + TransactionTemplate with different settings (for example, a different isolation level), then two distinct - TransactionTemplate instances would need to be + TransactionTemplate instances would need to be created and used. - + Using the PlatformTransactionManager You can also use the PlatformTransactionManager directly to manage @@ -1930,7 +1947,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); the TransactionDefinition and ITransactionStatus objects, you can initiate transactions, rollback and commit. - DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.PropagationBehavior = TransactionPropagation.Required; ITransactionStatus status = transactionManager.GetTransaction(def); @@ -1970,28 +1987,28 @@ transactionManager.Commit(status); Transaction lifecycle and status information You can query the status of the current Spring managed transaction - with the class TransactionSynchronizationManager. + with the class TransactionSynchronizationManager. Typical application code should not need to rely on using this class but in some cases it is convenient to receive events around the lifecycle of the transaction, i.e. before committing, after committing. - TransactionSynchronizationManager provides a method + TransactionSynchronizationManager provides a method to register a callback object that is informed on all significant stages in the transaction lifecycle. Note that you can register for lifecycle call back information for any of the transaction managers you use, be it NHibernate or local ADO.NET transactions. The method to register a callback with the - TransactionSynchronizationManager is + TransactionSynchronizationManager is - public static void RegisterSynchronization( ITransactionSynchronization synchronization ) + public static void RegisterSynchronization( ITransactionSynchronization synchronization ) Please refer to the SDK docs for information on other methods in this class. - The ITransactionSynchronization interface + The ITransactionSynchronization interface is - public interface ITransactionSynchronization + public interface ITransactionSynchronization { // Typically used by Spring resource management code @@ -2006,7 +2023,7 @@ transactionManager.Commit(status); void AfterCompletion( TransactionSynchronizationStatus status ); } - The TransactionSynchronizationStatus is an + The TransactionSynchronizationStatus is an enum with the values Committed, Rolledback, and Unknown. \ No newline at end of file diff --git a/doc/reference/src/tx-quickstart.xml b/doc/reference/src/tx-quickstart.xml index c2955d5b..a9def177 100644 --- a/doc/reference/src/tx-quickstart.xml +++ b/doc/reference/src/tx-quickstart.xml @@ -1,5 +1,22 @@ - + + Transactions QuickStart
@@ -35,17 +52,17 @@ is blatantly taken from the book Pro ADO.NET by Sahil Malik. The transfer service is defined by the - interface IAccountManager with the - implementation AccountManager located in the - namespace Spring.TxQuickStart.Services. The money + interface IAccountManager with the + implementation AccountManager located in the + namespace Spring.TxQuickStart.Services. 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 - IAccountCreditDao interface and the debit - operation via an IAccountDebitDao + IAccountCreditDao interface and the debit + operation via an IAccountDebitDao interface. Implementations of these interfaces using - AdoTemplate are in the namespace + AdoTemplate are in the namespace Spring.TxQuickStart.Dao.Ado.
@@ -53,7 +70,7 @@ The Manager and DAO interfaces are shown below - public interface IAccountManager + public interface IAccountManager { void DoTransfer(float creditAmount, float debitAmount); } @@ -76,7 +93,7 @@ The implementation of the Account Credit DAO is shown below - public class AccountCreditDao : AdoDaoSupport, IAccountCreditDao + public class AccountCreditDao : AdoDaoSupport, IAccountCreditDao { public void CreateCredit(float creditAmount) { @@ -88,7 +105,7 @@ and for the Debit DAO - public class AccountDebitDao : AdoDaoSupport, IAccountDebitDao + public class AccountDebitDao : AdoDaoSupport, IAccountDebitDao { public void DebitAccount(float debitAmount) { @@ -99,17 +116,17 @@ } Both of these DAO implementations inherit from Spring's - AdoDaoSupport class that provides convenient access - to an AdoTemplate for performing data access + AdoDaoSupport class that provides convenient access + to an AdoTemplate 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 DbProvider property representing + AdoDaoSupport's DbProvider property representing the connection to the database. The implementation of the service layer interface, - IAccountManager, is shown below. + IAccountManager, is shown below. - public class AccountManager : IAccountManager + public class AccountManager : IAccountManager { private IAccountCreditDao accountCreditDao; @@ -160,7 +177,7 @@ The NUnit unit test for AccountManager is shown below - public class AccountManagerUnitTests + public class AccountManagerUnitTests { private IAccountManager accountManager; @@ -196,7 +213,7 @@ named application-config.xml and is an embedded resource inside the 'main' project, Spring.TxQuickStart. - <objects xmlns='http://www.springframework.net'> + <objects xmlns='http://www.springframework.net'> <!-- DAO Implementations --> <object id="accountCreditDao" type="Spring.TxQuickStart.Dao.Ado.AccountCreditDao, Spring.TxQuickStart"> @@ -225,7 +242,7 @@ and what transaction manager (local or distribute) to use. The code for this integration style NUnit test is shown below - [TestFixture] + [TestFixture] public class AccountManagerTests { private AdoTemplate adoTemplateCredit; @@ -281,7 +298,7 @@ The essential element is to create an instance of Spring's application context where the relevant layers of the application are - 'wired' together. The IAccountManager + 'wired' together. The IAccountManager 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 @@ -301,7 +318,7 @@ AdoPlatformTransactionManager. This configuration file is shown below - <objects xmlns="http://www.springframework.net" + <objects xmlns="http://www.springframework.net" xmlns:db="http://www.springframework.net/database" xmlns:tx="http://www.springframework.net/tx"> @@ -354,7 +371,7 @@ To switch to a distributed transaction you can refer to the configuration file system-test-dtc-config.xml, which is shown below - objects xmlns='http://www.springframework.net' + <objects xmlns='http://www.springframework.net' xmlns:db="http://www.springframework.net/database" xmlns:tx="http://www.springframework.net/tx"> @@ -409,7 +426,7 @@ AccountManager's DoTransfer method (included in the sample code) is shown below. - [Transaction(NoRollbackFor = new Type[] { typeof(ArithmeticException) })] + [Transaction(NoRollbackFor = new Type[] { typeof(ArithmeticException) })] public void DoTransfer(float creditAmount, float debitAmount) { accountCreditDao.CreateCredit(creditAmount); @@ -431,7 +448,7 @@ database transaction. Running the test code below verifies that the exception still propagates out of the method. - [Test] + [Test] public void DeclarativeWithAttributesNoRollbackFor() { try @@ -461,12 +478,12 @@ additional functionality. Instead all you have to do is uncomment the line - <import resource="assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/aspects-config.xml"/> + <import resource="assembly://Spring.TxQuickStart.Tests/Spring.TxQuickStart/aspects-config.xml"/> in either system-test-dtc-config.xml or system-test-local-config.xml The aspect configuration file is shown below - <objects xmlns='http://www.springframework.net' + <objects xmlns='http://www.springframework.net' xmlns:aop="http://www.springframework.net/aop"> @@ -515,11 +532,11 @@ 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 - Spring.Aspects.Exceptions.ExceptionHandlerAdvice. + Spring.Aspects.Exceptions.ExceptionHandlerAdvice. 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 - Spring.Aspects.Logging.SimpleLoggingAdvice. + Spring.Aspects.Logging.SimpleLoggingAdvice. 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 diff --git a/doc/reference/src/validation.xml b/doc/reference/src/validation.xml index acd957b6..f3bab082 100644 --- a/doc/reference/src/validation.xml +++ b/doc/reference/src/validation.xml @@ -1,6 +1,23 @@ - - Validation Framework + + + Validation Framework
Introduction @@ -84,7 +101,7 @@ validation rules defined for the Trip object in the SpringAir sample application: - <objects xmlns="http://www.springframework.net" xmlns:v="http://www.springframework.net/validation"> + <objects xmlns="http://www.springframework.net" xmlns:v="http://www.springframework.net/validation"> <object type="TripForm.aspx" parent="standardPage"> <property name="TripValidator" ref="tripValidator" /> @@ -255,13 +272,13 @@ The condition validator evaluates any logical expression that is supported by Spring's evaluation engine. The syntax is - <v:condition id="id" test="testCondition" when="applicabilityCondition" parent="parentValidator"> + <v:condition id="id" test="testCondition" when="applicabilityCondition" parent="parentValidator"> actions -</v:condition> +</v:condition> An example is shown below - <v:condition test="StartingFrom.Date >= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue"> + <v:condition test="StartingFrom.Date >= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue"> <v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"/> </v:condition> @@ -295,13 +312,13 @@ This validator ensures that the specified test value is not empty. The syntax is - <v:required id="id" test="requiredValue" when="applicabilityCondition" parent="parentValidator"> + <v:required id="id" test="requiredValue" when="applicabilityCondition" parent="parentValidator"> actions -</v:required> +</v:required> An example is shown below - <v:required test="ReturningFrom.AirportCode"> + <v:required test="ReturningFrom.AirportCode"> <v:message id="error.destinationAirport.required" providers="destinationAirportErrors, validationSummary"/> </v:required> @@ -361,15 +378,15 @@ The syntax is - <v:regex id="id" test="valueToEvaluate" when="applicabilityCondition" parent="parentValidator"> - <v:property name="Expression" value="regularExpressionToMatch"/> + <v:regex id="id" test="valueToEvaluate" when="applicabilityCondition" parent="parentValidator"> + <v:property name="Expression" value="regularExpressionToMatch"/> <v:property name="Options" value="regexOptions"/> actions -</v:regex> +</v:regex> An example is shown below - <v:regex test="ReturningFrom.AirportCode"> + <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> @@ -389,13 +406,13 @@ The syntax is - <v:validator id="id" test="requiredValue" when="applicabilityCondition" type="validatorType" parent="parentValidator"> + <v:validator id="id" test="requiredValue" when="applicabilityCondition" type="validatorType" parent="parentValidator"> actions -</v:validator> +</v:validator> An example is shown below - <v:validator test="ReturningFrom.AirportCode" type="MyNamespace.MyAirportCodeValidator, MyAssembly"> + <v:validator test="ReturningFrom.AirportCode" type="MyNamespace.MyAirportCodeValidator, MyAssembly"> <v:message id="error.destinationAirport.invalid" providers="destinationAirportErrors, validationSummary"/> </v:required> @@ -425,7 +442,7 @@ TripMode.RoundTrip enum value. In order to achieve that we created following validator definition: - <v:group id="returnDateValidator" when="Mode == 'RoundTrip'"> + <v:group id="returnDateValidator" when="Mode == 'RoundTrip'"> // nested validators </v:group> @@ -463,13 +480,13 @@ The syntax is - <v:message id="messageId" providers="errorProviderList" when="messageApplicabilityCondition"> + <v:message id="messageId" providers="errorProviderList" when="messageApplicabilityCondition"> <v:param value="paramExpression"/> </v:message> An example is shown below - <v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"> + <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> @@ -505,13 +522,13 @@ The syntax is - <v:action type="actionType" when="actionApplicabilityCondition"> + <v:action type="actionType" when="actionApplicabilityCondition"> properties </v:action> An example is shown below - <v:action type="Spring.Validation.Actions.ExpressionAction, Spring.Core" when="#page != null"> + <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> @@ -527,7 +544,7 @@ implementation, which is fairly simple thing to do – all you need to do is implement IValidationAction interface: - public interface IValidationAction + public interface IValidationAction { /// <summary> /// Executes the action. @@ -554,11 +571,11 @@ The syntax is shown below - <v:ref name="referencedValidatorId" context="validationContextForTheReferencedValidator"/> + <v:ref name="referencedValidatorId" context="validationContextForTheReferencedValidator"/> An example is shown below - <v:group id="objectA.validator"> + <v:group id="objectA.validator"> <v:ref name="objectC.validator" context="MyObjectC"/> // other validators for ObjectA </v:group> @@ -588,7 +605,7 @@ You can also create Validators programmatically using the API. An example is shown below - UserInfo userInfo = new UserInfo(); // has Name and Password props + UserInfo userInfo = new UserInfo(); // has Name and Password props ValidatorGroup userInfoValidator = new ValidatorGroup(); @@ -610,7 +627,7 @@ bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); useful for performing server-side validation.
-
+
Usage tips within ASP.NET Now that you know how to configure validation rules, let's see what @@ -620,7 +637,7 @@ bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); 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: - <objects xmlns="http://www.springframework.net" xmlns:v="http://www.springframework.net/validation"> + <objects xmlns="http://www.springframework.net" xmlns:v="http://www.springframework.net/validation"> <object type="TripForm.aspx" parent="standardPage"> <property name="TripValidator" ref="tripValidator" /> @@ -635,7 +652,7 @@ bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); Once that's done, you need to perform validation in one or more of the page event handlers, which typically looks similar to this: - public void SearchForFlights(object sender, EventArgs e) + public void SearchForFlights(object sender, EventArgs e) { if (Validate(Controller.Trip, tripValidator)) { @@ -654,7 +671,7 @@ bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); <spring:validationSummary/> controls to the ASP.NET form: - <%@ Page Language="c#" MasterPageFile="~/Web/StandardTemplate.master" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %> + <%@ 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"> @@ -674,7 +691,7 @@ bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); <asp:Content ID="body" ContentPlaceHolderID="body" runat="server"> <div style="text-align: center"> <h4><asp:Label ID="caption" runat="server"></asp:Label></h4> - <spring:ValidationSummary ID="validationSummary" runat="server" /> + <spring:ValidationSummary ID="validationSummary" runat="server" /> <table> <tr class="formLabel"> <td>&nbsp;</td> @@ -690,13 +707,13 @@ bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); <asp:Label ID="leavingFrom" runat="server" /></td> <td nowrap="nowrap"> <asp:DropDownList ID="leavingFromAirportCode" AutoCallBack="true" runat="server" /> - <spring:ValidationError id="departureAirportErrors" runat="server" /> + <spring:ValidationError id="departureAirportErrors" runat="server" /> </td> <td class="formLabel" align="right"> <asp:Label ID="goingTo" runat="server" /></td> <td nowrap="nowrap"> <asp:DropDownList ID="goingToAirportCode" AutoCallBack="true" runat="server" /> - <spring:ValidationError id="destinationAirportErrors" runat="server" /> + <spring:ValidationError id="destinationAirportErrors" runat="server" /> </td> </tr> <tr> @@ -704,14 +721,14 @@ bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); <asp:Label ID="leavingOn" runat="server" /></td> <td nowrap="nowrap"> <spring:Calendar ID="leavingFromDate" runat="server" Width="75px" AllowEditing="true" Skin="system" /> - <spring:ValidationError id="departureDateErrors" runat="server" /> + <spring:ValidationError id="departureDateErrors" runat="server" /> </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" /> - <spring:ValidationError id="returnDateErrors" runat="server" /> + <spring:ValidationError id="returnDateErrors" runat="server" /> </div> </td> </tr> @@ -805,7 +822,7 @@ bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); Spring.Web.Validation.IValidationErrorsRenderer interface: - namespace Spring.Web.Validation + namespace Spring.Web.Validation { /// <summary> /// This interface should be implemented by all validation errors renderers. @@ -842,7 +859,7 @@ bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); templates for <spring:validationSummary> and <spring:validationError> controls: - <!-- Validation errors renderer configuration --> + <!-- Validation errors renderer configuration --> <object id="Spring.Web.UI.Controls.ValidationError" abstract="true"> <property name="Renderer"> <object type="Spring.Web.Validation.IconValidationErrorsRenderer, Spring.Web"> diff --git a/doc/reference/src/vsnet.xml b/doc/reference/src/vsnet.xml index 3d5597d6..08c15b41 100644 --- a/doc/reference/src/vsnet.xml +++ b/doc/reference/src/vsnet.xml @@ -1,8 +1,25 @@ - + + Visual Studio.NET Integration - + XML Editing and Validation @@ -15,15 +32,15 @@ validated against the Spring.NET XML Schema at runtime. The location of the XML configuration data to create an IApplicationContext can be any of the resource - locations supported by Spring's IResource + locations supported by Spring's IResource abstraction. (See for more - information.) To create an IApplicationContext + information.) To create an IApplicationContext using a "standalone" XML configuration file the custom configuration section in the standard .NET application configuration would read: - <spring> + <spring> <context> <resource uri="file://objects.xml"/> @@ -41,7 +58,7 @@ 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. - <?xml version="1.0" encoding="UTF-8"?> + <?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"> @@ -101,7 +118,7 @@ - <?xml version="1.0" encoding="UTF-8"?> + <?xml version="1.0" encoding="UTF-8"?> <objects xmlns="http://www.springframework.net"> <object id="..." type="..."> ... @@ -128,9 +145,9 @@ location to store the object definitions that will be managed by the object factory. - - + + <configuration> <configSections> @@ -183,7 +200,7 @@ - + Versions of XML Schema The schema was updated from Spring 1.0.1 to 1.0.2 in order to @@ -193,7 +210,7 @@ http://www.springframework.net/xsd/ - + Integrated API help Spring provides API documentation that can be integrated within diff --git a/doc/reference/src/wcf-quickstart.xml b/doc/reference/src/wcf-quickstart.xml index a40f030e..22ebe18a 100644 --- a/doc/reference/src/wcf-quickstart.xml +++ b/doc/reference/src/wcf-quickstart.xml @@ -1,5 +1,22 @@ - + + WCF QuickStart
@@ -26,7 +43,7 @@ The service contract is shown below - [ServiceContract(Namespace = "http://Spring.WcfQuickStart")] + [ServiceContract(Namespace = "http://Spring.WcfQuickStart")] public interface ICalculator { [OperationContract] @@ -45,7 +62,7 @@ that controls how long each method should sleep. An abbreviated listing of the implementation is shown below - public class CalculatorService : ICalculator + public class CalculatorService : ICalculator { private int sleepInSeconds; @@ -76,10 +93,10 @@ configuration of your service is done as you would typically do with Spring, including applying of any AOP advice. The class is hosted inside the console application through the use of Spring's - ServiceHostFactoryObject exporter. The + ServiceHostFactoryObject exporter. The configuration for the server console application is shown below. - <objects xmlns="http://www.springframework.net" + <objects xmlns="http://www.springframework.net" xmlns:aop="http://www.springframework.net/aop"> <!-- Service definition --> @@ -118,9 +135,9 @@ This approach uses Spring specific implementation of the WCF interfaces - System.ServiceModel.Dispatcher.IInstanceProvider + System.ServiceModel.Dispatcher.IInstanceProvider and - System.ServiceModel.Description.IServiceBehavior + System.ServiceModel.Description.IServiceBehavior are used to integrate Spring directly into the instancing of WCF services. For more information on this approach refer to this section of the reference @@ -132,7 +149,7 @@ Spring.ServiceModel.Activation.ServiceHostFactory. The .svc file is shown below. - <%@ ServiceHost Language="C#" Debug="true" Service="Spring.WcfQuickStart.CalculatorService" + <%@ ServiceHost Language="C#" Debug="true" Service="Spring.WcfQuickStart.CalculatorService" Factory="Spring.ServiceModel.Activation.ServiceHostFactory" %>
diff --git a/doc/reference/src/wcf.xml b/doc/reference/src/wcf.xml index 3dc02212..4e21949e 100644 --- a/doc/reference/src/wcf.xml +++ b/doc/reference/src/wcf.xml @@ -1,8 +1,25 @@ - + + Windows Communication Foundation (WCF) -
+
Introduction Spring's WCF support allows you to configure your WCF services via @@ -26,7 +43,7 @@ the WcfQuickStart application in the examples directory.
-
+
Configuring WCF services via Dependency Injection In this approach the container will creates an implementation of @@ -34,14 +51,14 @@ instance of your service type from the Spring container. This dynamic proxy is then the final service type that is hosted. -
+
Dependency Injection using dynamic proxies In this approach you develop your WCF services as you would normally do. For example here is a sample service type taken from the quickstart example. - [ServiceContract(Namespace = "http://Spring.WcfQuickStart")] + [ServiceContract(Namespace = "http://Spring.WcfQuickStart")] public interface ICalculator { [OperationContract] @@ -61,7 +78,7 @@ is the property we will configure via dependency injection. Here is a partial listing of the implementation - public class CalculatorService : ICalculator + public class CalculatorService : ICalculator { private int sleepInSeconds; @@ -87,7 +104,7 @@ configuration metadata as shown below as you would with any Spring managed object. - <object id="calculator" singleton="false" type="Spring.WcfQuickStart.CalculatorService, Spring.WcfQuickStart.ServerApp"> + <object id="calculator" singleton="false" type="Spring.WcfQuickStart.CalculatorService, Spring.WcfQuickStart.ServerApp"> <property name="SleepInSeconds" value="1"/> </object> @@ -99,30 +116,30 @@ To host this service type in a standalone application define an instance of a - Spring.ServiceModel.Activation.ServiceHostFactoryObject - and set is property TargetName to the id value of + Spring.ServiceModel.Activation.ServiceHostFactoryObject + and set is property TargetName to the id value of the previously defined service type. - ServiceHostFactoryObject is a Spring - IFactoryObject implementation. (See here for more - information on IFactoryObjects and their + ServiceHostFactoryObject is a Spring + IFactoryObject implementation. (See here for more + information on IFactoryObjects and their interaction with the container.) The - ServiceHostFactoryObject will create an instance + ServiceHostFactoryObject will create an instance of - Spring.ServiceModel.Activation.SpringServiceHost + Spring.ServiceModel.Activation.SpringServiceHost that will be the ServiceHost instance associated with your service type. This configuration for this step is shown below. - <object id="calculatorServiceHost" type="Spring.ServiceModel.Activation.ServiceHostFactoryObject, Spring.Services"> + <object id="calculatorServiceHost" type="Spring.ServiceModel.Activation.ServiceHostFactoryObject, Spring.Services"> <property name="TargetName" value="calculator" /> </object> Additional service configuration can be done declaratively in the standard App.config file as shown below - <system.serviceModel> + <system.serviceModel> <services> - <service name="calculator" behaviorConfiguration="DefaultBehavior"> + <service name="calculator" behaviorConfiguration="DefaultBehavior"> <host> ... </host> <endpoint> ... </endpoint> </service> @@ -137,21 +154,21 @@ definition - Spring.ServiceModel.Activation.SpringServiceHost - is where the dynamic proxy for your service type is + Spring.ServiceModel.Activation.SpringServiceHost + is where the dynamic proxy for your service type is generated. This dynamic proxy will implement a single 'WCF' interface, the same on that your service type implements. The implementation of the service interface methods on the proxy will delegate to a wrapped 'target' object which is the object instance retrieved by name from the Spring container using the Spring API, - ApplicationContext.GetObject(name). Since the + ApplicationContext.GetObject(name). Since the object retrieved in this manner is fully configured, your WCF service is as well. Outside of a standalone application you can also use the class - Spring.ServiceModel.Activation.ServiceHostFactory + Spring.ServiceModel.Activation.ServiceHostFactory (which inherits from - System.ServiceModel.Activation.ServiceHostFactory) + System.ServiceModel.Activation.ServiceHostFactory) to host your services so that they can be configured via dependency injection. To use the dynamic proxy approached described here you should still refer to the name of the service as the name of the object @@ -162,12 +179,12 @@ need to specify the service name as the name of the object definition in the Spring container and to ensure that singleton=false is used in the object definition. You can also use - Spring.ServiceModel.Activation.ServiceHostFactory + Spring.ServiceModel.Activation.ServiceHostFactory to host your service inside IIS but should still refer to the service by the name of the object in the Spring container.
-
+
Dependency Injection using WCF extensibility points. The second approach uses the extensibility points in WCF itself to @@ -176,38 +193,38 @@ url="http://orand.blogspot.com/2006/10/wcf-service-dependency-injection.html">blog and several other folks on the web since then. In this approach Spring specific implementations of the WCF interfaces - System.ServiceModel.Dispatcher.IInstanceProvider + System.ServiceModel.Dispatcher.IInstanceProvider and - System.ServiceModel.Description.IServiceBehavior + System.ServiceModel.Description.IServiceBehavior are used to integrate Spring directly into the instancing of WCF services. Spring's implementation of - IInstanceProvider is - Spring.ServiceModel.Support.SpringInstanceProvider. + IInstanceProvider is + Spring.ServiceModel.Support.SpringInstanceProvider. This implementation will look for an object by type in the Spring container and retrieve an instance configured using DI. If there is more than one object of the type registered with the container than an exception will be thrown. The - SpringInstanceProvider is used by a custom + SpringInstanceProvider is used by a custom service behavior class, - Spring.ServiceModel.Support.SpringServiceBehavior + Spring.ServiceModel.Support.SpringServiceBehavior where it is applied to all the service endpoints. This behavior is then added to the custom service host - Spring.ServiceModel.Activation.SpringServiceHost + Spring.ServiceModel.Activation.SpringServiceHost The service type is used to locate the object in the container. In your .svc file you specify the custom service host type and also the type of the service. Here is an example taken from the WcfQuickStart application that shows the use of this approach inside IIS. - <%@ ServiceHost Language="C#" Debug="true" Service="Spring.WcfQuickStart.CalculatorService" + <%@ ServiceHost Language="C#" Debug="true" Service="Spring.WcfQuickStart.CalculatorService" Factory="Spring.ServiceModel.Activation.ServiceHostFactory" %> The Spring configuration for the object is shown below. - <object id="calculator" type="Spring.WcfQuickStart.CalculatorService, App_Code" <object id="calculator" type="Spring.WcfQuickStart.CalculatorService, App_Code" singleton="false"> <property name="SleepInSeconds" value="1"/> </object> @@ -223,7 +240,7 @@ approach to be viable. The issue is that if the service is configured to be a singleton, for example using [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] - then the invocation of the IInstanceProvider is + then the invocation of the IInstanceProvider is short-circuited. See the notes on the MSDN class documentation here. One workaround, which is not very appealing, is to use the PerCall @@ -233,17 +250,17 @@
-
+
Apply AOP advice to WCF services In either approach to performing dependency injection you can apply additional AOP advice to your WCF services in the same way as you have always done in Spring. The following configuration shows how to apply some simple performance monitoring advice to all services in the - Spring.WcfQuickStart namespace and is taken from + Spring.WcfQuickStart namespace and is taken from the QuickStart example. - <object id="serviceOperation" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop"> + <object id="serviceOperation" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop"> <property name="pattern" value="Spring.WcfQuickStart.*"/> </object> @@ -268,7 +285,7 @@ snippit that takes uses Spring's support for calling factory methods on object instances. - <!-- returns ChannelFactory<ICalculator>("calculatorEndpoint").CreateChannel() --> + <!-- returns ChannelFactory<ICalculator>("calculatorEndpoint").CreateChannel() --> <object id="serverAppCalculator" type="Spring.WcfQuickStart.ICalculator, Spring.WcfQuickStart.ClientApp" factory-object="serverAppCalculatorChannelFactory" diff --git a/doc/reference/src/web-quickstart.xml b/doc/reference/src/web-quickstart.xml index 69ddd3c3..03fd49b4 100644 --- a/doc/reference/src/web-quickstart.xml +++ b/doc/reference/src/web-quickstart.xml @@ -1,5 +1,22 @@ - - + + + Web Quickstarts
diff --git a/doc/reference/src/web.xml b/doc/reference/src/web.xml index a861b91b..a123cc48 100644 --- a/doc/reference/src/web.xml +++ b/doc/reference/src/web.xml @@ -1,8 +1,25 @@ - + + Spring.NET Web Framework - + Introduction One of the objections many developers have to the ASP.NET @@ -46,8 +63,8 @@ Spring.Web also adds support for applying the dependency injection - principle to one's ASP.NET Pages and - Controls as well as http modules and custom + principle to one's ASP.NET Pages and + Controls as well as http modules and custom provider modules. This means that application developers can easily inject service dependencies into web controllers by leveraging the power of the Spring.NET IoC container. See Dependency Injection @@ -57,7 +74,7 @@ should not have to deal with ASP.NET UI controls directly. Such event handlers should rather work with the presentation model of the page, represented either as a hierarchy of domain objects or an ADO.NET - DataSet. It is for that reason that the Spring.NET + DataSet. It is for that reason that the Spring.NET team implemented bidirectional data binding framework to handle the mapping of values to and from the controls on a page to the underlying data model. The data binding framework also transparently takes care of @@ -70,9 +87,9 @@ concern that is addressed by Spring.NET Web Framework. Typical ASP.NET applications will use Response.Redirect or Server.Transfer calls within - Page logic to navigate to an appropriate page after + Page logic to navigate to an appropriate page after an action is executed. This typically leads to hard-coded target URLs in - the Page, which is never a good thing. Result + the Page, which is never a good thing. Result mapping solves this problem by allowing application developers to specify aliases for action results that map to target URLs based on information in an external configuration file that can easily be edited. Under @@ -84,7 +101,7 @@ Standard localization support is also limited in versions of ASP.NET prior to ASP.NET 2.0. Even though Visual Studio 2003 generates a local - resource file for each ASP.NET Page and user + resource file for each ASP.NET Page and user control, those resources are never used by the ASP.NET infrastructure. This means that application developers have to deal directly with resource managers whenever they need access to localized resources, which in the @@ -106,12 +123,12 @@ In order to implement some of the above mentioned features the Spring.NET team had to extend (as in the object-oriented sense) the - standard ASP.NET Page and - UserControl classes. This means that in order to + standard ASP.NET Page and + UserControl classes. This means that in order to take advantage of the full feature stack of Spring.Web (most notably bidirectional data binding, localization and result mapping), your code-behind classes will have to extend Spring.Web - specific base classes such as Spring.Web.UI.Page; + specific base classes such as Spring.Web.UI.Page; however, some very powerful features such as dependency injection for ASP.NET Pages, Controls, and providers can be leveraged without having to extend Spring.Web-specific base classes. It is worth stating that by @@ -130,26 +147,26 @@ linkend="springair" />). - + Automatic context loading and hierarchical contexts - + Configuration Unsurprisingly, Spring.Web builds on top of the Spring.NET IoC container, and makes heavy use (internally) of the easy pluggability and standardized configuration afforded by the IoC container. This also - means that all of the controllers (ASP.NET Pages) + means that all of the controllers (ASP.NET Pages) that make up a typical Spring.Web enabled application will be configured using the same standard Spring.NET XML configuration syntax. Spring.Web - uses a custom PageHandlerFactory implementation + uses a custom PageHandlerFactory implementation to load and configure a Spring.NET IoC container, which is in turn used - to locate an appropriate Page to handle a HTTP - request. The WebSupportModule configures + to locate an appropriate Page to handle a HTTP + request. The WebSupportModule configures miscellaneous Spring infrastructure classes for use in a web environment, for example setting the storage strategy of - LogicalThreadContext to be - HybridContextStorage. + LogicalThreadContext to be + HybridContextStorage. The instantiation and configuration of the Spring.NET IoC container by the Spring.Web infrastructure is wholly transparent to @@ -163,7 +180,7 @@ path properties can of course be changed from the values that are shown below): - <system.web> + <system.web> <httpHandlers> <add verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/> </httpHandlers> @@ -184,9 +201,9 @@ The above XML configuration snippet will direct the ASP.NET infrastructure to use Spring.NET's page factory, which will in turn create instances of the appropriate .aspx - Page, (possibly) inject dependencies into said - Page (as required), and then forward the handling - of the request to said Page. + Page, (possibly) inject dependencies into said + Page (as required), and then forward the handling + of the request to said Page. After the Spring.Web page factory is configured, you will also need to define a root application context by adding a Spring.NET @@ -194,7 +211,7 @@ The final configuration file should look a little like this (your exact configuration will no doubt vary in particulars)... - <?xml version="1.0" encoding="utf-8"?> + <?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> @@ -248,9 +265,9 @@ The custom configuration section handler is of the type - Spring.Context.Support.WebContextHandler + Spring.Context.Support.WebContextHandler which will in turn instantiate an IoC container of the type - Spring.Context.Support.WebApplicationContext. + Spring.Context.Support.WebApplicationContext. This will ensure that all of the features provided by Spring.Web are handled properly (such as request and session-scoped object definitions). @@ -265,8 +282,8 @@ be fully-qualified paths or URLs, or non-qualified, as in the example above. Non-qualified resources will be loaded using the default resource type for the context, which for the - WebApplicationContext is the - WebResource type. + WebApplicationContext is the + WebResource type. @@ -286,7 +303,7 @@ The configuration for IIS7 is shown below - <system.webServer> + <system.webServer> <validation validateIntegratedModeConfiguration="false"/> <modules> <add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/> @@ -299,7 +316,7 @@ - + Context Hierarchy ASP.NET provides a hierarchical configuration mechanism by @@ -338,7 +355,7 @@ component Web.config similar to the following one: - <?xml version="1.0" encoding="utf-8"?> + <?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> @@ -388,23 +405,23 @@ - + Dependency Injection for ASP.NET Pages Spring.Web builds on top of the feature set and capabilities of ASP.NET; one example of this can be seen the way that Spring.Web has used - the code-behind class of the Page mechanism to + the code-behind class of the Page mechanism to satisfy the Controller portion of the MVC architectural pattern. In MVC-based (web) applications, the Controller is typically a thin wrapper around one or more service objects. In the specific case of Spring.Web, the Spring.NET team realized that it was very important that service object dependencies - be easily injected into Page + be easily injected into Page Controllers. Accordingly, Spring.Web provides first class support for dependency injection in ASP.NET - Pages. This allows application developers to inject + Pages. This allows application developers to inject any required service object dependencies (and indeed any other - dependencies) into their Pages using standard + dependencies) into their Pages using standard Spring.NET configuration instead of having to rely on custom service locators or manual object lookups in a Spring.NET application context. @@ -414,7 +431,7 @@ application context, said developer can easily create object definitions for the pages that compose that web application: - <objects xmlns="http://www.springframework.net"> + <objects xmlns="http://www.springframework.net"> <object name="basePage" abstract="true"> <property name="MasterPageFile" value="~/Web/StandardTemplate.master"/> @@ -460,7 +477,7 @@ to the type attribute. As can be seen in the above configuration snippet the type name is actually the path to the .aspx file for the - Page, relative to the directory context it is + Page, relative to the directory context it is defined in. In the case of the above example, those definitions are in the root context so Login.aspx and Default.aspx also must be in the root of the web @@ -476,7 +493,7 @@ Spring.NET, where the id or name attributes are typically mandatory (although not always, as in the case of inner object definitions). This is actually intentional, because in the - case of Spring.Web Page + case of Spring.Web Page Controller instances one typically wants to use the name of the .aspx file name as the identifier. If an id is not specified, the Spring.Web infrastructure will @@ -493,7 +510,7 @@ attribute should be used instead of the id attribute on the abstract object definition. - + Injecting Dependencies into Controls Spring.Web also allows application developers to inject @@ -504,7 +521,7 @@ This is similar to injecting into .aspx pages shown above. - <object type="~/controls/MyControl.ascx" abstract="true"> + <object type="~/controls/MyControl.ascx" abstract="true"> <!-- inject dependencies here... --> </object> @@ -519,13 +536,13 @@ You can perform dependency injection on custom HTTP modules through the use of the class - Spring.Context.Support.HttpApplicationConfigurer. + Spring.Context.Support.HttpApplicationConfigurer. You register your custom HTTP module as you would normally, for example - a module of the type HtmlCommentAppenderModule, + a module of the type HtmlCommentAppenderModule, taken from the Web Quickstart, appends additional comments into the http response. It is registered as shown below - <httpModules> + <httpModules> <add name="HtmlCommentAppender" type="HtmlCommentAppenderModule"/> </httpModules> @@ -538,7 +555,7 @@ object with Spring. An example is shown below. HttpApplicationConfigurer' ModuleTemplates property. - <object name="HttpApplicationConfigurer" type="Spring.Context.Support.HttpApplicationConfigurer, Spring.Web"> + <object name="HttpApplicationConfigurer" type="Spring.Context.Support.HttpApplicationConfigurer, Spring.Web"> <property name="ModuleTemplates"> <dictionary> <entry key="HtmlCommentAppender"> <!-- this name must match the module name --> @@ -584,7 +601,7 @@ Here is an example of how to register the adapter for membership providers. - <membership defaultProvider="mySqlMembershipProvider"> + <membership defaultProvider="mySqlMembershipProvider"> <providers> <clear/> <add connectionStringName="" name="mySqlMembershipProvider" type="Spring.Web.Providers.MembershipProviderAdapter, Spring.Web"/> @@ -623,7 +640,7 @@ Here is an example configuration taken from the Web Quickstart that simply sets the description property and connection string. - <object id="mySqlMembershipProvider" type="Spring.Web.Providers.ConfigurableSqlMembershipProvider"> + <object id="mySqlMembershipProvider" type="Spring.Web.Providers.ConfigurableSqlMembershipProvider"> <property name="connectionStringName" value="MyLocalSQLServer" /> <property name="parameters"> <name-values> @@ -636,7 +653,7 @@ configuration specific to your implementation. - + Customizing control dependency injection There might be situations where it is necessary to customize @@ -647,7 +664,7 @@ implementing the interface ISupportsWebDependencyInjection as shown below: - [C#] + [C#] class MyControl : Control, ISupportsWebDependencyInjection { private IApplicationContext _defaultApplicationContext; @@ -671,7 +688,7 @@ class MyControl : Control, ISupportsWebDependencyInjection way to turn of dependency injection for parts of your page. Example use is shown below - <spring:Panel runat="server" + <spring:Panel runat="server" suppressDependencyInjection="true" renderContainerTag="false"> @@ -688,12 +705,12 @@ class MyControl : Control, ISupportsWebDependencyInjection - + Object Scope Spring.NET web applications support an additional attribute within object definition elements that allows you to control the scope of an - object: <object id="myObject" type="MyType, MyAssembly" scope="application | session | request"/>As + object: <object id="myObject" type="MyType, MyAssembly" scope="application | session | request"/>As you can see, there are three possible values for the scope attribute -- application, session or request. Application scope is the default, and will be used for all objects that don't have scope attribute defined. As @@ -725,7 +742,7 @@ class MyControl : Control, ISupportsWebDependencyInjection objects. - + Master Pages in ASP.NET 1.1 Support for ASP.NET 1.1 master pages in Spring.Web is very similar @@ -736,7 +753,7 @@ class MyControl : Control, ISupportsWebDependencyInjection pages can then reference and populate. A sample master page (MasterLayout.ascx) could look like this: - <%@ Control language="c#" Codebehind="MasterLayout.ascx.cs" AutoEventWireup="false" Inherits="MyApp.Web.UI.MasterLyout" %> + <%@ Control language="c#" Codebehind="MasterLayout.ascx.cs" AutoEventWireup="false" Inherits="MyApp.Web.UI.MasterLyout" %> <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <html> @@ -781,7 +798,7 @@ class MyControl : Control, ISupportsWebDependencyInjection A page (Child.aspx) that uses this master page might look like this: - <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> + <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> <%@ Page language="c#" Codebehind="Child.aspx.cs" AutoEventWireup="false" Inherits="ArtFair.Web.UI.Forms.Child" %> <html> <body> @@ -804,8 +821,8 @@ class MyControl : Control, ISupportsWebDependencyInjection elements for the head and title place holders, they will be displayed using the default content supplied in the master page. - Both the ContentPlaceHolder and - Content controls can contain any valid ASP.NET + Both the ContentPlaceHolder and + Content controls can contain any valid ASP.NET markup: HTML, standard ASP.NET controls, user controls, etc. @@ -820,17 +837,17 @@ class MyControl : Control, ISupportsWebDependencyInjection ignored when the page is rendered. - + Linking child pages to their master - The Spring.Web.UI.Page class exposes a + The Spring.Web.UI.Page class exposes a property called MasterPageFile, which can be used to specify the master page. The recommended way to do this is by leveraging the Spring.NET IoC container and creating definitions similar to the following: - <?xml version="1.0" encoding="utf-8" ?> + <?xml version="1.0" encoding="utf-8" ?> <objects xmlns="http://www.springframework.net"> <object name="basePage" abstract="true"> @@ -852,7 +869,7 @@ class MyControl : Control, ISupportsWebDependencyInjection - + Bidirectional Data Binding and Model Management A problem with the existing data binding support in ASP.NET is that @@ -878,8 +895,8 @@ class MyControl : Control, ISupportsWebDependencyInjection data binding and model management support provided by Spring.Web, you will have to couple your presentation layer to Spring.Web; this is because features requires you to - extend a Spring.Web.UI.Page instead of the usual - System.Web.UI.Page class. + extend a Spring.Web.UI.Page instead of the usual + System.Web.UI.Page class. Spring.Web data binding is very easy to use. Application developers simply need to override the protected @@ -888,7 +905,7 @@ class MyControl : Control, ISupportsWebDependencyInjection management methods: InitializeModel, LoadModel and SaveModel. This is perhaps best illustrated by an example from the SpringAir reference - application. First, let's take a look at the page markup:<%@ Page Language="c#" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %> + application. First, let's take a look at the page markup:<%@ Page Language="c#" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %> <asp:Content ID="body" ContentPlaceHolderID="body" runat="server"> <div style="text-align: center"> @@ -950,7 +967,7 @@ class MyControl : Control, ISupportsWebDependencyInjection returnDate. Next, let's take a look at the model we will be binding this form - to:namespace SpringAir.Domain + to:namespace SpringAir.Domain { [Serializable] public class Trip @@ -1032,16 +1049,16 @@ class MyControl : Control, ISupportsWebDependencyInjection OneWay, RoundTrip } -}As you can see, Trip class uses the - TripPoint class to represent departure and return, +}As you can see, Trip class uses the + TripPoint class to represent departure and return, which are exposed as StartingFrom and ReturningFrom properties. It also uses - TripMode enumeration to specify whether the trip is + TripMode enumeration to specify whether the trip is one way or return trip, which is exposed as Mode property. Finally, let's see the code-behind class that ties everything - together:public class TripForm : Spring.Web.UI.Page + together:public class TripForm : Spring.Web.UI.Page { // model private Trip trip; @@ -1117,7 +1134,7 @@ class MyControl : Control, ISupportsWebDependencyInjection As such, SaveModel simply returns the trip object and LoadModel casts the savedModel argument to - Trip and assigns it to the + Trip and assigns it to the trip field within the page. In the more complex scenarios, you will typically return a dictionary containing your model objects from the SaveModel method, and read @@ -1176,7 +1193,7 @@ class MyControl : Control, ISupportsWebDependencyInjection some additional features that make data binding framework usable in real-world applications. - + Data Binding Under the Hood Spring.NET Data Binding framework revolves around two main @@ -1185,7 +1202,7 @@ class MyControl : Control, ISupportsWebDependencyInjection interface is definitely the more important one of the two, as it has to be implemented by all binding types. This interface defines several methods, with some of them being overloaded for - convenience:public interface IBinding + convenience:public interface IBinding { void BindSourceToTarget(object source, object target, ValidationErrors validationErrors); @@ -1231,7 +1248,7 @@ class MyControl : Control, ISupportsWebDependencyInjection The IBindingContainer interface extends the IBinding interface and adds the following - members:public interface IBindingContainer : IBinding + members:public interface IBindingContainer : IBinding { bool HasBindings { get; } @@ -1249,13 +1266,13 @@ class MyControl : Control, ISupportsWebDependencyInjection commonly used binding type, SimpleExpressionBinding. The SimpleExpressionBinding is what we used in the example at the beginning of this section to bind our web form to a - Trip instance. It uses Spring.NET Expression + Trip instance. It uses Spring.NET Expression Language to extract and to set values within source and target objects. We discussed sourceExpression and targetExpression arguments earlier, so let's focus on the remaining ones. - + Binding Direction The direction argument determines whether the binding is @@ -1273,9 +1290,9 @@ class MyControl : Control, ISupportsWebDependencyInjection form doesn't have a simple one-to-one mapping to presentation model. In our earlier trip form example, the presentation model was intentionally designed to allow for simple one-to-one mappings. For - the sake of discussion, let's add the Airport - class and modify our TripPoint class like - this:namespace SpringAir.Domain + the sake of discussion, let's add the Airport + class and modify our TripPoint class like + this:namespace SpringAir.Domain { [Serializable] public class TripPoint @@ -1329,18 +1346,18 @@ class MyControl : Control, ISupportsWebDependencyInjection } } }Instead of the string property - AirportCode, our TripPoint + AirportCode, our TripPoint class now exposes an Airport property of type - Airport, which is defined above. Now we have a + Airport, which is defined above. Now we have a problem: what used to be a simple string to string binding, with the airport code selected in a dropdown being copied directly into the TripPoint.AirportCode property and vice versa, now becomes a not so - simple string to Airport binding, so let's see + simple string to Airport binding, so let's see how we can solve this mismatch problem. First of all, binding from the model to the control is still very straight forward. We just need to set up one-way bindings from - the model to controls:protected override void InitializeDataBindings() + the model to controls:protected override void InitializeDataBindings() { BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.Airport.Code", BindingDirection.TargetToSource); BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.Airport.Code", BindingDirection.TargetToSource); @@ -1350,9 +1367,9 @@ class MyControl : Control, ISupportsWebDependencyInjection Trip.StartingFrom.AirportCode. Unfortunately, binding from the control to the model the same way won't work: we might be able to set Code property of the - Airport object, but that will likely make the + Airport object, but that will likely make the Airport.Name property invalid. What we really want - do is find an instance of the Airport class + do is find an instance of the Airport class based on the airport code and set the TripPoint.Airport property to it. Fortunately, this is very simple to do with Spring.NET data binding, especially because @@ -1362,7 +1379,7 @@ class MyControl : Control, ISupportsWebDependencyInjection bindings from source to target that will invoke this finder method when evaluating the source expression. Our complete set of bindings for these two drop down lists will then look like - this:protected override void InitializeDataBindings() + this:protected override void InitializeDataBindings() { BindingManager.AddBinding("@(airportDao).GetAirport(leavingFromAirportCode.SelectedValue)", "Trip.StartingFrom.Airport", BindingDirection.SourceToTarget); BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.Airport.Code", BindingDirection.TargetToSource); @@ -1376,7 +1393,7 @@ class MyControl : Control, ISupportsWebDependencyInjection this non-trivial data binding problem. - + Formatters The last argument to AddBinding method that @@ -1390,7 +1407,7 @@ class MyControl : Control, ISupportsWebDependencyInjection Spring.Globalization.Formatters namespace, but if you have requirements that cannot be satisfied by one of the standard formatters it is easy enough to write your own -- all you need to do - is implement a very simple IFormatter interface:public interface IFormatter + is implement a very simple IFormatter interface:public interface IFormatter { string Format(object value); object Parse(string value); @@ -1406,7 +1423,7 @@ class MyControl : Control, ISupportsWebDependencyInjection most usage scenarios. - + Type Conversion Because the data binding framework uses the same expression @@ -1419,10 +1436,10 @@ class MyControl : Control, ISupportsWebDependencyInjection mechanisms. - + Data Binding Events - Spring.Web's base Page class adds two + Spring.Web's base Page class adds two events to the standard .NET page lifecycle - DataBound and DataUnbound. @@ -1447,7 +1464,7 @@ class MyControl : Control, ISupportsWebDependencyInjection controls) are updated prior to the actual rendering. - + Rendering Binding Errors If there are errors in the databinding, for example, trying to @@ -1456,7 +1473,7 @@ class MyControl : Control, ISupportsWebDependencyInjection example of this shown below taken from the WebQuickStart 'RobustEmployeeInfo' example. - [Default.aspx.cs] + [Default.aspx.cs] protected override void InitializeDataBindings() { @@ -1479,7 +1496,7 @@ protected override void InitializeDataBindings() See - + HttpRequestListBindingContainer HttpRequestListBindingContainer extracts posted raw values from @@ -1490,7 +1507,7 @@ protected override void InitializeDataBindings() Please checkout the WebQuickStart sample's demo of HttpRequestListBindingContainer. Below - protected override void InitializeDataBindings() + protected override void InitializeDataBindings() { // HttpRequestListBindingContainer unbinds specified values from Request -> Productlist HttpRequestListBindingContainer requestBindings = @@ -1509,7 +1526,7 @@ protected override void InitializeDataBindings() - + Using DataBindingPanel To simplify use of Spring's Data Binding feature on web pages and @@ -1518,7 +1535,7 @@ protected override void InitializeDataBindings() allows for specifying additional, data binding related attributes to its child controls: - <%@ Page Language="C#" CodeFile="Default.aspx.cs" Inherits="DataBinding_EasyEmployeeInfo_Default" %> + <%@ Page Language="C#" CodeFile="Default.aspx.cs" Inherits="DataBinding_EasyEmployeeInfo_Default" %> <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> <html> <body> @@ -1593,7 +1610,7 @@ protected override void InitializeDataBindings() - + Localization and Message Sources While recognizing that the .NET framework has excellent support for @@ -1624,7 +1641,7 @@ protected override void InitializeDataBindings() Practices for ASP.NET 2.0 by Michele Leroux Bustamante. - + Automatic Localization Using Localizers ("Push" Localization) @@ -1635,7 +1652,7 @@ protected override void InitializeDataBindings() an application developer could define a page such as UserRegistration.aspx... - <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> + <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> <%@ Page language="c#" Codebehind="UserRegistration.aspx.cs" AutoEventWireup="false" Inherits="ArtFair.Web.UI.Forms.UserRegistration" %> <html> @@ -1676,8 +1693,8 @@ protected override void InitializeDataBindings() </html> A close inspection of the above .aspx code - reveals that none of the Label or - Button controls have had a value assigned to the + reveals that none of the Label or + Button controls have had a value assigned to the Text property. The values of the Text property for these controls are stored in the local resource file (of the page) using the following convention to @@ -1688,7 +1705,7 @@ protected override void InitializeDataBindings() The corresponding local resource file, UserRegistration.aspx.resx, is shown below. - <root> + <root> <data name="$this.emailLabel.Text"> <value>Email:</value> </data> @@ -1742,7 +1759,7 @@ protected override void InitializeDataBindings() Finally a localizer must be configured for the page to enable automatic localization: - <object id="localizer" type="Spring.Globalization.Localizers.ResourceSetLocalizer, Spring.Core"/> + <object id="localizer" type="Spring.Globalization.Localizers.ResourceSetLocalizer, Spring.Core"/> <object type="UserRegistration.aspx"> <property name="Localizer" ref="localizer"/> @@ -1752,13 +1769,13 @@ protected override void InitializeDataBindings() linkend="web-localizers" /> - + Global Message Sources The last two resource definitions from the previous section require some additional explanation: - <data name="$this.saveButton.Text"> + <data name="$this.saveButton.Text"> <value>$messageSource.save</value> </data> <data name="$this.cancelButton.Text"> @@ -1792,7 +1809,7 @@ protected override void InitializeDataBindings() 'messageSource', which one can add to one's Spring.NET configuration file as shown below. - <object id="messageSource" type="Spring.Context.Support.ResourceSetMessageSource, Spring.Core"> + <object id="messageSource" type="Spring.Context.Support.ResourceSetMessageSource, Spring.Core"> <property name="ResourceManagers"> <list> <value>MyApp.Web.Resources.Strings, MyApp.Web</value> @@ -1812,12 +1829,12 @@ protected override void InitializeDataBindings() The global resources are cached within the Spring.NET - IApplicationContext and are accessible through + IApplicationContext and are accessible through the Spring.NET IMessageSource interface. The Spring.Web Page and UserControl classes have a reference to their owning - IApplicationContext and it's associated + IApplicationContext and it's associated IMessageSource. As such, they will automatically redirect resource lookups to a global message source if a local resource cannot be found. @@ -1826,7 +1843,7 @@ protected override void InitializeDataBindings() only message source implementation that ships with Spring.NET. - + Working with Localizers In order to apply resources automatically, a localizer needs to be @@ -1839,15 +1856,15 @@ protected override void InitializeDataBindings() rendered. A localizer is simply an object that implements the - Spring.Globalization.ILocalizer interface. - Spring.Globalization.AbstractLocalizer is + Spring.Globalization.ILocalizer interface. + Spring.Globalization.AbstractLocalizer is provided as a convenient base class for localization: this class has one abstract method, LoadResources. This method must load and return a list of all the resources that must be automatically applied from the resource store. Spring.NET ships with one concrete implementation of a localizer, - Spring.Globalization.Localizers.ResourceSetLocalizer, + Spring.Globalization.Localizers.ResourceSetLocalizer, that retrieves a list of resources to apply from the local resource file. Future releases of Spring.NET may provide other localizers that read resources from an XML file or even a flat text file that contains @@ -1855,14 +1872,14 @@ protected override void InitializeDataBindings() store resources within the files in a web application instead of as embedded resources in an assembly. Of course, if an application developer would rather store such resources in a database, he or she can - write their own ILocalizer implementation that + write their own ILocalizer implementation that will load a list of resources to apply from a database. As mentioned previously, one would typically configure the localizer to be used within an abstract base definition for those pages that require localization as shown below. - <object id="localizer" type="Spring.Globalization.Localizers.ResourceSetLocalizer, Spring.Core"/> + <object id="localizer" type="Spring.Globalization.Localizers.ResourceSetLocalizer, Spring.Core"/> <object name="basePage" abstract="true"> <description> @@ -1879,13 +1896,13 @@ protected override void InitializeDataBindings() automatically one can completely omit the localizer definition. One last thing to note is that Spring.NET - UserControl instances will (by default) inherit + UserControl instances will (by default) inherit the localizer and other localization settings from the page that they are contained within, but one can similarly also override that behavior using explicit dependency injection. - + Applying Resources Manually ("Pull" Localization) While automatic localization as described above works great for @@ -1901,7 +1918,7 @@ protected override void InitializeDataBindings() localization, which boils down to a simple GetMessage call as shown below. - <asp:Repeater id="outboundFlightList" Runat="server"> + <asp:Repeater id="outboundFlightList" Runat="server"> <HeaderTemplate> <table border="0" width="90%" cellpadding="0" cellspacing="0" align="center" class="suggestedTable"> <thead> @@ -1923,13 +1940,13 @@ protected override void InitializeDataBindings() </HeaderTemplate> The GetMessage method is available within both - the Spring.Web.UI.Page and - Spring.Web.UI.UserControl classes, and it will + the Spring.Web.UI.Page and + Spring.Web.UI.UserControl classes, and it will automatically fall back to a global message source lookup if a local resource is not found. - + Localizing Images within a Web Application Spring.Web provides an easy (and consistent) way to localize @@ -1967,7 +1984,7 @@ protected override void InitializeDataBindings() order to place a localized image on a page, one needs to use the <spring:LocalizedImage> as shown below. - <%@ Page language="c#" Codebehind="StandardTemplate.aspx.cs" + <%@ Page language="c#" Codebehind="StandardTemplate.aspx.cs" AutoEventWireup="false" Inherits="SpringAir.Web.StandardTemplate" %> <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > @@ -1988,54 +2005,54 @@ protected override void InitializeDataBindings() serves as an invariant culture folder). - + User Culture Management In addition to global and local resource management, Spring.Web also adds support for user culture management by exposing the current - CultureInfo through the + CultureInfo through the UserCulture property on the - Page and UserControl + Page and UserControl classes. The UserCulture property will simply delegate culture resolution to an implementation of - Spring.Globalization.ICultureResolver interface. + Spring.Globalization.ICultureResolver interface. One can specify exactly which culture resolver to use by configuring the CultureResolver property of the - Page class in the relevant object definition as + Page class in the relevant object definition as shown below. - <object name="BasePage" abstract="true"> + <object name="BasePage" abstract="true"> <property name="CultureResolver"> <object type="Spring.Globalization.Resolvers.CookieCultureResolver, Spring.Web"/> </property> </object> Several useful implementations of - ICultureResolver ship as part of Spring.Web, so + ICultureResolver ship as part of Spring.Web, so it is unlikely that application developers will have to implement their own culture resolver. However, if one does have such a requirement, the resulting implementation should be fairly straightforward as there are only two methods that one need implement. The following sections discuss each available implementation of the - ICultureResolver interface. + ICultureResolver interface. DefaultWebCultureResolver This is default culture resolver implementation. It will be used if one does not specify a culture resolver for a page, or if one - explicitly injects a DefaultWebCultureResolver + explicitly injects a DefaultWebCultureResolver into a page definition explicitly. The latter case (explicit injection) is sometimes useful because it allows one to specify a culture that should always be used by providing a value to the DefaultCulture property on the resolver. - The DefaultWebCultureResolver will first + The DefaultWebCultureResolver will first look at the DefaultCulture property and return its value if said property value is not null. If it is null, the - DefaultWebCultureResolver will fall back to + DefaultWebCultureResolver will fall back to request header inspection, and finally, if no 'Accept-Lang' request headers are present it will return the UI culture of the currently executing thread. @@ -2045,7 +2062,7 @@ protected override void InitializeDataBindings() RequestCultureResolver This resolver works in a similar way to the - DefaultWebCultureResolver with the exception + DefaultWebCultureResolver with the exception that it always checks request headers first, and only then falls back to the value of the DefaultCulture property or the culture code of the @@ -2058,7 +2075,7 @@ protected override void InitializeDataBindings() This resolver will look for culture information in the user's session and return it if it finds one. If not, it will fall back to the behavior of the - DefaultWebCultureResolver. + DefaultWebCultureResolver. @@ -2066,16 +2083,16 @@ protected override void InitializeDataBindings() This resolver will look for culture information in a cookie, and return it if it finds one. If not, it will fall back to the behavior - of the DefaultWebCultureResolver. + of the DefaultWebCultureResolver. - CookieCultureResolver will not work if + CookieCultureResolver will not work if your application uses localhost as the server URL, which is a typical setting in a development environment. In order to work around this limitation you should use - SessionCultureResolver during development and - switch to CookieCultureResolver before you + SessionCultureResolver during development and + switch to CookieCultureResolver before you deploy the application in a production. This is easily accomplished in Spring.Web (simply change the config file) but is something that you should be aware of. @@ -2083,26 +2100,26 @@ protected override void InitializeDataBindings() - + Changing Cultures In order to be able to change the culture application developers will need to use one of the culture resolvers that support culture - changes, such as SessionCultureResolver or - CookieCultureResolver. One could also write a - custom ICultureResolver that will persist culture + changes, such as SessionCultureResolver or + CookieCultureResolver. One could also write a + custom ICultureResolver that will persist culture information in a database, as part of a user's profile. Once that requirement is satisfied, all that one need do is to set the UserCulture property to a new - CultureInfo object before the page is rendered. + CultureInfo object before the page is rendered. In the following .aspx example, there are two link buttons that can be used to change the user's culture. In the code-behind, this is all one need do to set the new culture. A code snippet for the code-behind file (UserRegistration.aspx.cs) is shown below. - protected override void OnInit(EventArgs e) + protected override void OnInit(EventArgs e) { InitializeComponent(); @@ -2119,7 +2136,7 @@ private void SetLanguage(object sender, CommandEventArgs e) - + Result Mapping One of the problems evident in many ASP.NET applications is that @@ -2142,10 +2159,10 @@ private void SetLanguage(object sender, CommandEventArgs e) flow. In Spring.Web, a logical result is encapsulated and defined by the - Result class; because of this one can configure + Result class; because of this one can configure results just like any other object: - + <objects xmlns="http://www.springframework.net"> <object id="homePageResult" type="Spring.Web.Support.Result, Spring.Web"> @@ -2189,13 +2206,13 @@ private void SetLanguage(object sender, CommandEventArgs e) If one's target page requires parameters, one can define them using the Parameters dictionary property. One simply - specifies either literal values or object + specifies either literal values or object navigation expressions for such parameter values; if one specifies an expression, this expression will be evaluated in the context of the page in which the result is being referenced... in the specific case of the above example, this means that any page that uses the homePageResult needs to expose a - UserInfo property on the page class itself. + UserInfo property on the page class itself. In Spring 1.1.0 and before the prefix used to indicate an object navigation expression in the Parameters dictionary property was the dollar sign, i.e. @@ -2225,12 +2242,12 @@ private void SetLanguage(object sender, CommandEventArgs e) The above example shows independent result object definitions, which are useful for global results such as a home- and login- page. - Result definitions that are only going to be used + Result definitions that are only going to be used by one page should be simply embedded within the definition of a page, either as inner object definitions or using a special shortcut notation for defining a result definition: - + <object type="~/UI/Forms/UserRegistration.aspx" parent="basePage"> <property name="UserManager"> <ref object="userManager"/> @@ -2278,7 +2295,7 @@ private void SetLanguage(object sender, CommandEventArgs e) within the event handlers of one's pages (UserRegistration.apsx.cs)... - private void SaveUser(object sender, EventArgs e) + private void SaveUser(object sender, EventArgs e) { UserManager.SaveUser(UserInfo); SetResult("userSaved"); @@ -2305,7 +2322,7 @@ protected override void OnInit(EventArgs e) pages. - + Client-Side Scripting ASP.NET has decent support for client-side scripting through the use @@ -2317,11 +2334,11 @@ protected override void OnInit(EventArgs e) of a page, which is (in many cases) exactly what you would like to do. - + Registering Scripts within the head HTML section Spring.Web adds several methods to enhance client-side scripting - to the base Spring.Web.UI.Page class: + to the base Spring.Web.UI.Page class: RegisterHeadScriptBlock and RegisterHeadScriptFile, each with a few overrides. You can call these methods from your custom pages and controls in order @@ -2334,7 +2351,7 @@ protected override void OnInit(EventArgs e) of using the standard HTML <head> element. This is shown below. - <%@ Page language="c#" Codebehind="StandardTemplate.aspx.cs" + <%@ Page language="c#" Codebehind="StandardTemplate.aspx.cs" AutoEventWireup="false" Inherits="SpringAir.Web.StandardTemplate" %> <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > @@ -2363,7 +2380,7 @@ protected override void OnInit(EventArgs e) Register* scripts to work properly. - + Adding CSS Definitions to the head Section In a similar fashion, you can add references to CSS files, or even @@ -2378,7 +2395,7 @@ protected override void OnInit(EventArgs e) document. - + Well-Known Directories In order to make the manual inclusion of client-side scripts, CSS @@ -2400,7 +2417,7 @@ protected override void OnInit(EventArgs e) are normally shared by all the pages in the application). An example of such configuration is shown below: - <object name="basePage" abstract="true"> + <object name="basePage" abstract="true"> <description> Convenience base page definition for all the pages. @@ -2429,12 +2446,12 @@ protected override void OnInit(EventArgs e) The location in the web page where validation errors are to be rendered can be specifies by using the - ValidationSummary and - ValidationError controls. There are two controls + ValidationSummary and + ValidationError controls. There are two controls since they have different defaults for how errors are rendered. - ValidationSummary is used to display potentially + ValidationSummary is used to display potentially multiple errors identified by the validation framework. - ValidationError is used to display field-level + ValidationError is used to display field-level validation errors. Please refer to the section ASP.NET usage tips in the chapter on the Validation Framework @@ -2446,8 +2463,8 @@ protected override void OnInit(EventArgs e) Some standard controls are not easy to use with Spring's databinding support. Examples are check boxes and ratio button groups. - In this case you should use the CheckBoxList and - RadioButtonGroup controls. Databinding itself can + In this case you should use the CheckBoxList and + RadioButtonGroup controls. Databinding itself can be done using the DataBindingPanel instead of the using the BindingManager API within the code behind page. @@ -2467,7 +2484,7 @@ protected override void OnInit(EventArgs e) You can suppress dependency injection for controls inside your ASP.NET by using the Panel control. See the section Customizing control dependency + linkend="web-controlling-di">Customizing control dependency injection for more information. diff --git a/doc/reference/src/webservices.xml b/doc/reference/src/webservices.xml index 6d4255cc..bb960164 100644 --- a/doc/reference/src/webservices.xml +++ b/doc/reference/src/webservices.xml @@ -1,5 +1,22 @@ - - + + + Web Services @@ -24,7 +41,7 @@ an inheritance hierarchy across the application. - + Server-side One thing that the Spring.NET team didn't like much is that we had @@ -60,12 +77,12 @@ Spring.NET allows application developers to expose existing web services easily by registering a custom implementation of the - WebServiceHandlerFactory class and by creating a + WebServiceHandlerFactory class and by creating a standard Spring.NET object definition for the service. By way of an example, consider the following web service... - + namespace MyComany.MyApp.Services { [WebService(Namespace="http://myCompany/services")] @@ -81,8 +98,8 @@ namespace MyComany.MyApp.Services This is just a standard class that has methods decorated with the - WebMethod attribute and (at the class-level) the - WebService attribute. Application developers can + WebMethod attribute and (at the class-level) the + WebService attribute. Application developers can create this web service within Visual Studio just like any other class. @@ -90,11 +107,11 @@ namespace MyComany.MyApp.Services is: 1. Register the - Spring.Web.Services.WebServiceFactoryHandler as + Spring.Web.Services.WebServiceFactoryHandler as the HTTP handler for *.asmx requests within one's web.config file. - + <system.web> <httpHandlers> <add verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/> @@ -110,7 +127,7 @@ namespace MyComany.MyApp.Services If you are using IIS7 the following configuration is needed - <system.webServer> + <system.webServer> <validation validateIntegratedModeConfiguration="false"/> <handlers> <add name="SpringWebServiceSupport" verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/> @@ -120,7 +137,7 @@ namespace MyComany.MyApp.Services 2. Create an object definition for one's web service. - <object name="HelloWorld" type="MyComany.MyApp.Services.HelloWorldService, MyAssembly" abstract="true"/> + <object name="HelloWorld" type="MyComany.MyApp.Services.HelloWorldService, MyAssembly" abstract="true"/> Note that one is not absolutely required to make the web service object definition abstract (via the @@ -128,7 +145,7 @@ namespace MyComany.MyApp.Services 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 System.Type of the service class, + provide is the System.Type of the service class, which can be retrieved from the object definition even if it is marked as abstract. @@ -157,7 +174,7 @@ namespace MyComany.MyApp.Services within one's web service class and have Spring.NET inject the message value into it: - + namespace MyApp.Services { public interface IHelloWorld @@ -196,12 +213,12 @@ namespace MyApp.Services service instance that will process requests. This proxying requires that one export the web service explicitly - using the Spring.Web.Services.WebServiceExporter + using the Spring.Web.Services.WebServiceExporter class; in the specific case of this example, one must also not forget to configure the Message property for said service: - + <object id="HelloWorld" type="MyApp.Services.HelloWorldService, MyApp"> <property name="Message" value="Hello, World!"/> </object> @@ -211,11 +228,11 @@ namespace MyApp.Services </object> - The WebServiceExporter copies the existing + The WebServiceExporter 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 - WebServiceExporter. + WebServiceExporter. Interface Requirements @@ -227,7 +244,7 @@ namespace MyApp.Services (service) interface. Only methods that belong to an interface will be exported by the - WebServiceExporter. + WebServiceExporter. @@ -236,23 +253,23 @@ namespace MyApp.Services 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 WebMethod. Because + services need to have, such as WebMethod. 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. This means that we can safely remove the - WebService and WebMethod + WebService and WebMethod 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 - WebMethod attributes to all methods of the + WebMethod attributes to all methods of the exported interfaces. However, that is still not the ideal solution. You would lose - information that the optional WebService and - WebMethod attributes provide, such as service + information that the optional WebService and + WebMethod 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, @@ -262,7 +279,7 @@ namespace MyApp.Services set all the necessary values within the definition of the service exporter, like so... - + <object id="HelloWorldExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web"> <property name="TargetName" value="HelloWorld"/> <property name="Namespace" value="http://myCompany/services"/> @@ -309,7 +326,7 @@ namespace MyApp.Services One can also export only certain interfaces that a service class implements by setting the Interfaces property of the - WebServiceExporter. + WebServiceExporter. Distributed Objects Warning @@ -342,12 +359,12 @@ namespace MyApp.Services 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 WebServiceExporter's + is set the WebServiceExporter's TargetName property to the id (or indeed the name or alias) of the AOP proxy. The following code snippets show how to do this... - + <object id="DebugAdvice" type="MyApp.AOP.DebugAdvice, MyApp"/> <object id="TimerAdvice" type="MyApp.AOP.TimerAdvice, MyApp"/> @@ -379,7 +396,7 @@ namespace MyApp.Services - + Client-side On the client side, the main objection the Spring.NET team has is @@ -404,10 +421,10 @@ namespace MyApp.Services and makes it impossible to change the implementation at a later date without modifying and recompiling the client. - Spring.NET provides a simple IFactoryObject + Spring.NET provides a simple IFactoryObject implementation that will generate a "proxy for proxy" (however obtuse that may sound). Basically, the - Spring.Web.Services.WebServiceProxyFactory class + Spring.Web.Services.WebServiceProxyFactory 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 @@ -417,7 +434,7 @@ namespace MyApp.Services conveying what is happening; consider the following interface definition that we wish to expose as a web service... - + namespace MyCompany.Services { public interface IHelloWorld @@ -431,7 +448,7 @@ namespace MyCompany.Services this interface, you need to add a definition similar to the example shown below to your client's application context: - + <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"/> @@ -440,7 +457,7 @@ namespace MyCompany.Services What is important to notice is that the underlying implementation class for the web service does not have to implement the same - IHelloWorld service interface... so long as + IHelloWorld 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 @@ -450,18 +467,18 @@ namespace MyCompany.Services 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 - WebServiceExporter, which requires an interface + WebServiceExporter, which requires an interface in order to work. Generating proxies dynamically - The WebServiceProxyFactory can also + The WebServiceProxyFactory can also dynamically generate a web-service proxy. The XML object definition for this factory object is shown below - + <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"/>--> @@ -490,30 +507,30 @@ namespace MyCompany.Services Configuring the proxy instance - The WebServiceProxyFactory also implements + The WebServiceProxyFactory also implements the interface, - Spring.Objects.Factory.IConfigurableFactoryObject, + Spring.Objects.Factory.IConfigurableFactoryObject, allowing to specify configuration for the product that the - WebServiceProxyFactory creates. This is done by + WebServiceProxyFactory creates. This is done by specifying the ProductTemplate property. This is particularly useful for securing the web service. An example is shown below. - + <object id="PublicarAltasWebService" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services"> <property name="ProxyType" value="My.WebService" /> <property name="ServiceInterface" value="My.IWebServiceInterface" /> - <property name="ProductTemplate"> + <property name="ProductTemplate"> <object> <!-- Configure the web service URL --> <property name="Url" value="https://localhost/MyApp/webservice.jws" /> - <!-- Configure the Username and password for the web service --> + <!-- Configure the Username and password for the web service --> <property name="Credentials"> <object type="System.Net.NetworkCredential, System"> <property name="UserName" value="user"/> <property name="Password" value="password"/> </object> </property> - <!-- Configure client certificate for the web service --> + <!-- Configure client certificate for the web service --> <property name="ClientCertificates"> <list> <object id="MyCertificate" type="System.Security.Cryptography.X509Certificates.X509Certificate2, System"> diff --git a/doc/reference/src/windows-service.xml b/doc/reference/src/windows-service.xml index 682e0066..5f5e6fd0 100644 --- a/doc/reference/src/windows-service.xml +++ b/doc/reference/src/windows-service.xml @@ -1,4 +1,22 @@ - + + + Windows Services @@ -89,15 +107,15 @@ the command line for Spring.Services.WindowsService.Installer.exe is as follow: - Spring.Services.WindowsService.Installer.exe + 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 for example, to install, you can invoke it with the following: - ... install Spring.Services.WindowsService.Process.exe "Spring.Service Support" spring-service + ... install Spring.Services.WindowsService.Process.exe "Spring.Service Support" spring-service and to uninstall it: - ... uninstall spring-service + ... uninstall spring-service @@ -111,8 +129,7 @@ usage: This file also define the context run by this process; here the file in its current beauty: - -<configuration> + <configuration> <configSections> <section name="log4net" type="System.Configuration.IgnoreSectionHandler" /> @@ -170,6 +187,6 @@ usage: 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 IObjectFactoryPostProcessor and so will be + very important, it is important that it is an IObjectFactoryPostProcessor and so will be automatically applied to this application context: \ No newline at end of file diff --git a/doc/reference/src/xml-config-reference.xml b/doc/reference/src/xml-config-reference.xml index 480fe28b..c9f778c3 100644 --- a/doc/reference/src/xml-config-reference.xml +++ b/doc/reference/src/xml-config-reference.xml @@ -1,5 +1,22 @@ - - + + + XML Configuration Reference @@ -56,7 +73,7 @@ Find below an example of defining an object that has no dependencies. - <object name="service" <object name="service" type="Example.Foo, FooAssembly"/> + in the reference documentation). @@ -102,8 +119,8 @@ 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 Foo class. The default constructor of the - Foo class will be invoked, and since no + the Foo class. The default constructor of the + Foo 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. @@ -111,11 +128,11 @@ Further (un-annotated) examples of defining an object that has no dependencies can be found below... - <object id="anException" type="System.ArgumentException, Mscorlib"/> + <object id="anException" type="System.ArgumentException, Mscorlib"/> - <object id="anEmptyList" type="System.Collections.ArrayList, Mscorlib"/> + <object id="anEmptyList" type="System.Collections.ArrayList, Mscorlib"/> - <object id="anSqlCommand" type="System.Data.SqlClient.SqlCommand, System.Data"/> + <object id="anSqlCommand" type="System.Data.SqlClient.SqlCommand, System.Data"/> @@ -140,16 +157,16 @@ - + Primitives This section details the various configuration options available for injecting, handoing, and otherwise defining the classic primitive - types. The string and - date types are not primitives, but they are + types. The string and + date types are not primitives, but they are described here nevertheless. - Spring.NET uses the TypeConverter + Spring.NET uses the TypeConverter 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 @@ -157,7 +174,7 @@ if you are having type conversion issues... - + Numbers This section describes configuring the various numeric types @@ -168,7 +185,7 @@ Find below the class definition that is used to illustrate configuring numeric values in the following examples. - [C#] + [C#] namespace Example { public class Gauge @@ -188,18 +205,18 @@ namespace Example } } - <object id="aGauge" type="Example.Gauge, FooAssembly"> + <object id="aGauge" type="Example.Gauge, FooAssembly"> <property name="setting" value="213"/> </object> We can also use any of the normal supported conventions (such as hexadecimal) to set values, as shown below. - <object id="aGauge" type="Example.Gauge, FooAssembly"> + <object id="aGauge" type="Example.Gauge, FooAssembly"> <property name="setting" value="0x10"/> </object> - <object id="aGauge" type="Example.Gauge, FooAssembly"> + <object id="aGauge" type="Example.Gauge, FooAssembly"> <property name="sensitivity" value="31000.00"/> </object> @@ -217,7 +234,7 @@ namespace Example Find below the class definition that is used to illustrate configuring date values in the following examples. - [C#] + [C#] namespace Example { public class Gauge @@ -231,20 +248,20 @@ namespace Example } } - <object id="aGauge" type="Example.Gauge, FooAssembly"> + <object id="aGauge" type="Example.Gauge, FooAssembly"> <property name="lastChecked" value=""/> </object> - <object id="aGauge" type="Example.Gauge, FooAssembly"> + <object id="aGauge" type="Example.Gauge, FooAssembly"> <property name="lastChecked" value=""/> </object> - <object id="aGauge" type="Example.Gauge, FooAssembly"> + <object id="aGauge" type="Example.Gauge, FooAssembly"> <property name="lastChecked" value=""/> </object> - + Booleans Configuring boolean values in one's configuration file (s) is @@ -266,7 +283,7 @@ namespace Example Find below the class definition that is used to illustrate configuring boolean values in the following examples. - [C#] + [C#] namespace Example { public class Gauge @@ -280,11 +297,11 @@ namespace Example } } - <object id="aGauge" type="Example.Gauge, FooAssembly"> + <object id="aGauge" type="Example.Gauge, FooAssembly"> <property name="IsSwitchedOn" value="true"/> </object> - <object id="aGauge" type="Example.Gauge, FooAssembly"> + <object id="aGauge" type="Example.Gauge, FooAssembly"> <property name="IsSwitchedOn" value="false"/> </object> @@ -297,43 +314,43 @@ namespace Example If you wanted to use different values for the true and false string values (perhaps on and off values in - the case of the preceding Gauge example), you - would need to register a custom TypeConverter + the case of the preceding Gauge example), you + would need to register a custom TypeConverter implementation (see ). - + Strings - Unsurprisingly, String values are the + Unsurprisingly, String values are the easiest to configure. Consider the following example of strings that are defined as top level objects... - <object id="supportTeamEmail" type="string"> + <object id="supportTeamEmail" type="string"> <constructor-arg index="0" value="support@my.company.com"/> </object> - <object id="projectManagerEmail" type="string"> + <object id="projectManagerEmail" type="string"> <constructor-arg index="0" value="projectManager@my.company.com"/> </object> The index="0" attribute value pair of the constructor-arg element is required so that the - correct constructor of the String class can + correct constructor of the String class can be invoked... don't forget to put it in. (If you do forget to put it in, then a not-very-helpful - UnsatisfiedDependencyException will be thrown + UnsatisfiedDependencyException will be thrown by the Spring.NET container). - + Enumerations Find below the class definition and XML snippets that illustrate the configuration of enumerations. - [C#] + [C#] namespace Example { public enum RunningMode @@ -356,11 +373,11 @@ namespace Example } } - <object id="aGauge" type="Example.Gauge, FooAssembly"> + <object id="aGauge" type="Example.Gauge, FooAssembly"> <property name="RunMode" value="Starting"/> </object> - <object id="aGauge" type="Example.Gauge, FooAssembly"> + <object id="aGauge" type="Example.Gauge, FooAssembly"> <property name="RunMode" value="SwitchingOff"/> </object> @@ -431,13 +448,13 @@ namespace Example - + Singleton - + Prototype @@ -455,7 +472,7 @@ namespace Example Perhaps unsurprisingly, implementations of the classic Factory pattern can be found all over the Spring.NET codebase... indeed, the - core IApplicationContext class is a compelling + core IApplicationContext 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 @@ -463,8 +480,8 @@ namespace Example External factory classes would include any factory classes that you may have written: examples of this would include (perhaps) - IWiGFactory (to create - IWiG implementations), etc. You can integrate any + IWiGFactory (to create + IWiG 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 Spring.NET also has the notion of a special Factory Object (and this notion is encapsulated by the - IFactoryObject interface). The - IFactoryObject interface is (unsurprisingly) a + IFactoryObject interface). The + IFactoryObject interface is (unsurprisingly) a factory for creating one or more objects. Please do read for a - comprehensive explanation of the IFactoryObject + comprehensive explanation of the IFactoryObject 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 - IFactoryObject implementations that come provided + IFactoryObject implementations that come provided out of the box with every Spring.NET release. @@ -495,34 +512,34 @@ namespace Example Factory Objects This section of the documentation presents examples for most of - the IFactoryObject implementations that come + the IFactoryObject implementations that come out of the box with every Spring.NET release. A notable exception to - this catalogue of IFactoryObject configuration + this catalogue of IFactoryObject configuration examples is the AOP-specific - ProxyFactoryObject... see ProxyFactoryObject... see for more details regarding that particular - IFactoryObject implementation. + IFactoryObject implementation. - Most (if not all) of the IFactoryObject + Most (if not all) of the IFactoryObject implementations referenced in the following configuration examples can be found in the Spring.Objects.Factory.Config namespace; do also consult the attendant API documentation (because - most of the IFactoryObject implementations + most of the IFactoryObject implementations carry configuration examples specific to the objects that they create). DelegateFactoryObject - One can use the DelegateFactoryObject + One can use the DelegateFactoryObject to (unsurprisingly) create and configure - Delegate objects. One trenchant use case for - this IFactoryObject (and indeed the very + Delegate objects. One trenchant use case for + this IFactoryObject (and indeed the very reason that prompted it's creation) is to create declaratively a - ConfigListener delegate for use with the - IBatis.NET project's SqlMapper class. This - approach (of using the DelegateFactoryObject) - allows one to keep all of one's SqlMapper + ConfigListener delegate for use with the + IBatis.NET project's SqlMapper class. This + approach (of using the DelegateFactoryObject) + allows one to keep all of one's SqlMapper configuration together, nice and tidy, in the one place. So lets say we have a service object that we need to inject @@ -531,7 +548,7 @@ namespace Example that supplies the method that will be passed to the delegate when it is created can be found below. - [C#] + [C#] namespace Example { public delegate void GaugeCallback (object sender, GuageEventArgs e); @@ -560,11 +577,11 @@ namespace Example } The attendant configuration to supply an instance of the - Gauge class with a configured - GuageCallback delegate would look like + Gauge class with a configured + GuageCallback delegate would look like so... - <objects xmlns="http://www.springframework.net"> + <objects xmlns="http://www.springframework.net"> <object id="gauge" type="Example.Gauge, FooAssembly"> <property name="callback"> <object type="Spring.Objects.Factory.Config.DelegateFactoryObject"> diff --git a/doc/reference/src/xml-custom.xml b/doc/reference/src/xml-custom.xml index 803f9916..e9262e44 100644 --- a/doc/reference/src/xml-custom.xml +++ b/doc/reference/src/xml-custom.xml @@ -1,8 +1,25 @@ - - + + + Extensible XML authoring -
+
Introduction Spring supports adding custom schema-based extensions to the basic @@ -29,13 +46,13 @@ Coding - a custom INamespaceParser + a custom INamespaceParser implementation (this is an easy step, don't worry). Coding one or - more IObjectDefinitionParser + more IObjectDefinitionParser implementations (this is where the real work is done). @@ -47,26 +64,26 @@ 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 Regex + allows us to configure objects of the type Regex (from the System.Text.RegularExpressions namespace) in an easy manner. When we are done, we will be able to define object - definitions of type Regex like this: + definitions of type Regex like this: - <myns:regex id="regex" + <myns:regex id="regex" pattern="(^\d{5}$)|(^\d{5}-\d{4}$)" options="Compiled"/>
-
+
Authoring the schema 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 - Regex objects. + Regex objects. - <?xml version="1.0" encoding="utf-8" ?> + <?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" @@ -85,7 +102,7 @@ <xsd:element name="regex"> <xsd:complexType> <xsd:complexContent> - <xsd:extension base="objects:identifiedType"> + <xsd:extension base="objects:identifiedType"> <xsd:attribute name="pattern" type="xsd:string" use="required"/> <xsd:attribute name="options" type="xsd:string" use="optional"/> </xsd:extension> @@ -104,11 +121,11 @@ VS.NET. The above schema will be used to configure - Regex objects, directly in an XML application + Regex objects, directly in an XML application context file using the <myns:regex/> element. - <myns:regex id="usZipCodeRegex" + <myns:regex id="usZipCodeRegex" pattern="(^\d{5}$)|(^\d{5}-\d{4}$)" options="Compiled"/> @@ -116,10 +133,10 @@ 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 'usZipCodeRegex' of type - Regex, with a couple of constructor arguments + Regex, with a couple of constructor arguments set. - <object id="usZipCodeRegex" type="System.Text.RegularExpressions.Regex, System"> + <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> @@ -134,23 +151,23 @@
-
- Coding a <interfacename>INamespaceParser</interfacename> +
+ Coding a <literal>INamespaceParser</literal> In addition to the schema, we need an - INamespaceParser that will parse all + INamespaceParser that will parse all elements of this specific namespace Spring encounters while parsing - configuration files. The INamespaceParser + configuration files. The INamespaceParser should in our case take care of the parsing of the myns:regex element. - The INamespaceParser interface is + The INamespaceParser interface is pretty simple in that it features just two methods: Init() - allows for initialization of - the INamespaceParser and will be + the INamespaceParser and will be called by Spring before the handler is used @@ -164,17 +181,17 @@ Although it is perfectly possible to code your own - INamespaceParser for the entire namespace + INamespaceParser 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 <myns:regex/> element - results in a single Regex object definition). + results in a single Regex object definition). Spring features a number of convenience classes that support this scenario. In this example, we'll make use the - NamespaceParserSupport class: + NamespaceParserSupport class: - using Spring.Objects.Factory.Xml; + using Spring.Objects.Factory.Xml; namespace CustomNamespace { @@ -188,23 +205,23 @@ namespace CustomNamespace { public override void Init() { - RegisterObjectDefinitionParser("regex", new RegexObjectDefinitionParser()); + RegisterObjectDefinitionParser("regex", new RegexObjectDefinitionParser()); } } } Notice that there isn't actually a whole lot of parsing logic in - this class. Indeed... the NamespaceParserSupport + this class. Indeed... the NamespaceParserSupport class has a built in notion of delegation. It supports the registration of - any number of IObjectDefinitionParser + any number of IObjectDefinitionParser 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 - INamespaceParser to handle the + INamespaceParser to handle the orchestration of the parsing of all of the custom elements in it's namespace, while delegating to IObjectDefinitionParsers to do the grunt work of the XML parsing; this means that each - IObjectDefinitionParser will contain just + IObjectDefinitionParser will contain just the logic for parsing a single custom element, as we can see in the next step. @@ -215,21 +232,21 @@ namespace CustomNamespace of the XML Schema file as an embedded assembly resource.
-
+
Coding an - <interfacename>IObjectDefinitionParser</interfacename> + IObjectDefinitionParser - A IObjectDefinitionParser will be - used if the INamespaceParser encounters an + A IObjectDefinitionParser will be + used if the INamespaceParser encounters an XML element of the type that has been mapped to the specific object definition parser (which is 'regex' in this case). In - other words, the IObjectDefinitionParser is + other words, the IObjectDefinitionParser is responsible for parsing one 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: - using System; + using System; using System.Text.RegularExpressions; using System.Xml; using Spring.Objects.Factory.Support; @@ -272,24 +289,24 @@ namespace CustomNamespace We use the Spring-provided - AbstractSingleObjectDefinitionParser to handle + AbstractSingleObjectDefinitionParser to handle a lot of the basic grunt work of creating a single - IObjectDefinition. + IObjectDefinition. We supply the - AbstractSingleObjectDefinitionParser superclass + AbstractSingleObjectDefinitionParser superclass with the type that our single - IObjectDefinition will + IObjectDefinition will represent. In this simple case, this is all that we need to do. The creation of - our single IObjectDefinition is handled by - the AbstractSingleObjectDefinitionParser + our single IObjectDefinition is handled by + the AbstractSingleObjectDefinitionParser superclass, as is the extraction and setting of the object definition's unique identifier. The property ShouldGenerateIdAsFallback will generate a throw-away @@ -297,29 +314,29 @@ namespace CustomNamespace definitions.
-
+
Registering the handler and the schema 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 - INamespaceParser using a special + INamespaceParser 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 Namespace attribute. -
+
<filename>NamespaceParsersSectionHandler</filename> The custom configuration section handler is of the type - Spring.Context.Support.NamespaceParsersSectionHandler + Spring.Context.Support.NamespaceParsersSectionHandler and is registered with .NET in the normal manner. The custom configuration section will simply point to the - INamespaceParser implementation that has the - Namespace attribute. For our example, we need to + INamespaceParser implementation that has the + Namespace attribute. For our example, we need to write the following: - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> @@ -337,7 +354,7 @@ namespace CustomNamespace
-
+
Using a custom extension in your Spring XML configuration Using a custom extension that you yourself have implemented is no @@ -346,7 +363,7 @@ namespace CustomNamespace <regex/> element developed in the previous steps in a Spring XML configuration file. - <?xml version="1.0" encoding="utf-8" ?> + <?xml version="1.0" encoding="utf-8" ?> <objects xmlns="http://www.springframework.net" xmlns:myns="http://www.mycompany.com/schema/myns"> @@ -365,7 +382,7 @@ namespace CustomNamespace </objects>
-
+
Further Resources Find below links to further resources concerning XML Schema and the diff --git a/doc/reference/src/xsd-configuration.xml b/doc/reference/src/xsd-configuration.xml index 78f1624b..1fdf2142 100644 --- a/doc/reference/src/xsd-configuration.xml +++ b/doc/reference/src/xsd-configuration.xml @@ -1,8 +1,25 @@ - - + + + XML Schema-based configuration -
+
Introduction This appendix details the use of XML Schema-based configuration in @@ -28,16 +45,16 @@ appendix entitled .
-
+
XML Schema-based configuration -
+
Referencing the schemas As a reminder, you reference the standard objects schema as shown below - + <?xml version="1.0" encoding="UTF-8"?> <objects xmlns="http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" @@ -64,7 +81,7 @@ The rest of this chapter gives an overview of custom XML Schema based configuration that are included with the release. -
+
The <literal>tx</literal> (transaction) schema The tx tags deal with configuring objects in @@ -88,10 +105,10 @@ the following snippet references the correct schema so that the tags in the tx namespace are available to you. - <?xml version="1.0" encoding="UTF-8"?> + <?xml version="1.0" encoding="UTF-8"?> <object xmlns="http://www.springframework.net" xmlns:aop="http://www.springframework.net/aop" - xmlns:tx="http://www.springframework.net/tx"> + xmlns:tx="http://www.springframework.net/tx"> <!-- <object/> definitions here --> @@ -115,7 +132,7 @@ parsers in the main .NET application configuration file as shown below - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> @@ -134,7 +151,7 @@ </configuration>
-
+
The <literal>aop</literal> schema The aop tags deal with configuring all things @@ -147,9 +164,9 @@ the following snippet references the correct schema so that the tags in the aop namespace are available to you. - <?xml version="1.0" encoding="UTF-8"?> + <?xml version="1.0" encoding="UTF-8"?> <objects xmlns="http://www.springframework.net" - xmlns:aop="http://www.springframework.net/aop"> + xmlns:aop="http://www.springframework.net/aop"> <!-- <object/> definitions here --> @@ -160,7 +177,7 @@ You will also need to configure the AOP namespace parser in the main .NET application configuration file as shown below - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> @@ -178,19 +195,19 @@ </configuration>
-
+
The <literal>db</literal> schema The db tags deal with creating - IDbProvider instances for a given database client + IDbProvider instances for a given database client library. The following snippet references the correct schema so that the tags in the db namespace are available to you. The tags are comprehensively covered in the chapter entitled . - <?xml version="1.0" encoding="UTF-8"?> + <?xml version="1.0" encoding="UTF-8"?> <objects xmlns="http://www.springframework.net" - xmlns:db="http://www.springframework.net/db"> + xmlns:db="http://www.springframework.net/db"> <!-- <object/> definitions here --> @@ -201,7 +218,7 @@ You will also need to configure the Database namespace parser in the main .NET application configuration file as shown below - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> @@ -219,7 +236,7 @@ </configuration>
-
+
The <literal>remoting</literal> schema The remoting tags are for use when you want to @@ -227,9 +244,9 @@ client side .NET remoting proxy. The tags are comprehensively covered in the chapter - <?xml version="1.0" encoding="UTF-8"?> + <?xml version="1.0" encoding="UTF-8"?> <objects xmlns="http://www.springframework.net" - xmlns:r="http://www.springframework.net/remoting"> + xmlns:r="http://www.springframework.net/remoting"> <!-- <object/> definitions here --> @@ -240,7 +257,7 @@ You will also need to configure the remoting namespace parser in the main .NET application configuration file as shown below - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> @@ -258,16 +275,16 @@ </configuration>
-
+
The <literal>nms</literal> messaging schema The nms tags are for use when you want to configure Spring's messaging support. The tags are comprehensively covered in the chapter - <?xml version="1.0" encoding="UTF-8"?> + <?xml version="1.0" encoding="UTF-8"?> <objects xmlns="http://www.springframework.net" - xmlns:r="http://www.springframework.net/nms"> + xmlns:r="http://www.springframework.net/nms"> <!-- <object/> definitions here --> @@ -278,7 +295,7 @@ You will also need to configure the remoting namespace parser in the main .NET application configuration file as shown below - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> @@ -296,7 +313,7 @@ </configuration>
-
+
The <literal>validation</literal> schema The validation tags are for use when you want @@ -304,9 +321,9 @@ comprehensively covered in the chapter - <?xml version="1.0" encoding="UTF-8"?> + <?xml version="1.0" encoding="UTF-8"?> <objects xmlns="http://www.springframework.net" - xmlns:v="http://www.springframework.net/validation"> + xmlns:v="http://www.springframework.net/validation"> <!-- <object/> definitions here --> @@ -317,7 +334,7 @@ You will also need to configure the validation namespace parser in the main .NET application configuration file as shown below - <configuration> + <configuration> <configSections> <sectionGroup name="spring"> @@ -335,7 +352,7 @@ </configuration>
-
+
The <literal>objects</literal> schema Last but not least we have the tags in the @@ -345,7 +362,7 @@ linkend="object-factory-properties-detailed" /> (and indeed in that entire chapter). - <?xml version="1.0" encoding="UTF-8"?> + <?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"> @@ -358,7 +375,7 @@
-
+
Setting up your IDE To setup VS.NET to provide intellisence while editing XML file for diff --git a/doc/reference/src/xsd-template.xml b/doc/reference/src/xsd-template.xml index 7ab52c33..7d42402f 100644 --- a/doc/reference/src/xsd-template.xml +++ b/doc/reference/src/xsd-template.xml @@ -1,4 +1,22 @@ - + + + Spring.NET's <literal>spring-objects.xsd</literal> - + diff --git a/doc/reference/src/xsd.xml b/doc/reference/src/xsd.xml index 944d63a2..bb2a26d2 100644 --- a/doc/reference/src/xsd.xml +++ b/doc/reference/src/xsd.xml @@ -1,6 +1,24 @@ - + + + Spring.NET's <literal>spring-objects.xsd</literal> - + diff --git a/doc/reference/styles/fopdf.xsl b/doc/reference/styles/fopdf.xsl deleted file mode 100644 index bb9020ca..00000000 --- a/doc/reference/styles/fopdf.xsl +++ /dev/null @@ -1,475 +0,0 @@ - - - - - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Copyright ©right; 2004-2006 - - - , - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -5em - -5em - - - - - - - - - - - Spring Framework () - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bold - - - - - - - - - - - - - - - - - - - - - - - - - - - 1 - 0 - 1 - - 1 - - - - - - book toc - - - - 2 - - - - - - - - - - 0 - 0 - 0 - - - 5mm - 10mm - 10mm - - 15mm - 10mm - 0mm - - 18mm - 18mm - - - 0pc - - - - - justify - false - - - 11 - 8 - - - 1.4 - - - - - - - 0.8em - - - - - - 17.4cm - - - - 4pt - 4pt - 4pt - 4pt - - - - 0.1pt - 0.1pt - - - - - 1 - - - - - - - - left - bold - - - pt - - - - - - - - - - - - - - - 0.8em - 0.8em - 0.8em - - - pt - - 0.1em - 0.1em - 0.1em - - - 0.6em - 0.6em - 0.6em - - - pt - - 0.1em - 0.1em - 0.1em - - - 0.4em - 0.4em - 0.4em - - - pt - - 0.1em - 0.1em - 0.1em - - - - - bold - - - pt - - false - 0.4em - 0.6em - 0.8em - - - - - - - - - pt - - - - - 1em - 1em - 1em - #444444 - solid - 0.1pt - 0.5em - 0.5em - 0.5em - 0.5em - 0.5em - 0.5em - - - - 1 - - #F0F0F0 - - - - - - 0 - 1 - - - 90 - - - - - '1' - &admon_gfx_path; - - - - - - figure after - example before - equation before - table before - procedure before - - - - 1 - - - - 0.8em - 0.8em - 0.8em - 0.1em - 0.1em - 0.1em - - - - - - - - - - - - - - - - - diff --git a/doc/reference/styles/html.xsl b/doc/reference/styles/html.xsl deleted file mode 100644 index 7bcd1582..00000000 --- a/doc/reference/styles/html.xsl +++ /dev/null @@ -1,100 +0,0 @@ - - - - - -]> - - - - - - - - ../styles/html.css - - - 1 - 0 - 1 - 0 - - - - - - book toc - - - - 3 - - - - - 1 - - - - - - - 1 - &callout_gfx_path; - - - 90 - - - - - '1' - &admon_gfx_path; - - - - - figure after - example before - equation before - table before - procedure before - - - - , - - - - - - - - -
-

Authors

-

- -

-
- -
diff --git a/doc/reference/styles/html_chunk.xsl b/doc/reference/styles/html_chunk.xsl deleted file mode 100644 index 5dce0cca..00000000 --- a/doc/reference/styles/html_chunk.xsl +++ /dev/null @@ -1,217 +0,0 @@ - - - - - -]> - - - - '5' - '1' - ../styles/html.css - - 1 - 0 - 1 - 0 - - - - book toc - - - 3 - - - 1 - - - - - 1 - &callout_gfx_path; - - 90 - - - '1' - &admon_gfx_path; - - - - figure after - example before - equation before - table before - procedure before - - - - , - - - - - - - - -
-

Authors

-

- -

-
- - - - - - - - 1 - - - - - - - - - - - - - -
diff --git a/doc/reference/styles/htmlhelp-common.xsl b/doc/reference/styles/htmlhelp-common.xsl deleted file mode 100644 index 9f8fd0ff..00000000 --- a/doc/reference/styles/htmlhelp-common.xsl +++ /dev/null @@ -1,1230 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ID ' - - ' not found in document. - - - - Formatting from - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0x - - - - - - 0x - - - - -[OPTIONS] - - -Auto Index=Yes - - -Binary TOC=Yes - -Compatibility=1.1 or later -Compiled file= -Contents file= - - -Default Window= - -Default topic= - -Display compile progress= - - - No - - - Yes - - - -Full-text search=Yes - - -Index file= - -Language= - - - - - - - -Title= - - -Enhanced decompilation= - - - Yes - - - No - - - - - - -[WINDOWS] - - -=" - -"," -", - - " - - " - -," - -", -" - - - - - - - - -" -, - - " - - " - -, - - " - - " - -, - - " - - " - -, - - " - - " - -, - -,, - -,,,,,,,,0 - - - - - - - - - - -[FILES] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[ALIAS] -#include - -[MAP] -#include - - - - - - - - - - - - - - - - - - - - - - - - - 1 - - - - - - - - - 0 - - - - 0 - - - - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <HTML> -<HEAD> -</HEAD> - <BODY> - - - <OBJECT type="text/site properties"> - <param name="ImageType" value="Folder"> -</OBJECT> - - - -<UL> - - - - - - - - - - - - - - </UL> - - - </BODY> -</HTML> - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - - - - - - <UL> - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - - - - - - <UL> - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - - , - - - - , - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ]]> - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - ]]> - - - - - - ]]> - - - - - ]]> - - ]]> - - - - - - - - - - - - - - - ]]> - - - - - - - - - - - - - - - - ]]> - - - ]]> - - - - - ]]> - - ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #define - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - = - - - - - - - - - - - - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - A - B - C - D - E - F - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - - - - - -

    -
    - - diff --git a/doc/reference/styles/htmlhelp.xsl b/doc/reference/styles/htmlhelp.xsl deleted file mode 100644 index f9e6e21a..00000000 --- a/doc/reference/styles/htmlhelp.xsl +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - 1 - #F4F4F4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/reference/styles/profile-htmlhelp-common.xsl b/doc/reference/styles/profile-htmlhelp-common.xsl deleted file mode 100644 index 41981669..00000000 --- a/doc/reference/styles/profile-htmlhelp-common.xsl +++ /dev/null @@ -1,1199 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ID ' - - ' not found in document. - - - - Formatting from - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0x - - - - - - 0x - - - - -[OPTIONS] - - -Auto Index=Yes - - -Binary TOC=Yes - -Compatibility=1.1 or later -Compiled file= -Contents file= - - -Default Window= - -Default topic= - -Display compile progress= - - - No - - - Yes - - - -Full-text search=Yes - - -Index file= - -Language= - - - - - - - -Title= - - -Enhanced decompilation= - - - Yes - - - No - - - - - - -[WINDOWS] - - -=" - -"," -", - - " - - " - -," - -", -" - - - - - - - - -" -, - - " - - " - -, - - " - - " - -, - - " - - " - -, - - " - - " - -, - -,, - -,,,,,,,,0 - - - - - - - - - - -[FILES] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -[ALIAS] -#include - -[MAP] -#include - - - - - - - - - - - - - - - - - - - - - - - - - 1 - - - - - - - - - 0 - - - - 0 - - - - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <HTML> -<HEAD> -</HEAD> - <BODY> - - - <OBJECT type="text/site properties"> - <param name="ImageType" value="Folder"> -</OBJECT> - - - -<UL> - - - - - - - - - - - - - - </UL> - - - </BODY> -</HTML> - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - - - - - - <UL> - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - - - - - - <UL> - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - </OBJECT> - - - <UL> - - </UL> - - - - - - - - - - - - - - - - - - - - - - - , - - - - , - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<HTML> -<HEAD> -<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1"> -<!-- Sitemap 1.0 --> -</HEAD><BODY> -<OBJECT type="text/site properties"> -</OBJECT> -<UL> - - - - - - - - - - - -</UL> -</BODY></HTML> - - - - - - - - - - - - - - - - - - - - - - - - - - <UL> - - - - - - - <UL> - - - - - - </UL> - - - </UL> - - - - - - - - - - - - - - - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value=""> - - - - - - - - - - - - - - <param name="Name" value=" - - "> - <param name="Local" value=" - - "> - - - <param name="See Also" value=" - - "> - - </OBJECT> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #define - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - = - - - - - - - - - - - - - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - A - B - C - D - E - F - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - - - - - -

    -
    - -
    diff --git a/doc/reference/styles/profile-htmlhelp.xsl b/doc/reference/styles/profile-htmlhelp.xsl deleted file mode 100644 index 3d245370..00000000 --- a/doc/reference/styles/profile-htmlhelp.xsl +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - diff --git a/doc/reference/styles/tld.to.docbook.xsl b/doc/reference/styles/tld.to.docbook.xsl deleted file mode 100644 index 139a3529..00000000 --- a/doc/reference/styles/tld.to.docbook.xsl +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -intro - - Introduction - - - - One of the view technologies you can use with the Spring Framework - is Java Server Pages (JSPs). To help you implement views using Java Server Pages - the Spring Framework provides you with some tags for evaluating errors, setting - themes and outputting internationalized messages. - - - - This appendix describes the - - - - tag library. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The - - - - tag - - - - - - - - - - - - .table - - - Attributes - - - - 3 - - - - description.span - - - Attribute - - - Runtime.Expression - - - left - - - - - center - - - Attribute - - - - - center - - - Required - - - - - center - - - Runtime.Expression - - - - - - - - center - - Attribute - - - - center - - Required? - - - - center - - Runtime Expression? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - description.span - - - - - - - - - - - - - description.span - - - - - - - - - - - . - - - diff --git a/doc/reference/styles/xsd.to.docbook.xsl b/doc/reference/styles/xsd.to.docbook.xsl deleted file mode 100644 index 4a9bb86d..00000000 --- a/doc/reference/styles/xsd.to.docbook.xsl +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - -intro - - - Introduction - - - - - This appendix describes the - - - - schema. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The - - - - element - - - [TODO : insert the description of the element here] - - - - - - - . - - -