CustomSQLChange and ClassNotFoundException

Hi,

I am having problems using a CustomChange.  I have created a class, MyCustomChange, which implements CustomSQLChange and compiled it.

                       

When I try to run the update via ant, I get the following exception

    liquibase.exception.CustomChangeException: java.lang.ClassNotFoundException: com.example.MyCustomChange

Earlier in the ant file, I set my base.classpath to the lib directory which contains hibernate.jar, all the database drivers and liquibase. 
The updateDatabase depends on compile, which compiles my custom change and places it in ${basedir}/foo/bin.  I have verified that the compiled class file is present.  In the updateDatase, I set my classpath composed of the base.classpath and ${basedir}/foo/bin.  The classpath is printed out and contains ${basedir}/foo/bin.

    	<path id="classpath">
    		<pathelement location="${basedir}/foo/bin"/>
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <path refid="base.classpath"/>
    	</path>
    
    	<property name="cp" refid="classpath"/>
    	<echo message="cp=${cp}"/>
    
    	<updateDatabase
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; changeLogFile="${db.changelog.file}"
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; driver="${database.driver}"
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; url="${url}"
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; username="${username}"
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; password="${password}"
    		&nbsp; &nbsp; defaultSchemaName="${schema.name}"
    		&nbsp; &nbsp; classpathref="classpath">
    		&nbsp;  <changeLogProperty name="db.path" value="${db.path}"/>
    		&nbsp;  <changeLogProperty name="database.type" value="${database.type}"/>
    		&nbsp;  <changeLogProperty name="hibernate.username" value="${username}"/>
    		&nbsp;  <changeLogProperty name="hibernate.password" value="${password}"/>
    		&nbsp;  <changeLogProperty name="liquibase.dir" value="${liquibase.dir}"/>
    	</updateDatabase>
    </target>
    

When liquibase runs, I get the exception.  The exception is originating in CustomChangeWrapper.setClass.

I have tried creating a jar file from the class file and using that in the classpath instead.  Same results. 

Any idea on why liquibase is unable to load the custom class?  This is with the latest liquibase from the trunk. 

Thanks,
Alana

That should work.  I’ll look into it.

Thanks,
Nathan

Thanks. 

I did a little bit of debugging in CustomChangeWrapper.setClass.

        public void setClass(String className) throws CustomChangeException {         ....         try {             try {                 Object o =  Class.forName(className, true, classLoader).newInstance();                 System.out.println("*** o = " + o.toString());                 System.out.println("***class = " + o.getClass().getCanonicalName());                                 System.out.println("o is an instance of CustomSqlChange: " + CustomSqlChange.class.isInstance(o));                 System.out.println("o is an instance of CustomChange: " + CustomChange.class.isInstance(o));                 customChange = (CustomChange)o;             } catch (ClassCastException e) { //fails in Ant in particular             ...

When I recompile and run this updated jar, my output is:

    [updateDatabase] *** o = com.example.MyExample@3f6843 [updateDatabase] ***class = com.example.MyExample [updateDatabase] o is an instance of CustomSqlChange: false [updateDatabase] o is an instance of CustomChange: false

So the first Class.forName actually loads something that is of the correct class.  Yet for some bizarre reason it doesn’t think it is an instance of CustomSQLChange or its parent CustomChange.  Thus the class cast exception, which forces it to try the other two methods of loading the class.  Neither of those are able to load it,  resulting in the ClassNotFoundException.

This is weird, because if I add a little reflection code to see the interfaces on o, it returns CustomSqlChange.  Which is correct, it does implement CustomSqlChange.

Thanks,
Alana

Hi again,

I think I found a possible work around. I know my team mates are not going to like it though. 

Running ant with the “-lib” option, such as

ant -lib c:/dir/where/liqubase.jar/is/located redoDB

produces the following debugging output:

    [updateDatabase] ***class = com.example.MyExample [updateDatabase] o is an instance of CustomSqlChange: true [updateDatabase] o is an instance of CustomChange: true

So it seems like some issue where ant classloader doesn’t find the liquibase.jar.  It is part of my classpath defined in ant though. 

Alana

Glad you found a work-around.  I’ll get it fixed for 2.0 final.

Nathan

Hi,

I’m using 2.01 and still getting this problem. Any ideas?

Do you have a full stacktrace you get?

Nathan

Hi

    I’m using 2.0 and it looks like I’ve also hit this problem when trying to use custom change (and in trying to resolve the issue all my searching has brought me here !). I’ve created a class called ‘Show’  which implements CustomSqlChange and compiled it.  My liquibase changeset is as follows :-

 


  
   
       
 

 

When I run liquibase (directly , not via ANT) I get :-

 


INFO 03/04/13 10:03:liquibase: Successfully released change log lock
Liquibase Update Failed: java.lang.ClassNotFoundException: Show
SEVERE 03/04/13 10:03:liquibase: java.lang.ClassNotFoundException: Show
liquibase.exception.ChangeLogParseException: Invalid Migration File: java.lang.C
lassNotFoundException: Show
        at liquibase.parser.core.xml.XMLChangeLogSAXParser.parse(XMLChangeLogSAX
Parser.java:132)
        at liquibase.parser.core.xml.XMLChangeLogSAXHandler.handleIncludedChange
Log(XMLChangeLogSAXHandler.java:504)
        at liquibase.parser.core.xml.XMLChangeLogSAXHandler.startElement(XMLChan
geLogSAXHandler.java:143)
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startEle
ment(Unknown Source)
        at com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.
emptyElement(Unknown Source)
        at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.emptyEl
ement(Unknown Source)
        at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scan
StartElement(Unknown Source)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImp
l$FragmentContentDriver.next(Unknown Source)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(U
nknown Source)
        at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next
(Unknown Source)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImp
l.scanDocument(Unknown Source)
        at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(U
nknown Source)
        at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(U
nknown Source)
        at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown So
urce)
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(Un
known Source)
        at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.p
arse(Unknown Source)
        at liquibase.parser.core.xml.XMLChangeLogSAXParser.parse(XMLChangeLogSAX
Parser.java:98)
        at liquibase.Liquibase.update(Liquibase.java:107)
        at liquibase.integration.commandline.Main.doMigration(Main.java:825)
        at liquibase.integration.commandline.Main.main(Main.java:134)
Caused by: org.xml.sax.SAXException: java.lang.ClassNotFoundException: Show
liquibase.exception.CustomChangeException: java.lang.ClassNotFoundException: Show

 

Any advice on how to resolve this would be appreciated.

 

Regards,

Shaun.