Spring-Boot minimizes the configuration needed to configure a Spring application. It does this using so-called Spring-Boot starters.

Above this Spring-Boot allows the inversion of the typical deployment. Usually a container is provided in which a Spring application is run in. Even if in theory an application server or a servlet container can host multiple applications, it is more a reality that usually one container hosts only a single application. With Spring-Boot this concept is inverted and the application provides the container it needs. So if your application provides a Spring rest-controller (A class annotated with @Controller) Spring-Boot detects this and automatically configures a servlet-engine to run the Controller in. 

With the Spring-Boot starter that is now part of BlazeDS 4.7.3-SNAPSHOT this concept is extended to BlazeDS. This makes it possible to create a Spring application in which individual Spring services are exposed via BlazeDS just by adding one Maven dependency, providing a services-config.xml in the classpath and annotating at least one service with @RemotingDestination.

Example

The basic setup of a Spring-Boot application is handled by the spring-boot-maven-plugin which is configured in a war Maven module.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.apache.flex.blazeds.examples</groupId>
    <artifactId>blazeds-spring-boot-example</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <build>
        <plugins>
            <!-- Make Spring-Boot build an executable war -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>1.3.3.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <classifier>exec</classifier>
                </configuration>
            </plugin>
            <!--
                We are building a servlet 3.0 application we don't
                need a web.xml, so we have to disable a check on the
                plugin.
            -->
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <!--
            This will pull in and configure the servlet engine as
            well as the BlazeDS server.
        -->
        <dependency>
            <groupId>org.apache.flex.blazeds</groupId>
            <artifactId>blazeds-spring-boot-starter</artifactId>
            <version>4.7.3-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

Important to note that the classifier of "exec" produces an executable war. Executable wars are normal war archived, but they come with a bootloader, that allows to run the war as an executable jar from the commandline. Without this option you would be creating a simple war that you have to manually deploy in a servlet engine. The second important thing is that usually war files have to provide a web.xml inside, this is no longer required with servlet 3.0 and as our Spring-Boot application is automatically a servlet 3.0 application, we have to tell the war plugin not to complain about the missing web.xml file.

Last but most important is a reference to the blazeds-spring-boot-starter artifact, which pulls in all the BlazeDS magic.

After creating this pom, we have generally setup the Spring-Boot part of the application. In order to activate the magic of the BlazeDS starter, we also need to provide a services-config.xml in the classpath. This is located in src/main/resources/META-INF/flex/services-config.xml.

Here an example services-config.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
    <services>
        <service id="remoting-service" class="flex.messaging.services.RemotingService">
            <adapters>
                <adapter-definition
                        id="java-object"
                        class="flex.messaging.services.remoting.adapters.JavaAdapter"
                        default="true"/>
            </adapters>
            <default-channels>
                <channel ref="websocketAmf"/>
                <channel ref="longPollingAmf"/>
                <channel ref="shortPollingAmf"/>
            </default-channels>
        </service>
    </services>

    <channels>
        <channel-definition id="websocketAmf" class="mx.messaging.channels.StreamingAMFChannel">
            <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/websocket-amf"
                      class="flex.messaging.endpoints.StreamingAMFEndpoint"/>
            <properties>
                <server-to-client-heartbeat-millis>5000</server-to-client-heartbeat-millis>
                <add-no-cache-headers>true</add-no-cache-headers>
            </properties>
        </channel-definition>
        <channel-definition id="longPollingAmf" class="mx.messaging.channels.AMFChannel">
            <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/long-polling-amf"
                     class="flex.messaging.endpoints.AMFEndpoint"/>
            <properties>
                <polling-enabled>true</polling-enabled>
                <wait-interval-millis>0</wait-interval-millis>
                <polling-interval-millis>1000</polling-interval-millis>
                <max-waiting-poll-requests>100</max-waiting-poll-requests>
                <piggybacking-enabled>true</piggybacking-enabled>
                <add-no-cache-headers>true</add-no-cache-headers>
            </properties>
        </channel-definition>
        <channel-definition id="shortPollingAmf" class="mx.messaging.channels.AMFChannel">
            <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/short-polling-amf"
                      class="flex.messaging.endpoints.AMFEndpoint"/>
            <properties>
                <polling-enabled>true</polling-enabled>
                <polling-interval-millis>3000</polling-interval-millis>
                <piggybacking-enabled>true</piggybacking-enabled>
                <add-no-cache-headers>true</add-no-cache-headers>
            </properties>
        </channel-definition>
    </channels>

    <flex-client>
        <!-- Make sure clients are automatically expired -->
        <timeout-minutes>720</timeout-minutes>
    </flex-client>

    <logging>
        <!--
                Logging inside BlazeDS is completely turned off.
                The UniversalExceptionTranslator will handle logging
                of exceptions inside Spring.
        -->
        <target class="flex.messaging.log.ConsoleTarget" level="None"/>
    </logging>
</services-config>

This config consists of two important parts:

  1. services (In this part we configure the remoting-service which will act as a bridge between BlazeDS and Spring. This service allows to invoke methods of Spring services)
  2. channels (In this part we configure the different types of communication we want to allow. In this case we have a web-socket-like server push direct connection, some simulated server-push and a default polling connection. The application can choose which channel it want's to use)
  3. flex-client (Controlls some basic default settings of BlazeDS clients) (Optional)
  4. logging (This simply disables BlazeDS logging as we are using Springs default logging. Without this the amount of log-output could be insane) (Optional)

As soon as this file is in place, BlazeDS is completely setup. But till now we would not be able to access a single Spring service. In order to register a Spring service as remoting destination we need to annotate that service with the RemotingDestination annotation:

import org.springframework.flex.remoting.RemotingDestination;
import org.springframework.stereotype.Service;

@Service("movementService")
@RemotingDestination
public class MovementServiceImpl {

    public MovementServiceImpl() {
    }

    public void stop() {
        System.out.println("Stop");
    }

    public void moveForward() {
        System.out.println("Forward");
    }

    public void moveLeft() {
        System.out.println("Left");
    }

    public void moveRight() {
        System.out.println("Right");
    }

    public void moveBack() {
        System.out.println("Back");
    }
}

On the Flex side, we only have to configure a simple RemoteObject object:

<fx:Declarations>
    <s:RemoteObject id="movementService"
                    destination="movementService"
                    endpoint="http://localhost:8080/messagebroker/short-polling-amf"
                    fault="onFault(event)">
        <s:method name="stop" result="onResult(event)"/>
        <s:method name="moveForward" result="onResult(event)"/>
        <s:method name="moveLeft" result="onResult(event)"/>
        <s:method name="moveRight" result="onResult(event)"/>
        <s:method name="moveBack" result="onResult(event)"/>
    </s:RemoteObject>
</fx:Declarations>

With this in place, calling the remote method is as easy as this:

protected function onForwardClick(event:MouseEvent):void {
    movementService.moveForward();
}

Enjoy and have fun with this cool new addition to BlazeDS.

This feature is a first version. It will definitely not fit in 100% of the use-cases, but it's a good start and it makes using BlazeDS insanely easy.