javassist compilation error no such class - compilation

I'm writing a program using javassist to compile another Java class. The generated class use some objects like BigDecimal, List, ArrayList. So I import their packages:
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(classDir); //classDir is my program Directory
pool.importPackage("java.util.List");
pool.importPackage("java.math.BigDecimal");
pool.importPackage("java.util.ArrayList");
Then I make some objects using CtField.make() for each object. When I use javassist to compile, it throws error:
CannotCompileException: [source error] no such class: BigDecimal
List is working fine, however, BigDecimal or ArrayList aren't. Is there any clue for this problem? Thanks!

As the name implies, ClassPool.importPackage() (JavaDoc) is for importing packages, not classes. Considering that, you should use:
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(classDir); //classDir is my program Directory
pool.importPackage("java.util");
pool.importPackage("java.math");
Note: starting from Javassist 3.14, it does support importing also fully-qualified-class-names. So with that version, your original code should also work.

changing from new BigDecimal() by adding exact classPath as new java.math.BigDecimal() solves the problem!

Related

kotlin sealed class with Spring #Component

I am relatively new to kotlin but already loving it. In one of our projects, we use kotlin; when I tried to annotate a sealed class with Spring's #Component, the compiler threw the following exception,
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'ShutDownManager' available
The simple shutdown manager class
package com.tes.streamconsumer.stream.processor
#Component
sealed class ShutDownManager(
#Autowired private val applicationContext: ApplicationContext
) {
fun shutDownApplication() {
SpringApplication.exit(applicationContext)
}
}
That is Autowired in another class,
package com.tes.streamconsumer.stream.processor
#Component
class AccountFacade(
#Autowired private val shutDownManager: ShutDownManager
) {
}
From the Kotlin documentation on sealed class, I understand this is useful to have restricted class hierarchies that provide more control over inheritance, so my questions below,
Is the sealed class not meant to be used with spring injection
or the ApplicationContext not ready hence the bean was not created?
Please shed light on what I miss here; thanks.
Your problem is nothing to do with the sealed class but elsewhere. Typically this kind of error occurs because Spring is not scanning your code looking for Beans in the way you expect.
You have correctly annotated your ShutDownManager class with #Component but you don't give enough information on your package structure.
This is the right kind of package structure for a Spring project:
com.mydomain.myapp
.facades
.AccountFacade.kt
.managers
.ShutDownManager.kt
.MyApp.kt
What is important is the Spring entrypoint class is higher than all the packages where you declare your Beans. The default behaviour of Spring is to Scan the packages below looking for Components/Services/etc. (You can override the behaviour to scan packages, etc explicitly, but my general preference is to locate the entry point for your application at the top of the tree on its own so it is easy to find in the tree structure and then everything beneath.)
One other word of caution is that in Java the package structure is intrinsically linked to the file system folder structure - you must keep them matched. There is no such restriction in Kotlin. I recommend not making use of this, since many Java devs will use the folder structure and never notice the package declaration differs; this could also be the source of Spring not finding your Beans.
Use of sealed classes/interfaces
I guess you might be thinking of using sealed to protect your ShutDownManager from being subclassed or overriden, but actually by default Kotlin makes all classes final. (You have to explicitly permit subclassing using the open keyword.)
sealed classes have some specific benefits in other places - most often when you are creating data objects, say Apple and Pear that implement/extend from Fruit. You can then write code that knows that there can only be two fruits if you had said sealed class Fruit. In Kotlin there is a when statement that's like Java's switch...case, and the compiler would know there is no need for an else if you were using a sealed Fruit class. See this article:
https://commonsware.com/Kotlin/pages/chap-sealed-002.html

Problem with EntityScan Spring anotation. It stops working when moving model classes to new package

This is a strange case, I think. Certainly a fringe problem, but I don't know exactly where it is or if it's a spring problem or an IntelliJ problem or even a user problem.
Here's the story:
I have a spring boot app that uses spring data and works just fine.
Running this configuration, it runs great on IntelliJ:
#EntityScan(basePackages = {"com.legosoft.disperser.event.model"})
#SpringBootApplication(scanBasePackages = {"com.legosoft.disperser.event.bean"})
#EnableJpaRepositories(basePackages = "com.legosoft.disperser.event.bean.repositories")
One of the repositories I use is this one, and again, with the above configuration everything is ok:
package com.legosoft.disperser.event.bean.repositories;
import com.legosoft.disperser.event.model.FileConfiguration;
import com.legosoft.disperser.event.model.FileConfigurationId;
import org.springframework.data.jpa.repository.JpaRepository;
public interface FileConfigurationDao extends JpaRepository<FileConfiguration, FileConfigurationId> {
}
We needed to rename certain elements of the application, among other things, the packages and modules. So today I get into the office thinking that it'd be at most a couple hours worth of work and that only because there's a lot of documentation to write, thinking that the IDE's refactor -> rename option would actually do the heavy lifting for me.
So, I renamed the model package using the afore mentioned option to
com.legosoft.fileengine.core.model
which left my Application config like so:
#EntityScan(basePackages = {"com.legosoft.fileengine.core.model"})
#SpringBootApplication(scanBasePackages = {"com.legosoft.disperser.event.bean"})
#EnableJpaRepositories(basePackages = "com.legosoft.disperser.event.bean.repositories")
and the previously shown repository changed its imports accordingly:
package com.legosoft.disperser.event.bean.repositories;
import com.legosoft.fileengine.core.model.FileConfiguration;
import com.legosoft.fileengine.core.model.FileConfigurationId;
import org.springframework.data.jpa.repository.JpaRepository;
public interface FileConfigurationDao extends JpaRepository<FileConfiguration, FileConfigurationId> {
}
At first glance, everything was ok, it was just a package change. Everything compiled fine, but upon trying to run the application I got:
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.legosoft.fileengine.core.model.FileConfiguration
at org.hibernate.metamodel.internal.MetamodelImpl.managedType(MetamodelImpl.java:552)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:74)
at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getEntityInformation(JpaEntityInformationSupport.java:66)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:201)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:151)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:134)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:65)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:305)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:297)
at org.springframework.data.util.Lazy.getNullable(Lazy.java:211)
at org.springframework.data.util.Lazy.get(Lazy.java:94)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:300)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:121)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774)
... 42 common frames omitted
Now, here's the part I don't understand. Debugging into the dependencies I found this bit of code that produces the exception in the org.hibernate.metamodel.internal.JpaMetamodelEntityInformation class:
public <X> ManagedType<X> managedType(Class<X> cls) {
ManagedType<?> type = (ManagedType)this.jpaEntityTypeMap.get(cls);
if (type == null) {
type = (ManagedType)this.jpaMappedSuperclassTypeMap.get(cls);
}
if (type == null) {
type = (ManagedType)this.jpaEmbeddableTypeMap.get(cls);
}
if (type == null) {
throw new IllegalArgumentException("Not a managed type: " + cls);
} else {
return type;
}
Where from my debugging I've determined that the class it looks for is correct (it gets the class object in its new package) but it seems the entity scanning stops working because all maps used to look for the class in the entity model are empty (jpaEntityTypeMap, jpaMappedSuperclassTypeMap or jpaEmbeddableTypeMap) and literally the only thing that's changed is that the classes changed package.
Now, I don't know if its a problem inherent to the refactor->rename option in IntelliJ since there appears to be nothing obviously wrong (the project compiles, the references are correctly updated) but clearly something strange happened because there's no actual scanning going on, or perhaps I'm not using the tool correctly or maybe there's actually something I've done incorrectly with Spring.
I know the problem is a product of this refactor, because as soon as I revert it the project goes back to working fine, repositories and all. Does anybody know if the problem is IntelliJ, something I haven't yet seen on the Spring side of things or something I'm doing wrong to work the refactor maybe?
Any details that clear up this mess would be greatly appreciated. It's had me grumbling all day and for something that seemed so simple!
Thanks in advance.
Found the problem. Even if you mark "refactor all places" on the refactor -> rename dialog, the package name in entitymanager.packagesToScan in application.properties was not changing.
Since the package path in the annotation in the config application class WAS changing, it never crossed my mind to check the properties file.

Inject code into method with Javassist

I'm trying to add some code to a class that is inside a jar(maven dependecy) and i'm doing it in the following way:
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.get("xyz.abc.ClassInADependecy");
CtMethod method = ctClass.getDeclaredMethod("getSomeValue");
method.insertBefore("{ System.out.println(\"modified\"); }");
I'm using Spring and the above code is being called using a #Configuration annotation.
When i call the method getSomeValue nothing is printed.
Can you help me find out what i'm doing wrong?
Thank you very much.
You are only changing the implementation as it is represented in Javassists type pool. You have to make sure that the class is also loaded by the respective class loader. Also, this must happen before the class is loaded for the first time, i.e. before your Spring application loads that class.
One way to do so is to manipulate the class from a Java agent: https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package-summary.html

Neo4j/SDN warining: No identity field found for class of type for exception class

In my Neo4j/Spring Data Neo4j project I have a following exception class:
public class CriterionNotFoundException extends NotFoundDomainException {
private static final long serialVersionUID = -2226285877530156902L;
public CriterionNotFoundException(String message) {
super(message);
}
}
During application startup I see a following WARN:
WARN o.s.d.n.m.Neo4jPersistentProperty - No identity field found for class of type: com.example.domain.dao.decision.exception.DecisionAlreadyExistsException when creating persistent property for field: null
Why Neo4j/SDN is looking for identity field in this class ? How to correctly configure my application in order to skip this warning ?
You can ignore this warning- this is produced by SDN when building metadata Spring Data REST integration. It should not be doing this for Exceptions of course, and we'll have this fixed.
One way "to correctly configure [your] application" would be add EnableNeo4jRepositories and EntityScan annotations to your SpringBootApplication (or your config bean) as mentioned here and specify the names of your packages with Neo4J relevant classes.
I've debugged the SDN/Neo4j code only for 5 minutes, so my guesses may be off, but, I believe those warnings are generated when you don't specify packages to scan for your entities, and repositories. I'm guessing in that case SpringBoot+Neo4J-mapping scans each and every class in your project, and if a class has some fields, but nothing resembling an "id" field, it spits this warning. (So adding a Long id field to the classes with warnings may be another (yes, very ugly) work-around as well)
I've seen those warnings vanished when I tried explicitly specifying package names in my project using SpringBoot 2.0.6 + spring-data-neo4j 5.0.11.

Hibernate and JPA error: duplicate import on dependent Maven project

I have two Maven projects, one called project-data and the other one call project-rest which has a dependency on the project-data project.
The Maven build is successful in the project-data project but it fails in the project-rest project, with the exception:
Caused by: org.hibernate.DuplicateMappingException: duplicate import: TemplatePageTag refers to both com.thalasoft.learnintouch.data.jpa.domain.TemplatePageTag and com.thalasoft.learnintouch.data.dao.domain.TemplatePageTag (try using auto-import="false")
I could see some explanation here: http://isolasoftware.it/2011/10/14/hibernate-and-jpa-error-duplicate-import-try-using-auto-importfalse/
What I don't understand, is why this message does not occur when building the project-data project and occurs when building the project-rest project.
I tried to look up in the pom.xml files to see if there was something in there that could explain the issue.
I also looked up the way the tests are configured and run on the project-rest project.
But I haven't yet seen any thing.
The error is basically due to the fact that the sessionFactory bean underlies two entities with the same logical name TemplatePageTag :
One lies under the com.thalasoft.learnintouch.data.jpa.domain package.
The other under the com.thalasoft.learnintouch.data.dao.domain.
Since this fall to an unusual case, you will have Hibernate complaining about the case. Mostly because you may run in eventual issues when running some HQL queries (which are basically entity oriented queries) and may have inconsistent results.
As a solution, you may need either to:
Rename your Entity beans with different name to avoid confusion which I assume is not a suitable solution in your case since it may need much re-factoring and can hurt your project compatibility.
Configure your EJB entities to be resolved with different names. As you are configuring one entity using xml based processing and the other through annotation, the schema is not the same to define the entities names:
For the com.thalasoft.learnintouch.data.jpa.domain.TemplatePageTag entity, you will need to add the name attribute to the #Entity annotation as below:
#Entity(name = "TemplatePageTag_1")
public class TemplatePageTag extends AbstractEntity {
//...
}
For the com.thalasoft.learnintouch.data.dao.domain.TemplatePageTag, as it is mapped using an hbm xml declaration, you will need to add the entity-name attribute to your class element as follows:
<hibernate-mapping>
<class name="com.thalasoft.learnintouch.data.dao.domain.TemplatePageTag"
table="template_page_tag"
entity-name="TemplatePageTag_2"
dynamic-insert="true"
dynamic-update="true">
<!-- other attributes declaration -->
</class>
</hibernate-mapping>
As I took a look deeper into your project strucure, you may need also to fix entity names for other beans as you have been following the same schema for many other classes, such as com.thalasoft.learnintouch.data.jpa.domain.AdminModule and com.thalasoft.learnintouch.data.dao.domain.AdminModule.
This issue could be fixed by using a combination of #Entity and #Table annotations. Below link provides a good explanation and difference between both.
difference between name-attribute-in-entity-and-table

Resources