Foro de la JavaCup > Problema con el ClassLoader
Creo que algo avancé pero ahora me atasqué de nuevo.
Modifiqué la clase MyClassLoader para dejarla tal que así:
package org.javahispano.javaleague;
import java.util.Hashtable;
public class MyClassLoader extends ClassLoader {
private byte[] classData;
private Hashtable classes = new Hashtable();
public MyClassLoader() {
super(MyClassLoader.class.getClassLoader());
}
public Class loadClass(String name) throws ClassNotFoundException {
try {
return findClass(name);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public Class findClass(String className){
byte classByte[];
Class result = null;
System.out.println("Class Name is : " + className);
result = (Class) classes.get(className); //checks in cached classes
if (result != null) {
return result;
}
try {
return findSystemClass(className);
} catch (Exception e) {
e.printStackTrace();
}
try {
classByte = getClassData();
result = defineClass(className, classByte, 0, classByte.length, null);
classes.put(className, result);
return result;
} catch (Exception e) {
return null;
}
}
public byte[] getClassData() {
return classData;
}
public void setClassData(byte[] classData) {
this.classData = classData;
}
}
Y la clase Play quedó de la siguiente manera:
package org.javahispano.javaleague;
import java.io.IOException;
import java.util.List;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.javahispano.javacup.model.Tactic;
import org.javahispano.javacup.model.engine.Partido;
import com.google.appengine.api.blobstore.BlobInfo;
import com.google.appengine.api.blobstore.BlobInfoFactory;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
@SuppressWarnings("serial")
public class Play extends HttpServlet {
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws
IOException, ServletException {
Partido partido = null;
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
PersistenceManager pm = PMF.get().getPersistenceManager();
Query query = pm.newQuery(MediaObject.class, "owner == userParam");
query.declareImports("import com.google.appengine.api.users.User");
query.declareParameters("User userParam");
MyClassLoader classLoader = new MyClassLoader();
List<MediaObject> results = (List<MediaObject>) query.execute(user);
Object l = null;
Object v = null;
if (results.size() > 0) {
try {
Class claseAsignable = Tactic.class;
for (int i = 0; i < results.size(); i++) {
MediaObject item = results.get(i);
BlobKey blobKey = new BlobKey(item.getBlobkey().getKeyString());
byte[] byteClass = readBlobFully(blobKey);
classLoader.setClassData(byteClass);
String packageName = req.getParameter("packageName");
int tamanio = item.getTitle().indexOf(".class");
String className = packageName + "." + item.getTitle().substring(0, tamanio);
Class cl = classLoader.loadClass(className);
if (claseAsignable.isAssignableFrom(cl)) {
l = cl.newInstance();
v = cl.newInstance();
}
}
partido = new Partido((Tactic) l, (Tactic) v, false);
//partido.inicioRapido();
int iter = 0;
for (int i = 0; partido.getEstado() != 7; i++) {
partido.iterar();
iter = partido.getIteracion();
if (i > 10000) {
throw new Exception("partido bloqueado");
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
req.setAttribute("golesLocal", partido.getGolesLocal());
req.setAttribute("golesVisitante", partido.getGolesVisita());
RequestDispatcher dispatcher = req.getRequestDispatcher("WEB-INF/templates/result.jsp");
dispatcher.forward(req, resp);
}
public static byte[] readBlobFully(BlobKey blobKey){
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
BlobInfo blobInfo = new BlobInfoFactory().loadBlobInfo(blobKey);
if( blobInfo == null )
return null;
if( blobInfo.getSize() > Integer.MAX_VALUE )
throw new RuntimeException("This method can only process blobs up to " + Integer.MAX_VALUE + " bytes");
int blobSize = (int)blobInfo.getSize();
int chunks = (int)Math.ceil(((double)blobSize / BlobstoreService.MAX_BLOB_FETCH_SIZE));
int totalBytesRead = 0;
int startPointer = 0;
int endPointer;
byte[] blobBytes = new byte[blobSize];
for( int i = 0; i < chunks; i++ ){
endPointer = Math.min(blobSize - 1, startPointer + BlobstoreService.MAX_BLOB_FETCH_SIZE - 1);
byte[] theBytes = blobstoreService.fetchData(blobKey, startPointer, endPointer);
for( int j = 0; j < theBytes.length; j++ )
blobBytes[j + totalBytesRead] = theBytes[j];
startPointer = endPointer + 1;
totalBytesRead += theBytes.length;
}
return blobBytes;
}
}
En principio parece que carga todas las clases sin problemas.
Para hacer pruebas el usuario debe subir todos los .class de su táctica e indicar el nombre del paquete en el que se encuentran (para las pruebas se considera que todas la clases de la táctica están en el mismo paquete).
Al pulsar sobre el botón Play se ejecuta el código de la clase Play que puse antes.
El problema está ahora en las líneas:
Class cl = classLoader.loadClass(className);
if (claseAsignable.isAssignableFrom(cl)) {
l = cl.newInstance();
v = cl.newInstance();
}
La idea para probar es crear dos instancias de la clase que implemente Tactic y ejecutar un partido entre ellas (aunque sea la misma táctica, solo es para probar).
El caso es que la clase que cargo con MyClassLoader solo puedo acceder al nombre, el resto de campos de Class están a null, por lo que la comparación claseAsignable.isAssignableFrom(cl)
nunca me devuelve cierto y no puedo instanciar dos objetos para ejecutar el partido.
Creo que el problema lo tengo en MyClassLoader, pero por más que miro y busco por internet no he encontrado nada que me ayude.
Si alguien tiene alguna idea o sugerencia que pueda compartir, se lo agradecería.
Un saludo,
Sito
Al final encontré esta página con un ejemplo casi a medida:
http://www.8bitcloud.com/2011/05/14/GAE-classloader.html
Modifiqué este ejemplo para lo que quería y la clase resultante es esta:
package org.javahispano.javaleague.classloader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import com.google.appengine.api.blobstore.BlobInfo;
import com.google.appengine.api.blobstore.BlobInfoFactory;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.files.AppEngineFile;
import com.google.appengine.api.files.FileService;
import com.google.appengine.api.files.FileServiceFactory;
/**
* Classloader that fetches its class definitions from zip files stored in the GAE DataStore.
*
* @author bruce
*/
public class MyDataStoreClassLoader extends ClassLoader {
private FileService fileService = FileServiceFactory.getFileService();
/**
* Cached copy of the byte streams of every class in the zip files. Not efficient, but good for demonstration purposes
*/
private Map<String, byte[]> byteStreams = new HashMap<String, byte[]>();
Logger logger = Logger.getLogger(MyDataStoreClassLoader.class.getName());
/**
*
* @param parent Parent class loader.
* @param keys varargs array of the Blob keys that specify the zips that will be scanned.
* @throws EntityNotFoundException
* @throws IOException
*/
public MyDataStoreClassLoader(ClassLoader parent) throws EntityNotFoundException, IOException {
super(parent);
}
public void addClassBlob(String name, BlobKey key) throws IOException {
if (byteStreams.containsKey(name)) {
logger.warning("duplicate defintion of class/resource " + name + ". It will be ignored");
} else {
addClass(name, readBlobFully(key));
}
}
/**
* Adds an additional class into the byteStream cache directly. Used by
* AgentLoader to include classes in the WARish thing (hence why it is
* package protected)
*
* @param className The file name of the class to add with slashes (/) instead of periods, and .class on the end
* @param data The bytes that represent the class.
* @throws IOException
*/
void addClass(String className, byte[] data) throws IOException {
byteStreams.put(className, data);
}
@Override
protected Class<?> loadClass (String name, boolean resolve)
throws ClassNotFoundException {
if (name == null) {
throw new NullPointerException();
}
// Since all support classes of loaded class use same class loader
// must check subclass cache of classes for things like Object
// Class loaded yet?
Class<?> c = findLoadedClass (name);
if (c == null) {
try {
c = getParent().loadClass(name);
} catch (ClassNotFoundException ex) {
// Load class data from file and save in byte array
//String fileName = name.replace('.','/') + ".class";
byte data[] = byteStreams.get(name);
if (data == null)
throw new ClassNotFoundException(name);
// Convert byte array to Class
c = defineClass (name, data, 0, data.length);
// If failed, throw exception
if (c == null)
throw new ClassNotFoundException (name);
}
}
// Resolve class definition if approrpriate
if (resolve)
resolveClass (c);
// Return class just created
return c;
}
@Override
public InputStream getResourceAsStream(String name) {
byte data[] = byteStreams.get(name);
if (data == null)
return null;
return new ByteArrayInputStream(data);
}
public static byte[] readBlobFully(BlobKey blobKey){
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
BlobInfo blobInfo = new BlobInfoFactory().loadBlobInfo(blobKey);
if( blobInfo == null )
return null;
if( blobInfo.getSize() > Integer.MAX_VALUE )
throw new RuntimeException("This method can only process blobs up to " + Integer.MAX_VALUE + " bytes");
int blobSize = (int)blobInfo.getSize();
int chunks = (int)Math.ceil(((double)blobSize / BlobstoreService.MAX_BLOB_FETCH_SIZE));
int totalBytesRead = 0;
int startPointer = 0;
int endPointer;
byte[] blobBytes = new byte[blobSize];
for( int i = 0; i < chunks; i++ ){
endPointer = Math.min(blobSize - 1, startPointer + BlobstoreService.MAX_BLOB_FETCH_SIZE - 1);
byte[] theBytes = blobstoreService.fetchData(blobKey, startPointer, endPointer);
for( int j = 0; j < theBytes.length; j++ )
blobBytes[j + totalBytesRead] = theBytes[j];
startPointer = endPointer + 1;
totalBytesRead += theBytes.length;
}
return blobBytes;
}
}
Y con esto, ya consigo cargar los .class con las tácticas de los partidos y ejecutarlos en google.
Ahora me queda almacenar el partido ejecutado en el blobstore y luego poder recuperarlo desde el applet para que se pueda visualizar.
A ver si no tardo mucho en conseguirlo.
Saludos.
Hola,
estoy probando a modificar la aplicación ejemplo del google sdk (MediaStore) para permitir subir las tácticas a los usuarios y que se ejecute el partido utilizando el TaskEngine de google.
De momento el usuario se identifica con una cuenta de correo de google y entonces podrá subir su táctica. Se debe introducir nombre, una descripción opcional y el fichero .class que implemente la clase Tactic.
Una vez subido el fichero, se mostrará al usuario identificado las tácticas que ha subido. Por cada táctica aparecerá un botón que le permitirá ejecutar un partido de la táctica contra si misma. Habrá que introducir el paquete de la clase antes de poder ejecutarla.
Esto es más o menos lo que hay ahora publicado en javaleagueprueba.appspot.com
El problema está cuando intento ejecutar el partido. Adapté algo el framework JavaCup para poder subirlo al GAE, solo subí las clases indispensables que me permiten ejecutar un partido sin visualizarlo. Tuve que crearme una clase local Color con lo justo porque GAE no aceptaba la clase Color utilizada por el framework de JavaCup.
La táctica subida por el usuario la almaceno utilizando el blobstore. Al intentar ejecutar la táctica, la recupero del blobstore y la intento cargar utilizando una clase propia de ClassLoader.
Esta es la clase en cuestión:
package org.javahispano.javaleague;
public class MyClassLoader extends ClassLoader {
private byte[] classData;
public MyClassLoader(ClassLoader parent, byte[] data) {
super(parent);
this.classData = data;
}
public Class loadClass(String name) throws ClassNotFoundException {
try {
return defineClass(name, classData, 0, classData.length);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Y este es el código que utilizo para cargar la táctica:
package org.javahispano.javaleague;
import java.io.IOException;
import java.lang.reflect.Constructor;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.javahispano.javacup.model.Tactic;
import org.javahispano.javacup.model.engine.Partido;
import com.google.appengine.api.blobstore.BlobInfo;
import com.google.appengine.api.blobstore.BlobInfoFactory;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
@SuppressWarnings("serial")
public class Play extends HttpServlet {
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws
IOException {
BlobKey blobKey = new BlobKey(req.getParameter("blobKey"));
ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader();
MyClassLoader classLoader = new MyClassLoader(parentClassLoader, readBlobFully(blobKey));
try {
Class l = classLoader.loadClass(req.getParameter("packageName"));
Class v = classLoader.loadClass(req.getParameter("packageName"));
Constructor cl = (l.getConstructor(new Class[]{}));
Constructor cv = (v.getConstructor(new Class[]{}));
Tactic tl = (Tactic) cl.newInstance(new Object[]{});
Tactic tv = (Tactic) cv.newInstance(new Object[]{});
Partido partido = new Partido(tl, tv, true);
//partido.inicioRapido();
int iter = 0;
for (int i = 0; partido.getEstado() != 7; i++) {
partido.iterar();
iter = partido.getIteracion();
if (i > 10000) {
throw new Exception("partido bloqueado");
}
}
req.setAttribute("golesLocal", partido.getGolesLocal());
req.setAttribute("golesVisitante", partido.getGolesVisita());
RequestDispatcher dispatcher =
req.getRequestDispatcher("WEB-INF/templates/result.jsp");
dispatcher.forward(req, resp);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
public static byte[] readBlobFully(BlobKey blobKey){
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
BlobInfo blobInfo = new BlobInfoFactory().loadBlobInfo(blobKey);
if( blobInfo == null )
return null;
if( blobInfo.getSize() > Integer.MAX_VALUE )
throw new RuntimeException("This method can only process blobs up to " + Integer.MAX_VALUE + " bytes");
int blobSize = (int)blobInfo.getSize();
int chunks = (int)Math.ceil(((double)blobSize / BlobstoreService.MAX_BLOB_FETCH_SIZE));
int totalBytesRead = 0;
int startPointer = 0;
int endPointer;
byte[] blobBytes = new byte[blobSize];
for( int i = 0; i<chunks; i++ ){
endPointer = Math.min(blobSize - 1, startPointer + BlobstoreService.MAX_BLOB_FETCH_SIZE - 1);
byte[] losbytes = blobstoreService.fetchData(blobKey, startPointer, endPointer);
for( int j = 0; j < losbytes.length; j++ )
blobBytes[j + totalBytesRead] = bytes[j];
startPointer = endPointer + 1;
totalBytesRead += bytes.length;
}
return blobBytes;
}
}
Y este es el error que me da:
Uncaught exception from servlet
java.lang.ClassCircularityError: org/javahispano/javacup/tacticas/tacticas_aceptadas/pringaos2011/Pringaos2011
at com.google.appengine.runtime.Request.process-cc48da676deabb87(Request.java)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:158)
at org.javahispano.javaleague.MyClassLoader.loadClass(MyClassLoader.java:13)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:158)
at org.javahispano.javaleague.MyClassLoader.loadClass(MyClassLoader.java:13)
at org.javahispano.javaleague.Play.doPost(Play.java:32)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:454)
at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:461)
at com.google.tracing.TraceContext.runInContext(TraceContext.java:703)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:338)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:330)
at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:458)
at java.lang.Thread.run(Thread.java:679)
La línea que provoca el error es la siguiente:
Class l = classLoader.loadClass(req.getParameter("packageName"));
¿ Alguna idea de como solucionarlo ?
Por lo que busqué por internet, solo encontré referencias a problemas con el nombre del paquete, pero por más pruebas que he hecho en este sentido no consigo solucionarlo.