1

当我们使用 org.eclipse.wst.wsdl.util.WSDLResourceImpl 加载 WSDL 文件并发现 Nullpointer 异常问题时,我们遇到了问题。

我们观察到,当 EMF 模型在一个类加载器中初始化并且第二个类加载器调用 EMF WSDl 加载时,org.eclipse.wst.wsdl.internal.impl.MessageImpl 中的以下代码会导致 Nullpointer 异常。

public void handleUnreconciledElement(Element child, Collection remainingModelObjects)
  {
    switch (WSDLUtil.getInstance().getWSDLType(child))
    {
      case WSDLConstants.PART:
      {
    Part part = ((WSDLPackage)EPackage.Registry.INSTANCE.getEPackage(WSDLPackage.eNS_URI)).getWSDLFactory().createPart();
    part.setEnclosingDefinition(getEnclosingDefinition());
    part.setElement(child);
    getEParts().add(part);
    break;
      }
      default:
      {
    super.handleUnreconciledElement(child, remainingModelObjects);
    break;
      }
    }
  }

Nullpointer 异常发生在这里

部分部分 = ((WSDLPackage)EPackage.Registry.INSTANCE.getEPackage(WSDLPackage.eNS_URI)).getWSDLFactory().createPart();

因为注册表没有第二个类加载器的 WSDL 命名空间 URI 条目

上述问题的stackTrace如下

MessageImpl.handleUnreconciledElement(Element, Collection) line: 411    
MessageImpl(WSDLElementImpl).reconcileContents(Element) line: 1296  
MessageImpl(WSDLElementImpl).reconcile(Element) line: 1242  
MessageImpl(WSDLElementImpl).changeAttribute(EAttribute) line: 1215 
MessageImpl.changeAttribute(EAttribute) line: 467   
MessageImpl(WSDLElementImpl).eNotify(Notification) line: 472    
MessageImpl(WSDLElementImpl).setElementGen(Element) line: 181   
MessageImpl(WSDLElementImpl).setElement(Element) line: 367  
DefinitionImpl.handleUnreconciledElement(Element, Collection) line: 1785    
DefinitionImpl(WSDLElementImpl).reconcileContents(Element) line: 1296   
DefinitionImpl(WSDLElementImpl).reconcile(Element) line: 1242   
DefinitionImpl(WSDLElementImpl).changeAttribute(EAttribute) line: 1215  
DefinitionImpl.changeAttribute(EAttribute) line: 1997   
DefinitionImpl(WSDLElementImpl).eNotify(Notification) line: 472 
DefinitionImpl.eNotify(Notification) line: 515  
DefinitionImpl(WSDLElementImpl).setElementGen(Element) line: 181    
DefinitionImpl(WSDLElementImpl).setElement(Element) line: 367   
DefinitionImpl.setElement(Element) line: 1704   
DefinitionImpl.createDefinition(Node, String, boolean) line: 1511   
WSDLResourceImpl.handleDefinitionElement(Element) line: 572 
WSDLResourceImpl.findDefinition(Element) line: 540  
WSDLResourceImpl.doLoad(InputSource, Map) line: 285 
WSDLResourceImpl.doLoad(InputStream, Map) line: 358 
WSDLResourceImpl(ResourceImpl).load(InputStream, Map<?,?>) line: 1505   

我们的场景是我们有一个线程,其中 EMF WSDL 模型使用 Classloader 1 进行初始化。接下来有另一个线程正在调用模型 WSDL 文件加载,而 Classloader 2 正在执行加载。然后我们在 org.eclipse.wst.wsdl.internal.impl.MessageImpl 中使用以下代码失败并出现 Nullpointer 异常

部分部分 = ((WSDLPackage)EPackage.Registry.INSTANCE.getEPackage(WSDLPackage.eNS_URI)).getWSDLFactory().createPart();

因此很明显,如果第一次加载 EMF 类,则使用两个类加载器我们无法初始化 EMF 模型,因为第二次 isInited 为真且未完成初始化。截取的代码如下所示

 public static WSDLPackage init()
  {
    if (isInited)
      return (WSDLPackage)EPackage.Registry.INSTANCE.getEPackage(WSDLPackage.eNS_URI);

    // Obtain or create and register package
    WSDLPackageImpl theWSDLPackage = (WSDLPackageImpl)(EPackage.Registry.INSTANCE.getEPackage(eNS_URI) instanceof WSDLPackageImpl
      ? EPackage.Registry.INSTANCE.getEPackage(eNS_URI) : new WSDLPackageImpl());

    isInited = true;

    // Initialize simple dependencies
    XSDPackage.eINSTANCE.eClass();

    // Create package meta-data objects
    theWSDLPackage.createPackageContents();

    // Initialize created meta-data
    theWSDLPackage.initializePackageContents();

    // Mark meta-data to indicate it can't be changed
    theWSDLPackage.freeze();

    return theWSDLPackage;
  }

现在 XSD 确实会出现同样的问题,因为当我们使用 org.eclipse.xsd.util.XSDResourceImpl 加载 XSD 文件时,它会出现以下方法

XSDResourceImpl.handleSchemaElement(元素,布尔值)

这里的代码如下

protected void handleSchemaElement(Element element, boolean isMeta)
  {
    XSDSchema xsdSchema;
    if (element == null)
    {
      xsdSchema = XSDFactory.eINSTANCE.createXSDSchema();
      xsdSchema.getQNamePrefixToNamespaceMap().put(null, XSDConstants.SCHEMA_FOR_SCHEMA_URI_2001);
    }
    else if (isMeta)
    {
      xsdSchema = XSDSchemaImpl.createMetaSchema(element);
    }
    else
    {
      xsdSchema = XSDSchemaImpl.createSchema(element);
    }
    getContents().add(xsdSchema);
  }

这里 XSDFCatory 以流翼方式使用

xsdSchema = XSDFactory.eINSTANCE.createXSDSchema();

所以我们想知道为什么不能在 org.eclipse.wst.wsdl.internal.impl.MessageImpl 中更改此代码,如下所示,这可以解决问题并且与 XSD 行为一致

 public void handleUnreconciledElement(Element child, Collection remainingModelObjects)
   {
     switch (WSDLUtil.getInstance().getWSDLType(child))
     {
       case WSDLConstants.PART:
       {
     Part part = WSDLFactory.eINSTANCE.createPart();
     part.setEnclosingDefinition(getEnclosingDefinition());
     part.setElement(child);
     getEParts().add(part);
     break;
       }
       default:
       {
     super.handleUnreconciledElement(child, remainingModelObjects);
     break;
       }
     }
  }

否则,请让我们知道是否有针对此问题的其他替代解决方案。显然,当存在多个类加载器时,类加载会导致 EPackage.Registry.INSTANCE 出现问题。

我们还有两个疑问

1)为什么注册表是每个类加载器。如果是这种情况,那么如何为多个类加载器填充注册表,因为 EMF 一旦初始化,它就不允许再次初始化

2) 同样在 org.eclipse.wst.wsdl.internal.impl.MessageImpl 为什么不能使用 WSDFactory.eINSTANCE 代替注册表

请让我们知道这对于我们正在进行的项目之一非常重要。我们需要有非常高优先级的解决方案

感谢和问候阿南思

4

0 回答 0