Skip to content

Commit 3068b3a

Browse files
committed
Add LiveReload support on watch goal
1 parent 37f7d4b commit 3068b3a

File tree

9 files changed

+1449
-6
lines changed

9 files changed

+1449
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Version 2.7.0 (2017-11-31)
22

33
* [new] Add watch goal which automatically refreshes the application when a source file change.
4+
* [new] Support for [LiveReload](http://livereload.com/) on the watch goal.
45

56
# Version 2.6.4 (2017-10-19)
67

pom.xml

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,26 @@
3434
<bintray.package>seedstack-maven-plugin</bintray.package>
3535

3636
<compatibility.skip>true</compatibility.skip>
37+
<jetty-server.version>8.1.8.v20121106</jetty-server.version>
3738
</properties>
3839

3940
<build>
4041
<pluginManagement>
4142
<plugins>
43+
<plugin>
44+
<groupId>com.mycila</groupId>
45+
<artifactId>license-maven-plugin</artifactId>
46+
<version>${mycila.license-maven-plugin.version}</version>
47+
<configuration>
48+
<excludes>
49+
<exclude>AUTHORS</exclude>
50+
<exclude>CONTRIBUTORS</exclude>
51+
<exclude>LICENSE</exclude>
52+
<exclude>**/src/license/THIRD-PARTY.properties</exclude>
53+
<exclude>**/src/main/resources/livereload.js</exclude>
54+
</excludes>
55+
</configuration>
56+
</plugin>
4257
<plugin>
4358
<groupId>org.codehaus.mojo</groupId>
4459
<artifactId>license-maven-plugin</artifactId>
@@ -61,15 +76,17 @@
6176
<licenseMerge>Apache 2|Apache License, Version 2.0</licenseMerge>
6277
<licenseMerge>Apache 2|Apache Public License 2.0</licenseMerge>
6378
<licenseMerge>Apache 2|Unknown license</licenseMerge>
79+
<licenseMerge>Apache 2|Apache Software License - Version 2.0</licenseMerge>
6480
<licenseMerge>BSD|BSD 3-Clause License</licenseMerge>
6581
<licenseMerge>BSD|BSD style modified by Coverity</licenseMerge>
6682
<licenseMerge>BSD|The BSD License</licenseMerge>
67-
<licenseMerge>CDDL 1.0|COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
68-
</licenseMerge>
83+
<licenseMerge>CDDL 1.0|COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0</licenseMerge>
6984
<licenseMerge>CPL 1.0|Common Public License Version 1.0</licenseMerge>
7085
<licenseMerge>EPL 1.0|Eclipse Public License, Version 1.0</licenseMerge>
86+
<licenseMerge>EPL 1.0|Eclipse Public License - Version 1.0</licenseMerge>
7187
<licenseMerge>MIT|MIT License</licenseMerge>
7288
<licenseMerge>MPL|Mozilla Public License 1.1 (MPL 1.1)</licenseMerge>
89+
<licenseMerge>Public Domain|Public domain (Unlicense)</licenseMerge>
7390
</licenseMerges>
7491
</configuration>
7592
</plugin>
@@ -239,6 +256,21 @@
239256
<artifactId>asm</artifactId>
240257
<version>3.3.1</version>
241258
</dependency>
259+
<dependency>
260+
<groupId>org.eclipse.jetty</groupId>
261+
<artifactId>jetty-server</artifactId>
262+
<version>${jetty-server.version}</version>
263+
</dependency>
264+
<dependency>
265+
<groupId>org.eclipse.jetty</groupId>
266+
<artifactId>jetty-websocket</artifactId>
267+
<version>${jetty-server.version}</version>
268+
</dependency>
269+
<dependency>
270+
<groupId>com.googlecode.json-simple</groupId>
271+
<artifactId>json-simple</artifactId>
272+
<version>1.1.1</version>
273+
</dependency>
242274
<dependency>
243275
<groupId>junit</groupId>
244276
<artifactId>junit</artifactId>

src/license/THIRD-PARTY.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# Please fill the missing licenses for dependencies :
1414
#
1515
#
16-
#Tue Nov 21 14:05:44 CET 2017
16+
#Tue Nov 21 23:24:03 CET 2017
1717
classworlds--classworlds--1.1-alpha-2=
1818
commons-collections--commons-collections--3.1=
1919
dom4j--dom4j--1.6.1=

src/main/java/org/seedstack/maven/WatchMojo.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.objectweb.asm.FieldVisitor;
4242
import org.objectweb.asm.MethodVisitor;
4343
import org.seedstack.maven.classloader.ReloadingClassLoader;
44+
import org.seedstack.maven.livereload.LRServer;
4445
import org.seedstack.maven.runnables.DefaultLauncherRunnable;
4546
import org.seedstack.maven.watcher.AggregatingFileChangeListener;
4647
import org.seedstack.maven.watcher.DirectoryWatcher;
@@ -53,12 +54,14 @@
5354
requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
5455
@Execute(phase = LifecyclePhase.PROCESS_CLASSES)
5556
public class WatchMojo extends AbstractExecutableMojo {
57+
public static final int LIVE_RELOAD_PORT = 35729;
5658
private DirectoryWatcher directoryWatcher;
5759
private Thread watcherThread;
5860
private AggregatingFileChangeListener fileChangeListener;
5961
private List<String> compileSourceRoots;
6062
private ReloadingClassLoader reloadingClassLoader;
6163
private DefaultLauncherRunnable launcherRunnable;
64+
private LRServer lrServer;
6265

6366
@Override
6467
public void execute() throws MojoExecutionException, MojoFailureException {
@@ -82,11 +85,11 @@ public void execute() throws MojoExecutionException, MojoFailureException {
8285
}
8386
}
8487

85-
this.watcherThread = new Thread(this.directoryWatcher);
88+
this.watcherThread = new Thread(this.directoryWatcher, "watcher");
8689
this.launcherRunnable = new DefaultLauncherRunnable(getArgs(), getMonitor(), getLog());
8790
execute(launcherRunnable, false);
8891

89-
Runtime.getRuntime().addShutdownHook(new Thread() {
92+
Runtime.getRuntime().addShutdownHook(new Thread("watcher") {
9093
@Override
9194
public void run() {
9295
fileChangeListener.stop();
@@ -100,8 +103,26 @@ public void run() {
100103
}
101104
});
102105

106+
// Start live reload server
107+
try {
108+
getLog().info("Starting LiveReload server on port " + LIVE_RELOAD_PORT);
109+
lrServer = new LRServer(LIVE_RELOAD_PORT);
110+
lrServer.start();
111+
} catch (Exception e) {
112+
getLog().error("Unable to start LiveReload server", e);
113+
}
114+
115+
// Start watching sources
103116
this.watcherThread.start();
104117
waitForShutdown();
118+
119+
if (lrServer != null) {
120+
try {
121+
lrServer.stop();
122+
} catch (Exception e) {
123+
getLog().error("Unable to stop LiveReload server", e);
124+
}
125+
}
105126
}
106127

107128
@Override
@@ -136,6 +157,10 @@ public void onAggregatedChanges(Set<FileEvent> fileEvents) {
136157
removeFiles(compiledFilesToRemove);
137158
recompile();
138159
launcherRunnable.refresh();
160+
if (lrServer != null) {
161+
getLog().info("Triggering LiveReload");
162+
lrServer.notifyChange("/");
163+
}
139164
}
140165
} catch (Exception e) {
141166
Throwable toLog = e.getCause();
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
9+
package org.seedstack.maven.livereload;
10+
11+
import java.util.LinkedHashMap;
12+
import java.util.LinkedList;
13+
import java.util.Map;
14+
import org.json.simple.JSONValue;
15+
16+
/**
17+
* Modified from: https://github.com/davidB/livereload-jvm
18+
* @author dwayne
19+
*/
20+
class LRProtocol {
21+
String hello() {
22+
LinkedList<String> protocols = new LinkedList<String>();
23+
protocols.add("http://livereload.com/protocols/official-7");
24+
25+
LinkedHashMap<String, Object> obj = new LinkedHashMap<String, Object>();
26+
obj.put("command", "hello");
27+
obj.put("protocols", protocols);
28+
obj.put("serverName", "livereload-jvm");
29+
return JSONValue.toJSONString(obj);
30+
}
31+
32+
String alert(String msg) throws Exception {
33+
LinkedHashMap<String, Object> obj = new LinkedHashMap<String, Object>();
34+
obj.put("command", "alert");
35+
obj.put("message", msg);
36+
return JSONValue.toJSONString(obj);
37+
}
38+
39+
String reload(String path) throws Exception {
40+
LinkedHashMap<String, Object> obj = new LinkedHashMap<String, Object>();
41+
obj.put("command", "reload");
42+
obj.put("path", path);
43+
obj.put("liveCSS", true);
44+
return JSONValue.toJSONString(obj);
45+
}
46+
47+
@SuppressWarnings("unchecked")
48+
boolean isHello(String data) throws Exception {
49+
Object obj = JSONValue.parse(data);
50+
boolean back = obj instanceof Map;
51+
back = back && "hello".equals(((Map<Object, Object>) obj).get("command"));
52+
return back;
53+
}
54+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
9+
package org.seedstack.maven.livereload;
10+
11+
import java.io.IOException;
12+
import java.net.MalformedURLException;
13+
import org.eclipse.jetty.server.Server;
14+
import org.eclipse.jetty.server.handler.ResourceHandler;
15+
import org.eclipse.jetty.server.nio.SelectChannelConnector;
16+
import org.eclipse.jetty.util.resource.Resource;
17+
18+
/**
19+
* Modified from: https://github.com/davidB/livereload-jvm
20+
*/
21+
public class LRServer {
22+
private final int _port;
23+
private Server _server;
24+
private LRWebSocketHandler _wsHandler;
25+
26+
public LRServer(int port) {
27+
this._port = port;
28+
}
29+
30+
private void init() throws Exception {
31+
SelectChannelConnector connector = new SelectChannelConnector();
32+
connector.setPort(_port);
33+
34+
ResourceHandler rHandler = new ResourceHandler() {
35+
@Override
36+
public Resource getResource(String path) throws MalformedURLException {
37+
if ("/livereload.js".equals(path)) {
38+
try {
39+
return Resource.newResource(LRServer.class.getResource(path));
40+
} catch (IOException e) {
41+
throw new RuntimeException(e);
42+
}
43+
}
44+
return null;
45+
}
46+
};
47+
48+
_wsHandler = new LRWebSocketHandler();
49+
_wsHandler.setHandler(rHandler);
50+
51+
_server = new Server();
52+
_server.setHandler(_wsHandler);
53+
_server.addConnector(connector);
54+
}
55+
56+
public void start() throws Exception {
57+
this.init();
58+
_server.start();
59+
}
60+
61+
public void stop() throws Exception {
62+
_server.stop();
63+
}
64+
65+
public void notifyChange(String path) throws Exception {
66+
_wsHandler.notifyChange(path);
67+
}
68+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
9+
package org.seedstack.maven.livereload;
10+
11+
import java.io.IOException;
12+
import java.util.concurrent.ConcurrentLinkedQueue;
13+
import javax.servlet.http.HttpServletRequest;
14+
import org.eclipse.jetty.websocket.WebSocket;
15+
import org.eclipse.jetty.websocket.WebSocketHandler;
16+
17+
/**
18+
* Modified from: https://github.com/davidB/livereload-jvm
19+
*/
20+
class LRWebSocketHandler extends WebSocketHandler {
21+
private final ConcurrentLinkedQueue<LRWebSocket> _broadcast = new ConcurrentLinkedQueue<LRWebSocket>();
22+
private final LRProtocol _protocol = new LRProtocol();
23+
24+
@Override
25+
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
26+
if ("/livereload".equals(request.getPathInfo())) {
27+
return new LRWebSocket();
28+
}
29+
return new WebSocket() {
30+
@Override
31+
public void onOpen(Connection connection) {
32+
connection.close();
33+
}
34+
35+
@Override
36+
public void onClose(int code, String msg) {
37+
}
38+
};
39+
}
40+
41+
void notifyChange(String path) throws Exception {
42+
String msg = _protocol.reload(path);
43+
for (LRWebSocket ws : _broadcast) {
44+
try {
45+
ws._connection.sendMessage(msg);
46+
} catch (IOException e) {
47+
_broadcast.remove(ws);
48+
e.printStackTrace();
49+
}
50+
}
51+
}
52+
53+
class LRWebSocket implements WebSocket.OnTextMessage {
54+
Connection _connection;
55+
56+
@Override
57+
public void onOpen(Connection connection) {
58+
_connection = connection;
59+
_broadcast.add(this);
60+
}
61+
62+
@Override
63+
public void onClose(int code, String message) {
64+
_broadcast.remove(this);
65+
}
66+
67+
@Override
68+
public void onMessage(final String data) {
69+
try {
70+
if (_protocol.isHello(data)) {
71+
_connection.sendMessage(_protocol.hello());
72+
}
73+
} catch (Exception exc) {
74+
exc.printStackTrace();
75+
}
76+
}
77+
}
78+
}

src/main/java/org/seedstack/maven/watcher/AggregatingFileChangeListener.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ public void run() {
4242

4343
public void stop() {
4444
stop = true;
45-
timer.cancel();
45+
if (timer != null) {
46+
timer.cancel();
47+
}
4648
}
4749

4850
protected abstract void onAggregatedChanges(Set<FileEvent> fileEvents);

0 commit comments

Comments
 (0)