0

When I try to connect my model class using defineClass (findClass URLClassLoader or self-realization ClassLoader), occurs exception.

Any other methods are being loaded my model, but do not allow dynamic overloading the classes.

@Table("accounts")
@BelongsToParents({
  @BelongsTo(parent = Customer.class, foreignKeyName = "Customer"),
  @BelongsTo(parent = Currency.class, foreignKeyName = "Currency")
})
public class Account extends Document{
  public static DocFields prepareDocument(...){

    ... fields = new ...(getTableName());
    ...
  }
...
}

public abstract class Document extends Model{
  public static DocFields prepareDocument(Session session){
    return null;
  }
  ...
}

public class DynCLoader extends ClassLoader {

  public DynCLoader(ClassLoader parentClass) {
    super(parentClass);
  }
  @Override
  public Class<?> loadClass(String className) throws ClassNotFoundException {
    if(StrFuncs.isEmpty(className)){
      throw new ClassNotFoundException("Ошибка в модуле DynCLoader, в функцие loadClass. Детали: className is empty");
    }
    try {
      ...
      if(...){ // Загрузка нового, неопределенного класса
        Class<?> clazz;
        try{
          String url = "file:"+...+".class";
          URL myUrl = new URL(url);
          URLConnection connection = myUrl.openConnection();
          InputStream input = connection.getInputStream();
          ByteArrayOutputStream buffer = new ByteArrayOutputStream();
          int data = input.read();
          while(data != -1){
              buffer.write(data);
              data = input.read();
          }
          input.close();
          byte[] classData = buffer.toByteArray();
          clazz = defineClass(className, classData, 0, classData.length);
          ...
          return clazz;
        }
        catch (FileNotFoundException ex) {
          clazz = Class.forName(className);
          ...
          return clazz;
        }
        catch (MalformedURLException ex) {
          ...
          return null;
        }
        catch (Throwable ex) {
          try{
            clazz = Class.forName(className, true, this);
            return clazz;
          }
          catch(ClassNotFoundException oErr){
            ...
          }
          ...
          return null;
        }
      }
      else if(...) // Загрузка системного класса
        return Class.forName(className);
      else{
        return Class.forName(className, true, this);
      }
    }
    catch (ClassNotFoundException ex) {
      throw ex;
    }
  }
}

Log:

[main] INFO org.javalite.activejdbc.DB - Opened connection: com.mysql.jdbc.JDBC4Connection@49fd9b
[main] INFO org.javalite.activejdbc.ConnectionsAccess - Attached connection named: default: to current thread: com.mysql.jdbc.JDBC4Connection@49fd9b. Extra info: jdbc:mysql://127.0.0.1:3306/***
[main] INFO org.javalite.activejdbc.Configuration - Load models from: file:/***/Product/***/Server/target/classes/activejdbc_models.properties
[main] INFO org.javalite.activejdbc.Configuration - Load models from: file:/***/Kernel/Server/target/classes/activejdbc_models.properties
[main] INFO org.javalite.activejdbc.Registry - Registered model: class kz.mwb.qupris.server.data.model.Account
***
[main] INFO org.javalite.activejdbc.Registry - Registered model: class kz.mwb.qupris.server.data.model.User
***
[main] INFO org.javalite.activejdbc.Registry - Fetched metadata for table: accounts
***
[main] INFO org.javalite.activejdbc.Registry - Fetched metadata for table: usertable
***
[main] INFO org.javalite.activejdbc.MetaModel - Association found: Customer  ----------<  Account, type: has-many
[main] INFO org.javalite.activejdbc.MetaModel - Association found: Account  >----------  Customer, type: belongs-to
***
[main] INFO org.javalite.activejdbc.cache.QueryCache - MISS, "SELECT * FROM *** WHERE ***
***
[Thread-20] INFO org.javalite.activejdbc.DB - Opened connection: com.mysql.jdbc.JDBC4Connection@1db686
[Thread-20] INFO org.javalite.activejdbc.ConnectionsAccess - Attached connection named: default: to current thread: com.mysql.jdbc.JDBC4Connection@1db686. Extra info: jdbc:mysql://127.0.0.1:3306/***
[Thread-20] INFO org.javalite.activejdbc.LazyList - Query: "SELECT * FROM *** WHERE ***, took: 5 milliseconds
***
00:42:52 > 34 > UserLib > ERROR > java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at kz.mwb.qupris.server.tool.loader.Include.processMethod(Include.java:111)
    at kz.mwb.qupris.server.userlib.doDoc.initMod(doDoc.java:59)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at kz.mwb.qupris.server.tool.loader.Include.processMethod(Include.java:111)
    at kz.mwb.qupris.server.tool.loader.Include.EvalFunction(Include.java:31)
    at kz.mwb.qupris.server.engine.ModEngine.MProcess(ModEngine.java:133)
    at kz.mwb.qupris.server.engine.ModEngine.IProcess(ModEngine.java:85)
    at kz.mwb.qupris.server.engine.ModEngine.XProcess(ModEngine.java:203)
    at kz.mwb.qupris.server.engine.Task.ProcessRequest(Task.java:396)
    at kz.mwb.qupris.server.engine.Task.GateWayLine(Task.java:162)
    at kz.mwb.qupris.server.engine.Task.ConnectToGateWay(Task.java:326)
    at kz.mwb.qupris.server.engine.Task.run(Task.java:61)
    at java.lang.Thread.run(Unknown Source)
Caused by: org.javalite.activejdbc.DBException: failed to find metamodel for class kz.mwb.qupris.server.data.model.Account. Are you sure that a corresponding table  exists in DB?
    at org.javalite.activejdbc.Registry.getTableName(Registry.java:414)
    at org.javalite.activejdbc.ModelDelegate.tableNameOf(ModelDelegate.java:326)
    at kz.mwb.qupris.server.data.model.Account.getTableName(Account.java:2831)
    at kz.mwb.qupris.server.data.model.Account.prepareDocument(Account.java:23)
    ... 20 more
  • what are you trying to achieve? – ipolevoy Jan 10 '17 at 14:44
  • also, you are getting an exception: "Are you sure that a corresponding table exists in DB" which is self-explanatory, The model could not initiale, because it could not pull metadata from a non-existng table. Additionally, you can remove annotation `@Table("accounts")` because it is redundant. – ipolevoy Jan 10 '17 at 14:49
  • This class successfully worked if I use Class.forName or loadClass from standard ClassLoader. Other my classes also worked with database tables fine, but extends Model> classes do not work! The problem probably in ActiveJdbc instrumentation, that can't pull metadata if the kernel and modules has different directories environment. In my case, the kernel is JAR, modules is directory class files. – Сергей Ходусов Jan 10 '17 at 16:02
  • I think the problem is somewhere here - Configuration.java - Configuration() - Line 51:Enumeration resources = getClass().getClassLoader().getResources("activejdbc_models.properties"); – Сергей Ходусов Jan 10 '17 at 17:36
  • Added log and better stacktrace... Can't make activejdbc project for debuging... – Сергей Ходусов Jan 10 '17 at 19:19
  • it is possible that you are running different class loaders, and AJ cannot locate the file `activejdbc_models.properties`. Debussing is easy: build ActiveJDBC with this command: `mvn clean installl -Dmaven.test.skip=true`. Then you can debug your project. Ensure you are running your projet's Maven in the offline mode. – ipolevoy Jan 11 '17 at 06:34
  • however, not clear what you are trying to achieve. – ipolevoy Jan 11 '17 at 06:36

2 Answers2

1

I changed getTableName in a MetaModels.java and it worked!

String getTableName(Class<? extends Model> modelClass) {
    MetaModel mm = null;
    for (Map.Entry<Class<? extends Model>, MetaModel> entry : metaModelsByClass.entrySet()){
      if(modelClass.getName().equals(entry.getKey().getName()))
        mm = entry.getValue();
    }
    return mm == null ? null : mm.getTableName();
}

Original function:

String getTableName(Class<? extends Model> modelClass) {
    MetaModel mm = metaModelsByClass.get(modelClass);
    return mm == null ? null : mm.getTableName();
}

I suggest not to use the class as a key or change the selection logic.

  • Sergey, this makes sense. I added a new issue: https://github.com/javalite/activejdbc/issues/570. It will get fixed soon, so you can pull down the snapshot – ipolevoy Jan 11 '17 at 20:32
0

This was a small bug in the framework related to models loaded by a different class loader. The fix was provided by the requester, the issue was created: https://github.com/javalite/activejdbc/issues/570 and already solved.

You can pull the latest snapshot with the fix from: http://repo.javalite.io/org/javalite/activejdbc/2.0-SNAPSHOT/

thanks for contribution!

ipolevoy
  • 5,432
  • 2
  • 31
  • 46