[prev in list] [next in list] [prev in thread] [next in thread] 

List:       xmlbeans-dev
Subject:    [jira] Created: (XMLBEANS-207) Big issue with bad validation of interfaces when .xsdconfig file is u
From:       "Denis Anisimov (JIRA)" <xmlbeans-dev () xml ! apache ! org>
Date:       2005-09-26 13:48:51
Message-ID: 1222724383.1127742531092.JavaMail.jira () ajax ! apache ! org
[Download RAW message or body]

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

         Key: XMLBEANS-207
         URL: http://issues.apache.org/jira/browse/XMLBEANS-207
     Project: XMLBeans
        Type: Bug
  Components: Compiler  
    Versions: Version 2    
    Reporter: Denis Anisimov


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++) {
    		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 .


-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@xmlbeans.apache.org
For additional commands, e-mail: dev-help@xmlbeans.apache.org


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic