Handling Java annotations in JSweet - jsweet

I'm attempting to transpile Java model classes to JavaScript using JSweet. The model classes contain JPA annotations like #Column. The transpilation fails as soon as it encounters import javax.persistence.Column.
The JPA annotations are irrelevant in JavaScript and should not be transpiled. Can this be done without changing the Java code?
More generally, is there a way to have JSweet ignore import statements, e.g., when all references to the imported packages are in #Erased methods?

Normally, JSweet just erases unknown annotations, so your code should transpile fine.
First thing to check: do you have a JPA jar in your classpath or in your Maven dependencies? JSweet uses javac, which requires all types to be in the classpath. I guess that the #Column annotation should be in there: https://mvnrepository.com/artifact/javax.persistence/persistence-api/1.0.2
As for the second part of your question, JSweet v2 provides an API to tune the generation of the code. See the specs. In the PrinterAdapter API, you can override the needsImport method to return null when the import is not needed. However I believe that you don't need this for your case since annotations are erased automatically.

Related

Spring AOP (AspectJ) with Kotlin and Gradle - I can't get it to work

I have a Spring Boot + Kotlin + Gradle project. I'd like to create a small library for my use-cases. This library should use AOP to remove some cross cutting concerns I observed.
Therefore I started adding these two dependencies to my gradle build file.
build.gradle.kts
implementation("org.springframework:spring-aop:5.2.9.RELEASE")
implementation("org.springframework:spring-aspects:5.2.9.RELEASE")
I also added the freefair aspectj plugin due some suggestions from the interwebs.
The following aspect I created should be placed in src/main/aspectj according to this documentation: https://docs.freefair.io/gradle-plugins/5.2.1/reference/#_io_freefair_aspectj
This plugin adds AspectJ support to the project, by adding a aspectj directory to every source set.
Source contained in the src/main/aspectj directory will be compiled with ajc by the compileAspectj task.
plugins {
// ...
id("io.freefair.aspectj") version "5.2.1"
// ...
}
I then started to create my first aspect that matches on every method which is annotated with #Foozy
src/main/aspectj/FoozyAspect.kt < the 'special' source path
#Component
#Aspect
class FoozyAspect {
#Before("#annotation(com.client.annotation.Foozy)")
fun doStuff() {
LOG.info("Do Stuff")
}
companion object {
private val LOG = LoggerFactory.getLogger(FoozyAspect::class.java)
}
}
Then I created this annotation
src/main/kotlin/com.client.annotation/Foozy.kt
#Target(AnnotationTarget.FUNCTION)
annotation class Foozy
Now to test if everything works as expected I created a unit test
src/test/kotlin/FoozyAspectTest.kt
#SpringBootTest
#EnableAspectJAutoProxy
internal class FoozyAspectTest {
private val testCandidate: TestCandidate = TestCandidate()
#Test
fun `should work with aspect`() {
testCandidate.doStuff()
}
}
src/test/TestCandidate.kt
class TestCandidate {
#Foozy
fun doStuff(): String {
return "stuff"
}
}
Result
Executing the text in debug mode does not yield the awaited info log Do Stuff and also does not cease the thread at the breakpoint in the FoozyAspect.kt doStuff() method.
I have no idea what to configure here.
For good reason I kinda have the suspicion that I am mixing up different "ways" to get this to work or am just missing some final steps in preconfiguration/prerequisites.
The AspectJ compiler can't compile Kotlin source code. Your .kt file in src/main/aspectj will be completely ignored.
You have different options depending on what you really want to do:
Do you want your Aspect to be woven by ajc at compile-time, or do you just want to use "plain" Spring AOP?
The differences are explained here: https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-choosing
If you just want to use Spring AOP, you dont need a special gradle plugin. Just put your .kt file in src/main/kotlin and follow the Spring AOP docs.
If you want to weave your aspect at compile-time with ajc, you have two options:
Keep the io.freefair.aspectj plugin to compile an weave the aspect in one step: Implement your aspect as .java or .aj so it can be compiled by ajc
Switch to the io.freefair.aspectj.post-compile-weaving plugin in order to separate the compilation an weaving steps: In this case you can keep your Aspect implementation as Kotlin, but you have to put it in src/main/kotlin. Your Aspect will then be compiled by kotlinc and then woven by ajc.
This looks like the 347th duplicate of a classical Spring AOP question: If you read the manual, you will notice that Spring AOP only works for Spring components, e.g. declared via #Component or #Bean.
Your TestCandidate seems to be a POJO, so Spring does not know about it. Also if you make it a component, make sure you get an instance from the container and do not just create one via constructor call in your test.

Spring AOP aspect doesn't get applied if included from an external jar with different package name

I have a spring boot rest service that included an external project in pom as it's dependency. That external project is basically a jar that has spring AOP code.
The base package in my main application that includes this external jar with spring AOP code is x.y.z
The class in external jar where the #before advice is, is under the package a.b.c
With this class under a.b.c package, it doesn't get recognized by the main application where I want to use the spring aop implementation and apply the aspect. However, when I change it's package from a.b.c to x.y.z (which I really can't do in real life) it works fine.
I know that in spring boot service which happens to be the including service, it scans everything under root package given in the application class, x.y.z in this case and that is why aspect works fine if it's class is under x.y.z.
however, the problem is that this spring app jar will be used across multiple applications. So changing package name like this is not an option.
Is there a way to accomplish this without changing the package name of the class where spring app code is ?
Probably component scan is only activated for your application class packages by default. You can extend it to multiple packages, including the aspect package:
XML style configuration:
<context:component-scan base-package="x.y.z, a.b.c" />
Annotation style configuration:
#ComponentScan(basePackages = {"x.y.z", "a.b.c"})
Disclaimer: I am not a Spring user, only an AspectJ expert. I just knew that you can configure component scan, googled the syntax for you and hope it is correct.
Please define the bean (of jar project )inside main application. Give the #ComponentScan(basePackages = {"x.y.z", "a.b.c"}) as well as #EnableAspectJAutoProxy. Also include below piece of code.
ex:
` #Bean
public LoggingHandler loggingHandler()
{
return new LoggingHandler();
}`
Also annotate external jar code with:
`#Aspect
#Component
public class LoggingHandler {`
What #kriegaex suggests is correct. In addition to that, please make sure you are using #Component along with #Aspect. Since #Aspect is not a Spring annotation, Spring won't recognize it and hence your aspect won't be registered. So, using #Component is mandatory to getting aspects to work in Spring environment.

Import by name in Spring Java configs

Say I have 3 Spring/Maven projects:
api-spec: Contains interface MyService.
api-impl: Contains class MyServiceImpl which implements MyService. Also contains class MyServiceConfiguration which is a Spring #Configuration, that defines a bean of type MyServiceImpl.
main: Contains a Spring application setup with Spring JavaConfig (e.g. a #SpringBootApplication). It has a bean with an #Autowired MyService myService field, which works as its configuration class is annotated with #Import(MyServiceConfiguration.class).
I would like the main-project to have api-spec as a Maven compile dependency and to have api-impl as a runtime dependency (to prevent us from making "hard" dependencies from the main project to the api-impl project by mistake). This is not possible, because #Import takes an array of Classes - e.g.: #Import(MyServiceConfiguration.class). I would like something like #Import("my.package.MyServiceConfiguration") instead.
Using class path scanning is not an option (we have seen too many beans getting picked up by accident), and I would prefer not having to use XML files. We could use SpringApplicationBuilder.source(..) as it accepts a class name as a String - but I can't find a way to use that in my tests...
Compile time check is one of the advantages of java config, so I don't think that it's possible to do such thinks with Java. As for me you should use XML to handle this. It doesn't mean that you should do all your configuration in XML, most of the beans of your api-impl module can be in Java and just imported to XML where will be only beans that you are going to change in runtime.
If you don't want to use XML maybe you should consider to use Groovy config instead:
https://spring.io/blog/2014/03/03/groovy-bean-configuration-in-spring-framework-4

Can I start with a spring boot application without the annotations componentscan,autoconfiguration,configuration,springbootapplication?

I have written some code in order test integration with mongoDB. Please find the link to the main method for running this spring boot application below,
https://github.com/siva54/simpleusercontrol/blob/master/src/main/java/com/siva/UserManagementApplication.java
From what I have read, An application should contain any of the configurations from the following URL to declare how the applications manages the context,
http://docs.spring.io/autorepo/docs/spring-boot/current/reference/html/using-boot-using-springbootapplication-annotation.html
I haven't used any of those contexts, However my application works fine and I'm able to run it without any issues. Am I missing something here? Can you please help with the info of how my application is able to start and manage the context/dependencies automatically?
Thanks in advance
#SpringBootApplication is equivalent of #Configuration, #EnableAutoConfiguration and #ComponentScan. Let's consider why your application works without of any of this three annotations.
Why it works without #Configuration:
When Spring will scan packages, it will find all classes marked by #Configuration and will use them as part of configuration. But in next line you manually passed UserManagementApplication as configuration source:
SpringApplication.run(UserManagementApplication.class, args);
So spring doesn't need to find this class by scan. Therefor it is not necessary to mark it by #Configuration.
Why it works without #ComponentScan:
Class UserManagementApplication has #ImportResource("classpath:spring/application-context.xml") annotation. That means file spring/application-context.xml will be included into configuration. And this file contains next line:
<context:component-scan base-package="com.siva.*" />
So, you don't need use annotation for scan packages, because you already declared it in the xml file.
Why it works without #EnableAutoConfiguration:
This annotation allows to Spring to try guess and configure the components automatically. For example, if you include the following dependency in your build.gradle:
dependencies {
compile 'org.springframework.boot:spring-boot-starter-data-mongodb'
}
Spring configures all the components required to work with MongoDB automatically. And all you need just specify host and user/pass in the aplication.proprties file.
But you preferred to declare all needed beans manually in the spring/application-context.xml file. So, you simply don't need #EnableAutoConfiguration annotation at all.

Combine two maven based projects on two frameworks

I have two maven projects say MvnSpring and MvnGuice.MvnSpring is working on spring and hibernate frame works.
And MvnGuice is working on google guice and mybatis. I need to combine both the features together.
Both are following singleton pattern. I need to get some class of MvnSpring in MvnGuice while coding. So that I created a jar of MvnSpring and put it in .m2 repository and give the dependacy details in MvnGuice. Now I can import classes of MvnSpring in MvnGuice classes.MvnSpring uses spring dependency injection and MvnGuice uses guice dependency injection for object creation. Now in MvnSpring flow is MSserviceImpl(implements MSservice) > MSdaoImpl(implements MSdao). Now I need to call MSService class from MvnGuice. Then at run time it shows error like MSService class is null. Then I made a guice dependency injection for MSService class in MvnGuice. Now the control reaches MSserviceImpl but now MSdao is null at here. Is it possible to start MvnSpring along with MvnGuice. I hope then I can solve the issue.
While Spring and Guice are targeted at the same problem, IoC, they take very different approaches to solve it. They differ both in functionality and in how they are configured, where Spring has bean definitions and Guice uses bindings.
Fortunately they do have common grounds in that they both support JSR-330, a standards specification that defines a set of annotations. This enables you to write your singletons and describe the injections that they need without depending on either Spring or Guice.
This way you can share your singletons between projects irregardless of the framework you use in a particular project. I would not recommend using both Guice and Spring in the same project, except if there's a clearly defined separation between them. For instance you might use Guice for a module that is used by Spring code via a defined API that hides the fact that it internally is based on Guice.
There was already mentioned JSR-330.
For some cases it can be not enough, e.g., you have code:
final String className = config.getProperty(«serviceImpl»);
// Class.forName(name) and check required interface for type safety
final Class<? extends Service> serviceClass = Reflection.classForName(className, Service.class);
final Service service = injector.getInstance(serviceClass);
In different DI environments you are supposed to support both com.guice.inject.Injector.getInstance() and org.springframework.context.ApplicationContext.getBean() implementations.
There is the draft solution sdif4j Simple Dependency Injection Facade.
The idea of this project is to encapsulate different DI frameworks logic with own abstraction to extend default JSR-330 possibilities. Note, there is no public releases yet, but you can find ideas how to solve your problem or make an internal release in a fork.
The general issue, is that your both MvnSpring and MvnGuice projects are supposed to be based on JSR-330 (instead of guice/spring annotations) and org.sdif4j:sdif4j-api (or your own abstraction; only if Injector functionality is required). It is recommended to make guice and spring dependencies optional (to compile but not export) to allow the library clients to choose the DI themselves.
In your MvnCompineGuiceAndSpring you just declare sdif4j-guice or sdif4j-spring dependency (it is similar to slf4j usage) and configure your DI environment. You can find different examples in testing subproject.
Some more notes:
Spring default scope is singleton, Guice - prototype (Spring terminology). So, if you want a prototype bean, you can use:
#org.springframework.context.annotation.Scope("prototype")
#javax.inject.Named
public class TestPrototype {
}
The Spring #Scope annotation should be ignored by guice even if spring does not present in your classpath.
Also you have to declare all your Singleton beans with #javax.inject.Named and #javax.inject.Singleton annotation to support both Spring and Guice, like this:
#javax.inject.Named
#javax.inject.Singleton
public class TestSingleton implements ITestSingleton {
public TestSingleton() {
}
}
As with #Scope annotation, you can use #ImplementedBy(#ProvidedBy) guice annotations on your code (when feasible; be careful with it, in general it is not a good practice), that should be also ignored in Spring DI (in both cases if Spring exists in classpath or not).
Hope, that's clear.

Resources