Tech notes for gradle


Gradle: Hello world Java with a fat jar

Create your build.gradle with your dependencies and a jar section that collects all your libraries into the jar and sets the Main class file.

apply plugin: 'java'

repositories {
   mavenCentral()
}

dependencies {
    compile 'org.mindrot:jbcrypt:0.3m'
}

jar {
    from {
        (configurations.runtime).collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }
    manifest {
        attributes("Main-Class": "Main" )
    }
}

Now create a basic hello world, using the library we imported:

import org.mindrot.jbcrypt.BCrypt;

public class Main {
        public static void main(String[] args) {
                String password = BCrypt.hashpw("password", BCrypt.gensalt(10));
                System.out.println(password);
        }
}

Now build and run your jar:

$ gradle clean build && java -jar build/libs/THE_NAME_OF_YOUR_JAR.jar
...
$2a$10$R6q8LOed8LqXCOIhBnzhMecyebv/8v1urKjU76JMJGUctnZ8VkyZu
java gradle gradle-jar java-jar

A single page Java/Jetty/Jersey REST app with no web.xml descriptor

First create the build.gradle file in your directory:

apply plugin: 'war'
apply plugin: 'eclipse'

repositories {
   mavenCentral()
}

dependencies {
    compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.16'
    compile 'org.glassfish.jersey.bundles:jaxrs-ri:2.16'
}

compileJava {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

Then, in src/main/java/com/example, create App.java:

package com.example;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath("rest") 
public class App extends ResourceConfig {
  public App() { packages("com.example"); }

  @Path("example")
  public static class Hello {
    
      @GET
      @Produces(MediaType.APPLICATION_JSON)
      public List<String> example() {
        List<String> l = new ArrayList<>();
        l.add("one"); l.add("two");
        return l; 
      }

  }  
  
}

Now

  1. Create the war file with gradle
  2. Download the Jetty runner jar
  3. Run the war file with the jetty runner (takes about 15 seconds to init usually)
  4. Use curl to test the app

From the command line:

gradle build war
wget http://central.maven.org/maven2/org/eclipse/jetty/jetty-runner/9.3.0.M1/jetty-runner-9.3.0.M1.jar
java -jar jetty-runner-9.3.0.M1.jar --port 8081 build/libs/your-directory-name.war
curl localhost:8081/rest/example && echo

The above curl command should print:

["one","two"]
java-jetty java-jersey gradle java

Gradle: Production resource files

Unlike Gradle's Android plugin, there doesn't seem (as of 1.10) a great way to specify production, or any other type, of resources.

There may be a better way, but simply specifying an additional resources directory works.

For instance, say your directory structure is:

src/main/java/...
src/main/resources/...

Then when you compile, the files in src/main/resources/ will be copied to, in the case of WAR files, WEB-INF/classes/.

However, if you also have an additional directory

src/prod/resources

And specify in your build.gradle file to include both the resources directory (in this case when -PPRODUCTION is passsed to Gradle, -P is for project variable)

if(project.hasProperty('PRODUCTION')) {
        sourceSets.main.resources.srcDirs 'src/main/resources', 'src/prod/resources'
}

Then gradle will place the resources in src/prod/resources in your, again in the case of WAR files, in WEB-INF/classes/, thereby overwriting the src/main/resources files.

gradle

PART 1: Quick start a RESTful project with Jersey, Jetty and Gradle

(This is part of a series http://newfivefour.com/category_java-web-quick-start.html

Make the project directory and the directory structure for Gradle:

mkdir YOUR_PROJECT_DIR && cd YOUR_PROJECT_DIR
mkdir -p {src/main/java/com/example/YOURPROJECT/,src/main/resources/META-INF,src/main/webapp/WEB-INF,logs}

Next create the Gradle build file, including the war and eclipse plugins, and the logging, jersey and jersey json jars.

echo "
apply plugin: 'war'
apply plugin: 'eclipse'

repositories {
    mavenCentral()
    // Below needed in later tutorials
    maven {
        url 'http://download.eclipse.org/rt/eclipselink/maven.repo'
    }
}

dependencies {
    compile 'log4j:log4j:1.2.7'
    compile 'org.slf4j:slf4j-log4j12:1.6.6'
    compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.6'
    // Following aren't needed atm, but will be in later parts.
    compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.6'
    compile 'org.eclipse.jetty:jetty-jsp:9.1.0.M0'
    compile 'postgresql:postgresql:9.1-901-1.jdbc4'
    compile 'org.eclipse.persistence:eclipselink:2.4.0'        
}" > build.gradle

Next create the log4j.properties file.

echo "
log4j.rootCategory=INFO, rollingFile 

log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.File=logs/YOUR_LOG_FILE.log
log4j.appender.rollingFile.MaxFileSize=10MB
log4j.appender.rollingFile.MaxBackupIndex=2
log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.ConversionPattern=%d{yyyy-MM-dd HH-mm-ss} %-5p [%t] %c %x - %m%n
" > src/main/resources/log4j.properties

Next create the web.xml file starting Jersey by pointing to a yet-to-be-created application class, specifying the package where the request classes will live, and defining a path for the REST calls.

echo '
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	version="2.4">

	<display-name>YOUR_SERVICE</display-name>
	
	<servlet>
		<servlet-name>YOUR_SERVLET_NAME</servlet-name>
		<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.example.YOURPROJECT.JerseyApplication</param-value>
        </init-param>		
	    <init-param>
	    	<param-name>jersey.config.server.provider.packages</param-name>
			<param-value>com.example.YOURPROJECT</param-value>
	    </init-param>
	    <load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>YOUR_SERVLET_NAME</servlet-name>
		<url-pattern>/YOUR_PATH/*</url-pattern>
	</servlet-mapping>
	
</web-app>
' > src/main/webapp/WEB-INF/web.xml

Now create that JerseyApplication file we referred to. (Specifying the JerseyApplication isn't strictly necessary, nor is registering Jackson for this setup, but if we're using JSON later on, it will be)

echo "
package com.example.YOURPROJECT;

import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;

public class JerseyApplication extends ResourceConfig {
	public JerseyApplication() {
		register(JacksonFeature.class);
	}
}

" > src/main/java/com/example/YOURPROJECT/JerseyApplication.java

Now create a simple Jersey request.

echo '
package com.example.YOURPROJECT;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.apache.log4j.Logger;

@Path("example")
public class ExampleRequest {
	
	@Path("{example}")
	@GET
	@Produces(MediaType.TEXT_PLAIN)
	public String example(@PathParam("example") String example) {
		Logger.getLogger(getClass()).info("Working???");
		return example + "!!!!";
	}

}
' > src/main/java/com/example/YOURPROJECT/ExampleRequest.java

Build the project, which creates a WAR file, download the jetty runner, and then use the runner to run the WAR on port 8081.

gradle build
wget http://repo1.maven.org/maven2/org/eclipse/jetty/jetty-runner/9.1.0.M0/jetty-runner-9.1.0.M0.jar
java -jar jetty-runner-9.1.0.M0.jar --port 8081 build/libs/YOUR_PROJECT_DIR.war

Now you should be able envoke the request you defined earlier by visiting its url (and check the log file after you do so).

http://localhost:8081/YOUR_PATH/example/some_text

If you want to use this project in Eclipse, run 'gradle eclipse' and import the project into Eclipse.

java-jersey java java-jetty gradle java-web-quick-start

Android: Gradle with UiAutomator

UiAutomator does not directly support Gradle as yet, but you can get it to work with this gradle build file, calling in the ant file in your sdk:

ant.properties['sdk.dir']="$System.env.ANDROID_HOME"
ant.properties['target']='android-19'
ant.properties['out.filename']=project.name+'_tests.jar'
ant.properties['out.dir']=project.projectDir.toString()+'/bin/'
ant.properties['source.dir']=project.projectDir.toString()+'/uiTests' // Or wherever your tests are


task uiBuild() << {
	ant.project.executeTarget('build')
}

task uiInstall(dependsOn: ['uiBuild']) << {
	ant.project.executeTarget('install')
}

task uiRun(dependsOn: ['uiInstall']) << {
    Process proc = ["adb", "shell", "uiautomator", "runtest", project.name+"_tests.jar", "-e because broken"].execute()
    proc.consumeProcessErrorStream(System.err)
    proc.consumeProcessOutputStream(System.out)
    if (proc.waitFor() != 0) {
	throw new RuntimeException('exec failed')
    }
}

(Note: Ensure your ANDROID_HOME is set)

You should change the 'target' and 'source.dir' above to suit you.

This means you do not need to put the uiautomator files in a different directory. I am running them in my main project, under the 'uiTests' directory.

Now run it:

$ gradle uiRun
:uiBuild
Android SDK Tools Revision 22.3.0
Installed at /home/user/android-sdk-linux
Using latest Build Tools: 19.0.0
Project Target:   Android 4.4
API level:        19
input: /home/user/android-sdk-linux/tools/ant/bin/classes
No new compiled code. No need to convert bytecode to dalvik format.
:uiInstall
:uiRun
INSTRUMENTATION_STATUS: numtests=1
INSTRUMENTATION_STATUS: stream=
org.denevell.droidnatch.uitests._1_ListThreads:
INSTRUMENTATION_STATUS: id=UiAutomatorTestRunner
INSTRUMENTATION_STATUS: test=testListThreads
INSTRUMENTATION_STATUS: class=Your.Test.Class
INSTRUMENTATION_STATUS: current=1
INSTRUMENTATION_STATUS_CODE: 1
// Any System.out.println you have set
INSTRUMENTATION_STATUS: numtests=1
INSTRUMENTATION_STATUS: stream=.
INSTRUMENTATION_STATUS: id=UiAutomatorTestRunner
INSTRUMENTATION_STATUS: test=testListThreads
INSTRUMENTATION_STATUS: class=Your.Test.Class
INSTRUMENTATION_STATUS: current=1
INSTRUMENTATION_STATUS_CODE: 0
INSTRUMENTATION_STATUS: stream=
Test results for WatcherResultPrinter=.
Time: 11.565

OK (1 test)


INSTRUMENTATION_STATUS_CODE: -1

BUILD SUCCESSFUL

Total time: 14.871 secs

Your new uiautormator jar will now be in your /bin directory. (You can change that in the above script).

Beware, that if you have no devices attached in debugging mode, it will just return

:uiInstall FAILED

FAILURE: Build failed with an exception.

* Where:
Build file '/home/user/workspace/Natch-Android/build.gradle' line: 12

* What went wrong:
Execution failed for task ':uiInstall'.
> exec returned: 1
android android-uiautomator gradle

Page 2 of 5
prev next
Click me