Search code examples
javagithub-apiurlconnectionokhttp

URLConnection created through OkHttp has useCache set to false


I am using the Kohsuke GitHub-API to connect to the GitHub from my Java (server-side) application and I wanted to use the OkHttp's ability to cache responses from the GitHub. This worked perfectly when I wrote a test for it, but it doesn't work in the application itself and I don't have a clue why that is. I have managed to trace the problem back to the creation of the URLConnection object that is created with its useCache variable set to false, but I cannot figure out why. Does it maybe have something to do with the server configuration or something like that?

I would appreciate any ideas or even a nudge in any direction, because frankly I don't have any ideas left... Thanks

Provider:

public class GitHubProvider implements Provider<GitHub> {

@Override
public GitHub get() {
    GitHub gitHub = null;
    HttpResponseCache cache = null;

    OkHttpClient okHttpClient = new OkHttpClient();
    File cacheDir = new File(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());

    try {
        cache = new HttpResponseCache(cacheDir, 10L * 1024 * 1024);
    } catch (IOException e) {
        // NOTHING
    }
    okHttpClient.setResponseCache(cache);

    try {
        gitHub = GitHub.connectUsingPassword("user", "password");
    } catch (Exception e) {
        // NOTHING
    }
    gitHub.setConnector(new OkHttpConnector(okHttpClient));

    return gitHub;
}
}

Test (works):

@RunWith(JukitoRunner.class)
public class SoftwareComponentServiceTest {

public static class Module extends TestModule {
    @Override
    protected void configureTest() {
        bind(GitHub.class).toProvider(GitHubProvider.class);
    }
}

@Inject
GitHub gitHub;

@Test
public void testInjectedGitHubResponseCache() {
    try {
        GHUser ghUser = gitHub.getUser("user");
        GHRepository repository = ghUser.getRepository("repository");

        int limit = gitHub.getRateLimit().remaining;
        repository.getFileContent("README.md");

        assertEquals(limit - 1, gitHub.getRateLimit().remaining);

        repository.getFileContent("README.md");

        assertEquals(limit - 1, gitHub.getRateLimit().remaining);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

Service that is used in the application (doesn't work):

@Singleton
@RequiresAuthentication
public class SoftwareComponentService {

@Inject
GitHub gitHub;

public List<SoftwareComponent> findAll() {
    List<SoftwareComponent> softwareComponentList = new ArrayList<SoftwareComponent>();

    try {
        GHUser ghUser = gitHub.getUser("user");
        List<GHRepository> repositories = ghUser.listRepositories().asList();

        for (int i = 0; i < repositories.size(); i++) {
            GHRepository repository = repositories.get(i);
            if (!repository.getName().startsWith("sc_")) {
                continue;
            }
            softwareComponentList.add(new SoftwareComponent(repository.getName(), repository.getDescription()));
        }
    } catch (IOException e) {
        // NOTHING
    }
    return softwareComponentList;
}
}

Solution

  • The reason

    The URLConnection object is created with its useCache variable set to false because its defaultUseCaches variable is also set to false by the Tomcat server at the time of initialization. Tomcat does this through its JreMemoryLeakPreventionListener class because reading resources from JAR files using java.net.URLConnections can sometimes result in the JAR file being locked (urlCacheProtection variable). The workaround they implemented to solve this problem was to disable URLConnection caching by default (!?!?).

    The solution

    The workaround to this workaround is to create a dummy URLConnection and use its setDefaultUseCaches() method to change the default value of every subsequently created URLConnection (as suggested by Jesse Wilson).

    URL url = new URL("jar:file://dummy.jar!/");
    URLConnection uConn = url.openConnection();
    uConn.setDefaultUseCaches(true);
    

    Big thanks to Jesse Wilson for pointing me in the right direction!