TreeSet引发的OSGI服务代理创建异常

来源:互联网 发布:易语言调用js脚本 编辑:程序博客网 时间:2024/06/06 09:26

看故障日志:

Method entry: getService, args org.apache.karaf.deployer.features.FeatureDeploymentListener@4c500c2d2017-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_c6a2cf4564f4at 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.zte.sdn.oscp.commons.utils.BundleUtils.getService(BundleUtils.java:123)at com.zte.sdn.oscp.framework.service.metainfo.impl.ServiceMetaTrackerManagerImpl.addTarget(ServiceMetaTrackerManagerImpl.java:32)at com.zte.sdn.oscp.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_c6a2cf4564f4at 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());            }        }    }




原创粉丝点击