Uploaded image for project: 'XMLBeans'
  1. XMLBeans
  2. XMLBEANS-207

Big issue with bad validation of interfaces when .xsdconfig file is used

    XMLWordPrintableJSON

    Details

    • Type: Bug
    • Status: Open
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: Version 2
    • Fix Version/s: None
    • Component/s: Compiler
    • Labels:
      None

      Description

      The main problem here is inside InterfaceExtensionImpl when methods validateMethods() and validateMethod() are called.
      The perform validation against just interface without any knowing of bean schema class which will be used for extention.

      Lets imagine the situation when schema have type "tOne" and "tTwo" and "tTwo" extends "tOne"

      <complexType name="tTwo">
      <complexContent>
      <extension base="tOne"/>
      </complexContent>
      </complexType>

      In this case generated bean TOne will extends TTwo.

      I want to create extention for TOne and TTwo. I want: TOne extends interface First, TTwo extends interface Second. But also I want to keep original
      schema hierarchy , so I want also interface Second extends First.

      Original implementation doesn't allow to me handle this situation : see bug http://issues.apache.org/jira/browse/XMLBEANS-205.
      If this bug will be resolved then anyway I will need to create corresponding methods in SecondHandler methods that will be responsible for
      delegation methods from First interface because all methods in First interface also exist in Second. BUT TTwo already extends TOne and
      I don't need to create static methods for delegation them from First interface. They already exists in TTwo because it originally extends TOne. And
      those methods delegated already in handler for First. But configuration file knows nothing about schema extentions and validate methiods just against
      handlers and interfaces as couple. So we have the problem.

      I suggest some changes in InterfaceExtensionImpl.java, SchemaTypeImpl.java, SchemaTypeSystemCompiler.java.
      Possibly this is not best fix but it works and more appropriate fix wil need changes in architecture......
      ( I suggest that bug http://issues.apache.org/jira/browse/XMLBEANS-205 already fixed as described )
      ( changes marked with "!" )

      changes in SchemaTypeSystemCompiler.java :

      public static boolean generateTypes(SchemaTypeSystem system, Filer filer, XmlOptions options)
      {
      // partial type systems not allowed to be saved
      if (system instanceof SchemaTypeSystemImpl && ((SchemaTypeSystemImpl)system).isIncomplete())
      return false;

      boolean success = true;

      List types = new ArrayList();
      types.addAll(Arrays.asList(system.globalTypes()));
      types.addAll(Arrays.asList(system.documentTypes()));
      types.addAll(Arrays.asList(system.attributeTypes()));

      for (Iterator i = types.iterator(); i.hasNext(); )
      {
      SchemaType type = (SchemaType)i.next();
      if (type.isBuiltinType())
      continue;
      if (type.getFullJavaName() == null)
      continue;

      String fjn = type.getFullJavaName();

      Writer writer = null;

      ! // here we perform validation for interfaces that should extends
      ! // this type. We check whether all methods in interfaces will be
      ! // present or some methods will be present in supertype, in this case
      ! // this also Ok.
      ! if (!((SchemaTypeImpl)type).validateMethods() )

      { ! System.err.println("Was found unimplemented methods. See above information"); ! return false; ! ! }

      try

      { // Generate interface class writer = filer.createSourceFile(fjn); SchemaTypeCodePrinter.printType(writer, type, options); }

      catch (IOException e)

      { System.err.println("IO Error " + e); success = false; }
      finally {
      try { if (writer != null) writer.close(); } catch (IOException e) {}
      }

      try
      { // Generate Implementation class fjn = type.getFullJavaImplName(); writer = filer.createSourceFile(fjn); SchemaTypeCodePrinter.printTypeImpl(writer, type, options); }
      catch (IOException e)
      { System.err.println("IO Error " + e); success = false; }

      finally {
      try

      { if (writer != null) writer.close(); }

      catch (IOException e) {}
      }
      }

      return success;
      }

      This block is new in SchemaTypeImpl.java :

      /**

      • This method should be called instead of <code>InterfaceExtension.validateMethods()</code>
      • for validation interfaces for THIS type.
      • The reason why <code>InterfaceExtension.validateMethods()</code> is bad :
      • methods from interface can be already implemented in base type. So
      • we don't need to implement them .
        */
        public boolean validateMethods(){
        InterfaceExtension[] extension = getInterfaceExtensions();
        for (int i = 0; i < extension.length; i++)
        Unknown macro: { if ( !validateMethods( (InterfaceExtensionImpl)extension[i])) { return false; } }

        return true;
        }

      private boolean validateMethods(InterfaceExtensionImpl extensionImpl) {
      InterfaceExtension.MethodSignature[] methods = extensionImpl.getAbsentMethods();
      InterfaceExtension.MethodSignature[] inherited = getAllInheritedMethods();
      for (int i = 0; i < methods.length; i++) {
      InterfaceExtension.MethodSignature method = methods[i];
      boolean flag = false;
      for (int j = 0; j < inherited.length; j++) {
      if ( extensionImpl.equals( method, inherited[j]))

      { flag = true; }

      }
      if ( !flag )

      { extensionImpl.error( method ); return false; }

      }

      return true;
      }

      private InterfaceExtension.MethodSignature[] getAllInheritedMethods(){
      List list = new LinkedList();
      SchemaType type = getBaseType();
      while ( type!= null ){
      if (type instanceof SchemaTypeImpl) {
      SchemaTypeImpl typeImpl = (SchemaTypeImpl) type;
      InterfaceExtension[] extensions = typeImpl.getInterfaceExtensions();
      if (extensions != null) {
      for (int i = 0; i < extensions.length; i++)

      { list.addAll(Arrays.asList(extensions[i].getMethods())); }

      }
      }
      type = type.getBaseType();
      }

      return (InterfaceExtension.MethodSignature[])
      list.toArray( new InterfaceExtension.MethodSignature[list.size()]);
      }

      Changes in InterfaceExtensionImpl.java :

      New attributes :

      private List myNotFoundMethods = new LinkedList();
      private XmlObject myXMLObject ;

      Inside method : static InterfaceExtensionImpl newInstance(JamClassLoader loader, NameSet xbeanSet, Extensionconfig.Interface intfXO)

      result.myXMLObject = intfXO;

      Changed block ( three new methods and changed old methods validateMethod() and validateMethods():

      public void error( MethodSignature signature)

      { BindingConfigImpl.error("Handler class '" + _delegateToClassName + "' does not contain method " + ((MethodSignatureImpl)signature).getSignature(), myXMLObject); }

      /**

      • This method perform "light" comparison for two methods signature
      • based only on methods names and signatures. Real comperison in
      • MethodSignatureImpl perform also checking for outer class name.
      • @param method1
      • @param method2
      • @return
        */
        public boolean equals( InterfaceExtension.MethodSignature method1 ,
        InterfaceExtension.MethodSignature method2 ) { return ( (MethodSignatureImpl)method1).lightEqual( method2 ); }

      /**

      • Changed - will always return true.
      • This is incorrect place for determining correctness of interface.
      • Validation logic is moved into SchemaTypeImpl.validateMethods()
      • @param interfaceJClass
      • @param delegateJClass
      • @param loc
      • @return
        */
        private boolean validateMethods(JClass interfaceJClass, JClass delegateJClass, XmlObject loc)
        {
        //assert _delegateToClass != null : "Delegate to class handler expected.";
        boolean valid = true;

      JMethod[] interfaceMethods = interfaceJClass.getMethods();
      List list = new LinkedList();
      //_methods = new MethodSignatureImpl[interfaceMethods.length];

      for (int i = 0; i < interfaceMethods.length; i++)
      {
      JMethod method;
      try {
      method = validateMethod(interfaceJClass, delegateJClass, interfaceMethods[i], loc);
      if (method != null)

      { //_methods[i] = new MethodSignatureImpl(getStaticHandler(), method); list.add(new MethodSignatureImpl(getStaticHandler(), method)); }

      else

      { valid = false; }

      }
      catch (MethodNotFoundException e)

      { // we didn't find method. If method was not found in static handler // by name and signature - then it was placed in myNotFoundMethods list. // in this case validation will be performed later. And we don't // get this exception. But if method was actually found in handler, // but its return type or exceptions that it can throws is not // the same as declared in interface then we got this exception // and we should stop. return false; }

      }

      _methods = (MethodSignatureImpl[])list.toArray( new MethodSignatureImpl[ list.size()] );
      //return valid;
      return true;
      }

      /**

      • Changed.
      • Incorrect place for decision about valid method.
      • Validation logic is moved into SchemaTypeImpl.validateMethods()
      • @param interfaceJClass
      • @param delegateJClass
      • @param method
      • @param loc
      • @return
        */
        private JMethod validateMethod(JClass interfaceJClass, JClass delegateJClass, JMethod method, XmlObject loc)
        throws MethodNotFoundException {
        String methodName = method.getSimpleName();
        JParameter[] params = method.getParameters();
        JClass returnType = method.getReturnType();

      JClass[] delegateParams = new JClass[params.length+1];
      delegateParams[0] = returnType.forName("org.apache.xmlbeans.XmlObject");
      for (int i = 1; i < delegateParams.length; i++)

      { delegateParams[i] = params[i-1].getType(); }

      JMethod handlerMethod = null;
      handlerMethod = getMethod(delegateJClass, methodName, delegateParams);
      if (handlerMethod==null)

      { MethodSignatureImpl signature = new MethodSignatureImpl( "", method); myNotFoundMethods.add( signature ); // BindingConfigImpl.error("Handler class '" + delegateJClass.getQualifiedName() + "' does not contain method " + methodName + "(" + listTypes(delegateParams) + ")", loc); return null; }

      // check for throws exceptions
      JClass[] intfExceptions = method.getExceptionTypes();
      JClass[] delegateExceptions = handlerMethod.getExceptionTypes();
      if ( delegateExceptions.length!=intfExceptions.length )

      { BindingConfigImpl.error("Handler method '" + delegateJClass.getQualifiedName() + "." + methodName + "(" + listTypes(delegateParams) + ")' must declare the same exceptions as the interface method '" + interfaceJClass.getQualifiedName() + "." + methodName + "(" + listTypes(params), loc); throw new MethodNotFoundException(); //return null; }

      for (int i = 0; i < delegateExceptions.length; i++)
      {
      if ( delegateExceptions[i]!=intfExceptions[i] )

      { BindingConfigImpl.error("Handler method '" + delegateJClass.getQualifiedName() + "." + methodName + "(" + listTypes(delegateParams) + ")' must declare the same exceptions as the interface method '" + interfaceJClass.getQualifiedName() + "." + methodName + "(" + listTypes(params), loc); throw new MethodNotFoundException(); //return null; }

      }

      if (!handlerMethod.isPublic() || !handlerMethod.isStatic())

      { BindingConfigImpl.error("Method '" + delegateJClass.getQualifiedName() + "." + methodName + "(" + listTypes(delegateParams) + ")' must be declared public and static.", loc); throw new MethodNotFoundException(); //return null; }

      if (!returnType.equals(handlerMethod.getReturnType()))

      { BindingConfigImpl.error("Return type for method '" + handlerMethod.getReturnType() + " " + delegateJClass.getQualifiedName() + "." + methodName + "(" + listTypes(delegateParams) + ")' does not match the return type of the interface method :'" + returnType + "'.", loc); throw new MethodNotFoundException(); //return null; }

      return method;
      }

      public InterfaceExtensionImpl.MethodSignatureImpl[] getAbsentMethods()

      { return (InterfaceExtensionImpl.MethodSignatureImpl[]) myNotFoundMethods.toArray( new InterfaceExtensionImpl.MethodSignatureImpl[myNotFoundMethods.size()]); }

      /**

      • This exception will be thrown if method was FOUND in handler
      • via name and signature but it have wrong return type, exceptions that
      • can be thrown , etc.
      • @author ads
        *
        */
        private class MethodNotFoundException extends Exception{

      }

      I can also send all these changed java files .

        Attachments

        1. SchemaTypeSystemCompiler.java
          18 kB
          Denis Anisimov
        2. SchemaTypeImpl.java
          82 kB
          Denis Anisimov
        3. SchemaTypeCodePrinter.java
          103 kB
          Denis Anisimov
        4. InterfaceExtensionImpl.java
          18 kB
          Denis Anisimov

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              ads Denis Anisimov
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated: