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

看故障日志:

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());
            }
        }
    }

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值