看故障日志:
Method entry: getService, args org.apache.karaf.deployer.features.FeatureDeploymentListener@4c500c2d
2017-12-01 14:12:24,220 | INFO | REF-CHECK-THREAD | ServiceRecipe | 15 - org.apache.aries.blueprint.core - 1.4.2 | Unable to create a proxy object for the service .component-1 defined in bundle org.apache.karaf.deployer.features at version 3.0.3 with id 21. Returning the original object instead.
2017-11-28 09:09:53,117 | INFO | REF-CHECK-THREAD | ServiceRecipe | 15 - org.apache.aries.blueprint.core - 1.4.2 | Unable to create a proxy object for the service .component-2 defined in bundle org.apache.karaf.features.core at version 3.0.3 with id 20. Returning the original object instead.
org.apache.aries.proxy.UnableToProxyException: java.lang.ClassFormatError: Duplicate interface name in class file Proxy7a227df2_4c60_4139_af16_c6a2cf4564f4
at org.apache.aries.proxy.impl.interfaces.ProxyClassLoader.createProxyClass(ProxyClassLoader.java:165)[11:org.apache.aries.proxy.impl:1.0.5]
at org.apache.aries.proxy.impl.interfaces.InterfaceProxyGenerator.getProxyInstance(InterfaceProxyGenerator.java:97)[11:org.apache.aries.proxy.impl:1.0.5]
at org.apache.aries.proxy.impl.AsmProxyManager.createNewProxy(AsmProxyManager.java:80)[11:org.apache.aries.proxy.impl:1.0.5]
at org.apache.aries.proxy.impl.AbstractProxyManager.createDelegatingInterceptingProxy(AbstractProxyManager.java:75)[11:org.apache.aries.proxy.impl:1.0.5]
at org.apache.aries.proxy.impl.AbstractProxyManager.createInterceptingProxy(AbstractProxyManager.java:53)[11:org.apache.aries.proxy.impl:1.0.5]
at org.apache.aries.blueprint.container.ServiceRecipe$TriggerServiceFactory.getService(ServiceRecipe.java:545)
at org.eclipse.osgi.internal.serviceregistry.ServiceUse$1.run(ServiceUse.java:141)
at java.security.AccessController.doPrivileged(Native Method)[:1.8.0_121]
at org.eclipse.osgi.internal.serviceregistry.ServiceUse.getService(ServiceUse.java:139)
at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.getService(ServiceRegistrationImpl.java:468)
at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.getService(ServiceRegistry.java:467)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.getService(BundleContextImpl.java:594)
at com.x.x.x.commons.utils.BundleUtils.getService(BundleUtils.java:123)
at com.x.x.x.framework.service.metainfo.impl.ServiceMetaTrackerManagerImpl.addTarget(ServiceMetaTrackerManagerImpl.java:32)
at com.x.x.x.framework.service.metainfo.impl.OSGiMetaTrackerServiceImpl.checkServiceReferences(OSGiMetaTrackerServiceImpl.java:121)[136:com.zte.sdn.oscp.oscp-os.oscp-framework-service:2.2.9.10R2B10]
at java.util.concurrent.ConcurrentLinkedQueue$CLQSpliterator.forEachRemaining(ConcurrentLinkedQueue.java:857)[:1.8.0_121]
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)[:1.8.0_121]
at com.zte.sdn.oscp.framework.service.metainfo.impl.OSGiMetaTrackerServiceImpl.checkStartedServiceReferences(OSGiMetaTrackerServiceImpl.java:116)[136:com.zte.sdn.oscp.oscp-os.oscp-framework-service:2.2.9.10R2B10]
at com.zte.sdn.oscp.framework.service.metainfo.impl.OSGiMetaTrackerServiceImpl.checkServiceReferences(OSGiMetaTrackerServiceImpl.java:93)[136:com.zte.sdn.oscp.oscp-os.oscp-framework-service:2.2.9.10R2B10]
at java.lang.Thread.run(Thread.java:745)[:1.8.0_121]
Caused by: java.lang.ClassFormatError: Duplicate interface name in class file Proxy7a227df2_4c60_4139_af16_c6a2cf4564f4
at java.lang.ClassLoader.defineClass1(Native Method)[:1.8.0_121]
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)[:1.8.0_121]
at org.apache.aries.proxy.impl.interfaces.ProxyClassLoader.createProxyClass(ProxyClassLoader.java:157)
... 19 more
从上面的日志,其异常信息是指是在创建服务代理类时,类格式错误。虽然在代码创建不成功后,会返回原始对象,对于功能目前暂无影响,但基于blueprint的延迟加载,或自定义的服务跟踪存在不可控的风险。
对于代理类的创建,并没有使用JDK和CGLIB的动态代码方式:
try {
byte[] bytes = icca.generateBytes();
Class<?> c = defineClass(className, bytes, 0, bytes.length,
PROXY_PROTECTION_DOMAIN);
String old = classes.putIfAbsent(createSet, className);
if(old != null) {
c = Class.forName(className, false, this);
}
return c;
} catch (ClassFormatError cfe) {
throw new UnableToProxyException(createSet.iterator().next(), cfe);
} catch (ClassNotFoundException e) {
throw new UnableToProxyException(createSet.iterator().next(), e);
}
byte[] bytes = icca.generateBytes();
对于需要被代码的类,通过字节数组化,再反编译成类,最终生成代理,至于为何要这么做,从代码中发现其主要是增加了权限的判断与控制。
如下的另一种应用,即为字节码进行加密控制:
@Test
public void test() throws IOException, IllegalAccessException, InstantiationException {
File f = new File("D:/out.file");
if (f.exists() && f.isFile()) {
FileInputStream in = new FileInputStream(f);
byte[] original = new byte[in.available()];
in.read(original);//got encode byte
in.close();
byte[] decode = Base64.getDecoder().decode(original);
// byte[] decode = original;
SQClassLoader loader = new SQClassLoader();
Class hello = loader.defineClass(decode, null);
Hello o = (Hello) hello.newInstance();
o.sayHello("sunquan");
}
}
进一步分析报错的原因,就是在将字节转化成类时,这个类实现的接口中,有重复接口从而导致反编译错误,那为什么一个类实现的接口会有重复呢?
考虑A实现
A implement B,C
B implement C,D
A 最终实现 B,C,D而不包含两个C,在进行动态代码创建时,亦不能传入2个C接口
InterfaceProxyGenerator.java 包含了接口的组装
getProxyInstance
SortedSet<Class<?>> interfaces = createSet(ifaces)
构建接口列表,使用了TreeSet,有序不重复,至此可以发现其报错的原因,就在TreeSet中的比较器。
SortedSet<Class<?>> classes = new TreeSet<Class<?>>(new Comparator<Class<?>>() {
public int compare(Class<?> object1, Class<?> object2) {
if (object1.getName().equals(object2.getName())) {
return 0;
} else if (object1.isAssignableFrom(object2)) {
// first class is parent of second, it occurs earlier in type hierarchy
return -1;
} else if (object2.isAssignableFrom(object1)) {
// second class is subclass of first one, it occurs later in hierarchy
return 1;
}
// types have separate inheritance trees, so it doesn't mater which one is first or second,
// however we can't mark them as equal cause one of them will be removed
return 1;
}
});
上面声明的比较器,表示父类比子类大。由于TreeSet使用的红黑树,上面的定义比较器,实际执行中是会造成数据重复插入
可以看本应该是有序无重复集合,却添加进了相同的两个A
建议修改方式:
使用Map,对类及其实现接口,预先过滤一遍,再采用上面比较器的TreeSet进行排序,则能保证无重复接口。
private void addInterface(Map<String, Class> map, Class[] cls) {
for (Class c : cls)
map.putIfAbsent(c.getName(), c);
for (Class c : cls) {
if (c.getInterfaces() != null && c.getInterfaces().length > 0) {
addInterface(map, c.getInterfaces());
}
}
}