【问题标题】:ClassCastException DTO OSGIClassCastException DTO OSGI
【发布时间】:2016-01-26 07:15:33
【问题描述】:

我有三个蓝图包(不同的罐子):

  • ap-data:只包含一个名为 Game.java 的类,应该用作 DTO。它是一个实体,带有 @Entity 注释。
  • ap-dao:一个持久性捆绑包,包含 persistence.xml、OSGI 清单中的 Meta-Persistence 标头,并从 ap-data 导入 Game.java。
  • ap-service:同样从ap-data导入Game.java和ap-dao暴露的接口IGameDAO.java。它还公开了 IGameService.java。

意识到这些捆绑包构成了模块游戏的层。场景:

  • 所有包都在 Karaf 3.0.5 中正确部署(都是“活动”)。
  • ap-dao 包被正确加载为持久包,可以访问配置的数据源,并且可以毫无问题地访问 EntityManager。它可以执行查询,我可以看到结果。一切都很好。
  • ap-service 可以毫无问题地注入 IGameDAO 的实例。到这里为止一切都很好。
  • Game.java (DTO) 类仅包含在 ap-data 包中。没有重复(我检查过)。

问题:

GameServiceImpl 中的 Game typedap-service 包类加载器加载,而 IGameDAO 服务返回的实例属于另一种类型因为它们是由 ap-dao 包类加载器加载的,因此会生成 ClassCastException。意识到这个类是相同的 (app.Game.java) ,应该是一个 DTO。 DTO 是我必须能够使用并通过捆绑(通过服务)传递它们的基本对象。在 OSGI 中如何处理 DTO?我该如何解决这个问题?

// code in GameServiceImpl, from ap-service bundle
public void prettyPrintGames() {
    List<Game> games = gameDao().findAll();

    /* ClassCastException in the for-loop, since the type Game, in ap-service
     * is not the same of the instances of varible 'games', 
     * returned by the DAO service instance. This is the reason
     * of the classloader issue.
     */
    for(Game g : games){
        System.out.println(g.toString()); 
    }
}

结构:

应用数据

+---ap-data
|   |   pom.xml
|   |       
|   +---src
|   |   \---main
|   |       +---java
|   |       |   \---br
|   |       |       \---com
|   |       |           \---company
|   |       |               \---game
|   |       |                   \---entity
|   |       |                           Game.java

捆绑清单:

Manifest-Version: 1.0
Bnd-LastModified: 1445945039034
Build-Jdk: 1.7.0_07
Built-By: user
Bundle-Description: ap-data
Bundle-ManifestVersion: 2
Bundle-Name: ap-data
Bundle-SymbolicName: ap-data
Bundle-Version: 1.0.0
Created-By: Apache Maven Bundle Plugin
Export-Package: br.com.company.game.entity;version="1.0.0";uses:="javax.
 persistence"
Import-Package: javax.persistence;version="[2.1,3)"
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))"
Tool: Bnd-3.0.0.201509101326

ap-dao

+---ap-dao
|   |   pom.xml
|   +---src
|   |   \---main
|   |       +---java
|   |       |   \---br
|   |       |       \---com
|   |       |           \---company
|   |       |               \---game
|   |       |                   \---dao
|   |       |                       +---api
|   |       |                       |       IGameDAO.java
|   |       |                       |       
|   |       |                       \---impl
|   |       |                               GameDAOImpl.java
|   |       |                               
|   |       \---resources
|   |           +---META-INF
|   |           |       persistence.xml
|   |           |       
|   |           \---OSGI-INF
|   |               \---blueprint
|   |                       blueprint.xml
|   |                       

捆绑清单

Manifest-Version: 1.0
Bnd-LastModified: 1445945039855
Build-Jdk: 1.7.0_07
Built-By: user
Bundle-Blueprint: OSGI-INF/blueprint/blueprint.xml
Bundle-Description: game-module
Bundle-ManifestVersion: 2
Bundle-Name: ap-dao
Bundle-SymbolicName: ap-dao
Bundle-Version: 1.0.0
Created-By: Apache Maven Bundle Plugin
Export-Package: br.com.company.game.dao.api;version="1.0.0";uses:="br.co
 m.company.game.entity"
Export-Service: br.com.company.game.dao.api.IGameDAO
Import-Package: br.com.company.game.dao.api;version="[1.0,2)",br.com.com
 pany.game.entity;version="[1.0,2)",javax.persistence;version="[2.1,3)",
 org.osgi.service.blueprint;version="[1.0.0,2.0.0)"
Meta-Persistence: META-INF/persistence.xml
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))"
Tool: Bnd-3.0.0.201509101326

ap-服务

+---ap-service
|   |   pom.xml
|   |   
|   +---src
|   |   \---main
|   |       +---java
|   |       |   \---br
|   |       |       \---com
|   |       |           \---company
|   |       |               \---game
|   |       |                   \---service
|   |       |                       +---api
|   |       |                       |       IGameService.java
|   |       |                       |       
|   |       |                       \---impl
|   |       |                               GameServiceImpl.java
|   |       |                               
|   |       \---resources
|   |           \---OSGI-INF
|   |               \---blueprint
|   |                       service.xml
|   |                       

捆绑清单

Manifest-Version: 1.0
Bnd-LastModified: 1445945040469
Build-Jdk: 1.7.0_07
Built-By: user
Bundle-Blueprint: OSGI-INF/blueprint/service.xml
Bundle-Description: game-module
Bundle-ManifestVersion: 2
Bundle-Name: ap-service
Bundle-SymbolicName: ap-service
Bundle-Version: 1.0.0
Created-By: Apache Maven Bundle Plugin
Export-Package: br.com.company.game.service.api;version="1.0.0"
Export-Service: br.com.company.game.service.api.IGameService
Import-Package: br.com.company.game.dao.api;version="[1.0,2)",br.com.com
 pany.game.entity;version="[1.0,2)",br.com.company.game.service.api;vers
 ion="[1.0,2)",org.osgi.service.blueprint;version="[1.0.0,2.0.0)"
Import-Service: br.com.company.game.dao.api.IGameDAO;multiple:=false
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))"
Tool: Bnd-3.0.0.201509101326

分析:

我在 ap-dao 中进行了一些测试,然后才意识到如果我返回实体而不通过 EntityManager,创建硬编码的对象,例如,一切正常。一旦我使用 entityManager 查询实体,问题又回来了,我得到 ClassCastException。

private EntityManager entityManager;

    @SuppressWarnings("unchecked")
    @Override
    public List<Game> findAll() {
        return _findAllHardCoded(); // this work just fine in service layer, no ClassCastException

        return entityManager.createQuery("SELECT s FROM Game s").getResultList(); // if I use this, ClassCastException is generated in the service layer

    }

    protected List<Game> _findAllHardCoded() {
        List<Game> games = new ArrayList<Game>();
        games.add(new Game(Short.valueOf("1"), "Game1"));
        games.add(new Game(Short.valueOf("2"), "Game2"));
        return games;
    }

【问题讨论】:

  • Christian Christian Schneider 能否提供帮助?
  • 如果我是你,我会在发生 ClassCastException 的地方使用 Object 类型的辅助变量。我会在那里调试并检查对象的真实类型并将其与我想要转换的类类型进行比较。我会检查他们的类加载器。有了它,我可能会得到一些信息,例如:JPA 使用黑魔法并在运行时创建一个类类型,或者 JPA 将类类型缓存在 Map 中并基于此实例化它,但其他人重新启动 Game.java 的捆绑包并执行捆绑包编织...
  • 顺便说一句,您使用什么持久性提供程序?
  • 类是一样的,我认为只有类加载器不同:java.lang.ClassCastException: br.com.company.game.entity.Game 不能强制转换为 br.com.company.game。 entity.Game at br.com.company.game.service.impl.GameServiceImpl.prettyPrintGames(GameServiceImpl.java:30) at Proxy561ddac9_3d0d_4154_ac2b_f63a1852e182.prettyPrintGames(Unknown Source) at Proxy63e0d3db_6f54_4231_a8b0_d20e2665a1e7. .rest.GameResourceImpl.prettyPrintGames(GameResourceImpl.java:15)
  • 我使用 Hibernate 作为 JPA 提供程序。查询执行成功。

标签: osgi apache-karaf blueprint-osgi


【解决方案1】:

根据 Christian Schneider 的说法,如 cmets 所示,尝试在运行时更新 KAR 文件,该文件在任何持久性捆绑中使用 EntityManager,在尝试转换实体类型的捆绑之间生成 ClassCastException,这是 Aries JPA 1.x 的一个已知问题. Aries JPA 2x 是要走的路。谢谢!

【讨论】:

    【解决方案2】:

    如果您的设置正确,那么 ap-data 应该包含 Game 类,并且有一个用于 Game 所在包的 Export-Package。另外两个捆绑包应该有一个针对这个包的 Import-Package 语句。在这种情况下,Game 类只能由 ap-data 的类加载器加载。

    还要注意如何设置 maven 捆绑插件。例如,如果您告诉它导出包含在另一个包中的包,那么它会将其嵌入到您自己的包中。所以可能最简单的方法是将其保留为默认值。

    您可以寻找的东西是。任何包裹都不应包含在一个以上的捆绑包中。您应该使用 Aries JPA 进行 JPA 访问。正如 Balazs 评论的那样,持久性提供者经常在内部使用黑魔法。与 Aries JPA 结合使用,它应该可以在 OSGi 中工作,但如果您使用普通 JPA,则可能会出现问题。

    我检查了 github 上的项目。我认为问题是persistence.xml 的位置。该文件应始终位于包含实体类的项目中。你能试着移动一下吗?

    【讨论】:

    • 我提供了上面的项目结构。我现在把项目放到github上。
    • 网址:github.com/jhnotammake202015/github。我正在 apache karaf 中部署生成的 KAR 文件(ap-kar/target)。我感谢任何帮助使其发挥作用。
    • 在这个存储库中,我还放置了已配置启动功能的 karaf 发行版。
    • 只是为了提供帮助:我在 ap-dao 中进行了一些测试,然后才意识到如果我在不通过 EntityManager 的情况下返回实体,例如创建硬编码的对象,一切正常。一旦我使用 entityManager 查询实体,问题又回来了,我得到 ClassCastException。我将在问题中提供上面的代码。请看!!
    • 我编辑了答案。我想我解决了这个问题。你可以尝试将persistence.xml移动到ap-data项目吗?
    猜你喜欢
    • 2016-04-12
    • 2021-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-30
    • 1970-01-01
    • 2018-02-24
    相关资源
    最近更新 更多