Fabric3, the SCA implementation (as foundation for SOA)

I will not write about SCA or fabric3 in detail, maybe another time... I've "met" SCA for the first time on project, where we started with SOA. As there were some indications, that starting with ESBs is not as good shortcut as it looks, we reach for SCA. The whole project is openSource based and it is openSource itself, so we were looking for some open solution for us.

At first we started to work with Apache Tuscany (back then fabric3 was not so mature project). After couple of weeks with Tuscany we were so dissapointed with it (not just the community, but also the code itself - many bugs) that we created our own bindings (among other things) for the functional prototype, in hope, that next versions will be better. We hoped, we just change the underlying bindings from our implementation to tuscany and it will work. Well, it wasn't - still lots of problems during implementation...

Couple of months ago I was just randomly browsing on Mule community page, I think they have some SCA implementation and I remembered the fabric3. I checked their page, it looks like they've made some progress after some time and I checked their solution. Version 1.4 was the first version I started to work with, and I was amazed, what it can do. We was hoping to see that in Tuscany implementation for some time, I'm glad, we found it in fabric3.

We was looking for framework that provides infrastructure to build fault tolerance, reliability and high availability (as written on Glassfish shoal home page :-) ). Fabric3 offer standalone engine (among others) which distributed environment was in version 1.4 based on Glassfish Shoal clustering (from 1.5 it's based on JGroups). The project itself is very mature - it is the most complete SCA implementation. Also I don't think I ever worked with openSource project, where the response for some question / issue was so quick. (you can dream about it with JBoss or Apache - if you not pay for support :-) )

I'm putting together some real experience with it and I hope I will publish them here soon...
I will start with running standalone fabric3 runtimes on linux based systems (as our servers are CentOS based with some XEN virtualization).




Fabric3 runtimes installation on linux (RedHat/CentOS/Fedora) from rpm packages


As I mentioned, we started to work with fabric3 and after some time we were in need of install/start/stop runtimes in a nicer cleaner (more automatic) way. Let's call it clean install - as our system administrators calls it. It was basicly their idea to let maven create rpm packages which could be easy installable on any RedHat/CentOS/Fedora based system with ease. Of course maven configuration was left on us, which wasn't as hard as I first thought. Basic knowledge of rpm is required. I think some light browsing of maven rpm plugin page could be a good start...

As example is woth more than empty words I will show listings from maven pom.xml file, which will help to anyone who works with maven. I will demonstrate creating rpm packages from fabric3 runtimes on project with controller and two runtimes. Pretty simple and of course creating a bigger project is not a problem when you get the basics here. This project will consists from one rpm which will contains all. That's not a very good aproach - according to our admins (it has some disadvantages), but for the simplicity it will suffice. The best will be to have one main rpm with not writable (common) dirs and another rpms (for example one for each zone) with writable dirs, which has dependency on the main one. But as I said, if you get the basics from simple example, creation
of more advanced one is piece of cake (I can provide some examples, if someone will require it)...

First here is fabric3 new directory layout (as from 1.6 version):
  • /bin - startup modules 
  • /lib - modules required to start the runtime host 
  • /boot - modules required for the runtime bootstrap and primordial system services 
  • /host - libraries shared between the runtime and applications (e.g. web services annotations)
  • /extensions - extension modules that are to be loaded by all runtime instances  
  • /runtimes - specific runtime instance configuration is hosted by default under this directory - top level directory for a runtime configuration 
    • /config - contains systemConfig.xml for configuring the runtime and extensions 
    • /deploy - file system deploy directory for the controller and single-VM runtimes
    • /data - persistent data directory for a runtime instance (e.g. transaction logs) 
    • /tmp - temporary data and artifact cache for a runtime instance 
    • /repository 
      • /runtime - extensions loaded only for the runtime image 
      • /user - user contributions (only populated on the controller and single-VM runtimes
Ok, we have simple maven project with one module and pom packaging. What we need is just some dirs with config files (systemConfig.xml), which will be installed to  writable dirs.
Our directory structure will be as follows:

/opt/myapp/ - common, not writable dirs
/var/myapp/ - writable dirs (temps, config, ...)

Before reading any further, I have to say, that this wasn't working for us on windows. That was actually the final drop to change our developers machines to linux. On servers we have CentOS and on developers machines we have fedora. Until now fedora was just a pleasure to work with (if you have skilled admins for support :-) )...

I will describe the maven project pom.xml. Besides that, there is almost nothing in the project (just some starting/stopping scripts and config files - I will get to that later).

 First you need to have fabric3-assembly-plugin in your pom, it will assemble the whole runtime to maven build dir:
<plugin>
  <groupId>org.codehaus.fabric3</groupId>
  <artifactId>fabric3-assembly-plugin</artifactId>
  <version>${fabric3.version}</version>
  <executions>
    <execution>
      <id>fabric3-assembly</id>
      <goals>
        <goal>fabric3-assembly</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <runtimeVersion>${fabric3.version}</runtimeVersion>
    <profiles>
      <!-- JMS support -->
      <profile>
        <groupId>org.codehaus.fabric3</groupId>
        <artifactId>profile-jms</artifactId>
        <version>${fabric3.version}</version>
      </profile>
      <!-- Web App support -->
      <profile>
        <groupId>org.codehaus.fabric3</groupId>
        <artifactId>profile-web</artifactId>
        <version>${fabric3.version}</version>
      </profile>
      <!-- JPA support -->
      <profile>
        <groupId>org.codehaus.fabric3</groupId>
        <artifactId>profile-jpa</artifactId>
        <version>${fabric3.version}</version>
      </profile>
    </profiles>
  </configuration>
</plugin>
As you can see, we have there profiles for JMS, JPA and WebApp - of course there are more of these profiles, you can use whatever you will need. Next is plugin for generating rpms:
<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>rpm-maven-plugin</artifactId>
  <version>2.1-20100401.201322-4</version>
  <extensions>true</extensions>
  <configuration>
    <copyright>MyCompany</copyright>
    <distribution>someDistribution</distribution>
    <group>someGroup</group>
    <packager>somePackager</packager>
    <changelogFile>src/changelog</changelogFile>
    <projversion>${project.version}</projversion>
    <name>fabric3-runtimes-all</name>
    <defaultDirmode>755</defaultDirmode>
    <defaultFilemode>644</defaultFilemode>
    <defaultUsername>someUser</defaultUsername>
    <defaultGroupname>someGroup</defaultGroupname>
    <preinstallScriptlet>
      <script>
        <!-- e.g. some script to create user/group -->
      </script>
    </preinstallScriptlet>
    <postremoveScriptlet>
      <script>
        <!-- e.g. some script to delete user/group -->
      </script>
    </postremoveScriptlet>
    <mappings>
      <!-- Main dirs -->
      <mapping>
        <filemode>755</filemode>
        <directory>/opt/myapp/</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/opt/myapp/bin</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/opt/myapp/lib</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/opt/myapp/boot</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/opt/myapp/host</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/opt/myapp/extensions</directory>
      </mapping>
      <mapping>
        <directory>/opt/myapp</directory>
        <directoryIncluded>true</directoryIncluded>
        <sources>
          <source>
            <location>${project.build.directory}/image/</location>
            <includes>
              <include>bin/</include>
              <include>lib/</include>
              <include>boot/</include>
              <include>host/</include>
              <include>extensions/</include>
            </includes>
          </source>
        </sources>
      </mapping>
      <!-- Writable dirs -->
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/controller</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime1</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime2</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/controller/data</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/controller/config</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/controller/tmp</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/controller/deploy</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/controller/repository</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/controller/repository/user</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/controller/repository/runtime</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/controller/data</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime1/config</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime1/tmp</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime1/deploy</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime1/repository</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime1/repository/user</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime1/repository/runtime</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime2/data</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime2/config</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime2/tmp</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime2/deploy</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime2/repository</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime2/repository/user</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/var/myapp/runtimes/runtime2/repository/runtime</directory>
      </mapping>
      <!-- Config dirs -->
      <!-- Here are all systemConfig.xml files -->
      <mapping>
        <configuration>noreplace</configuration>
        <filemode>644</filemode>
        <directory>/var/myapp/runtimes</directory>
        <directoryIncluded>true</directoryIncluded>
        <sources>
          <source>
            <filter>true</filter>
            <location>${basedir}/src/main/runtimes/</location>
          </source>
        </sources>
      </mapping>
      <!-- Scripts -->
      <mapping>
        <filemode>755</filemode>
        <directory>/opt/myapp/bin</directory>
        <directoryIncluded>true</directoryIncluded>
        <sources>
          <source>
            <filter>true</filter>
            <location>${basedir}/src/main/scripts/</location>
          </source>
        </sources>
      </mapping>
      <!-- Admin tools -->
      <mapping>
        <filemode>755</filemode>
        <directory>/opt/myapp/adminTools</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/opt/myapp/adminTools/bin</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/opt/myapp/adminTools/config</directory>
      </mapping>
      <mapping>
        <filemode>755</filemode>
        <directory>/opt/myapp/adminTools/lib</directory>
      </mapping>
      <mapping>
        <filemode>644</filemode>
        <directory>/opt/myapp/adminTools/bin</directory>
        <dependency>
          <includes>
            <include>org.codehaus.fabric3:fabric3-admin-cli</include>
          </includes>
        </dependency>
      </mapping>
      <mapping>
        <filemode>644</filemode>
        <directory>/opt/myapp/adminTools/lib</directory>
        <dependency>
          <excludes>
            <exclude>org.codehaus.fabric3:fabric3-admin-cli</exclude>
          </excludes>
        </dependency>
      </mapping>
      <mapping>
        <configuration>noreplace</configuration>
        <filemode>644</filemode>
        <directory>/opt/myapp/adminTools/config</directory>
        <directoryIncluded>false</directoryIncluded>
        <sources>
          <source>
            <filter>true</filter>
            <location>${basedir}/src/main/adminTools/config/settings.xml</location>
          </source>
        </sources>
      </mapping>
      <mapping>
        <filemode>644</filemode>
        <directory>/opt/myapp/adminTools/bin</directory>
        <directoryIncluded>false</directoryIncluded>
        <sources>
          <source>
            <filter>true</filter>
            <location>${basedir}/src/main/adminTools/bin/</location>
          </source>
        </sources>
      </mapping>
    </mappings>
  </configuration>
</plugin>
For creating rpm package be sure to have installed linux utility rpmbuild and run maven command:
mvn clean package rpm:attached-rpm

Ok, that's all for creating rpm package, actually pretty simple, as you can see. As you can see, I packed also fabric3 admin tools with the runtimes main dirs, so I can use it for deploying scripts. I will write more about all the scripts which was mentioned in plugin configuration in next article. Simple script for starting runtimes, deploying app, ...

If you want to install rpm package use
rpm -i packane_name.rpm
for uninstalling use
rpm -e packane_name 
But you can find out more about it in linux man pages about rpm.