Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit 03e7b3c

Browse files
committed
Reload example updated so that source code changes are propagated to the running application as the class files are being re-compiled on the fly.
Change-Id: Ib92ef20f28227c0f7cef0e43469e3d15680f01df Signed-off-by: Jakub Podlesak <[email protected]>
1 parent 60f09a8 commit 03e7b3c

File tree

10 files changed

+649
-52
lines changed

10 files changed

+649
-52
lines changed

examples/reload/README.MD

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ URI path | Resource class
6060

6161
The application gets configured via a plain text file named `resources`.
6262
The file contains a list of resource classes to be published by the
63-
application. After the application gets started, it checks the file
64-
regularily each 2 seconds and reloads the application as file updates
65-
are being detected.
63+
application. After the application gets started, it watches the file system
64+
and reloads the application as file updates are being detected.
65+
Changes in JAX-RS resource source files and updates of the above mentioned resources file
66+
are being detected. Java code re-compilation is done as required.
6667

6768
Running the Example
6869
-------------------
@@ -97,3 +98,6 @@ but the other, departures/arrivals, resources should remain functional.
9798

9899
Now you can revert the change back and check the stats resource is back
99100
providing actual statistics information.
101+
102+
You can also try to change the JAX-RS resources source code. These changes
103+
will be also picked up automatically.

examples/reload/pom.xml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,49 @@
7272

7373
<build>
7474
<plugins>
75+
<plugin>
76+
<artifactId>maven-dependency-plugin</artifactId>
77+
<!--<version>2.2</version>-->
78+
<executions>
79+
<execution>
80+
<phase>generate-sources</phase>
81+
<goals>
82+
<goal>build-classpath</goal>
83+
</goals>
84+
</execution>
85+
</executions>
86+
<configuration>
87+
<outputFile>${project.build.directory}/classpath.properties</outputFile>
88+
<outputFilterFile>true</outputFilterFile>
89+
<includeScope>compile</includeScope>
90+
</configuration>
91+
</plugin>
92+
<plugin>
93+
<groupId>org.codehaus.mojo</groupId>
94+
<artifactId>properties-maven-plugin</artifactId>
95+
<version>1.0-alpha-2</version>
96+
<executions>
97+
<execution>
98+
<id>read-project-properties</id>
99+
<goals>
100+
<goal>read-project-properties</goal>
101+
</goals>
102+
<!--<phase>initialize</phase>-->
103+
<phase>generate-sources</phase>
104+
<configuration>
105+
<files>
106+
<file>${project.build.directory}/classpath.properties</file>
107+
</files>
108+
</configuration>
109+
</execution>
110+
</executions>
111+
</plugin>
75112
<plugin>
76113
<groupId>org.codehaus.mojo</groupId>
77114
<artifactId>exec-maven-plugin</artifactId>
78115
<configuration>
79116
<mainClass>org.glassfish.jersey.examples.reload.App</mainClass>
117+
<arguments><argument>-cp=${classpath}</argument></arguments>
80118
</configuration>
81119
</plugin>
82120
</plugins>

examples/reload/resources

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
org.glassfish.jersey.examples.reload.DeparturesResource
22
org.glassfish.jersey.examples.reload.ArrivalsResource
3-
org.glassfish.jersey.examples.reload.StatsResource
3+
#org.glassfish.jersey.examples.reload.StatsResource

examples/reload/src/main/java/org/glassfish/jersey/examples/reload/App.java

Lines changed: 133 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,32 @@
4545
import java.io.IOException;
4646
import java.io.InputStreamReader;
4747
import java.net.URI;
48+
import java.nio.file.FileSystems;
49+
import java.nio.file.Path;
50+
import java.nio.file.Paths;
51+
import java.nio.file.StandardWatchEventKinds;
52+
import java.nio.file.WatchEvent;
53+
import java.nio.file.WatchKey;
54+
import java.nio.file.WatchService;
55+
import java.util.LinkedList;
56+
import java.util.List;
4857
import java.util.Timer;
4958
import java.util.TimerTask;
5059
import java.util.logging.Level;
5160
import java.util.logging.Logger;
5261

62+
import org.glassfish.jersey.examples.reload.compiler.AppClassLoader;
63+
import org.glassfish.jersey.examples.reload.compiler.Compiler;
64+
import org.glassfish.jersey.examples.reload.compiler.JavaFile;
5365
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
5466
import org.glassfish.jersey.server.ResourceConfig;
5567
import org.glassfish.jersey.server.spi.Container;
5668
import org.glassfish.jersey.server.spi.ContainerLifecycleListener;
5769

5870
import org.glassfish.grizzly.http.server.HttpServer;
5971

72+
import com.sun.nio.file.SensitivityWatchEventModifier;
73+
6074
/**
6175
* Reload example application.
6276
* <p/>
@@ -75,80 +89,126 @@ public class App {
7589
private static final URI BASE_URI = URI.create("http://localhost:8080/flights/");
7690
public static final String ROOT_PATH = "arrivals";
7791
public static final String CONFIG_FILENAME = "resources";
78-
public static final long REFRESH_PERIOD_MS = 2000;
92+
public static final String SRC_MAIN_JAVA = "src/main/java";
7993

8094
static Container container;
8195

82-
// @todo Java SE 7 - use java.nio.file.WatchService
8396
static class FileCheckTask extends TimerTask {
8497

85-
long lastModified;
86-
87-
FileCheckTask(final long lastModified) {
88-
this.lastModified = lastModified;
89-
}
90-
9198
@Override
9299
public void run() {
93-
final File configFile = new File(CONFIG_FILENAME);
94-
final long actualLastModified = configFile.lastModified();
95-
if (lastModified < actualLastModified) {
96-
lastModified = actualLastModified;
97-
reloadApp(configFile);
98-
}
99-
}
100100

101-
private void reloadApp(final File configFile) {
102-
LOGGER.info("Reloading resource classes:");
103-
final ResourceConfig rc = new ResourceConfig();
101+
WatchService watcher;
104102

105103
try {
106-
try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(configFile), "UTF-8"))) {
107-
while (r.ready()) {
108-
final String className = r.readLine();
109-
if (!className.startsWith("#")) {
110-
try {
111-
rc.registerClasses(Class.forName(className));
112-
LOGGER.info(String.format(" + loaded class %s.\n", className));
113-
} catch (final ClassNotFoundException ex) {
114-
LOGGER.info(String.format(" ! class %s not found.\n", className));
104+
watcher = FileSystems.getDefault().newWatchService();
105+
106+
Path srcDir = Paths.get("src/main/java/org/glassfish/jersey/examples/reload");
107+
registerWatcher(watcher, srcDir);
108+
109+
Path configFilePath = Paths.get(".");
110+
registerWatcher(watcher, configFilePath);
111+
112+
} catch (IOException e) {
113+
e.printStackTrace();
114+
throw new RuntimeException("Could not initialize watcher service!");
115+
}
116+
117+
for (;;) {
118+
119+
try {
120+
final WatchKey watchKey = watcher.take();
121+
122+
try {
123+
for (WatchEvent<?> event : watchKey.pollEvents()) {
124+
final WatchEvent.Kind<?> kind = event.kind();
125+
if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
126+
WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
127+
Path modifiedFile = pathEvent.context();
128+
System.out.printf("FILE MODIFIED: %s\n", modifiedFile);
115129
}
116-
} else {
117-
LOGGER.info(String.format(" - ignored class %s\n", className.substring(1)));
118130
}
131+
} finally {
132+
watchKey.reset(); // so that consecutive events could be processed
119133
}
134+
135+
final File configFile = new File(CONFIG_FILENAME);
136+
reloadApp(configFile);
137+
} catch (InterruptedException e) {
138+
Thread.currentThread().interrupt();
120139
}
121-
} catch (final Exception ex) {
122-
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
123140
}
141+
}
142+
143+
private void registerWatcher(WatchService watcher, Path directory) throws IOException {
144+
directory.register(watcher,
145+
new WatchEvent.Kind[]{
146+
StandardWatchEventKinds.ENTRY_MODIFY
147+
},
148+
SensitivityWatchEventModifier.HIGH);
149+
}
150+
151+
private void reloadApp(final File configFile) {
152+
LOGGER.info("Reloading resource classes:");
153+
final ResourceConfig rc = createResourceConfig(configFile);
124154
App.container.reload(rc);
125155
}
126156

127157
}
128158

129-
public static void main(final String[] args) {
159+
private static ResourceConfig createResourceConfig(File configFile) {
160+
final ResourceConfig rc = new ResourceConfig();
161+
130162
try {
131-
LOGGER.info("Resource Config Reload Jersey Example App");
163+
final AppClassLoader appClassLoader = new AppClassLoader(Thread.currentThread().getContextClassLoader());
164+
final List<JavaFile> javaFiles = getJavaFiles(configFile);
132165

133-
final ResourceConfig resourceConfig = new ResourceConfig(ArrivalsResource.class);
134-
resourceConfig.registerInstances(new ContainerLifecycleListener() {
135-
@Override
136-
public void onStartup(final Container container) {
137-
App.container = container;
138-
final Timer t = new Timer(true);
139-
t.scheduleAtFixedRate(new FileCheckTask(0), 0, REFRESH_PERIOD_MS);
166+
Compiler.compile(appClassLoader, javaFiles);
167+
168+
for (JavaFile javaFile : javaFiles) {
169+
try {
170+
rc.registerClasses(appClassLoader.loadClass(javaFile.getClassName()));
171+
} catch (final ClassNotFoundException ex) {
172+
LOGGER.info(String.format(" ! class %s not found.\n", javaFile.getClassName()));
140173
}
174+
}
175+
} catch (final Exception ex) {
176+
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
177+
}
141178

142-
@Override
143-
public void onReload(final Container container) {
144-
System.out.println("Application has been reloaded!");
179+
return rc;
180+
}
181+
182+
private static List<JavaFile> getJavaFiles(File configFile) throws Exception {
183+
184+
final List<JavaFile> javaFiles = new LinkedList<>();
185+
186+
try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(configFile), "UTF-8"))) {
187+
while (r.ready()) {
188+
final String className = r.readLine();
189+
if (!className.startsWith("#")) {
190+
javaFiles.add(new JavaFile(className, SRC_MAIN_JAVA));
191+
LOGGER.info(String.format(" + included class %s.\n", className));
192+
} else {
193+
LOGGER.info(String.format(" - ignored class %s\n", className.substring(1)));
145194
}
195+
}
196+
}
197+
return javaFiles;
198+
}
146199

147-
@Override
148-
public void onShutdown(final Container container) {
149-
// ignore
200+
public static void main(final String[] args) throws Exception {
201+
try {
202+
LOGGER.info("Resource Config Reload Jersey Example App");
203+
204+
for (String s : args) {
205+
if (s.startsWith("-cp=")) {
206+
Compiler.classpath = s.substring(4);
150207
}
151-
});
208+
}
209+
210+
final ResourceConfig resourceConfig = createResourceConfig(new File(CONFIG_FILENAME));
211+
registerReloader(resourceConfig);
152212

153213
final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, true);
154214
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@@ -167,4 +227,30 @@ public void run() {
167227
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
168228
}
169229
}
230+
231+
private static Class<?> loadClass(String className) throws Exception {
232+
final JavaFile javaFile = new JavaFile(className, SRC_MAIN_JAVA);
233+
return Compiler.compile(className, javaFile);
234+
}
235+
236+
private static void registerReloader(ResourceConfig resourceConfig) {
237+
resourceConfig.registerInstances(new ContainerLifecycleListener() {
238+
@Override
239+
public void onStartup(final Container container) {
240+
App.container = container;
241+
final Timer t = new Timer(true);
242+
t.schedule(new FileCheckTask(), 0);
243+
}
244+
245+
@Override
246+
public void onReload(final Container container) {
247+
System.out.println("Application has been reloaded!");
248+
}
249+
250+
@Override
251+
public void onShutdown(final Container container) {
252+
// ignore
253+
}
254+
});
255+
}
170256
}

examples/reload/src/main/java/org/glassfish/jersey/examples/reload/FlightsDB.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
33
*
4-
* Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved.
4+
* Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved.
55
*
66
* The contents of this file are subject to the terms of either the GNU
77
* General Public License Version 2 only ("GPL") or the Common Development

0 commit comments

Comments
 (0)