java (library) can not seem to find grooy classes - java

I'm in a complex situation, and I'm going to try to be brief but I'd happily any additional information.
I have inherited the responsibility for a massive, ancient (in Internet years), and poorly documented code base, mostly in groovy. Mercifully the vast majority of this large system of Apps and services and plugins will be "end of life"d in a few months, but there's once piece that needs to live on. I've have been trying to extract just that piece. So it can stand up on it's own.
Overall things have gone pretty well, there are some unit tests, so I've been able to use the compilation and the unit tests to figure out what class files I've needed to bring over and 3rd party libraries I've needed.
However I've run into an issue (both in the unit tests and when I try to actually "run" the application).
The Error Message
The error message looks something like this.
Exception in thread "main" org.codehaus.groovy.runtime.typehandling.GroovyCastException:
Cannot cast object '[
...
about 20 different classes, as far as I can tell these are ORM entities
...
]' with class 'com.google.common.collect.RegularImmutableList' to class 'java.lang.Class' due to:
java.lang.ClassNotFoundException: [
...
the exact same list of classes (and paths) only with the java style '/' instead of the groovy style '.'
... ]
To make things more complicated, even in my much smaller code base there are still a lot of dependencies (all very old).
I'm running Groovy Version: 2.2.2 JVM: 1.7.0_121 Vendor: Oracle Corporation OS: Linux
I had been running gradle 2.2.1 but after I ran into this problem I did some research and learned that I should be able to update gradle so I did, I'm now at 4.7 (that did not fix the problem).
More Information Than You Require
I'm afraid I don't know groovy or java or gradle that well, and I understand the ecosystem even less.
In the medium term I'm planning on bringing the dependencies and environment up to versions that are currently being supported but I think it would be best to get this to work the old way first.
The original project that I cribbed all this code from still does build. The code I brought over was spread out over many different projects that were all formed a very complicated dependency tree. I figured it would be best to bring them into one project since the "new" code base is going to be much smaller.
I've spend a fair amount of time looking at the code where this error is happening (and it's stack trace), it appears as though there's a lot going on
protected HibernateBundle<HohumDatabaseConfiguration> initializeHibernateBundle(List<Class<?>> serviceEntities) {
HibernateBundle bundle = new HibernateBundle<HohumDatabaseConfiguration>(
ImmutableList.copyOf(serviceEntities),
new HohumSessionFactoryFactory()) {
#Override
DatabaseConfiguration getDatabaseConfiguration(HohumDatabaseConfiguration configuration) {
return configuration.database
}
}
return bundle
}
The problematic line is ImmutableList.copyOf(serviceEntities).
Ideas
All of these Entities were in a "plugin" (I'm still not sure what a "plugin is exactly in this context). From reading the documentation I do know this was so the code could be shared with some other applications (not in this repo). Maybe in the "old" repo java has not issues finding the classes because they're all wrapped up in a nice jar by the time this casting needs to happen.
This is a bug in groovy or one of the 3rd party libraries I'm using and I should try some new versions of things and it will just start working.
(I'm not really excited about either of my ideas)
Question
If you know how to solve this problem that would be wonderful. If not, how should one proceed?
If any important information is missing I'd be happy to supply it. Thank you so much for reading all this.
Full Stack Trace
Exception in thread "main" org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '[class us.rupe.domain.AnswerEntity, class us.rupe.domain.AnswerTagEntity, class us.rupe.domain.BundleEntity, class us.rupe.domain.BundleProductEntity, class us.rupe.domain.IdealProductEntity, class us.rupe.domain.ProductEntity, class us.rupe.domain.QuestionEntity, class us.rupe.domain.TagEntity, class us.rupe.domain.SubjectEntity, class us.rupe.domain.SubjectDependentEntity, class us.rupe.domain.SurveyEntity, class us.rupe.domain.SurveyInstanceEntity, class us.rupe.domain.SurveyInstanceAnswerEntity, class us.rupe.domain.SurveyQuestionEntity, class us.rupe.domain.PurchaseEntity, class us.rupe.domain.QuestionGuardEntity, class us.rupe.domain.RecommenderVersionEntity, class us.rupe.domain.AdjusterConfigurationEntity, class us.rupe.domain.CurrentAdjusterConfigurationEntity]' with class 'com.google.common.collect.RegularImmutableList' to class 'java.lang.Class' due to: java.lang.ClassNotFoundException: [class us/rupe/domain/AnswerEntity, class us/rupe/domain/AnswerTagEntity, class us/rupe/domain/BundleEntity, class us/rupe/domain/BundleProductEntity, class us/rupe/domain/IdealProductEntity, class us/rupe/domain/ProductEntity, class us/rupe/domain/QuestionEntity, class us/rupe/domain/TagEntity, class us/rupe/domain/SubjectEntity, class us/rupe/domain/SubjectDependentEntity, class us/rupe/domain/SurveyEntity, class us/rupe/domain/SurveyInstanceEntity, class us/rupe/domain/SurveyInstanceAnswerEntity, class us/rupe/domain/SurveyQuestionEntity, class us/rupe/domain/PurchaseEntity, class us/rupe/domain/QuestionGuardEntity, class us/rupe/domain/RecommenderVersionEntity, class us/rupe/domain/AdjusterConfigurationEntity, class us/rupe/domain/CurrentAdjusterConfigurationEntity]
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToClass(DefaultTypeTransformation.java:380)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToType(DefaultTypeTransformation.java:249)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.castToType(ScriptBytecodeAdapter.java:599)
at us.rupe.service.HohumDatabaseService$1.<init>(HohumDatabaseService.groovy)
at us.rupe.service.HohumDatabaseService.initializeHibernateBundle(HohumDatabaseService.groovy:57)
at us.rupe.service.HohumDatabaseService.<init>(BloomDatabaseService.groovy:25)
at us.rupe.RecommendationService.<init>(RecommendationService.groovy)
at us.rupe.RecommendationService.main(RecommendationService.groovy:58)

Related

How to properly use Lombok #Data with interfaces

I'm using quite a bit of native queries in my spring book application since Hibernate won't allow me to write Join queries using Join Tables that are not mapped. I came across this article which helped me to project Native Queries into DTOs beautifully. As mentioned in the article I'm using a DTO interface with an #Data (Lombok) annotation. It worked like a charm while I was running it locally inside eclipse via "Run as Spring Boot Application". But when I'm trying to mvn package it in order to deploy the JAR, I'm getting a compilation error, stating
myDTO.java:[8,1] #Data is only supported on a class.
I don't understand how it works so well while I'm running it in eclipse and maven gives me a compilation error.
Edit: It works in the Java 8 (used in eclipse) and throws a compilation error in Java 11 (Global path version).
You found a lombok 'bug', in that the eclipse variant should also have generated that error instead of doing nothing. Because.. it's doing nothing.
The reason the appropriate response is that error is because #Data on an interface makes no sense. #Data does 4 things. Before I enumerate them, just to be clear, interfaces cannot have (non-static) fields. You can write it, but java will silently upgrade them to public static final.
#Data:
Makes a constructor. (Not legal to do to an interface, so, it can't do that).
Makes a getter and setter for each non-static field. (There can't be any, in an interface, so this does nothing).
It makes a toString that prints the type, and each non-static field (There CAN be one, even with an impl using the default feature, but all it would do is print the type's name; not a very useful toString!)
It makes an equals and hashCode impl (Here too it can exist, but all it would do is return a constant value for hashcode and return a convoluted mess for equals and canEqual which doesn't make much sense as the point is to compare the non-static fields and there aren't any). None of this makes any sense because any classes that implement this interface are effectively forced into overriding these methods for them to work anyway, at which point these implementations will not be used.
I think you're a bit confused, or at least, I am, so, let's take a step back and talk about what problem you're trying to solve / what you think would be happening if you put #Data on an interface.
NB: Java11 vs. Java8 difference is a red herring; that's not the problem here.
NB2: Disclaimer - I'm a core maintainer of project lombok.

Is Java inlining still an issue for most JVMs?

In a (now) relatively old book, "Java Puzzlers", the authors talk about the inlining problems that can occur in static final fields (Puzzle 93: Class Warfare discussed here).
Essentially Java used to have a problem where due to how classes load, you could run into the issue that if a library class (class A) is recompiled with a changed static final field, then a class (class B) which uses that field might not function properly. This could happen because the class A field might have been inlined into class B during compilation, so that when the library is updated, class B does not incorporate the change in the class A field.
My simple question is... Is this still a problem? Do the newer versions of Java redefine the class loading procedure so that we do not have to worry about such issues?
I can only find relatively old posts touching on this issue (pre-2014), which makes me think that this issue has somehow been addressed, but I can find no definitive source.
Also, if it makes any difference, I am particularly interested if this will be a problem in Android.

Java - How to avoid static and default methods in interface

My question is rather theoretical:
Lets say I have Java 8 project using static and default methods in interfaces. I need to get rid of them because I am porting the code to Java 7 (lets say it is an Android app prior to Android N).
How to do that? I know there is Retrolambda plugin for that and I successfully use it. But I am thinking about "pure" solution done by altering the code. What are the options?
I know I can remove static and default methods from interface and put them into abstract class that implements this interface and then alter the code wherever there is reference to that interface (this is what Retrolambda does, if I understood the process correctly).
My teacher had indicated it can be done just by "suitable change in interface definition" without using additional class. But so far I failed in finding the solution. I tried putting implementations directly into target classes but that caused a series of troubles in my particular project.
Anyone have some idea or clue I am missing?
I think it is at first questionable that you actually used such a thing in your Java8 project. Interface should remain without implementations. Default methods were introduced mainly for APIs backward compatibility(if method has to be added to Interface from previous versions and you can't afford to force users of API to change their code and you don't like creating InterfaceV2). Not for "daily usage".
I think when porting, you should just export the methods to the static *Util classes. This way you can reuse it and you are not breaking the main principle of Interface.

Case sensitivity of Java class names

If one writes two public Java classes with the same case-insensitive name in different directories then both classes are not usable at runtime. (I tested this on Windows, Mac and Linux with several versions of the HotSpot JVM. I would not be surprised if there other JVMs where they are usable simultaneously.) For example, if I create a class named a and one named A like so:
// lowercase/src/testcase/a.java
package testcase;
public class a {
public static String myCase() {
return "lower";
}
}
// uppercase/src/testcase/A.java
package testcase;
public class A {
public static String myCase() {
return "upper";
}
}
Three eclipse projects containing the code above are available from my website.
If try I calling myCase on both classes like so:
System.out.println(A.myCase());
System.out.println(a.myCase());
The typechecker succeeds, but when I run the class file generate by the code directly above I get:
Exception in thread "main" java.lang.NoClassDefFoundError: testcase/A (wrong name: testcase/a)
In Java, names are in general case sensitive. Some file systems (e.g. Windows) are case insensitive, so I'm not surprised the above behavior happens, but it seems wrong. Unfortunately the Java specifications are oddly non-commital about which classes are visible. The Java Language Specification (JLS), Java SE 7 Edition (Section 6.6.1, page 166) says:
If a class or interface type is declared public, then it may be accessed by
any code, provided that the compilation unit (ยง7.3) in which it is declared is
observable.
In Section 7.3, the JLS defines observability of a compilation unit in extremely vague terms:
All the compilation units of the predefined package java and its subpackages lang
and io are always observable. For all other packages, the host system determines which compilation units are observable.
The Java Virtual Machine Specification is similarly vague (Section 5.3.1):
The following steps are used to load and thereby create the nonarray class or
interface C denoted by [binary name] N using the bootstrap class loader [...]
Otherwise, the Java virtual machine passes the argument N to an invocation of a
method on the bootstrap class loader to search for a purported representation of C
in a platform-dependent manner.
All of this leads to four questions in descending order of importance:
Are there any guarantees about which classes are loadable by the default class loader(s) in every JVM? In other words, can I implement a valid, but degenerate JVM, that won't load any classes except those in java.lang and java.io?
If there are any guarantees, does the behavior in the example above violate the guarantee (i.e. is the behavior a bug)?
Is there any way to make HotSpot load a and A simultaneously? Would writing a custom class loader work?
Are there any guarantees about which classes are loadable by the bootstrap class loader in every JVM?
The core bits and pieces of the language, plus supporting implementation classes. Not guaranteed to include any class that you write. (The normal JVM loads your classes in a separate classloader from the bootstrap one, and in fact the normal bootstrap loader loads its classes out of a JAR normally, as this makes for more efficient deployment than a big old directory structure full of classes.)
If there are any guarantees, does the behavior in the example above violate the guarantee (i.e. is the behavior a bug)?
Is there any way to make "standard" JVMs load a and A simultaneously? Would writing a custom class loader work?
Java loads classes by mapping the full name of the class into a filename that is then searched for on the classpath. Thus testcase.a goes to testcase/a.class and testcase.A goes to testcase/A.class. Some filesystems mix these things up, and may serve the other up when one is asked for. Others get it right (in particular, the variant of the ZIP format used in JAR files is fully case-sensitive and portable). There is nothing that Java can do about this (though an IDE could handle it for you by keeping the .class files away from the native FS, I don't know if any actually do and the JDK's javac most certainly isn't that smart).
However that's not the only point to note here: class files know internally what class they are talking about. The absence of the expected class from the file just means that the load fails, leading to the NoClassDefFoundError you received. What you got was a problem (a mis-deployment in at least some sense) that was detected and dealt with robustly. Theoretically, you could build a classloader that could handle such things by keeping searching, but why bother? Putting the class files inside a JAR will fix things far more robustly; those are handled correctly.
More generally, if you're running into this problem for real a lot, take to doing production builds on a Unix with a case-sensitive filesystem (a CI system like Jenkins is recommended) and find which developers are naming classes with just case differences and make them stop as it is very confusing!
Donal's fine explanation leaves little to add, but let me briefly muse on this phrase:
... Java classes with the same case-insensitive name ...
Names and Strings in general are never case-insensitive in themselves, it's only there interpretation that can be. And secondly, Java doesn't do such an interpretation.
So, a correct phrasing of what you had in mind would be:
... Java classes whose file representations in a case-insensitive file-system have identical names ...
I tried to add or remove a character from one of the class names and it worked. I feel it's always better to use different class names.
Don't think just about folders.
Use explicit different namespaces ("packages") for your classes, and maybe use folders to match your classes.
When I mention "packages", I don't mean "*.JAR" files, but, just the concept of:
package com.mycompany.mytool;
// "com.mycompany.mytool.MyClass"
public class MyClass
{
// ...
} // class MyClass
When you do not specify a package for your code, the java tools (compiler, I.D.E., whatever), assume to use the same global package for all. And, in case of several similar classes, they have a list of folders, where to look for.
Packages are like "virtual" folders in your code, and apply to all your packages on your classpath, or installation of Java. You can have several classes, with the same I.D., but, if they are in different package, and you specify which package to look for, you won't have any problem.
Just my 2 cents, for your cup of Java coffe

Where in the world is com.ibm.ws.scripting.adminCommand.AdminTask?

Frustrated with the damn awful API provided by WebSphere Admin Server, I'm writing my own Java DSL wrapper. My jython files now simply read:
from my.package import MyDSL
config = MyDSL(AdminConfig, AdminTask)
config.goGoGadgetSkates() # or something like that
The essential part is that I send through the (#%$$!##) god objects AdminConfig and AdminTask so that the DSL can use them to perform operations in WAS.
In order to compile the DSL I need to include the class files for this two objects. I find them by first setting the constructor as:
public MyDSL(Object a, Object b) {
System.out.println(a.getClass());
System.out.println(b.getClass());
}
The output showed that the AdminConfig object is an instance of com.ibm.ws.scripting.AdminConfigClient. I easily located the jar that contains this class and all is well.
But AdminTask is an instance of com.ibm.ws.scripting.adminCommand.AdminTask. Despite being present at runtime, this class does not exist anywhere in my classpath or indeed anywhere on my computer's hard drive.
I can only assume com.ibm.ws.scripting.adminCommand.AdminTask is constructed magically by WSAdmin in the jython layer. Perhaps it is defined as a python class?
Before I resort to reflection, can someone please explain where com.ibm.ws.scripting.adminCommand.AdminTask might live and how I might extract a copy of the class file?
The AdminConfigClient class is not API/SPI, so you are creating a fragile infrastructure by relying on that class. The API/SPI entry point is ConfigServiceFactory.
The AdminTask object is backed by the data in CommandMgr. It should be possible to use CommandMgr to do anything you can do with AdminTask.

Categories