Skip to content

Commit

Permalink
jooby:run with live compiler fix #338
Browse files Browse the repository at this point in the history
  • Loading branch information
jknack committed Apr 3, 2016
1 parent e214998 commit 22c9fd8
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 22 deletions.
100 changes: 82 additions & 18 deletions jooby-maven-plugin/src/main/java/org/jooby/JoobyMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,19 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import org.apache.maven.Maven;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
Expand All @@ -45,16 +50,48 @@
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.jooby.hotreload.Watcher;

import com.google.common.io.Files;

import javaslang.control.Try;

@Mojo(name = "run", threadSafe = true, requiresDependencyResolution = ResolutionScope.TEST)
@Execute(phase = LifecyclePhase.TEST_COMPILE)
public class JoobyMojo extends AbstractMojo {

private static class ShutdownHook extends Thread {
private Log log;

private List<Command> commands;

private Watcher watcher;

public ShutdownHook(final Log log, final List<Command> commands) {
this.log = log;
this.commands = commands;
setDaemon(true);
}

@Override
public void run() {
if (watcher != null) {
log.info("stopping: watcher");
Try.run(watcher::stop).onFailure(ex -> log.debug("Stop of watcher resulted in error", ex));
}
commands.forEach(cmd -> {
log.info("stopping: " + cmd);
Try.run(cmd::stop).onFailure(ex -> log.error("Stop of " + cmd + " resulted in error", ex));
});
}
}

@Component
private MavenProject mavenProject;

@Parameter(defaultValue = "${session}", required = true, readonly = true)
protected MavenSession session;

@Parameter(property = "main.class", defaultValue = "${application.class}")
protected String mainClass;

Expand All @@ -79,6 +116,12 @@ public class JoobyMojo extends AbstractMojo {
@Parameter(defaultValue = "${plugin.artifacts}")
private List<org.apache.maven.artifact.Artifact> pluginArtifacts;

@Parameter(property = "compiler", defaultValue = "on")
private String compiler;

@Component
protected Maven maven;

@SuppressWarnings("unchecked")
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
Expand Down Expand Up @@ -141,10 +184,20 @@ public void execute() throws MojoExecutionException, MojoFailureException {
cmd.setWorkdir(mavenProject.getBasedir());
getLog().debug("cmd: " + cmd.debug());
}

Watcher watcher = setupCompiler(mavenProject, compiler, goal -> {
maven.execute(DefaultMavenExecutionRequest.copy(session.getRequest())
.setGoals(Arrays.asList(goal)));

});
ShutdownHook shutdownHook = new ShutdownHook(getLog(), cmds);
shutdownHook.watcher = watcher;
/**
* Shutdown hook
*/
Runtime.getRuntime().addShutdownHook(shutdownHook(cmds, getLog()));
Runtime.getRuntime().addShutdownHook(shutdownHook);

watcher.start();

/**
* Start process
Expand All @@ -160,6 +213,33 @@ public void execute() throws MojoExecutionException, MojoFailureException {

}

@SuppressWarnings("unchecked")
private static Watcher setupCompiler(final MavenProject project, final String compiler,
final Consumer<String> task) throws MojoFailureException {
File eclipseClasspath = new File(project.getBasedir(), ".classpath");
if ("off".equalsIgnoreCase(compiler) || eclipseClasspath.exists()) {
return null;
}
List<String> resources = resources(project.getResources());
resources.add(0, project.getBuild().getSourceDirectory());
Path[] paths = new Path[resources.size()];
for (int i = 0; i < paths.length; i++) {
paths[i] = Paths.get(resources.get(i));
}
try {
return new Watcher((kind, path) -> {
if (path.toString().endsWith(".java")) {
task.accept("compile");
} else if (path.toString().endsWith(".conf")
|| path.toString().endsWith(".properties")) {
task.accept("compile");
}
}, paths);
} catch (Exception ex) {
throw new MojoFailureException("Can't compile source code", ex);
}
}

private void dumpSysProps(final Path path) throws MojoFailureException {
try {
FileOutputStream output = new FileOutputStream(path.toFile());
Expand Down Expand Up @@ -334,7 +414,7 @@ private File localFile(final String... paths) {
return result;
}

private List<String> resources(final Iterable<Resource> resources) {
private static List<String> resources(final Iterable<Resource> resources) {
List<String> result = new ArrayList<String>();
for (Resource resource : resources) {
String dir = resource.getDirectory();
Expand All @@ -345,22 +425,6 @@ private List<String> resources(final Iterable<Resource> resources) {
return result;
}

private static Thread shutdownHook(final List<Command> cmds, final Log log) {
return new Thread() {
@Override
public void run() {
for (Command command : cmds) {
try {
log.info("stopping: " + command);
command.stop();
} catch (Exception ex) {
log.error("Stopping process: " + command + " resulted in error", ex);
}
}
}
};
}

private Optional<Artifact> extra(final List<Artifact> artifacts, final String name) {
for (Artifact artifact : artifacts) {
for (String tail : artifact.getDependencyTrail()) {
Expand Down
21 changes: 17 additions & 4 deletions md/doc/maven-plugin/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# jooby:run

Increase development productivity: run, debug and auto-reload Jooby applications.
Increase development productivity: run, debug, compile and auto-reload Jooby applications.

A {{maven}} plugin for running, debugging and reloading your application.

Expand Down Expand Up @@ -34,9 +34,7 @@ The plugin bounces the application every time a change is detected on:

Changes on templates and/or static files (*.html, *.js, *.css) wont restart the application, because they are not compiled/cached it while running on ```application.env = dev```.

**NOTE: For the time being, you need to use a tool that compiles your source code, usually an IDE. Otherwise, no changes will be found.**

Is it worth to mention that dynamic reload of classes at runtime is done via {{jboss-modules}}.
Is it worth to mention that dynamic reload of classes is done via {{jboss-modules}}.

## options

Expand All @@ -49,6 +47,7 @@ Is it worth to mention that dynamic reload of classes at runtime is done via {{j
<mainClass>${application.class}</mainClass>
<commands>
</commands>
<compiler>on</compiler>
<vmArgs></vmArgs>
<debug>true</debug>
<includes>
Expand All @@ -66,6 +65,20 @@ Is it worth to mention that dynamic reload of classes at runtime is done via {{j

A {{maven}} property that contains the fully qualified name of the ```main class```. **Required**.

### compiler

The compiler is ```on``` by default, unless:

* A ```.classpath``` file is present in the project directory. If present, means you're a Eclipse user and we turn off the compiler and let Eclipse recompiles the code on save.

* The compiler is set to ```off```.

On compilation success, the application is effectively reloaded.

On compilation error, the application won't reload.

Compilation success or error messages are displayed in the console (not at the browser).

### debug

The JVM is started in **debug mode by default**. You can attach a remote debugger at the ```8000``` port.
Expand Down

0 comments on commit 22c9fd8

Please sign in to comment.