从高速缓存反序列化数据时ClassNotFoundException

时间:2013-06-07 08:10:31

标签: java maven caching classloader mybatis

我有一个用于Web服务的EJB的EAR应用程序,部署在Glassfish 3.0.1服务器上。所有依赖项都由maven加载,关于pom.xml。我使用wsimport从WSDL文件生成类。一切都很完美。

然后由于一些性能问题,我不得不为mybatis查询实现缓存。在我打开缓存之后,我意识到,我的类需要可序列化。这不是问题。

<xs:annotation>
    <xs:appinfo>
        <jaxb:globalBindings>
            <xjc:serializable uid="1" />
        </jaxb:globalBindings>
    </xs:appinfo>
</xs:annotation>

应用程序已编译和部署,但是当我从soapUI第二次调用Web服务操作(第一次ok,cache为空)时,我收到以下错误。

org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: org.apache.ibatis.cache.CacheException: Error     deserializing object. Cause: java.lang.ClassNotFoundException: cz.cpost.esb.cenik.schema.CountryType
### Cause: org.apache.ibatis.cache.CacheException: Error deserializing object. Cause: java.lang.ClassNotFoundException: cz.cpost.esb.cenik.schema.CountryType
... some code ommited ...
Caused by: org.apache.ibatis.cache.CacheException: Error deserializing object.  Cause: java.lang.ClassNotFoundException: cz.cpost.esb.cenik.schema.CountryType
at org.apache.ibatis.cache.decorators.SerializedCache.deserialize(SerializedCache.java:79)
at org.apache.ibatis.cache.decorators.SerializedCache.getObject(SerializedCache.java:35)
at org.apache.ibatis.cache.decorators.LoggingCache.getObject(LoggingCache.java:35)
at org.apache.ibatis.cache.decorators.SynchronizedCache.getObject(SynchronizedCache.java:40)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:56)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:78)
... 83 more
Caused by: java.lang.ClassNotFoundException: cz.cpost.esb.cenik.schema.CountryType
at com.sun.enterprise.loader.ASURLClassLoader.findClassData(ASURLClassLoader.java:713)
at com.sun.enterprise.loader.ASURLClassLoader.findClass(ASURLClassLoader.java:626)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:247)
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:604)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1575)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1496)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1732)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
at java.util.ArrayList.readObject(ArrayList.java:593)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1849)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
at org.apache.ibatis.cache.decorators.SerializedCache.deserialize(SerializedCache.java:76)
... 88 more

似乎当app试图从缓存中获取数据时,找不到对象CountryType。 我不知道这是怎么可能的,我在maven和缓存方面都很新。

maven构建元素:

<build>
    <resources>
        <resource>
            <targetPath>META-INF/wsdl</targetPath>
            <directory>src/wsdl</directory>
            <includes/>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes/>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
                <compilerArguments>
                    <endorseddirs>${endorsed.dir}</endorseddirs>
                </compilerArguments>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-ejb-plugin</artifactId>
            <version>2.3</version>
            <configuration>
                <ejbVersion>3.1</ejbVersion>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jaxws-maven-plugin</artifactId>
            <version>1.12</version>
            <executions>
                <execution>
                    <goals>
                        <goal>wsimport</goal>
                    </goals>
                    <configuration>
                        <keep>true</keep>
                        <wsdlFiles>
                            <wsdlFile>CenikServices-v2.0.wsdl</wsdlFile>
                        </wsdlFiles>
                        <packageName>cz.cpost.esb.cenik.schema</packageName>
                        <staleFile>${project.build.directory}/jaxws/stale/cenik.stale</staleFile>
                        <bindingFiles>
                            <bindingFile>${basedir}/src/main/resources/jaxb-bindings.xml</bindingFile>
                        </bindingFiles>
                    </configuration>
                    <id>wsimport-generate-cenik</id>
                    <phase>generate-sources</phase>
                </execution>
            </executions>
            <dependencies>
                <dependency>
                    <groupId>javax.xml</groupId>
                    <artifactId>webservices-api</artifactId>
                    <version>1.4</version>
                </dependency>
            </dependencies>
            <configuration>
                <sourceDestDir>${project.build.directory}/generated-sources/jaxws-wsimport</sourceDestDir>
                <destDir>${project.build.directory}/classes</destDir>
                <xnocompile>true</xnocompile>
                <verbose>true</verbose>
                <extension>true</extension>
                <catalog>${basedir}/src/jax-ws-catalog.xml</catalog>
                <target>2.0</target>
            </configuration>
        </plugin>
    </plugins>
</build>

映射器配置中的缓存配置:

<cache type="org.mybatis.caches.ehcache.EhcacheCache">
    <property name="timeToIdleSeconds" value="7200"/>
    <property name="timeToLiveSeconds" value="28800"/>
    <property name="maxElementsInMemory" value="5000"/>
    <property name="maxElementsOnDisk" value="10000"/>
    <property name="memoryStoreEvictionPolicy" value="LRU"/>
 </cache>

我虽然我的源代码不在classpath中,但我将archive元素放到了ear pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
    <groupId>cz.cpost.esb</groupId>
    <artifactId>cenikservices-ear</artifactId>
    <version>2.0</version>
    <relativePath>../pom.xml</relativePath>
</parent>

<modelVersion>4.0.0</modelVersion>
<artifactId>ear</artifactId>
<packaging>ear</packaging>

<dependencies>
    <dependency>
        <groupId>${project.groupId}</groupId>
        <artifactId>cenikservices-ejb</artifactId>
        <version>${project.version}</version>
        <type>ejb</type>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-ear-plugin</artifactId>
            <version>2.5</version>
            <configuration>
                <defaultLibBundleDir>lib</defaultLibBundleDir>
                <modules>
                    <ejbModule>
                        <groupId>${project.groupId}</groupId>
                        <artifactId>cenikservices-ejb</artifactId>
                    </ejbModule>
                </modules>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>

EAR文件结构:

structure of deployed EAR file

但问题没有解决。为什么应用程序无法找到打包在cenikservices-ejb-2.0.jar中的生成源?


更新

我将EJB类的行添加到构造函数

import org.apache.ibatis.io.Resources;
...
public CenikEJB() {
    Resources.setDefaultClassLoader(this.getClass().getClassLoader());        // added
    this.sqlSessionFactory = MyBatisConnectionFactory.getSqlSessionFactory();
}

但仍然在ASURLClassLoader上得到相同的错误:-( 我测试过,如果类的CountryType在EJB方法中可用,那么它是。我在日志中看到 SUCCESS

@Override
public List<CountryType> listCountry(Integer codeTask, String langAlfaCode) throws CenikFault {
    methodName = "Cenik.listCountry";

    SqlSession session = this.sqlSessionFactory.openSession(); 
    try {
        this.getClass().getClassLoader().loadClass("cz.cpost.esb.cenik.schema.CountryType");
        ccpLogger.info( "SUCCESS    - Pokus o nalezeni tridy CountryType vysel");
    } catch (ClassNotFoundException ex) {
        ccpLogger.info("FAIL        - Pokus o nalezeni tridy CountryType nevysel",ex);
    }

    try
    {   
        ListCountryType param = this.of.createListCountryType();
        param.setCodeTask(codeTask);
        param.setLangAlfaCode(langAlfaCode);

        // ziskat seznam zemi pomoci SQL procedury
        List seznamZemi = session.selectList("Cenik.getListCountry", param);

        return seznamZemi; 
    } catch (IllegalArgumentException ex) {
        ccpLogger.error(methodName,ex);
                    ...

    } catch (Exception ex) {
        ccpLogger.error(methodName,ex);
                    ...
    } finally { 
        session.close(); 
    } 
}

1 个答案:

答案 0 :(得分:2)

问题是服务器端无法加载类cz.cpost.esb.cenik.schema.CountryType

最可能的原因是定义该类的JAR文件无法包含在您部署的EAR文件中。最可能的解释是,EAR项目的POM文件没有所需形式的必需依赖项。

请注意,依赖项应列在POM文件顶层的<dependencies>元素中;即作为<project>元素的子元素。


第一步应该是检查包含该类的JAR文件是否实际位于已部署的webapp中。 (事实上​​,问题就是其他问题......)

然后,您需要将必需的依赖项添加到EAR模块的POM文件中。


<强>更新

根据您提供的其他信息,我认为这是Ibatis课程加载的问题。似乎Ibatis正在尝试使用不包含您的类的类加载器来加载类。 (我的猜测是Ibatis正在使用“公共类”类加载器......它不包括你的应用程序JAR。)

显然,修复是在servlet类中执行此操作:

    import com.ibatis.common.resources.Resources;
    ...
    Resources.setDefaultClassLoader(this.getClass().getClassLoader());

参考文献:

更新#2

我在这一点上猜...但是这个错误报告(http://code.google.com/p/mybatis/issues/detail?id=622)似乎描述了MyBatis 3.x中setDefaultClassLoader无法正常工作的错误。他们似乎在说“在3.2.2中修复”。我注意到你使用的是3.0.5。看看将POM的依赖关系更新到更高版本是否有帮助。

如果没有,我建议的最好的方法是将一个调试器附加到您的Glassfish实例,看看是否可以挖掘出它正在使用的类加载器......以及为什么。很明显, 是一个类加载器问题。