Skip to content
This repository has been archived by the owner on Oct 14, 2020. It is now read-only.

Commit

Permalink
Added Gradle/Spring support to Java
Browse files Browse the repository at this point in the history
- GroovyServ is no longer used. Gradle’s daemon is used instead. Unfortunately performance is about 800ms slower on avg.
- Ability to customize which packages get loaded
- Spring support
- Ability to code blocks into multiple files (all languages)
- Multi-file Java support
- Improved Java exception output
- Java build info is now presented within output
  • Loading branch information
jhoffner committed Jul 10, 2017
1 parent 9f58552 commit 9df0faf
Show file tree
Hide file tree
Showing 25 changed files with 866 additions and 243 deletions.
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ node_modules/*
docker/*
.git/
**/.DS_Store
frameworks/java/.gradle
frameworks/java/4.0
frameworks/java/build
frameworks/java/buildOutputCleanup
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ TestFixture.class
TestFixture.java
frameworks/java/CwRunListener.class
frameworks/java/CwTestRunner.class
frameworks/java/prewarm.status
.DS_Store
*~
target/*
Expand All @@ -22,3 +23,7 @@ Dockerfile
*.hi
\#*
frameworks/csharp/extra
frameworks/java/.gradle
frameworks/java/4.0
frameworks/java/build
frameworks/java/buildOutputCleanup
3 changes: 2 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ services:
- ./examples:/runner/examples
- ./frameworks:/runner/frameworks
- ./test:/runner/test
- ./listen.js:/runner/listen.js
entrypoint: ''
command: bash

Expand Down Expand Up @@ -373,7 +374,7 @@ services:
- ./examples:/runner/examples
- ./frameworks:/runner/frameworks
- ./test:/runner/test
entrypoint: '/runner/debug.sh mocha -t 120000 test/runners/java_spec.js'
entrypoint: 'mocha -t 15000 test/runners/java_spec.js'

clojure:
image: codewars/jvm-runner
Expand Down
56 changes: 40 additions & 16 deletions docker/java.docker
Original file line number Diff line number Diff line change
Expand Up @@ -35,38 +35,62 @@ ENV PATH /usr/local/groovy/bin:${PATH}
RUN mv groovyserv-1.1.0 groovyserv
ENV PATH /usr/local/groovyserv/bin:${PATH}

# Install zip utils
RUN apt-get install -y zip unzip --no-install-recommends

# Install Gradle
ENV GRADLE_HOME /usr/local/gradle
ENV GRADLE_VERSION 4.0

ARG GRADLE_DOWNLOAD_SHA256=56bd2dde29ba2a93903c557da1745cafd72cdd8b6b0b83c05a40ed7896b79dfe
RUN set -o errexit -o nounset \
&& echo "Downloading Gradle" \
&& wget --no-verbose --output-document=gradle.zip "https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" \
\
&& echo "Checking download hash" \
&& echo "${GRADLE_DOWNLOAD_SHA256} *gradle.zip" | sha256sum --check - \
\
&& echo "Installing Gradle" \
&& unzip gradle.zip \
&& rm gradle.zip \
&& mv "gradle-${GRADLE_VERSION}" "${GRADLE_HOME}/" \
&& ln --symbolic "${GRADLE_HOME}/bin/gradle" /usr/bin/gradle

RUN mkdir /usr/local/.gradle
RUN chown codewarrior /usr/local/.gradle

# add the package json first to a tmp directory and build, copy over so that we dont rebuild every time
ADD package.json /tmp/package.json
RUN cd /tmp && npm install --production
RUN mkdir -p /runner && cp -a /tmp/node_modules /runner

# ADD cli-runner and install node deps
ADD . /runner

RUN ln -s /home/codewarrior /workspace
WORKDIR /runner
ADD package.json /runner/package.json
RUN npm install

RUN /usr/local/groovyserv/bin/setup.sh
# ADD cli-runner and install node deps
ADD frameworks/java /runner/frameworks/java
RUN chown codewarrior /runner/frameworks/java
RUN ln -s /home/codewarrior /workspace

# create a debug entry point to make running test code easier
RUN echo '#!/bin/bash\ngroovyserver -t 20 --debug --authtoken groovy || echo "server connection error"\n "$@"'>/runner/debug.sh ; chmod +x /runner/debug.sh
RUN gradle --version

# create a specific node entry point for running the node CLI executable with groovyserve started
RUN echo '#!/bin/bash\ngroovyserver -t 10 -q --authtoken groovy || echo ""\n timeout 15 node "$@"'>/runner/nodeentry.sh ; chmod +x /runner/nodeentry.sh
USER codewarrior

RUN /runner/debug.sh groovyclient -Ct 120 -Cdebug -Cauthtoken groovy -e "println 'Hello from GroovyServ'"
# pre-install the default referenced java libraries
RUN cd /runner/frameworks/java && gradle --stacktrace --no-daemon compileTestJava

# Run the test suite to make sure this thing works
USER codewarrior
ADD lib /runner/lib
ADD *.js /runner/
ADD frameworks/java/prewarm.sh /runner/prewarm.sh

# Set environment variables
ENV TIMEOUT 120000
ENV USER codewarrior
ENV HOME /home/codewarrior
RUN /runner/debug.sh mocha -t ${TIMEOUT} test/runners/java_spec.js
ADD test /runner/test

USER codewarrior
# Run the test suite to make sure this thing works
RUN mocha -t 20000 /runner/test/runners/java_spec.js

ENTRYPOINT ["/runner/nodeentry.sh"]
ENTRYPOINT ["node"]

71 changes: 71 additions & 0 deletions documentation/environments/java.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Environment

Code is executed within a Dockerized Ubuntu 14.04 container.

## Languages

- Java 8 (1.8.0_91)

## Loaded Dependencies

### The following depencies are always loaded

- junit 4.12
- lombok 1.16.18
- mockito-core 2.7.19
- assertj-core 3.8.0

### The following can be loaded through `@config reference` statements

- joda-time 2.2
- guava 20.0
- commons-lang3 3.6
- commons-math3 3.6.1
- jsoup 1.10.3
- dom4j 2.0.1
- assertj-guava 3.1.0
- hibernate-core 5.2.10.Final
- mongo-java-driver 3.4.2
- sqlite-jdbc 3.19.3
- postgresql 42.1.1
- spring-boot-starter-web 1.5.4
- spring-boot-starter-test 1.5.4
- spring-boot-starter-data-mongodb 1.5.4
- spring-boot-starter-data-redis 1.5.4
- spring-boot-starter-data-jpa 1.5.4
- spring-boot-starter-data-rest 1.5.4
- spring-boot-starter-validation 1.5.4


To make these packages available to the application, you must have access to the setup code block.
Within the setup code you can load any of these packages using reference config statements.

**Setup Example:**
```java
// @config reference guava
// @config reference commons-lang3
```


If you need to reference a package that is a dependency of one of the above packages, you will need to load those packages
in order to make that dependency available.

### Spring Boot Packages

If you require support for the Spring framework, you can include `spring-boot` as the reference name.
This will include both the web and test starter dependencies, as well as any additional requirements.

When including the Spring framework via `spring-boot`, if other services are configured, such as mongodb, then the required spring data packages will also be auto-included into the build.

# Build Process

Gradle is used as the build tool. Each time you run code, a fresh Docker container will be used. Under
typical conditions the Gradle daemon should have already loaded, causing build times to typically fall within
the 3 to 4 second range for trivial sized apps. However if the daemon has not finished loading then the build
process may take over 10 seconds to complete.

# Timeout

The sandbox environment will timeout the code within 20 seconds.

> For more information, view the [docker file](https://github.com/Codewars/codewars-runner-cli/blob/master/docker/jvm.docker)
51 changes: 51 additions & 0 deletions frameworks/java/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
plugins {
id 'java'
id 'groovy'
}

repositories {
jcenter()
}

dependencies {
compile 'junit:junit:4.12'
compileOnly 'org.projectlombok:lombok:1.16.18'
compile "joda-time:joda-time:2.2"
compile 'org.mockito:mockito-core:2.7.19'
compile 'com.google.guava:guava:20.0'
compile 'org.apache.commons:commons-lang3:3.6'
compile 'org.apache.commons:commons-math3:3.6.1'
compile 'org.jsoup:jsoup:1.10.3' // jsoup HTML parser library @ https://jsoup.org/
compile 'org.dom4j:dom4j:2.0.1' // Flexible XML framework
compile 'org.assertj:assertj-core:3.8.0'
compile 'org.assertj:assertj-guava:3.1.0'
compile 'com.fasterxml.jackson.core:jackson-annotations:2.8.0'
compile 'org.hibernate:hibernate-core:5.2.10.Final'
compile 'org.mongodb:mongo-java-driver:3.4.2'
compile 'org.xerial:sqlite-jdbc:3.19.3'
compile 'org.postgresql:postgresql:42.1.1'
compile 'org.springframework:spring-orm:4.3.9.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-web:1.5.4.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-test:1.5.4.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-data-mongodb:1.5.4.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-data-redis:1.5.4.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-data-jpa:1.5.4.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-data-rest:1.5.4.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-validation:1.5.4.RELEASE'
}

test {
reports {
html.enabled = false
junitXml.enabled = false
}
testLogging {
// TODO: Refactor so that we use Gradle to customize output, instead of custom test listener
// SEE: https://stackoverflow.com/questions/3963708/gradle-how-to-display-test-results-in-the-console-in-real-time
// SEE: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html
// Show standard streams so that we can parse STDOUT/ERR output.
showStandardStreams = true
showExceptions = true
showStackTraces = true
}
}
11 changes: 11 additions & 0 deletions frameworks/java/prewarm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

echo "loading" > /workspace/prewarm.status

# prewarm by starting the gradle daemon. Running an initial test build will also speed things up a bit
cd /runner/frameworks/java && gradle --daemon --offline test

echo "loaded" > /workspace/prewarm.status

# node run -l java -c "public class Solution {}" -f "import org.junit.Test;public class TestFixture{}"

11 changes: 11 additions & 0 deletions frameworks/java/src/main/java/Example.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import org.junit.Test;
import org.junit.runners.JUnit4;
//import org.hamcrest.Matchers.*;
//import com.google.common.base.Optional;

public class Example {
public Example() {
}

public String foo(){ return "foo"; }
}
10 changes: 10 additions & 0 deletions frameworks/java/src/main/java/ExampleTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runners.JUnit4;
public class ExampleTest {
@Test
public void myTestFunction(){
Example e = new Example();
assertEquals("Failed Message", "foo", e.foo());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,26 @@ public void testFailure(final Failure failure)
failed = true;
final String msg = failure.getMessage();
final boolean hasMessage = msg != null && msg.length() > 0;
System.out.println(String.format("\n<FAILED::>%s<:LF:>", formatMessage(hasMessage ? msg : "Runtime Error Occurred")));
if(!hasMessage && failure.getException() != null) {
System.out.println(formatException(failure.getException()));
System.out.println(String.format("\n<FAILED::>%s", formatMessage(hasMessage ? msg : "Runtime Error Occurred")));
if(failure.getException() != null) {
String prefix = "";
if (hasMessage) {
prefix = "<LOG::-Exception Details>";
}

System.out.println(prefix + formatMessage(formatException(failure.getException())));
}
}
public void testStarted(final Description description)
{
System.out.println(String.format("\n<DESCRIBE::>%s<:LF:>", formatMessage(description.getDisplayName())));
System.out.println(String.format("\n<DESCRIBE::>%s", formatMessage(description.getDisplayName())));
failed = false;
}
public void testFinished(final Description description)
{
if(!failed)
{
System.out.println("\n<PASSED::>Test Passed<:LF:>");
System.out.println("\n<PASSED::>Test Passed");
}
System.out.println("\n<COMPLETEDIN::>");
}
Expand Down
14 changes: 14 additions & 0 deletions frameworks/java/src/test/java/Start.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import org.junit.runner.JUnitCore;
import org.junit.Test;
import org.junit.runners.JUnit4;

public class Start {
@Test
public void start(){
JUnitCore runner = new JUnitCore();
runner.addListener(new CwRunListener());
runner.run(ExampleTest.class);
}
}


1 change: 1 addition & 0 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = {
go: 15000,
haskell: 15000,
sql: 14000,
java: 20000
},
moduleRegExs: {
haskell: /module\s+([A-Z]([a-z|A-Z|0-9]|\.[A-Z])*)\W/,
Expand Down
18 changes: 17 additions & 1 deletion lib/options.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const splitFiles = require('./utils/split-files.js');

module.exports.process = function(opts) {
opts = assignConfigJson(opts);

Expand Down Expand Up @@ -123,7 +125,7 @@ function assignPublishing(opts) {

if (opts.ably && opts.channel) {
try {
var ably = new require('ably').Rest({log: {level: 0}});
var ably = new require('ably').Rest(opts.ably, {log: {level: 0}});
var channel = ably.channels.get(opts.channel);
opts.publish = function(event, data) {
if (event && data) {
Expand Down Expand Up @@ -183,4 +185,18 @@ function assignConfigStatements(opts) {
});
}
}

assignSplits(opts, 'setup');
assignSplits(opts, 'solution');
assignSplits(opts, 'fixture');
}

function assignSplits(opts, field) {
if (opts[field]) {
let fileSplits = splitFiles(opts[field]);
if (fileSplits.splits) {
opts[field] = fileSplits.root;
opts.files = Object.assign(opts.files || {}, fileSplits.files);
}
}
}
Loading

0 comments on commit 9df0faf

Please sign in to comment.