You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by vo...@apache.org on 2019/06/29 13:38:40 UTC

[fineract] 01/05: Create one-touch deploy with docker

This is an automated email from the ASF dual-hosted git repository.

vorburger pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git

commit 357820a7a755dce27d6d2707f29ffd21c71f54d1
Author: conradsp <sc...@gmail.com>
AuthorDate: Thu Jun 20 08:37:18 2019 -0500

    Create one-touch deploy with docker
---
 docker/Dockerfile              |  28 +++
 docker/README.md               |  25 ++
 docker/build.gradle            | 553 +++++++++++++++++++++++++++++++++++++++++
 docker/docker-compose.yml      |  24 ++
 docker/initdb/01-databases.sql |   6 +
 docker/server.xml              | 191 ++++++++++++++
 6 files changed, 827 insertions(+)

diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000..5488e6f
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,28 @@
+FROM bitnami/tomcat:7.0.94 as fineract
+
+USER root
+RUN apt-get update -qq && apt-get install -y git openjdk-8-jdk wget
+ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/jre
+
+RUN git clone https://github.com/apache/fineract.git
+COPY build.gradle fineract/fineract-provider
+
+# To use for local fineract development (instead of installing from the latest code from Github)
+#   1) Copy Dockerfile, docker-compose.yml, and the initdb directory to the root of your project (/fineract)
+#   2) Edit Dockerfile in /fineract - comment out the 2 lines above (RUN git clone and COPY build.gradle)
+#   3) Uncomment the 2 lines below
+#   4) Update your local copy of build.gradle and replace all references to localhost with fineractmysql
+
+#RUN mkdir fineract
+#COPY . fineract
+
+WORKDIR fineract
+RUN ./gradlew clean war
+RUN mv build/libs/fineract-provider.war /opt/bitnami/tomcat/webapps
+
+RUN keytool -genkey -keyalg RSA -alias tomcat -keystore /opt/bitnami/tomcat/tomcat.keystore -keypass xyz123 -storepass xyz123 -noprompt -dname "CN=Fineract, OU=Fineract, O=Fineract, L=Unknown, ST=Unknown, C=Unknown"
+COPY server.xml /opt/bitnami/tomcat/conf
+RUN chmod 664 /opt/bitnami/tomcat/conf/server.xml
+WORKDIR /opt/bitnami/tomcat/lib
+RUN wget http://central.maven.org/maven2/org/drizzle/jdbc/drizzle-jdbc/1.3/drizzle-jdbc-1.3.jar
+
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 0000000..aae3739
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,25 @@
+Installing Fineract with Docker and docker-compose
+
+Prerequisites
+=============
+* docker and docker-compose installed on your machine
+
+
+Installing a new Fineract instance
+==================================
+
+* Clone the Fineract Github repository
+* Navigate to the docker directory
+* Run the following commands:
+    * docker-compose build
+    * docker-compose up -d
+* Fineract will run at https://localhost:8443/fineract-provider
+
+
+Using docker-compose for development
+====================================
+
+* Copy Dockerfile, docker-compose.yml, and the initdb directory to the root of your project (/fineract)
+* Edit Dockerfile in /fineract - comment out the 2 lines (RUN git clone and COPY build.gradle)
+* Uncomment the 2 lines in Dockerfile (RUN mkdir fineract, COPY . fineract)
+* Update your local copy of build.gradle and replace all references to localhost with fineractmysql
diff --git a/docker/build.gradle b/docker/build.gradle
new file mode 100644
index 0000000..d0964e8
--- /dev/null
+++ b/docker/build.gradle
@@ -0,0 +1,553 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+description = '''\
+Run as:
+gradle clean tomcatrunwar
+'''
+buildscript {
+  repositories {
+  	 jcenter()
+  	 mavenCentral()
+  	 maven { url "https://plugins.gradle.org/m2/" }
+  }
+
+  dependencies {
+     classpath 'org.gradle.api.plugins:gradle-tomcat-plugin:1.0',
+               'nl.javadude.gradle.plugins:license-gradle-plugin:0.11.0',
+               'org.zeroturnaround:gradle-jrebel-plugin:1.1.2',
+               'org.springframework.boot:spring-boot-gradle-plugin:1.1.6.RELEASE' // also change springDataJpaVersion below
+     classpath 'org.apache.openjpa:openjpa-all:2.4.1'
+     classpath 'at.schmutterer.oss.gradle:gradle-openjpa:0.2.0'
+     classpath 'gradle.plugin.org.nosphere.apache:creadur-rat-gradle:0.2.2'
+  }
+}
+
+apply plugin: "org.nosphere.apache.rat"
+apply plugin: 'rebel'
+apply plugin: 'license'
+apply plugin: 'war'
+apply plugin: 'spring-boot'
+apply plugin: 'eclipse'
+apply plugin: 'idea'
+apply plugin: 'tomcat'
+apply plugin: 'project-report'
+apply plugin: 'java'
+apply plugin: 'openjpa'
+apply plugin: 'pmd'
+apply plugin: 'findbugs'
+
+/* define the valid syntax level for source files */
+sourceCompatibility = JavaVersion.VERSION_1_8
+/* define binary compatibility version */
+targetCompatibility = JavaVersion.VERSION_1_8
+
+project.ext.springBootVersion = '1.2.8.RELEASE'
+project.ext.springVersion = '4.1.9.RELEASE'
+project.ext.springOauthVersion = '2.0.4.RELEASE'
+project.ext.jerseyVersion = '1.17'
+project.ext.springDataJpaVersion = '1.7.0.RELEASE' // also change spring-boot-gradle-plugin version above
+
+project.ext.mysqlUser='root'
+project.ext.mysqlPassword='mysql'
+
+
+group = 'org.apache.fineract'
+buildDir = new File(rootProject.projectDir, "../build")
+repositories {
+	jcenter()
+	// mavenLocal() // useful for local dev using MariaDB4j SNAPSHOTs (not needed for real-world non-SNAPHOT builds)
+}
+openjpa {
+    files = fileTree(sourceSets.main.output.classesDir).matching {
+		include '**/AbstractPersistableCustom.class'
+		include '**/domain/*.class'
+
+    }
+    enforcePropertyRestrictions = true
+}
+
+rat {
+  xmlOutput = false
+  htmlOutput = false
+  plainOutput = true
+  verbose = false
+//  inputDir = './..'
+  reportDir = new File(buildDir,'reports/rat')
+ excludes = [
+	'**/licenses/**',
+	'**/*.md',
+	'**/*.github/*',
+	'**/MANIFEST.MF',
+	'**/*.txt',
+	'**/*.log',
+	'**/fineractdev-eclipse-preferences.epf',
+	'**/template-expected.html',
+	'**/template.mustache',
+	'**/.classpath',
+	'**/.project',
+	'**/.idea/**',
+    '**/*.ipr',
+    '**/*.iws',
+	'**/.settings/**',
+	'**/bin/**',
+	'**/.git/**',
+    '**/.gitignore',
+    '**/.gitkeep',
+	'**/*.iml',
+	//Notice files
+	'**/NOTICE_RELEASE',
+	'**/NOTICE_SOURCE',
+    // Swagger License
+    '**/src/main/resources/swagger-ui/**',
+    // gradle
+    '**/.gradle/**',
+    '**/gradlew',
+    '**/gradlew.bat',
+    '**/gradle/wrapper/gradle-wrapper.properties',
+    '**/caches/**',
+    '**/daemon/**',
+    '**/native/**',
+    '**/wrapper/**',
+	'**/build/**',
+
+	//Api Docs
+	'**/api-docs/*.*',
+	'**/docs/system-architecture/.htaccess',
+	'**/docs/system-architecture/404.html',
+	'**/docs/system-architecture/index.html',
+	'**/docs/system-architecture/**/*.xml',
+	'**/bootstrap-3.0.0/assets/application.js',
+	'**/system-architecture/js/plugins.js',
+
+	//Apache License
+	'**/bootstrap-3.0.0/assets/less.js',
+	'**/css/bootstrap-3.0.0/**/*.*',
+
+	//Public Domain See http://www.JSON.org/js.html
+	'**/bootstrap-3.0.0/assets/json2.js.htm',
+
+	// MIT License
+	'**/modernizr-2.6.2.min.js',
+	'**/css/normalize.css',
+	'**/assets/filesaver.js',
+	'**/css/fonts/glyphicons-halflings-regular.svg',
+	'**/assets/jszip.js',
+	'**/assets/jquery.js',
+	'**/api-docs/jquery-1.7.min.js',
+	'**/css/toc-0.1.2/**/*.*',
+	'**/docs/system-architecture/css/main.css',
+	'**/system-architecture/js/vendor/jquery-1.9.1.min.js',
+	'**/system-architecture/js/vendor/toc-0.1.2/jquery.toc.min.js',
+	'**/assets/respond.min.js',
+	'**/assets/html5shiv.js',
+
+	//BSD License
+	'**/assets/uglify.js',
+         //Ignore out folder
+         '**/out/**'
+	]
+}
+
+configurations {
+	providedRuntime // needed for Spring Boot executable WAR
+    providedCompile
+	compile() {
+		exclude module: 'hibernate-entitymanager'
+		exclude module: 'hibernate-validator'
+		exclude module: 'activation'
+		exclude module: 'bcmail-jdk14'
+		exclude module: 'bcprov-jdk14'
+		exclude module: 'bctsp-jdk14'
+		exclude module: 'bval-core'
+		exclude module: 'org.apache.bval.bundle'
+		exclude module: 'bval-jsr303'
+		exclude module: 'c3p0'
+		exclude module: 'stax-api'
+		exclude module: 'jaxb-api'
+		exclude module: 'jaxb-impl'
+		exclude module: 'jboss-logging'
+		exclude module: 'itext-rtf'
+		exclude module: 'classworlds'
+		exclude module: 'jcl-over-slf4j'
+		exclude module: 'jul-to-slf4j'
+		exclude module: 'serp'
+	}
+	runtime
+	all*.exclude group: 'commons-logging'
+}
+/* Pick up dependencies based on the environemnt, defaults to production */
+if (project.hasProperty('env') && project.getProperty('env') == 'dev') {
+    apply from:  'dev-dependencies.gradle'
+}  else {
+    apply from: 'dependencies.gradle'
+}
+
+/* Enable Oauth2 authentication based on environment, default to HTTP basic auth */
+if (project.hasProperty('security') && project.getProperty('security') == 'oauth') {
+    if(project.hasProperty('twofactor') && project.getProperty('twofactor') == 'enabled') {
+        copy {
+            from './properties/oauth/twofactor/'
+            into 'src/main/resources/'
+            include '*.properties'
+        }
+    } else {
+        copy {
+            from './properties/oauth/'
+            into 'src/main/resources/'
+            include '*.properties'
+        }
+    }
+}  else {
+    if(project.hasProperty('twofactor') && project.getProperty('twofactor') == 'enabled') {
+        copy {
+            from './properties/basicauth/twofactor/'
+            into 'src/main/resources/'
+            include '*.properties'
+        }
+    } else {
+        copy {
+            from './properties/basicauth/'
+            into 'src/main/resources/'
+            include '*.properties'
+        }
+    }
+}
+
+task dist(type:Zip){
+	baseName = 'fineractplatform'
+	version = qualifyVersionIfNecessary(releaseVersion)
+    includeEmptyDirs = true
+	from('../') {
+		fileMode = 0755
+		include '*.md'
+	}
+    from('src/main/dist') {
+        fileMode = 0755
+        include '*'
+    }
+	from('../apps') {
+		fileMode = 0755
+        include '**/*'
+		into('apps')
+	}
+    from('../api-docs/') {
+        fileMode = 0755
+        include '*'
+        into('api-docs')
+    }
+    from('../fineract-db/') {
+        fileMode = 0755
+        include '*.sql'
+        into('database')
+    }
+    from('src/main/resources/sql/migrations') {
+        fileMode = 0755
+        include '**/*'
+        into('database/migrations')
+    }
+
+    from war.outputs.files
+    into(baseName + '-' + version)
+}
+
+compileJava{
+    dependsOn rat
+}
+pmd {
+    sourceSets = [sourceSets.main]
+    ignoreFailures = true
+}
+findbugs {
+    ignoreFailures = true
+    sourceSets = [sourceSets.main]
+}
+
+war {
+	from('../licenses/binary/') { // notice the parens
+		into "WEB-INF/licenses/binary/" // no leading slash
+	}
+	from('../LICENSE_RELEASE') { // notice the parens
+		into "WEB-INF/" // no leading slash
+	}
+	from('../NOTICE_RELEASE') { // notice the parens
+		into "WEB-INF/" // no leading slash
+	}
+	rename ('LICENSE_RELEASE', 'LICENSE')
+	rename ('NOTICE_RELEASE', 'NOTICE')
+
+	from('../DISCLAIMER') { // notice the parens
+		into "WEB-INF/" // no leading slash
+	}
+    war.finalizedBy(bootRepackage)
+}
+
+license {
+    header rootProject.file('../APACHE_LICENSETEXT.md')
+    excludes(["**/*.html", "**/*.mustache", "**/package-info.java", "**/keystore.jks", "**/swagger-ui/**"])
+    strictCheck true
+}
+
+task licenseFormatBuildScripts (type:nl.javadude.gradle.plugins.license.License) {
+    source = fileTree(dir: '../', includes: ['**/*.bat', '**/*.sh', '**/*.sql'])
+}
+licenseFormat.dependsOn licenseFormatBuildScripts
+
+tomcatRun {
+    httpPort = 8080
+    httpsPort = 8443
+    stopPort = 8081
+    stopKey=   'stopKey'
+    enableSSL = true
+    configFile = file('src/test/resources/META-INF/context.xml')
+}
+
+tomcatRunWar {
+    httpPort = 8080
+    httpsPort = 8443
+    stopPort = 8081
+    stopKey=   'stopKey'
+    enableSSL = true
+    keystoreFile = file('src/main/resources/keystore.jks')
+    keystorePass = 'openmf'
+    configFile = file('src/test/resources/META-INF/context.xml')
+}
+
+/* http://stackoverflow.com/questions/19653311/jpa-repository-works-in-idea-and-production-but-not-in-gradle */
+sourceSets.main.output.resourcesDir = sourceSets.main.output.classesDir
+sourceSets.test.output.resourcesDir = sourceSets.test.output.classesDir
+
+/* Exclude maria db and embedded tomcat related files for non dev builds */
+if (!(project.hasProperty('env') && project.getProperty('env') == 'dev')) {
+sourceSets {
+    main {
+        java {
+            exclude '**/Server*'
+            exclude '**/MariaDB4j*'
+            exclude '**/EmbeddedTomcatWithSSLConfiguration.java'
+        }
+    }
+    test {
+    	java {
+    		exclude '**/core/boot/tests/**'
+    	}
+    }
+}
+}
+
+sourceSets {
+ integrationTest {
+    	compileClasspath += main.output + test.output
+        runtimeClasspath += main.output + test.output
+    }
+}
+
+configurations {
+    integrationTestCompile.extendsFrom testCompile
+    integrationTestRuntime.extendsFrom testRuntime
+}
+
+task integrationTest(type:Test){
+    description = "Run integration tests (located in src/integrationTest/java). Starts tomcat in daemon mode before executing the tests."
+    it.dependsOn war
+    doFirst {
+        tomcatRunWar.daemon = true
+        tomcatRunWar.execute()
+    }
+    testClassesDir = project.sourceSets.integrationTest.output.classesDir
+    classpath = project.sourceSets.integrationTest.runtimeClasspath
+}
+
+
+import groovy.sql.Sql
+
+repositories {
+    mavenCentral()
+}
+configurations {
+    driver
+}
+dependencies {
+    driver 'org.drizzle.jdbc:drizzle-jdbc:1.3'
+}
+
+test {
+	filter {
+        //includeTestsMatching "org.apache.fineract.infrastructure.configuration.spring.SpringConfigurationTest.*"
+        includeTestsMatching "org.apache.fineract.template.TemplateMergeServiceTest.*"
+    }
+}
+
+URLClassLoader loader = GroovyObject.class.classLoader
+configurations.driver.each {File file ->
+    loader.addURL(file.toURL())
+}
+
+task createDB<<{
+    description= "Creates the Database. Needs database name to be passed (like: -PdbName=someDBname)"
+    def sql = Sql.newInstance( 'jdbc:mysql:thin://fineractmysql:3306/', mysqlUser, mysqlPassword, 'org.drizzle.jdbc.DrizzleDriver' )
+    sql.execute( 'create database '+"`$dbName`" )
+}
+
+task dropDB<<{
+    description= "Drops the specified database. The database name has to be passed (like: -PdbName=someDBname)"
+    def sql = Sql.newInstance( 'jdbc:mysql:thin://fineractmysql:3306/', mysqlUser, mysqlPassword, 'org.drizzle.jdbc.DrizzleDriver' )
+    sql.execute( 'DROP DATABASE '+"`$dbName`")
+}
+task setBlankPassword<<{
+    def sql = Sql.newInstance( 'jdbc:mysql:thin://fineractmysql:3306/', mysqlUser, mysqlPassword, 'org.drizzle.jdbc.DrizzleDriver' )
+    sql.execute('USE `mifosplatform-tenants`')
+    sql.execute('UPDATE mifosplatform-tenants.tenants SET schema_server = \'fineractmysql\', schema_server_port = \'3306\', schema_username = \'mifos\', schema_password = \'mysql\' WHERE id=1;')
+}
+
+
+apply plugin: 'flyway'
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath "org.flywaydb:flyway-gradle-plugin:3.0" // version upgraded during Spring Boot & MariaDB4j work, as prev. used v0.2 didn't work well after *.sql moved from fineract-db to fineract-provider/src/main/resources (new version also has clearer errors, e.g. in case of missing DB)
+
+        // NOT classpath 'org.drizzle.jdbc:drizzle-jdbc:1.3' as it is not compatible with Flyway 3.1+ (only up to 3.0)
+        // We require Flyway 3.1+ because =< 3.0 is not compatible with current Gradle versions (only ancient ones we originally used to use).
+        // Using LGPL ConnectorJ ()org.mariadb.jdbc:mariadb-java-client) instead of the BSD licensed drizzle-jdbc *IS OK* at Apache.org,
+        // as long as it's only used as a test library by developers at build time, and not end-user distributed, and optional.
+        // So it's OK here, but would not be as a default runtime dependency and in the dist JAR/WAR.
+        // see https://issues.apache.org/jira/browse/LEGAL-462
+        // see https://issues.apache.org/jira/browse/FINERACT-761
+        // see https://github.com/flyway/flyway/issues/2332
+        // see https://github.com/krummas/DrizzleJDBC/issues/46
+        // see https://github.com/apache/fineract/pull/525
+        // see https://github.com/apache/fineract/pull/550
+	classpath 'org.mariadb.jdbc:mariadb-java-client:2.4.1'
+    }
+}
+
+
+flyway {
+    url = "jdbc:mariadb://fineractmysql:3306/mifostenant-default"
+    driver = "org.mariadb.jdbc.Driver"
+    user = mysqlUser
+    password = mysqlPassword
+}
+
+task migrateTenantDB<<{
+    description="Migrates a Tenant DB. Optionally can pass dbName. Defaults to 'mifostenant-default' (Example: -PdbName=someTenantDBname)"
+
+	def filePath = "filesystem:$projectDir" + '/src/main/resources/sql/migrations/core_db'
+	def tenantDbName = 'mifostenant-default';
+    if (rootProject.hasProperty("dbName")) {
+		tenantDbName = rootProject.getProperty("dbName")
+	}
+
+    flyway.url= "jdbc:mariadb://fineractmysql:3306/$tenantDbName"
+    flyway.driver = "org.mariadb.jdbc.Driver"
+    flyway.locations= [filePath]
+    /**We use ${ as the prefix for strecthy reporting, do not want them to be interpreted by Flyway**/
+    flyway.placeholderPrefix = "\$\${"
+    flywayMigrate.execute()
+}
+
+task showTenantDBInfo<<{
+    description="Shows the migration info for a Tenant DB. Optionally can pass dbName. Defaults to 'mifostenant-default' (Example: -PdbName=someTenantDBname)"
+
+	def filePath = "filesystem:$projectDir" + '/src/main/resources/sql/migrations/core_db'
+    def tenantDbName = 'mifostenant-default';
+    if (rootProject.hasProperty("dbName")) {
+		tenantDbName = rootProject.getProperty("dbName")
+	}
+
+    flyway.url= "jdbc:mariadb://fineractmysql:3306/$tenantDbName"
+    flyway.driver = "org.mariadb.jdbc.Driver"
+    flyway.locations= [filePath]
+    flywayInfo.execute()
+}
+
+
+task migrateTenantListDB<<{
+    description="Migrates a Tenant List DB. Optionally can pass dbName. Defaults to 'mifosplatform-tenants' (Example: -PdbName=someDBname)"
+
+	def filePath = "filesystem:$projectDir" + '/src/main/resources/sql/migrations/list_db'
+ 	def tenantsDbName = 'mifosplatform-tenants';
+    if (rootProject.hasProperty("dbName")) {
+		tenantsDbName = rootProject.getProperty("dbName")
+	}
+
+    flyway.url= "jdbc:mariadb://fineractmysql:3306/$tenantsDbName"
+    flyway.driver = "org.mariadb.jdbc.Driver"
+    flyway.locations= [filePath]
+
+    flywayMigrate.execute()
+}
+
+task showTenantListDBInfo<<{
+    description="Shows the migration info for a List DB. Optionally can pass dbName. Defaults to 'mifosplatform-tenants' (Example: -PdbName=someDBname)"
+
+	def filePath = "filesystem:$projectDir" + '/src/main/resources/sql/migrations/list_db'
+    def tenantsDbName = 'mifosplatform-tenants';
+    if (rootProject.hasProperty("dbName")) {
+		tenantsDbName = rootProject.getProperty("dbName")
+	}
+
+    flyway.url= "jdbc:mariadb://fineractmysql:3306/$tenantsDbName"
+    flyway.driver = "org.mariadb.jdbc.Driver"
+    flyway.locations= [filePath]
+    flywayInfo.execute()
+}
+
+task repairTenantDB<<{
+    description="repair migrate"
+
+	def filePath = "filesystem:$projectDir" + '/src/main/resources/sql/migrations/list_db'
+    def tenantsDbName = 'mifosplatform-tenants';
+    if (rootProject.hasProperty("dbName")) {
+        tenantsDbName = rootProject.getProperty("dbName")
+    }
+
+    flyway.url= "jdbc:mariadb://fineractmysql:3306/$tenantsDbName"
+    flyway.driver = "org.mariadb.jdbc.Driver"
+    flyway.locations= [filePath]
+    flywayRepair.execute()
+}
+
+/*
+* Support publication of artifacts versioned by topic branch.
+* CI builds supply `-P BRANCH_NAME=<TOPIC>` to gradle at build time.
+* If <TOPIC> starts with 'MIFOSX-', change version
+* from BUILD-SNAPSHOT => <TOPIC>-SNAPSHOT
+* e.g. 1.1.0.BUILD-SNAPSHOT => 1.0.0.MIFOSX-1234-SNAPSHOT
+*/
+def qualifyVersionIfNecessary(version) {
+
+	if (rootProject.hasProperty("BRANCH_NAME")) {
+		def qualifier = rootProject.getProperty("BRANCH_NAME")
+		if (qualifier.startsWith("MIFOSX-")) {
+			return version.replace('BUILD', qualifier)
+		}
+	}
+	return version
+}
+
+springBoot {
+    mainClass = 'org.apache.fineract.ServerWithMariaDB4jApplication'
+}
+bootRepackage {
+    mainClass = 'org.apache.fineract.ServerWithMariaDB4jApplication'
+}
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
new file mode 100644
index 0000000..912124b
--- /dev/null
+++ b/docker/docker-compose.yml
@@ -0,0 +1,24 @@
+version: '3.7'
+services:
+  fineractmysql:
+    image: mysql:5.7
+    volumes:
+      - dbdata:/var/lib/mysql
+      - ./initdb:/docker-entrypoint-initdb.d
+    restart: always
+    environment:
+      MYSQL_ROOT_PASSWORD: mysql
+      MYSQL_USER: root
+      MYSQL_PASSWORD: mysql
+    ports:
+      - "3306:3306"
+  fineract-server:
+    build:
+      context: .
+      target: fineract
+    ports: 
+      - 443:443
+    depends_on:
+      - fineractmysql
+volumes:
+  dbdata:
\ No newline at end of file
diff --git a/docker/initdb/01-databases.sql b/docker/initdb/01-databases.sql
new file mode 100644
index 0000000..e4fa5fb
--- /dev/null
+++ b/docker/initdb/01-databases.sql
@@ -0,0 +1,6 @@
+# create databases
+CREATE DATABASE IF NOT EXISTS `mifosplatform-tenants`;
+CREATE DATABASE IF NOT EXISTS `mifostenant-default`;
+
+# create root user and grant rights
+GRANT ALL ON *.* TO 'root'@'%';
\ No newline at end of file
diff --git a/docker/server.xml b/docker/server.xml
new file mode 100644
index 0000000..3b4a0fc
--- /dev/null
+++ b/docker/server.xml
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<!-- Note:  A "Server" is not itself a "Container", so you may not
+     define subcomponents such as "Valves" at this level.
+     Documentation at /docs/config/server.html
+ -->
+<Server port="8005" shutdown="SHUTDOWN">
+  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
+  <!-- Security listener. Documentation at /docs/config/listeners.html
+  <Listener className="org.apache.catalina.security.SecurityListener" />
+  -->
+  <!--APR library loader. Documentation at /docs/apr.html -->
+  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
+  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
+  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
+  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
+  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
+
+  <!-- Global JNDI resources
+       Documentation at /docs/jndi-resources-howto.html
+  -->
+  <GlobalNamingResources>
+    <!-- Editable user database that can also be used by
+         UserDatabaseRealm to authenticate users
+    -->
+     <Resource type="javax.sql.DataSource"
+        name="jdbc/mifosplatform-tenants"
+        factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
+        driverClassName="org.drizzle.jdbc.DrizzleDriver"
+        url="jdbc:mysql:thin://fineractmysql:3306/mifosplatform-tenants"
+        username="root"
+        password="mysql"
+        initialSize="3"
+        maxActive="10"
+        maxIdle="6"
+        minIdle="3"
+        validationQuery="SELECT 1"
+        testOnBorrow="true"
+        testOnReturn="true"
+        testWhileIdle="true"
+        timeBetweenEvictionRunsMillis="30000"
+        minEvictableIdleTimeMillis="60000"
+        logAbandoned="true"
+        suspectTimeout="60"
+      />
+  </GlobalNamingResources>
+
+  <!-- A "Service" is a collection of one or more "Connectors" that share
+       a single "Container" Note:  A "Service" is not itself a "Container",
+       so you may not define subcomponents such as "Valves" at this level.
+       Documentation at /docs/config/service.html
+   -->
+  <Service name="Catalina">
+
+    <!--The connectors can use a shared executor, you can define one or more named thread pools-->
+    <!--
+    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
+        maxThreads="150" minSpareThreads="4"/>
+    -->
+
+
+    <!-- A "Connector" represents an endpoint by which requests are received
+         and responses are returned. Documentation at :
+         Java HTTP Connector: /docs/config/http.html
+         Java AJP  Connector: /docs/config/ajp.html
+         APR (HTTP/AJP) Connector: /docs/apr.html
+         Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
+    -->
+    <Connector port="8080" protocol="HTTP/1.1"
+               connectionTimeout="20000"
+               redirectPort="8443" />
+    <!-- A "Connector" using the shared thread pool-->
+    <!--
+    <Connector executor="tomcatThreadPool"
+               port="8080" protocol="HTTP/1.1"
+               connectionTimeout="20000"
+               redirectPort="8443" />
+    -->
+    <!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443
+         This connector uses the NIO implementation. The default
+         SSLImplementation will depend on the presence of the APR/native
+         library and the useOpenSSL attribute of the
+         AprLifecycleListener.
+         Either JSSE or OpenSSL style configuration may be used regardless of
+         the SSLImplementation selected. JSSE style configuration is used below.
+    -->
+	<Connector protocol="org.apache.coyote.http11.Http11Protocol"
+    port="8443" maxThreads="200" scheme="https"
+    secure="true" SSLEnabled="true"
+    keystoreFile="/opt/bitnami/tomcat/tomcat.keystore"
+    keystorePass="xyz123"
+    clientAuth="false" sslProtocol="TLS"
+    URIEncoding="UTF-8"
+    compression="force"
+    compressableMimeType="text/html,text/xml,text/plain,text/javascript,text/css"/>
+    <!--
+    <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
+               maxThreads="150" SSLEnabled="true">
+        <SSLHostConfig>
+	    <Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
+                         type="RSA" />
+        </SSLHostConfig>
+    </Connector>
+    -->
+    <!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
+         This connector uses the APR/native implementation which always uses
+         OpenSSL for TLS.
+         Either JSSE or OpenSSL style configuration may be used. OpenSSL style
+         configuration is used below.
+    -->
+    <!--
+    <Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
+               maxThreads="150" SSLEnabled="true" >
+        <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
+        <SSLHostConfig>
+            <Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
+                         certificateFile="conf/localhost-rsa-cert.pem"
+                         certificateChainFile="conf/localhost-rsa-chain.pem"
+                         type="RSA" />
+        </SSLHostConfig>
+    </Connector>
+    -->
+
+    <!-- Define an AJP 1.3 Connector on port 8009 -->
+    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
+
+
+    <!-- An Engine represents the entry point (within Catalina) that processes
+         every request.  The Engine implementation for Tomcat stand alone
+         analyzes the HTTP headers included with the request, and passes them
+         on to the appropriate Host (virtual host).
+         Documentation at /docs/config/engine.html -->
+
+    <!-- You should set jvmRoute to support load-balancing via AJP ie :
+    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
+    -->
+    <Engine name="Catalina" defaultHost="localhost">
+
+      <!--For clustering, please take a look at documentation at:
+          /docs/cluster-howto.html  (simple how to)
+          /docs/config/cluster.html (reference documentation) -->
+      <!--
+      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
+      -->
+
+      <!-- Use the LockOutRealm to prevent attempts to guess user passwords
+           via a brute-force attack -->
+      <Realm className="org.apache.catalina.realm.LockOutRealm">
+        <!-- This Realm uses the UserDatabase configured in the global JNDI
+             resources under the key "UserDatabase".  Any edits
+             that are performed against this UserDatabase are immediately
+             available for use by the Realm.  -->
+        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
+               resourceName="UserDatabase"/>
+      </Realm>
+
+      <Host name="localhost"  appBase="webapps"
+            unpackWARs="true" autoDeploy="true">
+
+        <!-- SingleSignOn valve, share authentication between web applications
+             Documentation at: /docs/config/valve.html -->
+        <!--
+        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
+        -->
+
+        <!-- Access log processes all example.
+             Documentation at: /docs/config/valve.html
+             Note: The pattern used is equivalent to using pattern="common" -->
+        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
+               prefix="localhost_access_log" suffix=".txt"
+               pattern="%h %l %u %t &quot;%r&quot; %s %b" />
+
+      </Host>
+    </Engine>
+  </Service>
+</Server>
\ No newline at end of file