Skip to content
This repository was archived by the owner on Mar 29, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/play/classloading/RebelClassloader.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,29 @@
import play.Play;
import play.exceptions.RestartNeededException;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class RebelClassloader extends ApplicationClassloader {
private static final Map<String, Class<?>> cache = new ConcurrentHashMap<>();
private static final Map<String, Boolean> unexistingClasses = new ConcurrentHashMap<>();

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
return super.loadClass(name, resolve);
Class<?> aClass = cache.get(name);
if (aClass == null) {
if (unexistingClasses.get(name) != null) return null;

try {
aClass = super.loadClass(name, resolve);
cache.put(name, aClass);
}
catch (ClassNotFoundException e) {
unexistingClasses.put(name, true);
}
}
return aClass;
}

@Override public Class<?> loadApplicationClass(String name) {
Expand Down
68 changes: 68 additions & 0 deletions test/play/classloading/RebelClassloaderTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package play.classloading;

import org.junit.Before;
import org.junit.Test;
import play.Play;
import play.classloading.ApplicationClasses.ApplicationClass;
import play.exceptions.RestartNeededException;

import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

public class RebelClassloaderTest {
RebelClassloader cl;

@Before
public void setUp() {
Play.classes = new ApplicationClasses();
cl = new RebelClassloader();
}

@Test
public void returnsNullForUnexistingClass() throws ClassNotFoundException {
assertThat(cl.loadClass("Script1BeanInfo", true), equalTo(null));
}

@Test
public void doesNotTryToLoadUnexistingClassMultipleTimes() throws ClassNotFoundException {
cl.loadClass("Script1BeanInfo", true);
cl = spy(cl);

assertThat(cl.loadClass("Script1BeanInfo", true), equalTo(null));
verify(cl, never()).loadApplicationClass(anyString());
}

@Test
public void loadsClassForApplicationClasses() throws ClassNotFoundException {
ApplicationClass applicationClass = new ApplicationClass("com.my.User");
applicationClass.javaClass = User.class;
Play.classes.add(applicationClass);

assertThat(cl.loadClass("com.my.User", true), equalTo(User.class));
}

@Test
public void cachesLoadedClasses() throws ClassNotFoundException {
ApplicationClass applicationClass = new ApplicationClass("com.my.User");
applicationClass.javaClass = User.class;
Play.classes.add(applicationClass);
cl.loadClass("com.my.User", true);

cl = spy(cl);
assertThat(cl.loadClass("com.my.User", true), equalTo(User.class));
verify(cl, never()).loadApplicationClass(anyString());
}

@Test
public void doesNotDetectChanges() throws RestartNeededException {
Play.classes = mock(ApplicationClasses.class);

cl.detectChanges();

verify(Play.classes, never()).all();
}

private static class User {}
}