Extensions - coding and testing

Starting with version 3

Coding the extension

Coding the extension is a little more difficult than normal, the recommended way is to split out the building of the extension and the testing of the extension. To this aim, we will look at the h2zero-extension-taglibs project which generates tag libraries from the h2zero object model.

There are multiple parts to this:

  1. The build.gradle file,
  2. the build.h2zero file, and
  3. the .h2zero file.

The build.gradle file

This file will compile, test and package the extension for use.

plugins {
	id 'java'
	id 'eclipse'
	id "maven"
	id "maven-publish"

	id "com.github.ben-manes.versions" version "0.28.0"
	id "com.jfrog.bintray" version "1.8.4"

	id 'com.gradle.plugin-publish' version '0.11.0'
	id 'co.riiid.gradle' version '0.4.2'
//	id 'net.saliman.cobertura' version '2.5.4'

	id 'synapticloop.copyrightr' version '1.3.1'
	id 'synapticloop.documentr' version '3.0.0'
	id 'synapticloop.h2zero' version '4.5.3'
}

group = 'synapticloop'
archivesBaseName = 'h2zero-extension-taglibs'
description = """Taglib extension for h2zero"""
version = '4.2.1'

tasks.withType(Javadoc).all { enabled = false }

sourceCompatibility = 1.9
targetCompatibility = 1.9

repositories {
	mavenLocal()
	mavenCentral()
	jcenter()
}

dependencies {
	implementation 'synapticloop:h2zero:4.5.3'
	implementation 'synapticloop:h2zero-extension-taglibs:4.2.1'

	implementation 'javax.servlet.jsp:jsp-api:2.2'
	implementation 'javax.servlet:javax.servlet-api:4.0.1'


	testImplementation 'junit:junit:4.12'
	testImplementation 'org.mockito:mockito-all:1.10.19'
	testImplementation 'com.github.stefanbirkner:system-rules:1.16.1'
	testImplementation 'mysql:mysql-connector-java:8.0.19'
	testImplementation 'org.xerial:sqlite-jdbc:3.30.1'
	testImplementation 'commons-io:commons-io:2.6'
	testImplementation 'com.mashape.unirest:unirest-java:1.4.9'
}

configurations.all {
	resolutionStrategy {
		failOnVersionConflict()
		force 'junit:junit:4.11',
					'org.slf4j:slf4j-api:1.7.25',
					'org.json:json:20180130',
					'commons-logging:commons-logging:1.2',
					'commons-collections:commons-collections:3.2.2',
					'com.github.stefanbirkner:system-rules:1.17.1',
					'org.apache.httpcomponents:httpclient:4.5.2'
	}
}

//cobertura {
//	coverageFormats = [ 'html', 'xml']
//	coverageDirs = [
//		project.sourceSets.main.output.classesDir,
//		file("build/classes/test/")
//	]
//	coverageSourceDirs = [ 
//		project.sourceSets.main.java.srcDirs, 
//		file("src/test/java/")
//	]
//}

test {
	include '**/*Test.class'
	maxParallelForks = 1
}

def javaApiUrl = 'http://docs.oracle.com/javase/1.7.0/docs/api/'
def groovyApiUrl = 'http://groovy.codehaus.org/gapi/'

tasks.withType(Javadoc) {
	options.links(javaApiUrl, groovyApiUrl)
}

task javadocJar(type: Jar, dependsOn: javadoc) {
	classifier = 'javadoc'
	from 'build/docs/javadoc'
}

task sourcesJar(type: Jar) {
	from sourceSets.main.allSource
	classifier = 'sources'
}

//test.finalizedBy(project.tasks.cobertura)


publishing {
	publications {
		Synapticloop(MavenPublication) {
			from components.java
			artifact sourcesJar
			artifact javadocJar

			groupId group

			artifactId archivesBaseName

			pom.withXml {
				configurations.compile.resolvedConfiguration.firstLevelModuleDependencies.each { dep ->
					asNode().dependencies[0].dependency.find {
						it.artifactId[0].text() == dep.moduleName &&
								it.groupId[0].text() == dep.moduleGroup
					}.scope[0].value = 'compile'
				}
			}
		}
	}
}

bintray {
	user = System.getenv('BINTRAY_USER')
	key = System.getenv('BINTRAY_PASSWORD')

	publications = [ 'Synapticloop' ]

	publish = true

	pkg {
		repo = 'maven'
		name = 'h2zero-extension-taglibs'
	}
}

github {
	owner = group
	repo = archivesBaseName
	if(System.getenv('GITHUB_TOKEN')) {
		token = System.getenv('GITHUB_TOKEN')
	}
	tagName = version
	name = version
	assets = [
		'build/libs/' + archivesBaseName + '-' + version + '.jar',
		'build/libs/' + archivesBaseName + '-' + version + '-all.jar'
	]
}

copyrightr {
	excludes [
	]
	dryRun = false
}

//task(dist).dependsOn( [ 'cobertura', 'jar', 'javadoc' ] )

task(dist).dependsOn( [ 'jar', 'javadoc' ] )

Notes:

  • within the dependencies section, the reference to 'synapticloop:h2zero:3.0.0', which is the minimum version to build against.
  • the version = '1.0.0' of the code - which is what we reference in the next part.

To build and test the extension, you will need to either publish this to a maven repository, or to maven local. We use maven local to iterate through the development process, until we are ready to push the extension to the world. Try:

gradle build pTML

The build.h2zero.gradle file

This file is the test build file that generates the code, by default we generate to src/test/java - which is defined in the sample.h2zero file.

buildscript {
	repositories {
		mavenCentral()
		jcenter()
		mavenLocal()
		maven {
			url "https://plugins.gradle.org/"
		}
	}

	dependencies {
		classpath 'synapticloop:h2zero-extension-taglibs:4.2.1'
	}
}

plugins {
	id 'java'
	id 'eclipse'
	id 'synapticloop.h2zero' version '4.5.3'
}

repositories {
	mavenLocal()
	mavenCentral()
	jcenter()
}

h2zero {
	inFile = 'src/test/resources/sample-include.h2zero'
	outDir = '.'
	verbose = 'true'
}

Notes:

  • within the buildscript dependencies section, the reference to 'synapticloop:h2zero-extension-taglibs:1.0.0', which will pick up the maven local reference
  • the version = '1.0.0' in the buildscript dependencies section, this needs to be updated, every time that the build.gradle version is updated

The command that we use is:

gradle -b build.h2zero.gradle h2zero

The sample.h2zero file

To test the generation of the code, we use a sample h2zero file as follows:

{
"options": {
	"database": "mysql",

	"metrics": false,

	"generators": [ "java", "sql" ],

	"extensions": [
		"synapticloop.h2zero.extension.TaglibExtension"
	],

	"validators": {
		"UpdaterNameValidator": {
			"allowablePrefixes": "reset,"
		},
		"FinderNameValidator": {
			"allowablePrefixes": "find,calculate"
		}
	},

	"output": {
		"code": "src/test/java/",
		"resources": "src/test/resources/",
		"build": "build/"
	}
},

"database": {
	"schema": "sample",
	"package": "synapticloop.sample.h2zero.mysql",
	"defaultStatementCacheSize": 1024,

	"tables": [
		{ "include": "./user_type.json" },
		{ "include": "./user_title.json" },
		{ "include": "./user.json" },
		{ "include": "./pet.json" },
		{ "include": "./user_pet.json" }
	],

	"views": [
		{ "include": "./view-pet.json" },
	]
}
}

Note the registration of the extension:

	"extensions": [
		"synapticloop.h2zero.extension.TaglibExtension"
	],

and the output directories for testing

	"output": {
		"code": "src/test/java/",
		"resources": "src/test/resources/",
		"build": "build/"
	}

Putting it all together

  1. Update the version number in the build.gradle file
  2. run gradle build pTML
  3. Update the version number in the build.h2zero.gradle in the dependencies section
  4. run gradle -b build.h2zero.gradle h2zero

If you are just iterating over the development of the generated code - just run:

gradle build pTML; gradle -b build.h2zero.gradle h2zero

to build the extension and generate the code. If there is an error with the generated code, just delete the src/test/java/* contents and start again.