Open Sourcing service-starter.
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f9d7cba
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,16 @@
+.gradle
+.idea
+build/
+target/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+*.iml
+
+*.log
+
+*.toDelete
diff --git a/HEADER b/HEADER
new file mode 100644
index 0000000..d47a70e
--- /dev/null
+++ b/HEADER
@@ -0,0 +1,13 @@
+Copyright ${year} ${name}.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..37c4378
--- /dev/null
+++ b/README.md
@@ -0,0 +1,27 @@
+# Mifos I/O Service Starter 
+
+
+This project makes it possible to start services locally for the purpose of writing integration tests.
+
+## Abstract
+Mifos I/O is an application framework for digital financial services, a system to support nationwide and cross-national financial transactions and help to level and speed the creation of an inclusive, interconnected digital economy for every nation in the world.
+
+## Versioning
+The version numbers follow the [Semantic Versioning](http://semver.org/) scheme.
+
+In addition to MAJOR.MINOR.PATCH the following postfixes are used to indicate the development state.
+
+* BUILD-SNAPSHOT - A release currently in development.
+* M - A _milestone_ release include specific sets of functions and are released as soon as the functionality is complete.
+* RC - A _release candidate_ is a version with potential to be a final product, considered _code complete_.
+* RELEASE - indicates that this release is the best available version and is recommended for all usage.
+
+The versioning layout is {MAJOR}.{MINOR}.{PATCH}-{INDICATOR}[.{PATCH}]. Only milestones and release candidates can  have patch versions. Some examples:
+
+1.2.3-BUILD-SNAPSHOT
+1.3.5-M.1
+1.5.7-RC.2
+2.0.0-RELEASE
+
+## License
+See [LICENSE](LICENSE) file.
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..1d72f8d
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,94 @@
+buildscript {
+    repositories {
+        jcenter()
+    }
+
+    dependencies {
+        classpath ("io.spring.gradle:dependency-management-plugin:0.6.0.RELEASE")
+    }
+}
+
+plugins {
+    id 'com.github.hierynomus.license' version '0.13.1'
+}
+
+group 'io.mifos'
+version '0.1.0-BUILD-SNAPSHOT'
+
+ext.versions = [
+        frameworkapi    : '0.1.0-BUILD-SNAPSHOT',
+        frameworktest   : '0.1.0-BUILD-SNAPSHOT',
+        frameworkanubis : '0.1.0-BUILD-SNAPSHOT',
+        validator   : '5.3.0.Final'
+]
+
+apply plugin: 'java'
+apply plugin: 'idea'
+apply plugin: 'io.spring.dependency-management'
+apply plugin: 'maven-publish'
+
+tasks.withType(JavaCompile) {
+    sourceCompatibility = JavaVersion.VERSION_1_8
+    targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+repositories {
+    jcenter()
+    mavenLocal()
+}
+
+dependencyManagement {
+    imports {
+        mavenBom 'io.spring.platform:platform-bom:Athens-RELEASE'
+        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Camden.SR1'
+    }
+}
+
+dependencies {
+    compile(
+            [group: 'com.google.code.findbugs', name: 'jsr305'],
+            [group: 'io.mifos.core', name: 'api', version: versions.frameworkapi],
+            [group: 'io.mifos.core', name: 'test', version: versions.frameworktest],
+            [group: 'io.mifos.anubis', name: 'test', version: versions.frameworkanubis],
+            [group: 'org.eclipse.aether', name: 'aether-api', version: '1.1.0'],
+            [group: 'org.eclipse.aether', name: 'aether-spi', version: '1.1.0'],
+            [group: 'org.eclipse.aether', name: 'aether-util', version: '1.1.0'],
+            [group: 'org.eclipse.aether', name: 'aether-impl', version: '1.1.0'],
+            [group: 'org.eclipse.aether', name: 'aether-connector-basic', version: '1.1.0'],
+            [group: 'org.eclipse.aether', name: 'aether-transport-file', version: '1.1.0'],
+            [group: 'org.eclipse.aether', name: 'aether-transport-http', version: '1.1.0'],
+            [group: 'org.apache.maven', name: 'maven-aether-provider', version: '3.1.0'],
+            [group: 'org.springframework.cloud', name: 'spring-cloud-starter-eureka-server'],
+    )
+}
+
+jar {
+    from sourceSets.main.allSource
+}
+
+license {
+    exclude("**/aether/*.java")
+    exclude("**/aether/*.html")
+    header rootProject.file('HEADER')
+    strictCheck true
+    mapping {
+        java = 'SLASHSTAR_STYLE'
+        xml = 'XML_STYLE'
+        yml = 'SCRIPT_STYLE'
+        yaml = 'SCRIPT_STYLE'
+    }
+    ext.year = Calendar.getInstance().get(Calendar.YEAR)
+    ext.name = 'The Mifos Initiative'
+}
+
+
+publishing {
+    publications {
+        serviceStarterPublication(MavenPublication) {
+            from components.java
+            groupId project.group
+            artifactId project.name
+            version project.version
+        }
+    }
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..c7d59a1
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..f1b1899
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Mar 15 18:38:13 CET 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..4453cce
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save ( ) {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windows variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..40dc664
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'service-starter'
diff --git a/src/main/java/io/mifos/core/test/servicestarter/ArtifactResolver.java b/src/main/java/io/mifos/core/test/servicestarter/ArtifactResolver.java
new file mode 100644
index 0000000..be0146c
--- /dev/null
+++ b/src/main/java/io/mifos/core/test/servicestarter/ArtifactResolver.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.core.test.servicestarter;
+
+import io.mifos.core.test.servicestarter.aether.Booter;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactRequest;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.util.repository.AuthenticationBuilder;
+
+import java.io.File;
+import java.util.Collections;
+
+/**
+ * @author Myrle Krantz
+ */
+class ArtifactResolver {
+  private final String artifactoryDirectory;
+  private final String username;
+  private final String password;
+
+  private RemoteRepository repositoryForProject(final String project)
+  {
+    return new RemoteRepository.Builder(project, "default", "https://mifosio-radar.bintray.com/io.mifos." + project)
+            .setAuthentication(new AuthenticationBuilder().addUsername(username).addPassword(password).build())
+            .build();
+  }
+
+
+  ArtifactResolver(final String artifactoryDirectory, final String username, final String password)
+  {
+    this.artifactoryDirectory = artifactoryDirectory;
+    this.username = username;
+    this.password = password;
+  }
+
+  File getJarFile(final String project, final String group, final String name, final String version)
+          throws ArtifactResolutionException {
+    final RepositorySystem system = Booter.newRepositorySystem();
+    final RepositorySystemSession session = Booter.newRepositorySystemSession(system, artifactoryDirectory);
+
+
+    final ArtifactRequest artifactRequest = new ArtifactRequest();
+    artifactRequest.setArtifact(new DefaultArtifact(group, name, "jar", version));
+    artifactRequest.setRepositories(Collections.singletonList(repositoryForProject(project)));
+
+    final ArtifactResult artifactResult = system.resolveArtifact(session, artifactRequest);
+
+    return artifactResult.getArtifact().getFile();
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/io/mifos/core/test/servicestarter/EurekaForTest.java b/src/main/java/io/mifos/core/test/servicestarter/EurekaForTest.java
new file mode 100644
index 0000000..8eade00
--- /dev/null
+++ b/src/main/java/io/mifos/core/test/servicestarter/EurekaForTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.core.test.servicestarter;
+
+import org.junit.rules.ExternalResource;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
+import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings({"unused", "WeakerAccess"})
+public class EurekaForTest extends ExternalResource {
+  static final int PORT = 8761;
+  static final String DEFAULT_ZONE = "http://localhost:" + PORT + "/eureka/";
+  private ConfigurableApplicationContext eurekaServer;
+
+  @SuppressWarnings("WeakerAccess")
+  @EnableEurekaServer
+  @Configuration
+  @EnableAutoConfiguration(exclude= {HibernateJpaAutoConfiguration.class, DataSourceAutoConfiguration.class})
+  static class EurekaServer {
+  }
+
+  protected void before() throws Throwable {
+
+    // start eureka server for test
+    eurekaServer = SpringApplication.run(EurekaServer.class,
+            "--server.port=" + PORT,
+            "--security.basic.enabled=false",
+            "--error.whitelabel.enabled=false",
+            "--eureka.instance.leaseRenewalIntervalInSeconds=1", //Speed up registration for test purposes.
+            "--eureka.server.responseCacheUpdateIntervalMs=500",
+            "--eureka.instance.hostname=localhost",
+            "--eureka.client.registerWithEureka=false",
+            "--eureka.client.fetchRegistry=false",
+            "--eureka.serviceUrl.defaultZone=" + DEFAULT_ZONE);
+  }
+
+  /**
+   * Override to tear down your specific external resource.
+   */
+  protected void after() {
+    eurekaServer.close();
+  }
+}
diff --git a/src/main/java/io/mifos/core/test/servicestarter/InitializedMicroservice.java b/src/main/java/io/mifos/core/test/servicestarter/InitializedMicroservice.java
new file mode 100644
index 0000000..7e66897
--- /dev/null
+++ b/src/main/java/io/mifos/core/test/servicestarter/InitializedMicroservice.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.core.test.servicestarter;
+
+import io.mifos.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+
+import java.io.IOException;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("unused")
+public class InitializedMicroservice<T> extends Microservice<T> {
+  public InitializedMicroservice(
+          final Class<T> clazz,
+          final String artifactName,
+          final String artifactVersion,
+          final IntegrationTestEnvironment integrationTestEnvironment) {
+    super(clazz, artifactName, artifactVersion, integrationTestEnvironment);
+    this.integrationTestEnvironment.addApplication(applicationName);
+  }
+
+  public void start() throws InterruptedException, IOException, ArtifactResolutionException {
+    super.start();
+    final TenantApplicationSecurityEnvironmentTestRule tenantApplicationSecurityEnvironment
+            = new TenantApplicationSecurityEnvironmentTestRule(applicationName, processEnvironment.serverURI(), integrationTestEnvironment.getSystemSecurityEnvironment());
+    tenantApplicationSecurityEnvironment.initializeTenantInApplication();
+  }
+}
diff --git a/src/main/java/io/mifos/core/test/servicestarter/IntegrationTestEnvironment.java b/src/main/java/io/mifos/core/test/servicestarter/IntegrationTestEnvironment.java
new file mode 100644
index 0000000..8c9dbbb
--- /dev/null
+++ b/src/main/java/io/mifos/core/test/servicestarter/IntegrationTestEnvironment.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.core.test.servicestarter;
+
+import io.mifos.anubis.test.v1.SystemSecurityEnvironment;
+import io.mifos.core.api.context.AutoUserContext;
+import io.mifos.core.lang.security.RsaKeyPairFactory;
+import io.mifos.core.test.fixture.DataStoreTenantInitializer;
+import io.mifos.core.test.fixture.TenantDataStoreTestContext;
+import org.junit.rules.ExternalResource;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.Socket;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.*;
+
+import static io.mifos.core.test.env.TestEnvironment.*;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("WeakerAccess")
+public class IntegrationTestEnvironment extends ExternalResource {
+
+
+  static String getJava()
+  {
+    final String javaHome = System.getProperty("java.home");
+    return javaHome + File.separator + "bin" + File.separator + "java";
+  }
+
+  static String getArtifactoryDirectory()
+  {
+    final String homeDirectory = System.getProperty("user.home");
+    return homeDirectory + File.separator + ".m2" + File.separator + "repository";
+  }
+
+  static String getBintrayUser()
+  {
+    return System.getenv().get("BINTRAY_USER");
+  }
+
+  static String getBintrayPassword()
+  {
+    return System.getenv().get("BINTRAY_PASSWORD");
+  }
+
+  private int nextPort;
+  private final Set<Integer> ports;
+  private final RsaKeyPairFactory.KeyPairHolder keyPairHolder;
+  private final SystemSecurityEnvironment systemSecurityEnvironment;
+  private final DataStoreTenantInitializer[] dataStoreTenantInitializers;
+  private final List<String> applicationNames;
+  private TenantDataStoreTestContext tenantDataStoreTestContext;
+
+  public IntegrationTestEnvironment(final DataStoreTenantInitializer... dataStoreTenantInitializers) {
+    final Properties properties = System.getProperties();
+    properties.setProperty(CASSANDRA_CLUSTER_NAME_PROPERTY, CASSANDRA_CLUSTER_NAME_DEFAULT);
+    properties.setProperty(CASSANDRA_CONTACT_POINTS_PROPERTY, CASSANDRA_CONTACT_POINTS_DEFAULT);
+    properties.setProperty(CASSANDRA_META_KEYSPACE_PROPERTY, CASSANDRA_META_KEYSPACE_DEFAULT);
+    properties.setProperty(CASSANDRA_CONSISTENCY_LEVEL_READ_PROPERTY, CASSANDRA_CONSISTENCY_LEVEL_DEFAULT);
+    properties.setProperty(CASSANDRA_CONSISTENCY_LEVEL_WRITE_PROPERTY, CASSANDRA_CONSISTENCY_LEVEL_DEFAULT);
+    properties.setProperty(CASSANDRA_CONSISTENCY_LEVEL_DELETE_PROPERTY, CASSANDRA_CONSISTENCY_LEVEL_DEFAULT);
+    properties.setProperty(MARIADB_DRIVER_CLASS_PROPERTY, MARIADB_DRIVER_CLASS_DEFAULT);
+    properties.setProperty(MARIADB_DATABASE_NAME_PROPERTY, MARIADB_DATABASE_NAME_DEFAULT);
+    properties.setProperty(MARIADB_HOST_PROPERTY, MARIADB_HOST_DEFAULT);
+    properties.setProperty(MARIADB_PORT_PROPERTY, MARIADB_PORT_DEFAULT);
+    properties.setProperty(MARIADB_USER_PROPERTY, MARIADB_USER_DEFAULT);
+    properties.setProperty(MARIADB_PASSWORD_PROPERTY, MARIADB_PASSWORD_DEFAULT);
+    properties.setProperty(HYSTRIX_ENABLED_PROPERTY, HYSTRIX_ENABLED_DEFAULT);
+    properties.setProperty(RIBBON_USES_EUREKA_PROPERTY, RIBBON_USES_EUREKA_DEFAULT);
+    this.keyPairHolder = RsaKeyPairFactory.createKeyPair();
+    properties.setProperty(SYSTEM_PUBLIC_KEY_MODULUS_PROPERTY, this.keyPairHolder.publicKey().getModulus().toString());
+    properties.setProperty(SYSTEM_PUBLIC_KEY_EXPONENT_PROPERTY, this.keyPairHolder.publicKey().getPublicExponent().toString());
+
+    this.systemSecurityEnvironment = new SystemSecurityEnvironment(keyPairHolder.publicKey(), keyPairHolder.privateKey());
+    this.dataStoreTenantInitializers = dataStoreTenantInitializers;
+
+    nextPort = 2020;
+    this.ports = new HashSet<>();
+    this.ports.add(0);
+    this.ports.add(3306); //MySQL
+    this.ports.add(9142); //Cassandra
+    this.ports.add(EurekaForTest.PORT);
+
+    this.applicationNames = new ArrayList<>();
+  }
+
+  @Override
+  protected void before() {
+    tenantDataStoreTestContext = TenantDataStoreTestContext.forRandomTenantName(dataStoreTenantInitializers);
+  }
+
+  @Override
+  protected void after() {
+    tenantDataStoreTestContext.close();
+  }
+
+  Integer getFreshPort() {
+    while (ports.contains(nextPort) || !available(nextPort))
+    {
+      nextPort += 1;
+    }
+
+    ports.add(nextPort);
+    return nextPort;
+  }
+
+  private static boolean available(int port) {
+    try (final Socket ignored = new Socket("localhost", port)) {
+      return false;
+    } catch (final IOException ignored) {
+      return true;
+    }
+  }
+
+  public RSAPublicKey getSeshatPublicKey() {
+    return this.keyPairHolder.publicKey();
+  }
+
+  public RSAPrivateKey getSeshatPrivateKey() {
+    return this.keyPairHolder.privateKey();
+  }
+
+  public SystemSecurityEnvironment getSystemSecurityEnvironment() {
+    return systemSecurityEnvironment;
+  }
+
+  void addApplication(final String applicationName) {
+    applicationNames.add(applicationName);
+  }
+
+  @SuppressWarnings("unused")
+  public AutoUserContext createAutoUserContext(final String userName) {
+    return systemSecurityEnvironment.createAutoUserContext(userName, applicationNames);
+  }
+}
diff --git a/src/main/java/io/mifos/core/test/servicestarter/Microservice.java b/src/main/java/io/mifos/core/test/servicestarter/Microservice.java
new file mode 100644
index 0000000..13db326
--- /dev/null
+++ b/src/main/java/io/mifos/core/test/servicestarter/Microservice.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.core.test.servicestarter;
+
+import io.mifos.core.api.util.ApiFactory;
+import io.mifos.core.test.env.TestEnvironment;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.junit.rules.ExternalResource;
+import org.springframework.cloud.netflix.feign.FeignClient;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import static io.mifos.core.test.env.TestEnvironment.SPRING_CLOUD_DISCOVERY_ENABLED_PROPERTY;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings({"unused", "WeakerAccess"})
+public class Microservice<T> extends ExternalResource {
+
+  private final Class<T> clazz;
+  private final String artifactName;
+  private final String artifactVersion;
+  final TestEnvironment processEnvironment;
+  final IntegrationTestEnvironment integrationTestEnvironment;
+  final String applicationName;
+  private ApiFactory apiFactory;
+  private Process process;
+  private T api;
+
+  public Microservice(
+          final Class<T> clazz,
+          final String artifactName,
+          final String artifactVersion,
+          final IntegrationTestEnvironment integrationTestEnvironment) {
+    this.clazz = clazz;
+    this.artifactName = artifactName;
+    this.artifactVersion = artifactVersion;
+    applicationName = AnnotatedElementUtils.getMergedAnnotationAttributes(clazz, FeignClient.class).getString("value");
+    this.processEnvironment = new TestEnvironment(applicationName);
+    processEnvironment.setProperty(TestEnvironment.SERVER_PORT_PROPERTY, integrationTestEnvironment.getFreshPort().toString());
+    processEnvironment.setKeyPair(integrationTestEnvironment.getSeshatPublicKey(), integrationTestEnvironment.getSeshatPrivateKey());
+
+    //https://github.com/spring-cloud/spring-cloud-netflix/issues/373
+    //http://blog.abhijitsarkar.org/technical/netflix-eureka/
+    processEnvironment.setProperty("eureka.client.serviceUrl.defaultZone", EurekaForTest.DEFAULT_ZONE);
+    processEnvironment.setProperty(SPRING_CLOUD_DISCOVERY_ENABLED_PROPERTY, "true");
+    processEnvironment.setProperty("eureka.instance.hostname", "localhost");
+    processEnvironment.setProperty("eureka.client.fetchRegistry", "true");
+    processEnvironment.setProperty("eureka.registration.enabled", "true");
+    processEnvironment.setProperty("eureka.instance.leaseRenewalIntervalInSeconds", "1"); //Speed up registration for test purposes.
+    processEnvironment.setProperty("eureka.client.initialInstanceInfoReplicationIntervalSeconds", "0");  //Speed up initial registration for test purposes.
+    processEnvironment.setProperty("eureka.client.instanceInfoReplicationIntervalSeconds", "1");
+
+    this.integrationTestEnvironment = integrationTestEnvironment;
+  }
+
+  @Override
+  protected void before() throws InterruptedException, IOException, ArtifactResolutionException {
+    start();
+  }
+
+  @Override
+  protected void after() {
+    try {
+      kill();
+    } catch (final InterruptedException e) {
+      System.out.println("Interrupt raised, but microservice is already going down, so ignoring.");
+    }
+  }
+
+  public void start() throws ArtifactResolutionException, IOException, InterruptedException {
+
+    final ArtifactResolver artifactResolver = new ArtifactResolver(
+            IntegrationTestEnvironment.getArtifactoryDirectory(),
+            IntegrationTestEnvironment.getBintrayUser(),
+            IntegrationTestEnvironment.getBintrayPassword());
+
+    final File jarFile = artifactResolver.getJarFile(artifactName, "io.mifos." + artifactName, "service-boot", artifactVersion);
+
+    final ProcessBuilder processBuilder
+            = new ProcessBuilder(IntegrationTestEnvironment.getJava(), "-jar", jarFile.getAbsolutePath());
+    processEnvironment.populateProcessEnvironment(processBuilder);
+    processBuilder.inheritIO();
+
+    process = processBuilder.start();
+    TimeUnit.SECONDS.sleep(40); //TODO: Replace this with event listening.
+  }
+
+  public int kill() throws InterruptedException {
+    process.destroy();
+    process.waitFor();
+    return process.exitValue();
+  }
+
+  public void setApiFactory(final ApiFactory newValue)
+  {
+    this.apiFactory = newValue;
+  }
+
+  public T api() {
+    if (this.api == null) {
+      this.api = this.apiFactory.create(clazz, processEnvironment.serverURI());
+    }
+    return this.api;
+  }
+
+  public String uri() {
+    return processEnvironment.serverURI();
+  }
+
+  public TestEnvironment getProcessEnvironment()
+  {
+    return processEnvironment;
+  }
+
+  public String name() {
+    return applicationName;
+  }
+}
diff --git a/src/main/java/io/mifos/core/test/servicestarter/aether/Booter.java b/src/main/java/io/mifos/core/test/servicestarter/aether/Booter.java
new file mode 100644
index 0000000..5c9e915
--- /dev/null
+++ b/src/main/java/io/mifos/core/test/servicestarter/aether/Booter.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Sonatype, Inc. - initial API and implementation
+ *******************************************************************************/
+package io.mifos.core.test.servicestarter.aether;
+
+import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.repository.LocalRepository;
+
+
+public class Booter {
+
+  public static RepositorySystem newRepositorySystem()
+  {
+    return ManualRepositorySystemFactory.newRepositorySystem();
+    // return org.eclipse.aether.examples.guice.GuiceRepositorySystemFactory.newRepositorySystem();
+    // return org.eclipse.aether.examples.sisu.SisuRepositorySystemFactory.newRepositorySystem();
+    // return org.eclipse.aether.examples.plexus.PlexusRepositorySystemFactory.newRepositorySystem();
+  }
+
+
+  public static DefaultRepositorySystemSession newRepositorySystemSession(RepositorySystem system, String artifactoryDirectory)
+  {
+    DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
+
+    LocalRepository localRepo = new LocalRepository(artifactoryDirectory);
+    session.setLocalRepositoryManager( system.newLocalRepositoryManager( session, localRepo ) );
+
+    session.setTransferListener( new ConsoleTransferListener() );
+    session.setRepositoryListener( new ConsoleRepositoryListener() );
+
+    // uncomment to generate dirty trees
+    // session.setDependencyGraphTransformer( null );
+
+    return session;
+  }
+}
diff --git a/src/main/java/io/mifos/core/test/servicestarter/aether/ConsoleRepositoryListener.java b/src/main/java/io/mifos/core/test/servicestarter/aether/ConsoleRepositoryListener.java
new file mode 100644
index 0000000..353dd06
--- /dev/null
+++ b/src/main/java/io/mifos/core/test/servicestarter/aether/ConsoleRepositoryListener.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Sonatype, Inc. - initial API and implementation
+ *******************************************************************************/
+package io.mifos.core.test.servicestarter.aether;
+
+
+import org.eclipse.aether.AbstractRepositoryListener;
+import org.eclipse.aether.RepositoryEvent;
+
+import java.io.PrintStream;
+
+public class ConsoleRepositoryListener
+        extends AbstractRepositoryListener
+{
+  private PrintStream out;
+
+  public ConsoleRepositoryListener()
+  {
+    this( null );
+  }
+
+  public ConsoleRepositoryListener( PrintStream out )
+  {
+    this.out = ( out != null ) ? out : System.out;
+  }
+
+  public void artifactDeployed( RepositoryEvent event )
+  {
+    out.println( "Deployed " + event.getArtifact() + " to " + event.getRepository() );
+  }
+
+  public void artifactDeploying( RepositoryEvent event )
+  {
+    out.println( "Deploying " + event.getArtifact() + " to " + event.getRepository() );
+  }
+
+  public void artifactDescriptorInvalid( RepositoryEvent event )
+  {
+    out.println( "Invalid artifact descriptor for " + event.getArtifact() + ": "
+            + event.getException().getMessage() );
+  }
+
+  public void artifactDescriptorMissing( RepositoryEvent event )
+  {
+    out.println( "Missing artifact descriptor for " + event.getArtifact() );
+  }
+
+  public void artifactInstalled( RepositoryEvent event )
+  {
+    out.println( "Installed " + event.getArtifact() + " to " + event.getFile() );
+  }
+
+  public void artifactInstalling( RepositoryEvent event )
+  {
+    out.println( "Installing " + event.getArtifact() + " to " + event.getFile() );
+  }
+
+  public void artifactResolved( RepositoryEvent event )
+  {
+    out.println( "Resolved artifact " + event.getArtifact() + " from " + event.getRepository() );
+  }
+
+  public void artifactDownloading( RepositoryEvent event )
+  {
+    out.println( "Downloading artifact " + event.getArtifact() + " from " + event.getRepository() );
+  }
+
+  public void artifactDownloaded( RepositoryEvent event )
+  {
+    out.println( "Downloaded artifact " + event.getArtifact() + " from " + event.getRepository() );
+  }
+
+  public void artifactResolving( RepositoryEvent event )
+  {
+    out.println( "Resolving artifact " + event.getArtifact() );
+  }
+
+  public void metadataDeployed( RepositoryEvent event )
+  {
+    out.println( "Deployed " + event.getMetadata() + " to " + event.getRepository() );
+  }
+
+  public void metadataDeploying( RepositoryEvent event )
+  {
+    out.println( "Deploying " + event.getMetadata() + " to " + event.getRepository() );
+  }
+
+  public void metadataInstalled( RepositoryEvent event )
+  {
+    out.println( "Installed " + event.getMetadata() + " to " + event.getFile() );
+  }
+
+  public void metadataInstalling( RepositoryEvent event )
+  {
+    out.println( "Installing " + event.getMetadata() + " to " + event.getFile() );
+  }
+
+  public void metadataInvalid( RepositoryEvent event )
+  {
+    out.println( "Invalid metadata " + event.getMetadata() );
+  }
+
+  public void metadataResolved( RepositoryEvent event )
+  {
+    out.println( "Resolved metadata " + event.getMetadata() + " from " + event.getRepository() );
+  }
+
+  public void metadataResolving( RepositoryEvent event )
+  {
+    out.println( "Resolving metadata " + event.getMetadata() + " from " + event.getRepository() );
+  }
+}
diff --git a/src/main/java/io/mifos/core/test/servicestarter/aether/ConsoleTransferListener.java b/src/main/java/io/mifos/core/test/servicestarter/aether/ConsoleTransferListener.java
new file mode 100644
index 0000000..0bccd0e
--- /dev/null
+++ b/src/main/java/io/mifos/core/test/servicestarter/aether/ConsoleTransferListener.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2013 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Sonatype, Inc. - initial API and implementation
+ *******************************************************************************/
+package io.mifos.core.test.servicestarter.aether;
+
+
+import org.eclipse.aether.transfer.AbstractTransferListener;
+import org.eclipse.aether.transfer.MetadataNotFoundException;
+import org.eclipse.aether.transfer.TransferEvent;
+import org.eclipse.aether.transfer.TransferResource;
+
+import java.io.PrintStream;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ConsoleTransferListener extends AbstractTransferListener
+  {
+
+    private PrintStream out;
+
+    private Map<TransferResource, Long> downloads = new ConcurrentHashMap<TransferResource, Long>();
+
+    private int lastLength;
+
+    public ConsoleTransferListener()
+    {
+      this( null );
+    }
+
+    public ConsoleTransferListener( PrintStream out )
+    {
+      this.out = ( out != null ) ? out : System.out;
+    }
+
+    @Override
+    public void transferInitiated( TransferEvent event )
+    {
+      String message = event.getRequestType() == TransferEvent.RequestType.PUT ? "Uploading" : "Downloading";
+
+      out.println( message + ": " + event.getResource().getRepositoryUrl() + event.getResource().getResourceName() );
+    }
+
+    @Override
+    public void transferProgressed( TransferEvent event )
+    {
+      TransferResource resource = event.getResource();
+      downloads.put( resource, Long.valueOf( event.getTransferredBytes() ) );
+
+      StringBuilder buffer = new StringBuilder( 64 );
+
+      for ( Map.Entry<TransferResource, Long> entry : downloads.entrySet() )
+      {
+        long total = entry.getKey().getContentLength();
+        long complete = entry.getValue().longValue();
+
+        buffer.append( getStatus( complete, total ) ).append( "  " );
+      }
+
+      int pad = lastLength - buffer.length();
+      lastLength = buffer.length();
+      pad( buffer, pad );
+      buffer.append( '\r' );
+
+      out.print( buffer );
+    }
+
+  private String getStatus( long complete, long total )
+  {
+    if ( total >= 1024 )
+    {
+      return toKB( complete ) + "/" + toKB( total ) + " KB ";
+    }
+    else if ( total >= 0 )
+    {
+      return complete + "/" + total + " B ";
+    }
+    else if ( complete >= 1024 )
+    {
+      return toKB( complete ) + " KB ";
+    }
+    else
+    {
+      return complete + " B ";
+    }
+  }
+
+  private void pad( StringBuilder buffer, int spaces )
+  {
+    String block = "                                        ";
+    while ( spaces > 0 )
+    {
+      int n = Math.min( spaces, block.length() );
+      buffer.append( block, 0, n );
+      spaces -= n;
+    }
+  }
+
+  @Override
+  public void transferSucceeded( TransferEvent event )
+  {
+    transferCompleted( event );
+
+    TransferResource resource = event.getResource();
+    long contentLength = event.getTransferredBytes();
+    if ( contentLength >= 0 )
+    {
+      String type = ( event.getRequestType() == TransferEvent.RequestType.PUT ? "Uploaded" : "Downloaded" );
+      String len = contentLength >= 1024 ? toKB( contentLength ) + " KB" : contentLength + " B";
+
+      String throughput = "";
+      long duration = System.currentTimeMillis() - resource.getTransferStartTime();
+      if ( duration > 0 )
+      {
+        long bytes = contentLength - resource.getResumeOffset();
+        DecimalFormat format = new DecimalFormat( "0.0", new DecimalFormatSymbols( Locale.ENGLISH ) );
+        double kbPerSec = ( bytes / 1024.0 ) / ( duration / 1000.0 );
+        throughput = " at " + format.format( kbPerSec ) + " KB/sec";
+      }
+
+      out.println( type + ": " + resource.getRepositoryUrl() + resource.getResourceName() + " (" + len
+              + throughput + ")" );
+    }
+  }
+
+  @Override
+  public void transferFailed( TransferEvent event )
+  {
+    transferCompleted( event );
+
+    if ( !( event.getException() instanceof MetadataNotFoundException) )
+    {
+      event.getException().printStackTrace( out );
+    }
+  }
+
+  private void transferCompleted( TransferEvent event )
+  {
+    downloads.remove( event.getResource() );
+
+    StringBuilder buffer = new StringBuilder( 64 );
+    pad( buffer, lastLength );
+    buffer.append( '\r' );
+    out.print( buffer );
+  }
+
+  public void transferCorrupted( TransferEvent event )
+  {
+    event.getException().printStackTrace( out );
+  }
+
+  protected long toKB( long bytes )
+  {
+    return ( bytes + 1023 ) / 1024;
+  }
+
+}
\ No newline at end of file
diff --git a/src/main/java/io/mifos/core/test/servicestarter/aether/Eclipse Public License - Version 1.0.html b/src/main/java/io/mifos/core/test/servicestarter/aether/Eclipse Public License - Version 1.0.html
new file mode 100644
index 0000000..51b3b87
--- /dev/null
+++ b/src/main/java/io/mifos/core/test/servicestarter/aether/Eclipse Public License - Version 1.0.html
@@ -0,0 +1,259 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!-- saved from url=(0042)https://www.eclipse.org/legal/epl-v10.html -->
+<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<title>Eclipse Public License - Version 1.0</title>
+<style type="text/css">
+  body {
+    size: 8.5in 11.0in;
+    margin: 0.25in 0.5in 0.25in 0.5in;
+    tab-interval: 0.5in;
+    }
+  p {  	
+    margin-left: auto;
+    margin-top:  0.5em;
+    margin-bottom: 0.5em;
+    }
+  p.list {
+  	margin-left: 0.5in;
+    margin-top:  0.05em;
+    margin-bottom: 0.05em;
+    }
+  </style>
+
+</head>
+
+<body lang="EN-US">
+
+<h2>Eclipse Public License - v 1.0</h2>
+
+<p>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR
+DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
+AGREEMENT.</p>
+
+<p><b>1. DEFINITIONS</b></p>
+
+<p>"Contribution" means:</p>
+
+<p class="list">a) in the case of the initial Contributor, the initial
+code and documentation distributed under this Agreement, and</p>
+<p class="list">b) in the case of each subsequent Contributor:</p>
+<p class="list">i) changes to the Program, and</p>
+<p class="list">ii) additions to the Program;</p>
+<p class="list">where such changes and/or additions to the Program
+originate from and are distributed by that particular Contributor. A
+Contribution 'originates' from a Contributor if it was added to the
+Program by such Contributor itself or anyone acting on such
+Contributor's behalf. Contributions do not include additions to the
+Program which: (i) are separate modules of software distributed in
+conjunction with the Program under their own license agreement, and (ii)
+are not derivative works of the Program.</p>
+
+<p>"Contributor" means any person or entity that distributes
+the Program.</p>
+
+<p>"Licensed Patents" mean patent claims licensable by a
+Contributor which are necessarily infringed by the use or sale of its
+Contribution alone or when combined with the Program.</p>
+
+<p>"Program" means the Contributions distributed in accordance
+with this Agreement.</p>
+
+<p>"Recipient" means anyone who receives the Program under
+this Agreement, including all Contributors.</p>
+
+<p><b>2. GRANT OF RIGHTS</b></p>
+
+<p class="list">a) Subject to the terms of this Agreement, each
+Contributor hereby grants Recipient a non-exclusive, worldwide,
+royalty-free copyright license to reproduce, prepare derivative works
+of, publicly display, publicly perform, distribute and sublicense the
+Contribution of such Contributor, if any, and such derivative works, in
+source code and object code form.</p>
+
+<p class="list">b) Subject to the terms of this Agreement, each
+Contributor hereby grants Recipient a non-exclusive, worldwide,
+royalty-free patent license under Licensed Patents to make, use, sell,
+offer to sell, import and otherwise transfer the Contribution of such
+Contributor, if any, in source code and object code form. This patent
+license shall apply to the combination of the Contribution and the
+Program if, at the time the Contribution is added by the Contributor,
+such addition of the Contribution causes such combination to be covered
+by the Licensed Patents. The patent license shall not apply to any other
+combinations which include the Contribution. No hardware per se is
+licensed hereunder.</p>
+
+<p class="list">c) Recipient understands that although each Contributor
+grants the licenses to its Contributions set forth herein, no assurances
+are provided by any Contributor that the Program does not infringe the
+patent or other intellectual property rights of any other entity. Each
+Contributor disclaims any liability to Recipient for claims brought by
+any other entity based on infringement of intellectual property rights
+or otherwise. As a condition to exercising the rights and licenses
+granted hereunder, each Recipient hereby assumes sole responsibility to
+secure any other intellectual property rights needed, if any. For
+example, if a third party patent license is required to allow Recipient
+to distribute the Program, it is Recipient's responsibility to acquire
+that license before distributing the Program.</p>
+
+<p class="list">d) Each Contributor represents that to its knowledge it
+has sufficient copyright rights in its Contribution, if any, to grant
+the copyright license set forth in this Agreement.</p>
+
+<p><b>3. REQUIREMENTS</b></p>
+
+<p>A Contributor may choose to distribute the Program in object code
+form under its own license agreement, provided that:</p>
+
+<p class="list">a) it complies with the terms and conditions of this
+Agreement; and</p>
+
+<p class="list">b) its license agreement:</p>
+
+<p class="list">i) effectively disclaims on behalf of all Contributors
+all warranties and conditions, express and implied, including warranties
+or conditions of title and non-infringement, and implied warranties or
+conditions of merchantability and fitness for a particular purpose;</p>
+
+<p class="list">ii) effectively excludes on behalf of all Contributors
+all liability for damages, including direct, indirect, special,
+incidental and consequential damages, such as lost profits;</p>
+
+<p class="list">iii) states that any provisions which differ from this
+Agreement are offered by that Contributor alone and not by any other
+party; and</p>
+
+<p class="list">iv) states that source code for the Program is available
+from such Contributor, and informs licensees how to obtain it in a
+reasonable manner on or through a medium customarily used for software
+exchange.</p>
+
+<p>When the Program is made available in source code form:</p>
+
+<p class="list">a) it must be made available under this Agreement; and</p>
+
+<p class="list">b) a copy of this Agreement must be included with each
+copy of the Program.</p>
+
+<p>Contributors may not remove or alter any copyright notices contained
+within the Program.</p>
+
+<p>Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.</p>
+
+<p><b>4. COMMERCIAL DISTRIBUTION</b></p>
+
+<p>Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial use of
+the Program, the Contributor who includes the Program in a commercial
+product offering should do so in a manner which does not create
+potential liability for other Contributors. Therefore, if a Contributor
+includes the Program in a commercial product offering, such Contributor
+("Commercial Contributor") hereby agrees to defend and
+indemnify every other Contributor ("Indemnified Contributor")
+against any losses, damages and costs (collectively "Losses")
+arising from claims, lawsuits and other legal actions brought by a third
+party against the Indemnified Contributor to the extent caused by the
+acts or omissions of such Commercial Contributor in connection with its
+distribution of the Program in a commercial product offering. The
+obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement. In
+order to qualify, an Indemnified Contributor must: a) promptly notify
+the Commercial Contributor in writing of such claim, and b) allow the
+Commercial Contributor to control, and cooperate with the Commercial
+Contributor in, the defense and any related settlement negotiations. The
+Indemnified Contributor may participate in any such claim at its own
+expense.</p>
+
+<p>For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those
+performance claims and warranties, and if a court requires any other
+Contributor to pay any damages as a result, the Commercial Contributor
+must pay those damages.</p>
+
+<p><b>5. NO WARRANTY</b></p>
+
+<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,
+ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement , including but not limited to
+the risks and costs of program errors, compliance with applicable laws,
+damage to or loss of data, programs or equipment, and unavailability or
+interruption of operations.</p>
+
+<p><b>6. DISCLAIMER OF LIABILITY</b></p>
+
+<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
+NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p>
+
+<p><b>7. GENERAL</b></p>
+
+<p>If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further action
+by the parties hereto, such provision shall be reformed to the minimum
+extent necessary to make such provision valid and enforceable.</p>
+
+<p>If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging that the
+Program itself (excluding combinations of the Program with other
+software or hardware) infringes such Recipient's patent(s), then such
+Recipient's rights granted under Section 2(b) shall terminate as of the
+date such litigation is filed.</p>
+
+<p>All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of time
+after becoming aware of such noncompliance. If all Recipient's rights
+under this Agreement terminate, Recipient agrees to cease use and
+distribution of the Program as soon as reasonably practicable. However,
+Recipient's obligations under this Agreement and any licenses granted by
+Recipient relating to the Program shall continue and survive.</p>
+
+<p>Everyone is permitted to copy and distribute copies of this
+Agreement, but in order to avoid inconsistency the Agreement is
+copyrighted and may only be modified in the following manner. The
+Agreement Steward reserves the right to publish new versions (including
+revisions) of this Agreement from time to time. No one other than the
+Agreement Steward has the right to modify this Agreement. The Eclipse
+Foundation is the initial Agreement Steward. The Eclipse Foundation may
+assign the responsibility to serve as the Agreement Steward to a
+suitable separate entity. Each new version of the Agreement will be
+given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new version
+of the Agreement is published, Contributor may elect to distribute the
+Program (including its Contributions) under the new version. Except as
+expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
+rights or licenses to the intellectual property of any Contributor under
+this Agreement, whether expressly, by implication, estoppel or
+otherwise. All rights in the Program not expressly granted under this
+Agreement are reserved.</p>
+
+<p>This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No party
+to this Agreement will bring a legal action under this Agreement more
+than one year after the cause of action arose. Each party waives its
+rights to a jury trial in any resulting litigation.</p>
+
+
+
+</body></html>
\ No newline at end of file
diff --git a/src/main/java/io/mifos/core/test/servicestarter/aether/ManualRepositorySystemFactory.java b/src/main/java/io/mifos/core/test/servicestarter/aether/ManualRepositorySystemFactory.java
new file mode 100644
index 0000000..b6c1fc8
--- /dev/null
+++ b/src/main/java/io/mifos/core/test/servicestarter/aether/ManualRepositorySystemFactory.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Sonatype, Inc. - initial API and implementation
+ *******************************************************************************/
+package io.mifos.core.test.servicestarter.aether;
+
+import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
+import org.eclipse.aether.impl.DefaultServiceLocator;
+import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
+import org.eclipse.aether.spi.connector.transport.TransporterFactory;
+import org.eclipse.aether.transport.file.FileTransporterFactory;
+import org.eclipse.aether.transport.http.HttpTransporterFactory;
+
+/**
+ * A factory for repository system instances that employs Aether's built-in service locator infrastructure to wire up
+ * the system's components.
+ */
+public class ManualRepositorySystemFactory
+{
+  public static RepositorySystem newRepositorySystem()
+  {
+        /*
+         * Aether's components implement org.eclipse.aether.spi.locator.Service to ease manual wiring and using the
+         * prepopulated DefaultServiceLocator, we only need to register the repository connector and transporter
+         * factories.
+         */
+    DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
+    locator.addService( RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class );
+    locator.addService( TransporterFactory.class, FileTransporterFactory.class );
+    locator.addService( TransporterFactory.class, HttpTransporterFactory.class );
+
+    locator.setErrorHandler( new DefaultServiceLocator.ErrorHandler()
+    {
+      @Override
+      public void serviceCreationFailed( Class<?> type, Class<?> impl, Throwable exception )
+      {
+        exception.printStackTrace();
+      }
+    } );
+
+    return locator.getService( RepositorySystem.class );
+  }
+
+}