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: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]