Funcatron plays well with Spring Boot.

Follow these steps to wrap your Spring Boot app as a Funcatron app. Funcatron currently works with Spring Boot 1.4.x.

  1. Make sure you annotate you Application class (the main entrypoint) with @EnableSwagger2.

  2. Include a file like FuncatronBridge.java that tell Funcatron where to find things.

    import funcatron.service.spring_boot.SpringBootWrapper;
    /**
     * Bridges Spring to Funcation. You must include a file like this in your project as
     * Spring's maven packaging doesn't pick up the services from included projects.
     */
    public class FuncatronBridge extends SpringBootWrapper {
    public FuncatronBridge() {
        super();
    }
        @Override
        public Class<?>[] classList() {
            return new Class<?>[]{Application.class};
        }
    }
  3. Include a file like MyMockServer.java that mocks the HTTP server

    import funcatron.service.spring_boot.MockServer;
    import org.springframework.stereotype.Component;
    /**
     * Register our own mock server. Once again, necessary to mock requests
     */
    @Component
    public class MyMockServer extends MockServer {
    }
  4. Include the following dependencies in your pom.xml file:

    <dependency>
        <groupId>funcatron</groupId>
        <artifactId>intf</artifactId>
        <version>0.3.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>funcatron</groupId>
        <artifactId>spring_boot</artifactId>
        <version>0.3.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>funcatron</groupId>
        <artifactId>devshim</artifactId>
        <version>0.3.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>1.4.3.RELEASE</version>
    </dependency>
  5. Include the following repositories in your pom.xml file:

    <repository>
        <id>snapshots-repo</id>
        <url>https://oss.sonatype.org/content/repositories/snapshots</url>
        <releases><enabled>false</enabled></releases>
        <snapshots><enabled>true</enabled></snapshots>
    </repository>
  6. Add the following to your pom.xml’s <plugins> section:

    <plugin>
        <groupId>eu.somatik.serviceloader-maven-plugin</groupId>
        <artifactId>serviceloader-maven-plugin</artifactId>
        <version>1.0.7</version>
        <configuration>
            <services>
                <param>funcatron.intf.ClassloaderProvider</param>
                <param>funcatron.intf.OperationProvider</param>
            </services>
        </configuration>
        <executions>
            <execution>
                <goals>
                    <goal>generate</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
    <plugin>
        <groupId>org.codehaus.gmaven</groupId>
        <artifactId>gmaven-plugin</artifactId>
        <version>1.5</version>
        <executions>
            <execution>
                <phase>validate</phase>
                <goals>
                    <goal>execute</goal>
                </goals>
                <configuration>
                    <providerSelection>2.0</providerSelection>
                    <properties>
                        <script>git rev-parse HEAD</script>
                    </properties>
                    <source>
                        def command = project.properties.script
                        def process = command.execute()
                        process.waitFor()
                        def describe = process.in.text.trim()
                        project.properties.setProperty('gitVersion',describe)
                    </source>
                </configuration>
            </execution>
        </executions>
    </plugin>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>2.1</version>
        <configuration>
            <archive>
                <manifest>
                    <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                </manifest>
                <manifestEntries>
                    <GitHeadRev>${gitVersion}</GitHeadRev>
                </manifestEntries>
            </archive>
        </configuration>
    </plugin>
    </plugins>

That’s about it. Funcatron should find and dispatch requests properly with this information.

So…​ here’s how to run the app devmode

What’s “devmode”? It’s the development-time connection between your running app (which can be running in your IDE and have breakpoints) and a very slimmed down Funcatron HTTP front-end. Doing super-fast turn-around (compile and go or even live development) is simple with “devshim”.

You have to do two things…​ fire up a “devmode” version of Funcatron and Register your app with the local, not clustered, version of Funcation.

Add this to your Spring Boot app…​ you start the Spring Boot app normally, then create a Funcatron “Context”, get the Swagger information, and then Register with the “devmode” Funcatron instance.

/**
 * Just a plain old Spring app... but make sure you include the {@code @EnableSwagger2}
 * annotation to hook up SpringFox and generate Swagger
 */
@SpringBootApplication
@EnableSwagger2
public class Application {

    public static void main(String[] args) throws Exception {
        SpringApplication sa = new SpringApplication(Application.class);

        sa.run(args);


        // Now that we've got the app running... start the whole funcatron stuff
        ContextImpl.initContext(new HashMap<>(),
                Application.class.getClassLoader(),
                Logger.getAnonymousLogger());

        // Using SpringFox, get the Swagger
        Map swagger = ContextImpl.runOperation(Constants.GetSwaggerConst,
                new HashMap<>(),
                Logger.getAnonymousLogger(), Map.class);

        // write it to a temp file
        File tmpFile = File.createTempFile("funcatron_swagger_", ".txt");
        tmpFile.createNewFile();
        FileOutputStream fos = new FileOutputStream(tmpFile);
        fos.write(swagger.get("swagger").toString().getBytes("UTF-8"));
        fos.flush();
        fos.close();

        // delete the temp file on exit
        tmpFile.deleteOnExit();

        // register with the devshim
        Register.register(tmpFile);
    }
}

Next, fire up a “devmode” Funcatron instance with:

docker run -ti --rm  -e TRON_1=--devmode -p 3001:3001 -p 54657:54657 funcatron/tron:latest

When you run your app and call the Register.register(…​) method, your app will connect to the Funcatron instance and you’ll be able to make http requests on http://localhost:3001/. Those requests will be forwarded to your app. If you run your app in debug mode in your IDE, you can set breakpoints in your running app and see what’s going on.