Chapter 11. Spring Services

11.1. Introduction

The main goal of Spring.Services is to provide location transparency for business services. We believe that users should be able to implement services the simplest way possible, using service interfaces and implementations in the form of plain .Net classes. We also think that decision on how a particular service is exposed to clients should be a configuration concern and not an implementation concern.

Spring.Services provides infrastructure that allows you to expose any normal object as a web service, System.EnterpriseServices serviced component or remoting object using service exporter definitions in the configuration file.

We believe that this approach will also provide the easiest migration path to Indigo. As long as your services are coded against interfaces and implemented as regular classes, we should be able to implement Indigo exporter for them once the Indigo ships.

In the meantime, you get the benefit of being able to expose business services any way you find appropriate using existing exporters and Spring configuration.

11.2. 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 your class and assemblies with relevant attributes, and configure your application by registering your serviced components with the COM+ catalog. The overall landscape of accessing and using COM+ services within .NET goes by the name .NET Enterprise Services.

Many of these services can be provided without the need to derive from a ServicedComponent though the use of Spring's Aspect-Oriented Programming functionality. Nevertheless, you may be interested in exporting your class as a serviced component and having client access that component in a location transparent manner. By using Spring's ServicedComponentExporter, EnterpriseServicesExporterand ServicedComponentFactory you can easily create and consume serviced components without having your class inherit from ServicedComponent and automate the manual deployment process that involves strongly signing your assembly and using the regsvcs utility.

Note that the following sections do not delve into the details of programming .NET Enterprise Services. An excellent reference for such information is Christian Nagel's "Enterprise Services with the .NET Framework"

11.3. Server Side

One of the main challenges for the exported to host a serviced component is the need for them to be contained within a physical assembly on the file system in order to be registered with the COM+ Services. To make things more complicated, this assembly has to be strongly named before it can be successfully registered.

Spring provides two classes that allow all of this to happen.

  • Spring.Enterprise.ServicedComponentExporter is responsible for exporting 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.
  • 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

Let's say that we have a simple service interface and implementation class, such as these:

namespace MyApp.Services
{
    public interface IUserManager
    {
        User GetUser(int userId);
        void SaveUser(User user);
    }

    public class SimpleUserManager : IUserManager
    {
        private IUserDao userDao;
        public IUserDao UserDao
        {
            get { return userDao; }
            set { userDao = value; }
        }

        public User GetUser(int userId)
        {
            return UserDao.FindUser(userId);
        }

        public void SaveUser(User user)
        {
            if (user.IsValid)
            {
                UserDao.SaveUser(user);
            }
        }
    }
} 

And the corresponding object definition for it in the application context config file:

<object id="userManager" type="MyApp.Services.SimpleUserManager">
    <property name="UserDao"><ref object="userDao"/></property>
            </object>

Let's say that we want to expose user manager as an serviced component so we can leverage its support for transactions. First we need to export our service using the exporter ServicedComponentExporter as shown below

<object id="MyApp.EnterpriseServices.UserManager" type="Spring.Enterprise.ServicedComponentExporter, Spring.Services">
    <property name="TargetName"><value>userManager</value></property>
    <property name="ClassAttributes">
        <list>
            <object type="System.EnterpriseServices.TransactionAttribute, System.EnterpriseServices"/>
        </list>
    </property>
    <property name="MethodAttributes">
        <dictionary>
            <entry key="*">
                <list>
                    <object type="System.EnterpriseServices.AutoCompleteAttribute, System.EnterpriseServices"/>
                </list>
            </entry>
        </dictionary>
    </property>
</object>

The exporter defined above will create a composition proxy for our SimpleUserManager class that extends ServicedComponent and delegates method calls to SimpleUserManager instance. It will also adorn the proxy class with a TransactionAtribute and all methods with an AutoCompleteAttribute.

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">
    <property name="ApplicationName"><value>My COM+ Application</value></property>
    <property name="Description"><value>My enterprise services application.</value></property>
    <property name="AccessControl">
        <object type="System.EnterpriseServices.ApplicationAccessControlAttribute, System.EnterpriseServices">
            <property name="AccessChecksLevel"><value>ApplicationComponent</value></property>
        </object>
    </property>
    <property name="Roles">
        <list>
            <value>Admin : Administrator role</value>
            <value>User : User role</value>
            <value>Manager : Administrator role</value>
        </list>
    </property>
    <property name="Components">
        <list>
            <ref object="MyApp.EnterpriseServices.UserManager"/>
        </list>
    </property>
    <property name="Assembly"><value>MyComPlusApp</value></property>
            </object>

This exporter will put all proxy classes for the specified list of components into the specified assembly, sign the assembly, and register it with the specified COM+ application name. If application does not exist it will create it and configure it using values specified for Description, AccessControl and Roles properties.

11.4. Client Side

Because serviced component classes are dynamically generated and registered, you cannot instantiate them in your code using new operator. Instead, you need to use Spring.Enterprise.ServicedComponentFactory definition, which also allows you to specify configuration template for the component as well as the name of the remote server component is running on, if necessary. An example is shown below

<object id="enterpriseUserManager" type="Spring.Enterprise.ServicedComponentFactory, Spring.Services">
    <property name="Name"><value>MyApp.EnterpriseServices.UserManager</value></property>
    <property name="Template"><value>userManager</value></property>
            </object>

You can then inject this instance of the IUserManager into a client class and use it just like you would use original SimpleUserManager implementation. As you can see, by coding your services as plain .Net objects, against well defined service interfaces, you can achieve true location transparency for your services through configuration.