Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.

Commit 70a0c65

Browse files
committed
some fixes around hotswap and few others
1 parent c9c6acf commit 70a0c65

File tree

4 files changed

+71
-59
lines changed

4 files changed

+71
-59
lines changed

jooby-hotreload/pom.xml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,6 @@
3333
</build>
3434

3535
<dependencies>
36-
<!-- Logging System -->
37-
<dependency>
38-
<groupId>org.slf4j</groupId>
39-
<artifactId>slf4j-api</artifactId>
40-
</dependency>
41-
4236
<!-- Test dependencies -->
4337
<dependency>
4438
<groupId>junit</groupId>

jooby-hotreload/src/main/java/org/jooby/Hotswap.java

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,22 @@
3434
import java.util.concurrent.ExecutorService;
3535
import java.util.concurrent.Executors;
3636

37-
import org.slf4j.Logger;
38-
import org.slf4j.LoggerFactory;
39-
37+
/**
38+
* Start a target app with a custom classloader. The classloader is responsible for loading
39+
* resources from:
40+
*
41+
* 1. public and config directories (probably src/main/resources too)
42+
* 2. target/classes, current app (*.class)
43+
*
44+
* The parent classloader must load 1) and all the *.jars files.
45+
*
46+
* On changes ONLY the custom classloader (App classlaoder) is bounced it.
47+
*
48+
* @author edgar
49+
*
50+
*/
4051
public class Hotswap {
4152

42-
/** The logging system. */
43-
private final Logger log = LoggerFactory.getLogger(getClass());
44-
4553
private URLClassLoader loader;
4654

4755
private volatile Object app;
@@ -62,10 +70,18 @@ public class Hotswap {
6270

6371
private boolean dcevm;
6472

73+
private List<File> dirs;
74+
6575
public Hotswap(final String mainClass, final File[] cp) throws IOException {
6676
this.mainClass = mainClass;
6777
this.cp = cp;
68-
this.paths = toPath(cp);
78+
this.dirs = new ArrayList<File>();
79+
for (File file : cp) {
80+
if (file.isDirectory()) {
81+
dirs.add(file);
82+
}
83+
}
84+
this.paths = toPath(dirs.toArray(new File[dirs.size()]));
6985
this.executor = Executors.newSingleThreadExecutor();
7086
this.scanner = new Watcher(this::onChange, paths);
7187
dcevm = System.getProperty("java.vm.version").toLowerCase().contains("dcevm");
@@ -124,12 +140,12 @@ private Hotswap excludes(final String excludes) {
124140
}
125141

126142
public void run() {
127-
log.info("Hotswap available on: {}", Arrays.toString(cp));
128-
log.info(" unlimited runtime class redefinition: {}", dcevm
143+
System.out.printf("Hotswap available on: %s\n", dirs);
144+
System.out.printf(" unlimited runtime class redefinition: %s\n", dcevm
129145
? "yes"
130146
: "no (see https://github.com/dcevm/dcevm)");
131-
log.info(" includes: {}", includes);
132-
log.info(" excludes: {}", excludes);
147+
System.out.printf(" includes: %s\n", includes);
148+
System.out.printf(" excludes: %s\n", excludes);
133149

134150
this.scanner.start();
135151
this.startApp();
@@ -140,27 +156,37 @@ private void startApp() {
140156
stopApp(app);
141157
}
142158
executor.execute(() -> {
143-
ClassLoader old = Thread.currentThread().getContextClassLoader();
159+
URLClassLoader old = loader;
144160
try {
145161
this.loader = newClassLoader(cp);
146-
Thread.currentThread().setContextClassLoader(loader);
147162
this.app = loader.loadClass(mainClass).getDeclaredConstructors()[0].newInstance();
148163
app.getClass().getMethod("start").invoke(app);
149164
} catch (InvocationTargetException ex) {
150-
log.error("Error found while starting: " + mainClass, ex.getCause());
165+
System.err.println("Error found while starting: " + mainClass);
166+
ex.printStackTrace();
151167
} catch (Exception ex) {
152-
log.error("Error found while starting: " + mainClass, ex);
168+
System.err.println("Error found while starting: " + mainClass);
169+
ex.printStackTrace();
153170
} finally {
154-
Thread.currentThread().setContextClassLoader(old);
171+
if (old != null) {
172+
try {
173+
old.close();
174+
} catch (Exception ex) {
175+
System.err.println("Can't close classloader");
176+
ex.printStackTrace();
177+
}
178+
// not sure it how useful is it, but...
179+
System.gc();
155180
}
156-
});
181+
}
182+
});
157183
}
158184

159185
private static URLClassLoader newClassLoader(final File[] cp) throws MalformedURLException {
160186
return new URLClassLoader(toURLs(cp), Hotswap.class.getClassLoader()) {
161187
@Override
162188
public String toString() {
163-
return "AppLoader@" + Arrays.toString(cp);
189+
return "Hotswap@" + Arrays.toString(cp);
164190
}
165191
};
166192
}
@@ -169,21 +195,22 @@ private void onChange(final Kind<?> kind, final Path path) {
169195
try {
170196
Path candidate = relativePath(path);
171197
if (candidate == null) {
172-
log.debug("Can't resolve path: {}... ignoring it", path);
198+
// System.("Can't resolve path: {}... ignoring it", path);
173199
return;
174200
}
175201
if (!includes.matches(path)) {
176-
log.debug("ignoring file {} -> ~{}", path, includes);
202+
// log.debug("ignoring file {} -> ~{}", path, includes);
177203
return;
178204
}
179205
if (excludes.matches(path)) {
180-
log.debug("ignoring file {} -> {}", path, excludes);
206+
// log.debug("ignoring file {} -> {}", path, excludes);
181207
return;
182208
}
183209
// reload
184210
startApp();
185211
} catch (Exception ex) {
186-
log.error("Err found while processing: " + path, ex);
212+
System.err.printf("Err found while processing: %s\n" + path);
213+
ex.printStackTrace();
187214
}
188215
}
189216

@@ -201,18 +228,13 @@ private void stopApp(final Object app) {
201228
app.getClass().getMethod("stop").invoke(app);
202229
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
203230
| NoSuchMethodException | SecurityException ex) {
204-
log.error("couldn't stop app", ex);
205-
} finally {
206-
try {
207-
this.loader.close();
208-
} catch (IOException ex) {
209-
log.debug("Can't close classloader", ex);
210-
}
231+
System.err.println("couldn't stop app");
232+
ex.printStackTrace();
211233
}
212234

213235
}
214236

215-
private static URL[] toURLs(final File[] cp) throws MalformedURLException {
237+
static URL[] toURLs(final File[] cp) throws MalformedURLException {
216238
URL[] urls = new URL[cp.length];
217239
for (int i = 0; i < urls.length; i++) {
218240
urls[i] = cp[i].toURI().toURL();

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

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,18 @@ public void execute() throws MojoExecutionException, MojoFailureException {
8080
appcp.add(buildOutputDirectory);
8181

8282
// *.jar
83+
Set<Artifact> artifacts = new LinkedHashSet<Artifact>(mavenProject.getArtifacts());
84+
8385
Set<String> classpath = new LinkedHashSet<String>();
86+
8487
Optional<Artifact> hotreload = hotreload(pluginArtifacts);
85-
if (hotreload.isPresent()) {
86-
classpath.add(hotreload.get().getFile().getAbsolutePath());
87-
} else {
88-
classpath.addAll(appcp);
89-
}
90-
for (Object candidate : mavenProject.getArtifacts()) {
91-
Artifact artifact = (Artifact) candidate;
92-
classpath.add(artifact.getFile().getAbsolutePath());
88+
classpath.add(hotreload.get().getFile().getAbsolutePath());
89+
90+
for (Artifact artifact : artifacts) {
91+
String scope = artifact.getScope();
92+
if ("runtime".equals(scope) || "compile".equals(scope)) {
93+
appcp.add(artifact.getFile().getAbsolutePath());
94+
}
9395
}
9496
String cp = classpath.stream().collect(Collectors.joining(File.pathSeparator));
9597

@@ -102,18 +104,14 @@ public void execute() throws MojoExecutionException, MojoFailureException {
102104
args.addAll(vmArgs(vmArgs));
103105
args.add("-cp");
104106
args.add(cp);
105-
if (hotreload.isPresent()) {
106-
args.add("org.jooby.Hotswap");
107-
args.add(mainClass);
108-
args.addAll(appcp);
109-
if (includes != null && includes.size() > 0) {
110-
args.add("includes=" + join(includes));
111-
}
112-
if (excludes != null && excludes.size() > 0) {
113-
args.add("excludes=" + join(excludes));
114-
}
115-
} else {
116-
args.add(mainClass);
107+
args.add("org.jooby.Hotswap");
108+
args.add(mainClass);
109+
args.addAll(appcp);
110+
if (includes != null && includes.size() > 0) {
111+
args.add("includes=" + join(includes));
112+
}
113+
if (excludes != null && excludes.size() > 0) {
114+
args.add("excludes=" + join(excludes));
117115
}
118116

119117
cmds.add(new Command(mainClass, "java", args));
@@ -224,4 +222,5 @@ private Optional<Artifact> hotreload(final List<Artifact> artifacts) {
224222
}
225223
return Optional.empty();
226224
}
225+
227226
}

jooby/src/main/java/org/jooby/Session.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929
import org.slf4j.Logger;
3030
import org.slf4j.LoggerFactory;
3131

32-
import com.sun.istack.internal.Nullable;
33-
3432
/**
3533
* Sessions are created on demand from {@link Request#session()}.
3634
*
@@ -199,7 +197,6 @@ interface Store {
199197
* @param builder A session builder.
200198
* @return A session or <code>null</code>.
201199
*/
202-
@Nullable
203200
Session get(Session.Builder builder);
204201

205202
/**

0 commit comments

Comments
 (0)