问题描述
在开发环境中执行没有错误,但是打包 jar 包到 linux 上执行报错
错误信息
java.lang.NullPointerException
at org.neo4j.ogm.MetaData.entityType(MetaData.java:277) ~[neo4j-ogm-core-2.0.5.jar!/:?]
at org.neo4j.ogm.session.Neo4jSession.entityType(Neo4jSession.java:485) ~[neo4j-ogm-core-2.0.5.jar!/:?]
at org.neo4j.ogm.session.delegates.LoadByTypeDelegate.loadAll(LoadByTypeDelegate.java:59) ~[neo4j-ogm-core-2.0.5.jar!/:?]
at org.neo4j.ogm.session.delegates.LoadByTypeDelegate.loadAll(LoadByTypeDelegate.java:112) ~[neo4j-ogm-core-2.0.5.jar!/:?]
at org.neo4j.ogm.session.Neo4jSession.loadAll(Neo4jSession.java:151) ~[neo4j-ogm-core-2.0.5.jar!/:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_171]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_171]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_171]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_171]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133) ~[spring-aop-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121) ~[spring-aop-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at com.sun.proxy.$Proxy181.loadAll(Unknown Source) ~[?:?]
at org.springframework.data.neo4j.template.Neo4jTemplate.loadAll(Neo4jTemplate.java:100) ~[spring-data-neo4j-4.1.3.RELEASE.jar!/:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_171]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_171]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_171]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_171]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333) ~[spring-aop-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
at com.sun.proxy.$Proxy183.loadAll(Unknown Source) ~[?:?]
at org.springframework.data.neo4j.repository.GraphRepositoryImpl.findAll(GraphRepositoryImpl.java:125) ~[spring-data-neo4j-4.1.3.RELEASE.jar!/:?]
at org.springframework.data.neo4j.repository.GraphRepositoryImpl.findAll(GraphRepositoryImpl.java:120) ~[spring-data-neo4j-4.1.3.RELEASE.jar!/:?]
问题定位
在 neo4j-ogm-core 包中的 ClassPathScanner 类的 scanClassFileEntry 方法
可以看出 classInfos 中没有找到对应类 这里在本地开发环境无法重现
后面会给出为什么本地无法重现
public ClassInfo classInfo(String name) {
if (classInfos.containsKey(name)) {
return classInfos.get(name);
}
ClassInfo classInfo = _classInfo(name, NodeEntity.class.getName(), "label");
if (classInfo != null) {
classInfos.put(name, classInfo);
return classInfo;
}
classInfo = _classInfo(name, RelationshipEntity.class.getName(), "type");
if (classInfo != null) {
classInfos.put(name, classInfo);
return classInfo;
}
classInfo = domainInfo.getClassSimpleName(name);
if (classInfo != null) {
classInfos.put(name, classInfo);
return classInfo;
}
// not found
classInfos.put(name, null);
return null;
}
解决方式
自行添加 org.neo4j.ogm.scanner.ClassPathScanner 类并修改 scanClassFileEntry 方法
private void scanClassFileEntry(InputStream inputStream, ZipEntry entry) throws IOException {
String name = entry.getName();
LOGGER.trace("Scanning class entry: {}", name);
for (String pathToScan : classPaths) {
if (name.contains(pathToScan)) {
LOGGER.debug("{} found in {}", pathToScan, name);
processor.process(inputStream);
break;
}
}
}
定位问题过程 (idea 远程调试 + 本地开发调试)进行比对
执行 jar 包时开启远程调试 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -Xms4048m -Xmx4048m -jar datamanage-business-bigdata-catalog-0.0.1-SNAPSHOT.jar
idea 添加远程配置
- 报错位置
- 开发和生产比对
开发
生产
开发中 calssInfos中有146个而生产只有64个
- 排查 classInfos 差异位置 跟踪 MetaData 生成位置
可以看出从这个位置开始开发和生产发生了不同
这里是上面方法的调用链
看出生产和开发的 pathFiles 不同
生产环境,会执行 scanZipFile 进行扫描
这里是开发环境,会执行 scanFolder 进行扫描
生产上执行位置, 这是重点!!! 其中 path 多了 BOOT-INFO/classes
这里截图的并不友好,实际在扫描neo4j实体时本应该加载进 classInfo 但是这里比对时导致没有加载进 classInfo
开发环境这里可以看出会执行 processor.process(inputStream);
processor.process() 方法
@Override
public void process(final InputStream inputStream) throws IOException {
ClassInfo classInfo = new ClassInfo(inputStream);
String className = classInfo.name();
String superclassName = classInfo.superclassName();
LOGGER.debug("Processing: {} -> {}", className, superclassName);
if (className != null) {
ClassInfo thisClassInfo = classNameToClassInfo.get(className)
if (thisClassInfo == null) {
thisClassInfo = classInfo;
classNameToClassInfo.put(className, thisClassInfo);
}
if (!thisClassInfo.hydrated()) {
thisClassInfo.hydrate(classInfo);
ClassInfo superclassInfo = classNameToClassInfo.get(superclassName);
if (superclassInfo == null) {
classNameToClassInfo.put(superclassName, new ClassInfo(superclassName, thisClassInfo));
} else {
superclassInfo.addSubclass(thisClassInfo);
}
}
if (thisClassInfo.isEnum()) {
LOGGER.debug("Registering enum class: {}", thisClassInfo.name());
enumTypes.add(thisClassInfo.getUnderlyingClass());
}
}
}
scan 扫描的部分代码
private void scanFolder(File folder, int prefixSize) throws IOException {
String absolutePath = folder.getPath();
String relativePath = prefixSize > absolutePath.length() ? "" : absolutePath.substring(prefixSize);
File[] subFiles = folder.listFiles();
if (subFiles != null) {
for (final File subFile : subFiles) {
if (subFile.isDirectory()) {
scanFolder(subFile, prefixSize);
} else if (subFile.isFile()) {
String leafSuffix = "/" + subFile.getName();
scanFile(subFile, relativePath + leafSuffix);
}
}
}
}
private void scanZipFile(final ZipFile zipFile) throws IOException {
LOGGER.debug("Scanning zipFile {}", zipFile.getName());
for (Enumeration<? extends ZipEntry> entries = zipFile.entries(); entries.hasMoreElements();) {
final ZipEntry entry = entries.nextElement();
if (!entry.isDirectory()) {
try (InputStream inputStream = zipFile.getInputStream(entry) ) {
if (entry.getName().endsWith(".class")) {
scanClassFileEntry(inputStream, entry);
} else if (isArchive(entry.getName())) {
scanZippedEntry(inputStream, entry);
}
}
}
}
}
private void scanClassFileEntry(InputStream inputStream, ZipEntry entry) throws IOException {
String name = entry.getName();
LOGGER.trace("Scanning class entry: {}", name);
for (String pathToScan : classPaths) {
if (name.contains(pathToScan)) {
LOGGER.debug("{} found in {}", pathToScan, name);
processor.process(inputStream);
break;
}
}
}
public void scan(List<String> classPaths, ClassFileProcessor processor) {
this.classPaths = classPaths;
this.processor = processor;
Set<File> classPathElements = getUniqueClasspathElements(classPaths);
LOGGER.debug("Classpath elements:");
for (File classPathElement : classPathElements) {
LOGGER.debug(classPathElement.getPath());
}
try {
for (File classPathElement : classPathElements) {
String path = classPathElement.getPath();
if (classPathElement.isDirectory()) {
scanFolder(classPathElement, path.length() + 1);
} else if (classPathElement.isFile()) {
if (isArchive(path)) {
scanZipFile(new ZipFile(classPathElement));
} else {
scanFile(classPathElement, classPathElement.getName());
}
}
}
processor.finish();
} catch (IOException e) {
throw new RuntimeException(e);
}
}