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

Commit 4ad385d

Browse files
japodGerrit Code Review
authored andcommitted
Merge "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."
2 parents e3e4aa1 + 03e7b3c commit 4ad385d

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)