3 changed files with 173 additions and 0 deletions
@ -0,0 +1,108 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-2018 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.boot.diagnostics.analyzer; |
||||||
|
|
||||||
|
import java.io.PrintWriter; |
||||||
|
import java.io.StringWriter; |
||||||
|
import java.net.URL; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; |
||||||
|
import org.springframework.boot.diagnostics.FailureAnalysis; |
||||||
|
import org.springframework.util.ClassUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* An {@link AbstractFailureAnalyzer} that analyzes {@link NoSuchMethodError |
||||||
|
* NoSuchMethodErrors}. |
||||||
|
* |
||||||
|
* @author Andy Wilkinson |
||||||
|
*/ |
||||||
|
class NoSuchMethodFailureAnalyzer extends AbstractFailureAnalyzer<NoSuchMethodError> { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected FailureAnalysis analyze(Throwable rootFailure, NoSuchMethodError cause) { |
||||||
|
String className = extractClassName(cause); |
||||||
|
if (className == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
List<URL> candidates = findCandidates(className); |
||||||
|
if (candidates == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
URL actual = getActual(className); |
||||||
|
if (actual == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
StringWriter description = new StringWriter(); |
||||||
|
PrintWriter writer = new PrintWriter(description); |
||||||
|
writer.print("An attempt was made to call the method "); |
||||||
|
writer.print(cause.getMessage()); |
||||||
|
writer.print(" but it does not exist. Its class, "); |
||||||
|
writer.print(className); |
||||||
|
writer.println(", is available from the following locations:"); |
||||||
|
writer.println(); |
||||||
|
for (URL candidate : candidates) { |
||||||
|
writer.print(" "); |
||||||
|
writer.println(candidate); |
||||||
|
} |
||||||
|
writer.println(); |
||||||
|
writer.println("It was loaded from the following location:"); |
||||||
|
writer.println(); |
||||||
|
writer.print(" "); |
||||||
|
writer.println(actual); |
||||||
|
return new FailureAnalysis(description.toString(), |
||||||
|
"Correct the classpath of your application so that it contains a single," |
||||||
|
+ " compatible version of " + className, |
||||||
|
cause); |
||||||
|
} |
||||||
|
|
||||||
|
private String extractClassName(NoSuchMethodError cause) { |
||||||
|
int descriptorIndex = cause.getMessage().indexOf('('); |
||||||
|
if (descriptorIndex == -1) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
String classAndMethodName = cause.getMessage().substring(0, descriptorIndex); |
||||||
|
int methodNameIndex = classAndMethodName.lastIndexOf('.'); |
||||||
|
if (methodNameIndex == -1) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return classAndMethodName.substring(0, methodNameIndex); |
||||||
|
} |
||||||
|
|
||||||
|
private List<URL> findCandidates(String className) { |
||||||
|
try { |
||||||
|
return Collections.list((NoSuchMethodFailureAnalyzer.class.getClassLoader() |
||||||
|
.getResources(ClassUtils.convertClassNameToResourcePath(className) |
||||||
|
+ ".class"))); |
||||||
|
} |
||||||
|
catch (Throwable ex) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private URL getActual(String className) { |
||||||
|
try { |
||||||
|
return getClass().getClassLoader().loadClass(className).getProtectionDomain() |
||||||
|
.getCodeSource().getLocation(); |
||||||
|
} |
||||||
|
catch (Throwable ex) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,64 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-2018 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.boot.diagnostics.analyzer; |
||||||
|
|
||||||
|
import javax.servlet.ServletContext; |
||||||
|
import javax.servlet.http.HttpServlet; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.runner.RunWith; |
||||||
|
|
||||||
|
import org.springframework.boot.diagnostics.FailureAnalysis; |
||||||
|
import org.springframework.boot.testsupport.runner.classpath.ClassPathOverrides; |
||||||
|
import org.springframework.boot.testsupport.runner.classpath.ModifiedClassPathRunner; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.mockito.Mockito.mock; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author awilkinson |
||||||
|
*/ |
||||||
|
@RunWith(ModifiedClassPathRunner.class) |
||||||
|
@ClassPathOverrides("javax.servlet:servlet-api:2.5") |
||||||
|
public class NoSuchMethodFailureAnalyzerTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void noSuchMethodErrorIsAnalyzed() { |
||||||
|
Throwable failure = createFailure(); |
||||||
|
assertThat(failure).isNotNull(); |
||||||
|
FailureAnalysis analysis = new NoSuchMethodFailureAnalyzer().analyze(failure); |
||||||
|
assertThat(analysis).isNotNull(); |
||||||
|
assertThat(analysis.getDescription()) |
||||||
|
.contains("the method javax.servlet.ServletContext.addServlet" |
||||||
|
+ "(Ljava/lang/String;Ljavax/servlet/Servlet;)" |
||||||
|
+ "Ljavax/servlet/ServletRegistration$Dynamic;") |
||||||
|
.contains("class, javax.servlet.ServletContext,"); |
||||||
|
} |
||||||
|
|
||||||
|
private Throwable createFailure() { |
||||||
|
try { |
||||||
|
ServletContext servletContext = mock(ServletContext.class); |
||||||
|
servletContext.addServlet("example", new HttpServlet() { |
||||||
|
}); |
||||||
|
return null; |
||||||
|
} |
||||||
|
catch (Throwable ex) { |
||||||
|
return ex; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue