ClassCastException con oggetto in ClassLoader diversi

DOMANDA:

Perchè ricevo un errore di ClassCastException se dalla mia webapp recupero lo stesso oggetto creato da un'altra webapp?


RISPOSTA:

Di solito questo errore si verifica quando stiamo utilizzando un oggetto in due class loader diversi. Ad esempio ho una webapp A che utilizza un oggetto X del jar X.jar presente nella sua WEB-INF/lib. Decido di recuperare l'oggetto X dalla webapp B che avrà anch'essa nella sua WEB-INF/lib il jar X.jar.

In questo caso è vero che l'oggetto X è lo stesso ma viene caricato sia nel class loader di A che in quello di B. Ciò comporta la generazione di un errore di ClassCastException quando si tenta di recuperare l'oggetto X da B.

Per ovviare facilmente a questo errore si potrebbe decidere di inserire il jar X.jar in un class loader condiviso tra le due webapp A e B (ad esempio il class loader dell'Application Server).

Una soluzione più elegante, che offre una serie di vantaggi e rispetta le tecniche di Design Pattern, è quella di inserire a livello di class loader condiviso, solo una interfaccia e demandare l'implementazione di tale interfaccia alle classi che saranno caricate dai diversi class loader.

Vediamo un esempio:
N.B. 

Per provare l'esempio, abilitare il crossContext nel file context.xml presente nella cartella config di tomcat
<context crosscontext="true">

    ...

</context>


Definizione dell'interfaccia ICar contenuta nel jar Interfaces.jar
package interfaces;
public interface ICar {
 
 public void setNome(String nome);
 public String getNome();
 
 public void setModello(String modello);
 public String getModello();
 
}


Definizione della classe BMWCar (che implementa ICar) contenuta nel jar Pojo.jar
import interfaces.ICar;

public class BMWCar implements ICar{
 
 private String nome;
 private String modello;

 public void setNome(String nome) {
  this.nome=nome;
 }
 public String getNome(){
  return this.nome;
 }
 
 public void setModello(String modello){
  this.modello=modello;
 }
 
 public String getModello(){
  return this.modello;
 }

}


Servlet CreateCar della webapp A: 
  • crea un oggetto bmw di tipo BMWCar utilizzando l'interfaccia ICar
  • memorizza l'oggetto bmw nell'Application Scope con chiave CAR
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import interfaces.ICar;
import pojo.BMWCar;

@WebServlet("/CreateBMWCar")
public class CreateBMWCar extends HttpServlet {
 private static final long serialVersionUID = 1L;

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  ServletContext sc = request.getServletContext();
  
  ICar bmw = new BMWCar();
  bmw.setModello("Serie 3");
  bmw.setNome("320d");
  
  sc.setAttribute("CAR", bmw);
  
  response.setContentType("text/plain");
  PrintWriter out = response.getWriter();
  out.write("Creata auto:\n");
  out.write("Nome: "+bmw.getNome() + "Modello: "+bmw.getModello() );
 }
}


Servlet ReadCar della WebApp B: recupera dall'Application Scope della webapp A l'auto creata:
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import interfaces.ICar;

@WebServlet("/ReadCar")
public class ReadCar extends HttpServlet {
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  
  ServletContext sc = request.getServletContext().getContext("/WebAppA");
  
  ICar car = (ICar) sc.getAttribute("CAR");
  
  response.setContentType("text/plain");
  PrintWriter out = response.getWriter();
  out.write("Auto recuperata:\n");
  out.write("Nome: "+car.getNome() + " Modello: "+car.getModello()+"" );
 }

}


Di seguito un'immagine per capire come e dove sono organizzate/definite le classi descritte


Commenti

Post popolari in questo blog

Arrotondamento e troncamento in Java

Eclipse: Shortcuts (scorciatoie) da tastiera

Strutture dati: List, Set, Map

Creare un eseguibile Java