I have three blueprint bundles (distinct jars):
- ap-data: contains only a class named Game.java, supposed to be used as a DTO. It's an entity, annotated with @Entity.
- ap-dao: a persistence bundle that contains persistence.xml, Meta-Persistence header in OSGI manifest and imports Game.java from ap-data.
- ap-service: also imports Game.java from ap-data and an interface exposed by ap-dao, IGameDAO.java. It exposes IGameService.java also.
Realized that these bundles form layers of a module Game. Scenario:
- All bundles are deployed correct in Karaf 3.0.5 (all are 'ACTIVE').
- ap-dao bundle is correctly loaded as a persistence bundle, can access a configured datasource and can access EntityManager without problems. It can execute queries and I can see the results. All is fine.
- ap-service can inject an instance of IGameDAO with no problem. All is fine until here.
- The class Game.java (DTO) is only contained in the ap-data bundle. There is no duplicates (I checked).
Problem:
The Game typed in GameServiceImpl is loaded by the ap-service bundle classloader and the instances returned by the IGameDAO service are of another type since they were loaded by the ap-dao bundle classloader, therefore generating ClassCastException. Realize that the class is the same (app.Game.java) , supposed to be a DTO. DTOs are basic objects that I must be able to use and pass them through bundles (by services). How DTOs are treated in OSGI? How can I solve this problem?
// 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());
}
}
Structure:
ap-data
+---ap-data
| | pom.xml
| |
| +---src
| | \---main
| | +---java
| | | \---br
| | | \---com
| | | \---company
| | | \---game
| | | \---entity
| | | Game.java
Bundle manifest:
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
| |
Bundle Manifest
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-service
+---ap-service
| | pom.xml
| |
| +---src
| | \---main
| | +---java
| | | \---br
| | | \---com
| | | \---company
| | | \---game
| | | \---service
| | | +---api
| | | | IGameService.java
| | | |
| | | \---impl
| | | GameServiceImpl.java
| | |
| | \---resources
| | \---OSGI-INF
| | \---blueprint
| | service.xml
| |
Bundle manifest
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
Analysis:
I made some tests here in ap-dao and just realized that if I return the entities without passing through EntityManager, creating the objects hardcoded, for instance, everything works. Once I use entityManager to query the entities, the problem come back again and I get 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;
}