Skip to content

Commit 22c9fd8

Browse files
committed
jooby:run with live compiler fix #338
1 parent e214998 commit 22c9fd8

2 files changed

Lines changed: 99 additions & 22 deletions

File tree

jooby-maven-plugin/src/main/java/org/jooby/JoobyMojo.java

Lines changed: 82 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,19 @@
2525
import java.nio.file.Path;
2626
import java.nio.file.Paths;
2727
import java.util.ArrayList;
28+
import java.util.Arrays;
2829
import java.util.LinkedHashSet;
2930
import java.util.List;
3031
import java.util.Optional;
3132
import java.util.Properties;
3233
import java.util.Set;
34+
import java.util.function.Consumer;
3335
import java.util.stream.Collectors;
3436

37+
import org.apache.maven.Maven;
3538
import org.apache.maven.artifact.Artifact;
39+
import org.apache.maven.execution.DefaultMavenExecutionRequest;
40+
import org.apache.maven.execution.MavenSession;
3641
import org.apache.maven.model.Resource;
3742
import org.apache.maven.plugin.AbstractMojo;
3843
import org.apache.maven.plugin.MojoExecutionException;
@@ -45,16 +50,48 @@
4550
import org.apache.maven.plugins.annotations.Parameter;
4651
import org.apache.maven.plugins.annotations.ResolutionScope;
4752
import org.apache.maven.project.MavenProject;
53+
import org.jooby.hotreload.Watcher;
4854

4955
import com.google.common.io.Files;
5056

57+
import javaslang.control.Try;
58+
5159
@Mojo(name = "run", threadSafe = true, requiresDependencyResolution = ResolutionScope.TEST)
5260
@Execute(phase = LifecyclePhase.TEST_COMPILE)
5361
public class JoobyMojo extends AbstractMojo {
5462

63+
private static class ShutdownHook extends Thread {
64+
private Log log;
65+
66+
private List<Command> commands;
67+
68+
private Watcher watcher;
69+
70+
public ShutdownHook(final Log log, final List<Command> commands) {
71+
this.log = log;
72+
this.commands = commands;
73+
setDaemon(true);
74+
}
75+
76+
@Override
77+
public void run() {
78+
if (watcher != null) {
79+
log.info("stopping: watcher");
80+
Try.run(watcher::stop).onFailure(ex -> log.debug("Stop of watcher resulted in error", ex));
81+
}
82+
commands.forEach(cmd -> {
83+
log.info("stopping: " + cmd);
84+
Try.run(cmd::stop).onFailure(ex -> log.error("Stop of " + cmd + " resulted in error", ex));
85+
});
86+
}
87+
}
88+
5589
@Component
5690
private MavenProject mavenProject;
5791

92+
@Parameter(defaultValue = "${session}", required = true, readonly = true)
93+
protected MavenSession session;
94+
5895
@Parameter(property = "main.class", defaultValue = "${application.class}")
5996
protected String mainClass;
6097

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

119+
@Parameter(property = "compiler", defaultValue = "on")
120+
private String compiler;
121+
122+
@Component
123+
protected Maven maven;
124+
82125
@SuppressWarnings("unchecked")
83126
@Override
84127
public void execute() throws MojoExecutionException, MojoFailureException {
@@ -141,10 +184,20 @@ public void execute() throws MojoExecutionException, MojoFailureException {
141184
cmd.setWorkdir(mavenProject.getBasedir());
142185
getLog().debug("cmd: " + cmd.debug());
143186
}
187+
188+
Watcher watcher = setupCompiler(mavenProject, compiler, goal -> {
189+
maven.execute(DefaultMavenExecutionRequest.copy(session.getRequest())
190+
.setGoals(Arrays.asList(goal)));
191+
192+
});
193+
ShutdownHook shutdownHook = new ShutdownHook(getLog(), cmds);
194+
shutdownHook.watcher = watcher;
144195
/**
145196
* Shutdown hook
146197
*/
147-
Runtime.getRuntime().addShutdownHook(shutdownHook(cmds, getLog()));
198+
Runtime.getRuntime().addShutdownHook(shutdownHook);
199+
200+
watcher.start();
148201

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

161214
}
162215

216+
@SuppressWarnings("unchecked")
217+
private static Watcher setupCompiler(final MavenProject project, final String compiler,
218+
final Consumer<String> task) throws MojoFailureException {
219+
File eclipseClasspath = new File(project.getBasedir(), ".classpath");
220+
if ("off".equalsIgnoreCase(compiler) || eclipseClasspath.exists()) {
221+
return null;
222+
}
223+
List<String> resources = resources(project.getResources());
224+
resources.add(0, project.getBuild().getSourceDirectory());
225+
Path[] paths = new Path[resources.size()];
226+
for (int i = 0; i < paths.length; i++) {
227+
paths[i] = Paths.get(resources.get(i));
228+
}
229+
try {
230+
return new Watcher((kind, path) -> {
231+
if (path.toString().endsWith(".java")) {
232+
task.accept("compile");
233+
} else if (path.toString().endsWith(".conf")
234+
|| path.toString().endsWith(".properties")) {
235+
task.accept("compile");
236+
}
237+
}, paths);
238+
} catch (Exception ex) {
239+
throw new MojoFailureException("Can't compile source code", ex);
240+
}
241+
}
242+
163243
private void dumpSysProps(final Path path) throws MojoFailureException {
164244
try {
165245
FileOutputStream output = new FileOutputStream(path.toFile());
@@ -334,7 +414,7 @@ private File localFile(final String... paths) {
334414
return result;
335415
}
336416

337-
private List<String> resources(final Iterable<Resource> resources) {
417+
private static List<String> resources(final Iterable<Resource> resources) {
338418
List<String> result = new ArrayList<String>();
339419
for (Resource resource : resources) {
340420
String dir = resource.getDirectory();
@@ -345,22 +425,6 @@ private List<String> resources(final Iterable<Resource> resources) {
345425
return result;
346426
}
347427

348-
private static Thread shutdownHook(final List<Command> cmds, final Log log) {
349-
return new Thread() {
350-
@Override
351-
public void run() {
352-
for (Command command : cmds) {
353-
try {
354-
log.info("stopping: " + command);
355-
command.stop();
356-
} catch (Exception ex) {
357-
log.error("Stopping process: " + command + " resulted in error", ex);
358-
}
359-
}
360-
}
361-
};
362-
}
363-
364428
private Optional<Artifact> extra(final List<Artifact> artifacts, final String name) {
365429
for (Artifact artifact : artifacts) {
366430
for (String tail : artifact.getDependencyTrail()) {

md/doc/maven-plugin/README.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# jooby:run
22

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

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

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

3535
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```.
3636

37-
**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.**
38-
39-
Is it worth to mention that dynamic reload of classes at runtime is done via {{jboss-modules}}.
37+
Is it worth to mention that dynamic reload of classes is done via {{jboss-modules}}.
4038

4139
## options
4240

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

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

68+
### compiler
69+
70+
The compiler is ```on``` by default, unless:
71+
72+
* 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.
73+
74+
* The compiler is set to ```off```.
75+
76+
On compilation success, the application is effectively reloaded.
77+
78+
On compilation error, the application won't reload.
79+
80+
Compilation success or error messages are displayed in the console (not at the browser).
81+
6982
### debug
7083

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

0 commit comments

Comments
 (0)