Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Building a RestTemplate using RestTemplateBuilder fails with a ClassNotFoundException when OkHttp 2 is on the classpath #13314

Closed
jmirc opened this issue May 31, 2018 · 7 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@jmirc
Copy link

jmirc commented May 31, 2018

I just updated a project with Spring Boot 2.0.2 and I am getting the following exception

Caused by: java.lang.IllegalArgumentException: Could not find class [org.springframework.http.client.OkHttpClientHttpRequestFactory]
	at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:313)
	at org.springframework.boot.web.client.RestTemplateBuilder.detectRequestFactory(RestTemplateBuilder.java:600)
	at org.springframework.boot.web.client.RestTemplateBuilder.configureRequestFactory(RestTemplateBuilder.java:566)
	at org.springframework.boot.web.client.RestTemplateBuilder.configure(RestTemplateBuilder.java:535)
	at org.springframework.boot.web.client.RestTemplateBuilder.build(RestTemplateBuilder.java:523)
	at org.springframework.boot.web.client.RestTemplateBuilder.build(RestTemplateBuilder.java:509)
	at org.springframework.boot.test.web.client.TestRestTemplate.<init>(TestRestTemplate.java:133)

The RestTemplateBuilder is trying to validate if some HTTP client factories exist and the org.springframework.http.client.OkHttpClientHttpRequestFactory doesn't exist anymore in Spring Framework.

@wilkinsona
Copy link
Member

Thanks for the report. Which version of Spring Boot were you using before you upgraded?

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged labels May 31, 2018
@jmirc
Copy link
Author

jmirc commented May 31, 2018

I used the version 2.0.1

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels May 31, 2018
@wilkinsona
Copy link
Member

Thanks. That's rather surprising as support for OkHttp 2 was dropped from Spring Framework in 5.0.0.RC1. We've missed aligning RestTemplateBuilder with that, but I would not expect an upgrade from Spring Boot 2.0.1 to 2.0.2 to trigger the problem. Rather, any Spring Boot 2.0.x version should be affected. Perhaps you could clarify the situation by providing a small sample that works with 2.0.1 and fails with 2.0.2?

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels May 31, 2018
@chrismathews
Copy link

chrismathews commented May 31, 2018

I just hit this same issue myself. I have a project that is using Spring Boot 2.0.1.RELEASE in my case. The project is very simple but pulls in an internal dependency that transitively depends on OkHttp2.

As best as I can tell the problem is in: https://github.com/spring-projects/spring-boot/blob/v2.0.1.RELEASE/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RestTemplateBuilder.java

In particular:

static {
		Map<String, String> candidates = new LinkedHashMap<>();
		candidates.put("org.apache.http.client.HttpClient",
				"org.springframework.http.client.HttpComponentsClientHttpRequestFactory");
		candidates.put("okhttp3.OkHttpClient",
				"org.springframework.http.client.OkHttp3ClientHttpRequestFactory");
		candidates.put("com.squareup.okhttp.OkHttpClient",
				"org.springframework.http.client.OkHttpClientHttpRequestFactory");
		REQUEST_FACTORY_CANDIDATES = Collections.unmodifiableMap(candidates);
	}

Since Spring Framework 5 doesn't support OkHttp2 it would seem to me that com.squareup.okhttp.OkHttpClient should not be an option in the candidates list. Since it is there when this code gets invoked:

private ClientHttpRequestFactory detectRequestFactory() {
		for (Map.Entry<String, String> candidate : REQUEST_FACTORY_CANDIDATES
				.entrySet()) {
			ClassLoader classLoader = getClass().getClassLoader();
			if (ClassUtils.isPresent(candidate.getKey(), classLoader)) {
				Class<?> factoryClass = ClassUtils.resolveClassName(candidate.getValue(),
						classLoader);
				return (ClientHttpRequestFactory) BeanUtils
						.instantiateClass(factoryClass);
			}
		}
		return new SimpleClientHttpRequestFactory();
	}

While debugging I can see ClassUtils.isPresent() returns true for com.squareup.okhttp.OkHttpClient and then ClassUtils.resolveClassName() promptly throws an exception for org.springframework.http.client.OkHttpClientHttpRequestFactory.

The resulting stacktrace looks like:

java.lang.IllegalStateException: Failed to load ApplicationContext

	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
	at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:44)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.test.web.client.TestRestTemplate': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: Could not find class [org.springframework.http.client.OkHttpClientHttpRequestFactory]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:587)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:741)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:327)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:138)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
	... 30 more
Caused by: java.lang.IllegalArgumentException: Could not find class [org.springframework.http.client.OkHttpClientHttpRequestFactory]
	at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:311)
	at org.springframework.boot.web.client.RestTemplateBuilder.detectRequestFactory(RestTemplateBuilder.java:600)
	at org.springframework.boot.web.client.RestTemplateBuilder.configureRequestFactory(RestTemplateBuilder.java:566)
	at org.springframework.boot.web.client.RestTemplateBuilder.configure(RestTemplateBuilder.java:535)
	at org.springframework.boot.web.client.RestTemplateBuilder.build(RestTemplateBuilder.java:523)
	at org.springframework.boot.web.client.RestTemplateBuilder.build(RestTemplateBuilder.java:509)
	at org.springframework.boot.test.web.client.TestRestTemplate.<init>(TestRestTemplate.java:133)
	at org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer$TestRestTemplateFactory.setApplicationContext(TestRestTemplateContextCustomizer.java:146)
	at org.springframework.context.support.ApplicationContextAwareProcessor.invokeAwareInterfaces(ApplicationContextAwareProcessor.java:120)
	at org.springframework.context.support.ApplicationContextAwareProcessor.postProcessBeforeInitialization(ApplicationContextAwareProcessor.java:96)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:422)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1694)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:579)
	... 45 more
Caused by: java.lang.ClassNotFoundException: org.springframework.http.client.OkHttpClientHttpRequestFactory
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at org.springframework.util.ClassUtils.forName(ClassUtils.java:274)
	at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:308)
	... 57 more

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels May 31, 2018
@wilkinsona
Copy link
Member

wilkinsona commented May 31, 2018

Thanks, @chrismathews. Your analysis matches my own which is why I was surprised that @jmirc had only seen the problem when upgrading from 2.0.1 to 2.0.2.

@wilkinsona wilkinsona changed the title Regression with RestTemplateBuilder and Spring Boot 2.0.2 Building a RestTemplate using RestTemplateBuilder fails with a ClassNotFoundException when OkHttp 2 is on the classpath May 31, 2018
@wilkinsona wilkinsona added this to the 2.0.3 milestone May 31, 2018
@wilkinsona wilkinsona added type: bug A general bug and removed status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged labels May 31, 2018
@wilkinsona wilkinsona self-assigned this May 31, 2018
@chrismathews
Copy link

Thanks for the quick turnaround Andy!

@jmirc
Copy link
Author

jmirc commented May 31, 2018

Hi Andy, sorry for my late reply. I checked why with the Spring Boot 2.0.1.RELEASE I didn't get the issue.

I didn't get the issue with the previous version because of the RestTemplateBuilder class that used the apache http client library provided by spring-cloud-commons.
With the Spring Boot 2.0.2.RELEASE, the apache http client library is not provided any more and the RestTemplateBuilder class used the okhttp client that I have on the classpath.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants