2 Configuration

Cinnamon can be configured in three different ways: You can use Cinnamons own XML configuration format, the custom Spring configuration namespace or you can configure Cinnamon programmatically. The sections of this chapter will present each of the configuration artifacts for all three options.

2.1 Spring vs. Cinnamon XML

The choice which format to chose should be quite easy: If you intend to use Spring in your project, the most convenient way to configure Cinnamon will be to use the custom Spring configuration namespace. This way there will be no separate configuration file for Cinnamon, all its configuration artifacts will live in a Spring ApplicationContext.

If you don't use Spring, you should use Cinnamons own XML configuration format and avoid any dependency on the Spring framework.

Programmatic configuration can be applied additionally, no matter which configuration option you chose.

2.2 web.xml Deployment Descriptor

First you have to configure the Cinnamon servlet which is the main entry point for each AMF request. The servlet is a very thin layer on top of the Cinnamon framework. Except for this layer the framework does not depend on the Servlet API, so you could use it in different deployment scenarios. In that case you would use the RequestProcessor class as the entry point. But since AMF via HTTP is the most common way to use Cinnamon we confine ourselves to show you how to configure the servlet.

Deployment without Spring

Here is an example configuration:

<servlet>
    <servlet-name>Cinnamon</servlet-name>
	<servlet-class>org.spicefactory.cinnamon.web.CinnamonServlet</servlet-class>
    <init-param>
        <param-name>configLocation</param-name>
        <param-value>/WEB-INF/services.xml</param-value>
    </init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
	 
<servlet-mapping>
    <servlet-name>Cinnamon</servlet-name>
    <url-pattern>/service/*</url-pattern>
</servlet-mapping>

The init parameter is optional, the default location for the configuration file is /WEB-INF/cinnamon-config.xml . Otherwise this is just standard servlet configuration.

Deployment with Spring

If you add Spring to the mix it gets a bit more involved:

<context-param>
    <param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/config/*.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<listener>
    <listener-class>
    	org.springframework.web.context.request.RequestContextListener
    </listener-class>
</listener>
		
<servlet>
    <servlet-name>Cinnamon</servlet-name>
	<servlet-class>org.spicefactory.cinnamon.web.CinnamonServlet</servlet-class>
    <init-param>
        <param-name>configLocation</param-name>
        <param-value>spring</param-value>
    </init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
	 
<servlet-mapping>
    <servlet-name>Cinnamon</servlet-name>
    <url-pattern>/service/*</url-pattern>
</servlet-mapping>

The context-param and listener entries are needed to set up Spring. The contextConfigLocation context parameter is just an example. You can point to any location you like as long as there will be a root WebApplicationContext since it will be that context where the Cinnamon framework will look for its own configuration artifacts. You do not need to set up the Spring DispatcherServlet if you do not intend to use it. If you include it the RequestContextListener would not be needed.

Finally you have to set the configLocation init parameter for the CinnamonServlet to the special value spring which will instruct Cinnamon to use the root WebApplicationContext for configuration and not its own configuration format.

All remaining configuration will live either in Springs ApplicationContext or in Cinnamons XML configuration file.

2.3 Root tags for XML configuration

When using Spring you just have to add the Cinnamon namespace to the root beans tag:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:cinnamon="http://www.spicefactory.org/cinnamon/0.2"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans   
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.spicefactory.org/cinnamon/0.2
        http://www.spicefactory.org/cinnamon/schema/0.2/cinnamon-spring.xsd">

    <!--bean declarations -->

</beans>

When using Cinnamon XML the framework expects all configuration to be in a single file:

<?xml version="1.0" encoding="UTF-8"?>
<cinnamon-config 
    xmlns="http://www.spicefactory.org/cinnamon/0.2"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.spicefactory.org/cinnamon/0.2
	    http://www.spicefactory.org/cinnamon/schema/0.2/cinnamon-config.xsd">

    <!- child tags -->

</cinnamon-config>

2.4 Setup for programmatic configuration

Sometimes you may need to dynamically configure Cinnamon on application startup or to dynamically add some configuration artifacts to the existing declarative configuration. You can accomplish that with an implementation of the ConfigurationProcessor interface and add that to the configuration.

Spring configuration

With Spring you can add the processor with a normal bean tag:

<bean class="com.domain.config.MyConfigurationProcessor"/>

Cinnamon XML

Cinnamons own XML format has a dedicated processor tag:

<processor type="com.domain.config.MyConfigurationProcessor"/>

You can add more than one processor. Each processor will be invoked once before the content of the configuration file is processed and once after processing. Accordingly the ConfigurationProcessor interface has a preProcess and a postProcess method. When preProcess is called, the CinnamonConfig instance that gets passed to the method is essentially empty. When postProcess is called, all configuration artifacts contained in the configuration file have already been added to the passed CinnamonConfig instance.

All examples for programmatic configuration in the following sections use the preProcess method. Examples for the postProcess method would be identical.

2.5 Service Factories

A ServiceFactory is responsible for locating or creating service instances for a particular request. Usually it will take into account the name of the service that can be retrieved with ServiceRequest.getServiceName() for locating the service. Cinnamon currently comes with two builtin ServiceFactories: One for Spring managed beans and one for plain Java classes. Support for EJB3 Session beans and Seam components is scheduled for version 0.4.0. Additionally you can easily implement you own ServiceFactory.

Spring configuration

You may not need to define any ServiceFactory when using Spring because in this case the builtin SpringServiceFactory will be added to the configuration by default. For exposing Spring beans as a Cinnamon service you will not need to add any further factories. In case you do need to include some other means of locating services you can add any number of additional service factories with standard bean tags. Those beans have to implement the ServiceFactory interface. The id of the bean will be used as the name of the factory:

<bean id="factoryName" class="com.domain.MySpecialServiceFactoryImpl"/>

Cinnamon XML

Without Spring no factory will be added by default, so you have to define at least one before you can add any services.

<factory name="factoryName" type="com.domain.MySpecialServiceFactoryImpl"/>

You can also use the nested <property> tag for setting simple properties (Boolean, Number, String, Class) of the factory.

Programmatic configuration

public void preProcess (CinnamonConfig config) {
    config.addServiceFactory("factoryName", new MySpecialServiceFactoryImpl());
}

In case you need to implement your own ServiceFactory, the most convenient way usually is to subclass AbstractMethodInvocationServiceFactory and implement the getTargetInstance (ServiceRequest request) method. For example implementations just have a look at the builtin factories.

2.6 Services

The main configuration task is to tell Cinnamon which services to expose. This involves specifying:

2.6.1 Exposing services

With Spring configuration this usually means exposing a Spring bean, with Cinnamon XML configuration you usually use a different kind of factory.

Spring configuration

Exposing a Spring bean as a Cinnamon service is the easiest option. Just add a child tag to the standard bean configuration:

<bean id="echoService" class="com.domain.service.EchoServiceImpl">
    <property name="someProperty" value="someValue"/>
    <property name="someOtherProperty" value="someOtherValue"/>
    <cinnamon:export-service service-interface="com.domain.service.EchoService"/>
</bean>

You have to explicitly specify the service interface. This is consistent with Springs own RemoteExporters. The name of the service will be the id of the bean. This is the name that you specify on the client side when you call ServiceChannel.createProxy(serviceName, service) .

The export-service tag has an optional attribute process-annotations which is true by default. If you set this to false only operations configured explicitly in XML will be exported. See 2.6.2 Service Operations for details.

In rare cases you may want to export other kinds of services than Spring beans even when you use Cinnamon with Spring. You then have to set up the service factory that you want to use for creating and locating that service as a Spring bean:

<bean id="specialFactory" class="com.domain.MySpecialServiceFactoryImpl"/>

After that you can use the special <cinnamon:service> tag to configure a service using that factory:

<cinnamon:service 
    name="special" 
    factory-ref="specialFactory" 
    service-interface="com.domain.SomeSpecialService"
/>

The attributes for that tag are the same as for the service tag in Cinnamons own configuration format. See the next section for details about their purpose.

Cinnamon XML configuration

An example including all optional attributes:

<service
    factory-ref="pojo"
    name="myService"
    service-interface="com.domain.service.EchoService"
    service-implementation="com.domain.service.EchoServiceImpl"
    scope="session"
    process-annotations="true"
/>

The purpose of each attribute in detail:

factory-ref required The name of the factory to use for this service. Should correspond to the name attribute of a factory tag.
name required The name of the service. Should correspond to the name you specify in ActionScript when creating the proxy with ServiceChannel.createProxy(serviceName, service) .
service-interface required The Java interface that all instances of this service implement.
service-implementation optional The Java class that implements the service interface. This attribute is currently only needed by the builtin PlainJavaServiceFactory.
scope optional The scope of the service, valid values: request, session or application. This attribute is currently only needed by the builtin PlainJavaServiceFactory. Depending on the value services created by that factory will be stored as an attribute in the ServletRequest, HttpSession or ServletContext.
process-annotations optional Whether annotations on the service interface should be processed or not. The default is true. Even if annotations are processed you can still overwrite them in XML with operation child tags. See 2.6.2 Service Operations for details.

Programmatic configuration

public void preProcess (CinnamonConfig config) {
    ServiceFactory factory = new PlainJavaServiceFactory();
    config.addServiceFactory("pojo", factory);
    ServiceConfig service = new ServiceConfig("pojo", "session", EchoService.class,
        EchoServiceImpl.class, new ArrayList<OperationConfig>(), true);
    service.setFactory(factory);
    config.addServiceConfig(service);
}

2.6.2 Service Operations

Finally you have to specify which operation to expose for each service. You can do that with annotations or in XML or any combination of both. This choice does not depend on the decision to use Spring or not, annotations and XML can be used with or without Spring. Discussions on the pros and cons of annotations vs. XML for configuration can become quite religious very quickly among Java developers. So if you don't like one of these two options please just ignore it.

Declaring operations with annotations

If the process-annotations attribute is set to true (or it is missing so that it defaults to true) the specified service interface and all of its superinterfaces are scanned for Cinnamons annotations. Cinnamon comes with two annotations:

@CinnamonService This can be applied to interfaces. If this annotation is present on a service interface, all methods declared on that interface will be exposed to clients. Methods of superinterfaces are not included. They must be added explicitly with annotations on the superinterface.
@CinnamonOperation This annotation can be applied to methods of service interfaces. It exposes a single method of a service interface to clients. It has an optional alias attribute if you want to use a different name for the method in the corresponding AS3 service interface. This might be necessary if you use a service interface with overloaded methods since AS3 has no support for overloading.

Declaring operations in XML

XML declarations can be added no matter if process-annotations is set to true or not. If it is set to true, operations declared with annotations will added to those declared in XML, if it is set to false annotations will be ignored.

Spring example:

<bean id="echoService" class="com.domain.service.EchoServiceImpl">
    <property name="someProperty" value="someValue"/>
    <property name="someOtherProperty" value="someOtherValue"/>
    <cinnamon:export-service service-interface="com.domain.service.EchoService">
        <cinnamon:operation name="getAllCustomers"/>
        <cinnamon:operation name="getCustomerById" param-types="int"/>
    </cinnamon:export-service>
</bean>

Cinnamon XML example:

<service
    factory-ref="pojo"
    name="myService"
    service-interface="com.domain.service.EchoService"
    service-implementation="com.domain.service.EchoServiceImpl"
    scope="session"
    process-annotations="false"
>
    <operation name="getAllCustomers"/>
    <operation name="getCustomerById" param-types="int"/>
</service>

Attributes of the operation tag:

name required The name of the method to add as an operation.
alias optional The name to use for the method in the AS3 service interface. This might be useful if you use a service interface with overloaded methods since AS3 has no support for overloading.
param-types optional A list of comma-separated fully qualified class names. Specifies the method parameters, omission of this attribute tells Cinnamon to look for a method without parameters.

2.7 Class Mappings

Each ActionScript class that should automatically map to its corresponding Java class has to be added to the Cinnamon configuration. Furthermore the mapping has to be set up in ActionScript, either with the [RemoteClass] metadata tag (which only works in Flex Applications) or through a call to flash.net.registerClassAlias (for details see 3.1 Registering Class Mappings).

There are two different types of mappings: Bean mappings which map public properties of ActionScript classes to public Java Bean properties and externalizable mappings which may be used if you need more control over encoding and decoding. Let's look at both options in detail.

2.7.1 Bean Mappings

Assume you want to map this simple Person class:

package com.domain.model {

    [RemoteClass(alias="model::Person")]
    public class Person {

        private var _firstname:String;
        private var _lastname:String;

        function Person () {

        }

        public function get firstname () : String {
            return _firstname;
        }

        public function get lastname () : String {
            return _lastname;
        }

        public function set firstname (value:String) : void {
            _firstname = value;
        }

        public function set lastname (value:String) : void {
            _lastname = value;
        }
    }
}

In this case we registered the alias with the RemoteClass metadata tag. Many use the fully qualified class name for the alias, but if you want to squeeze a few bytes you can use anything you want. Note that if you want a two-way mapping you need read-write properties. Only if you know that you will only send instances of a particular class from client to server and never receive them back you can chose to use read-only properties. Finally the ActionScript class needs a constructor that does not have any parameters except for optional ones.

The corresponding Java Bean class would look like this:

package com.domain.model;

public class Person {

    private String firstname;
    private String lastname;

    public Person () {

    }

    public String getFirstname () {
        return firstname;
    }

    public String getLastname () {
        return lastname;
    }

    public void setFirstname (String value) {
        firstname = value;
    }

    public void setLastname (String value) {
        lastname = value;
    }
}

Again the properties have to be read-write unless you know the mapping will only be needed one way. Finally you have to tell Cinnamon about this mapping.

Spring configuration

With Spring you should use a tag from the Cinnamon configuration namespace:

<cinnamon:bean-mapping alias="model::Person" java-class="com.domain.model.Person"/>

Cinnamon XML

It's the same tag, just now in the default namespace:

<bean-mapping alias="model::Person" java-class="com.domain.model.Person"/>

Attributes of the bean-mapping tag

The tag has several optional attributes. The only required one is java-class.

java-class required The fully qualified name of the Java class to be mapped.
alias optional The alias that was registered in ActionScript (default: the value of the java-class attribute).
action-script-class optional The fully qualified name of the ActionScript class (default: the value of the alias attribute) This attribute is reserved for future use. In later versions it will be used for auto-generation of ActionScript proxy classes.
java-factory optional The fully qualified name of the Java factory class. The class must implement the MappedObjectFactory interface. For an example see 2.7.3 Using factories and the Externalizer interface.
omit-properties optional A comma-separated list of property names. All properties listed in this attribute will not be sent from the server to the client. This might be useful for classes like User, where you don't want to send password or email properties to all clients. This attribute has no effect on which properties are sent from client to server.

Programmatic configuration

public void preProcess (CinnamonConfig config) {
    ReflectionFactory factory = config.getReflectionFactory();
    config.addMapping(new BeanClassMapping("model::Person", 
            null, Person.class, null, null, factory));
}

This is equivalent to the XML examples above. The null values correspond to optional attributes.

Excluding properties

You can exclude one or more properties from being encoded in AMF3. The necessary configuration tweaks are different for AS3 and Java.

For excluding properties from being sent from Java to AS3 you have to use the omit-properties attribute of the bean-mapping tag in the Cinnamon configuration:

<cinnamon:bean-mapping
    alias="User" 
    java-class="com.domain.model.User"
    omit-properties="password,roles"
/>

For excluding properties from being sent from AS3 to Java you just have to add the [transient] metadata tag to the corresponding properties of your AS3 class:

[Transient]
public function get stream () : NetStream {
    return _stream;
}

2.7.2 Externalizable Mappings

This type of mapping requires more work when implementing the mapped classes but offers a lot more flexibility than bean mappings. You can encode and decode private or protected properties and fields or decode data structures that do not correspond to properties at all. The ActionScript class has to implement flash.utils.IExternalizable , the corresponding Java class has to implement org.spicefactory.cinnamon.io.Externalizable . The latter interface is not related to java.io.Externalizable , it rather mirrors the methods of the Flash IExternalizable interface.

Let's show a simple example of a Book class that contains an Array of Chapter instances. Since we don't want to expose the internal Array as a public property, we chose to use an externalizable mapping for this class. This is the ActionScript implementation of this class:

package com.domain.model {

    import flash.utils.IExternalizable;

    [RemoteClass(alias="model::Book")]
    public class Book implements IExternalizable {

        private var chapters:Array;
   
        function Book () {
            chapters = new Array();
        }

        public function getChapter (index:uint) : Chapter {
            return chapters[index] as Chapter;
        }

        public function addChapter (chapter:Chapter) : void {
            chapters.push(chapter);
        }

        public function writeExternal (output:IDataOutput) : void {
            output.writeInt(chapters.length);
            for each (var chapter:Chapter in chapters) {
                output.writeObject(chapter);
            }
        }

        public function readExternal (input:IDataInput) : void {
            var length:uint = input.readInt();
            for (var i:uint = 0; i < length; i++) {
                addChapter(input.readObject());
            }
        }

    }
}

In the writeExternal and readExternal methods we first read/write the number of chapters and then each Chapter instance. For the Chapter instances we use the writeObject / readObject methods which in turn use the AMF object decoding/encoding facility of the Flash Player. This way we can decode any combination of bean mappings and externalizable mappings. The way the Chapter instances will be encoded solely depends on whether they implement IExternalizable or not.

The corresponding Java class would look like this:

package com.domain.model;

import org.spicefactory.cinnamon.io.Externalizable

public class Book implements Externalizable {

    private List<Chapter> chapters = new ArrayList<Chapter>();

    public Book () {

    }

    public Chapter getChapter (int index) {
        return chapters.get(index);
    }

    public void addChapter (Chapter chapter) {
        chapters.add(chapter);
    }

    public void writeExternal (DataOutput output) {
        output.writeInt(chapters.size());
        for (Chapter chapter : chapters) {
            output.writeObject(chapter);
        }
    }

    public function readExternal (DataInput input) {
        var int length = input.readInt();
        for (int i = 0; i < length; i++) {
            addChapter(input.readObject());
        }
    }
   
}

Finally, as with bean mappings, you have to add this mapping to Cinnamons configuration:

Spring configuration

With Spring you should use a tag from the Cinnamon configuration namespace:

<cinnamon:externalizable-mapping alias="model::Book" java-class="com.domain.model.Book"/>

Cinnamon XML

It's the same tag, just now in the default namespace:

<externalizable-mapping alias="model::Book" java-class="com.domain.model.Book"/>

Attributes of the externalizable-mapping tag

The tag has several optional attributes. The only required one is java-class.

java-class required The fully qualified name of the Java class to be mapped.
alias optional The alias that was registered in ActionScript (default: the value of the java-class attribute).
action-script-class optional The fully qualified name of the ActionScript class (default: the value of the alias attribute) This attribute is reserved for future use. In later versions it will be used for auto-generation of ActionScript proxy classes.
java-factory optional The fully qualified name of the Java factory class. The class must implement the MappedObjectFactory interface. For an example see 2.7.3 Using factories and the Externalizer interface.
externalizer optional An optional Externalizer implementation. This may be useful if the Java class of this mapping cannot implement Externalizable. In this case you can add a separate Externalizer implementation to which decoding and encoding for this mapping will be delegated instead. For an example see 2.7.3 Using factories and the Externalizer interface.

Programmatic configuration

public void preProcess (CinnamonConfig config) {
    config.addMapping(new ExternalizableClassMapping("model::Book", 
            null, Book.class, null, null));
}

This is equivalent to the XML examples above. The null values correspond to optional attributes.

2.7.3 Using factories and the Externalizer interface

Bean mappings and standard externalizable mappings like the example in the preceding section should cover more than 90% of use cases. If you think you don't need any further flexibility you can safely skip this section until you hit a wall with the other options.

One example where a bit more work is involved would be a mapping for an existing Java class that does not have a default no-arg constructor. In this case you will need a factory to create instances of that class. The factory has to implement org.spicefactory.cinnamon.io.MappedObjectFactory . Factories can be used with bean mappings as with externalizable mappings. For bean mappings the properties of that bean will be set on the factory not on the bean instance when decoding objects. The factory may than use those properties any way it wants to create new instances of the mapped class. For externalizable mappings the factory would be responsible for decoding the object. Since it would be unfortunate if both the mapped class and the factory have to implement Externalizable this is an example where it is more convenient to use a separate Externalizer . Let's show an example for the Java Locale class which indeed does not come with a no-arg default constructor.

First we need an ActionScript Locale class to map to:

package com.domain.model {

    import flash.utils.IExternalizable;

    [RemoteClass(alias="Locale")]
    public class Locale {

        private var _language:String;
        private var _country:String;

        function Locale (language:String = "", country:String = "") {

        }

        public function get language () : String {
            return _language;
        }

        public function get country () : String {
            return _country;
        }

        public function writeExternal (output:IDataOutput) : void {
            output.writeUTF(language);
            output.writeUTF(country);
        }

        public function readExternal (input:IDataInput) : void {
            _language = input.readUTF();
            _country = input.readUTF();
        }

    }
}

Next we implement the Java Externalizer that encodes Locale instances and decodes LocaleFactories:

package com.domain.io;

public class LocaleExternalizer implements Externalizer<LocaleFactory, Locale> {

    public void readExternal (LocaleFactory factory, DataInput input) {
        factory.setLanguage(input.readUTF());
        factory.setCountry(input.readUTF());
    }

    public void writeExternal (Locale locale, DataOutput output) {
        output.writeUTF(locale.getLanguage());
        output.writeUTF(locale.getCountry());
    }

}

Note that the Externalizer interface has two type parameters, because when a factory is used like in this example the decoded object is not the final mapped instance but the factory instead. Finally we need to implement the factory itself:

package com.domain.io;

public class LocaleFactory implements MappedObjectFactory<Locale> {

    private String language;
    private String country;

    public void setLanguage (String value) {
        language = value;
    }

    public void setCountry (String value) {
        country = value;
    }

    Locale createInstance () {
        return new Locale(language, country);
    }

}

For this all to work you need to specify both the factory and the externalizer in Cinnamons configuration. For Spring configuration this would look like this:

<cinnamon:externalizable-mapping
    alias="Locale"
    java-class="java.util.Locale"
    java-factory="com.domain.io.LocaleFactory"
    externalizer="com.domain.io.LocaleExternalizer"
/>

The Cinnamon XML example would be almost identical, just with the tag in the default namespace.

It's a bit cumbersome to put together all those pieces but fortunately for most use cases you won't need factories or separate externalizers. This example was just added to demonstrate an edge case.

2.8 Converters

While class mappings offer a lot of functionality for mapping ActionScript classes to Java classes, often you have to deal with primitive types or arrays and collections and other types where class mappings would not be the appropiate solution to describe the way ActionScript data types should map to Java types. In contrast to class mappings converters only work one-way - from ActionScript types to Java types. Cinnamon reflects on the target type (method parameter or bean property) and selects the appropiate converter if necessary.

Cinnamon has some builtin converters for handling numbers, collections and arrays. They are applied for method parameters of service methods and properties for bean mappings. The Number Converter converts all primitive number types as well as BigInteger and BigDecimal instances. Thus if you have a service method that expects an int as a method parameter, you can send Number, uint or int from ActionScript, conversion will be applied automatically.

The Converters for Arrays and Collections are even more versatile. They do not only convert to the required type of Collection or Array, they convert the element types too if necessary. Let's say you send an Array of ints from ActionScript for example and the service method has the following method signature:

public void doSomething (Set<Float> someFloats);

In this case Cinnamon will automatically do all necessary conversion (creating a HashSet and converting all ints of the decoded Array to floats and add them to the Set). Ok, a Set of floats is a somewhat exotic example, but you get the idea.

You can also write your own Converters and add them to Cinnamons configuration. See the API Documentation for the Converter interface for more information. For example implementations you can have a look at the converters in the org.spicefactory.lib.convert package in the latest Spicelib Java Release. Those converters are all used in Cinnamon. Just make sure that you specify a concrete type as the type parameter for the Converter interface (i.e. you shouldn't use implements Converter<Object> for example) since Cinnamon uses the type parameter information to apply the Converter to matching target types.

Spring configuration

There is no special tag for adding converters, just use the standard bean tag:

<bean class="com.domain.io.MySpecialConverter"/>

You do not need to add an id attribute, the framework assembles the converters by type.

Cinnamon XML configuration

<converter type="com.domain.io.MySpecialConverter"/>

You can also use the nested <property> tag for setting simple properties (Boolean, Number, String, Class).

Programmatic configuration

public void preProcess (CinnamonConfig config) {
    config.addConverter(new MySpecialConverter());
}

2.9 Interceptors

Interceptors offer a way to add processing logic before the actual service method gets invoked. Do not confuse this feature with Springs AOP method interceptors. Since Spring integration is entirely optional, Cinnamon does not rely on Spring AOP for interception. Instead it comes with its own ServiceInterceptor interface that gives you access to the ServiceRequest and ServiceResponse instances of the current request. Interceptors can be used for things like caching or security which should process before the actual method invocation.

In the following example we use message headers to send a security token to the server and check if the HTTP session contains that token and throw a SecurityException if not:

public class SecurityInterceptor implements ServiceInterceptor {
     
    public Object intercept (ServiceProcessor processor) {
        String securityToken = processor.getRequest().getHeaderValue("token");
        if (isValid(securityToken)) {
            return processor.proceed();
        } else {
            throw new SecurityException();
        }
    }
         
    protected boolean isValid (String headerToken) {
        String sessionToken = (String)RequestContext
                .getCurrentContext().getSessionMap().get("token");
        return sessionToken.equals(headerToken);
    }
     
}

If you want to know how to send headers from ActionScript see 3.6 Adding message headers in the Client API chapter.

Spring configuration

There is no special tag for adding interceptors, just use the standard bean tag:

<bean class="com.domain.service.SecurityInterceptor"/>

You do not need to add an id attribute, the framework assembles the interceptors by type.

Cinnamon XML configuration

<interceptor type="com.domain. service.SecurityInterceptor"/>

You can also use the nested <property> tag for setting simple properties (Boolean, Number, String, Class).

Programmatic configuration

public void preProcess (CinnamonConfig config) {
    config.addInterceptor(new SecurityInterceptor());
}

2.10 XML Adapter

Cinnamon allows you to chose the XML DOM implementation to use when decoding or encoding XML from/to AMF3. It comes with three implementations of the XmlAdapter interface: W3C DOM (the default), dom4j and XOM. The following examples show you how to tell Cinnamon to use dom4j:

Spring configuration

<bean class="org.spicefactory.cinnamon.io.xml.Dom4jXmlAdapter"/>

Cinnamon XML

<xml-adapter type="org.spicefactory.cinnamon.io.xml.Dom4jXmlAdapter"/>

Programmatic configuration

public void preProcess (CinnamonConfig config) {
    XmlAdapter adapter = new Dom4jXmlAdapter();
    adapter.init();
    config.setXmlAdapter(adapter);
}

If you want to use a DOM implementation that is not supported out of the box, you can easily implement the XmlAdapter interface yourself. For example implementations you can look at the three default implementations in the org.spicefactory.cinnamon.io.xml package.