Spring Boot - Spring Data REST generate swagger documentation from repository - maven

I have an application in Spring Boot with Spring Data Rest and I am trying to generate the documentation with Swagger using swagger-maven-plugin. The Controller documentation is generated without problems but the repository does not.
I have configured the swagger-maven-plugin of the following form in my pom.xml:
<plugin>
<groupId>com.github.kongchen</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<version>3.1.6</version>
<configuration>
<apiSources>
<apiSource>
<springmvc>true</springmvc>
<locations>
<location>com.abelendo.repository</location>
<location>com.abelendo.controller</location>
</locations>
<schemes>http</schemes>
<host>localhost:8080</host>
<basePath>/</basePath>
<info>
<title>Swagger Maven Plugin Spring Boot for cars</title>
<version>v1</version>
<description>Working sample of Spring Boot for cars annotations</description>
<termsOfService>
http://www.github.com
</termsOfService>
<contact>
<email>abelendo#email.com</email>
<name>Abelendo Cars</name>
<url>http</url>
</contact>
<license>
<url>http://www.license.com</url>
<name>License name</name>
</license>
</info>
<!-- Support classpath or file absolute path here.
1) classpath e.g: "classpath:/markdown.hbs", "classpath:/templates/hello.html"
2) file e.g: "${basedir}/src/main/resources/markdown.hbs",
"${basedir}/src/main/resources/template/hello.html" -->
<templatePath>${basedir}/templates/strapdown.html.hbs</templatePath>
<outputPath>${basedir}/generated/document.html</outputPath>
<outputFormats>yaml</outputFormats>
<swaggerApiReader>com.github.kongchen.swagger.docgen.reader.SpringMvcApiReader</swaggerApiReader>
<swaggerDirectory>generated/swagger-ui</swaggerDirectory>
</apiSource>
</apiSources>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
My CarRepository:
#Api(tags = "CarsRepo")
#RepositoryRestResource(path = "cars")
public interface CarRepository extends CrudRepository<Car, Long> {
<S extends Car> S save(#Valid S cars);
}
Is it possible to generate the repository documentation with swagger-maven-plugin?

In order to generate swagger documentation for a #RepositoryRestResource, all you need to do is:
Pull the io.springfox:springfox-data-rest dependency in your project
Import the springfox.documentation.spring.data.rest.configuration.SpringDataRestConfiguration class in your swagger configuration class.
#Configuration
#EnableSwagger2
#Import(SpringDataRestConfiguration.class)
public class SwaggerConfig {
#Bean
public Docket api() { ... }
}
Edit: the swagger config class doesn't work with spring-boot 2.0, spring-data 2.0 and spring-data-rest 3.0: there's an open issue in swagger (linked to swagger not being Java8 while spring-boot and spring-data are). See here for details: https://github.com/springfox/springfox/issues/2298.
However, I managed to solve it by patching a few swagger classes. The idea behind the patch is to use Java Optional instead of Guava and Java reflection. The patched classes are:
springfox.documentation.spring.data.rest.EntityServicesProvider
springfox.documentation.spring.data.rest.EntityContext
springfox.documentation.spring.data.rest.EntityDeleteExtractor
springfox.documentation.spring.data.rest.EntityFindAllExtractor
springfox.documentation.spring.data.rest.EntityFindOneExtractor
springfox.documentation.spring.data.rest.EntitySaveExtractor

I just 1 dependency in pom.xml
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-data-rest -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-data-rest</artifactId>
<version>3.0.0</version>
</dependency>
Then import #Import(SpringDataRestConfiguration.class) in Respository
and it generates documentation

Related

Use AspectJ aspects in Spring project

I am working on a large Spring Boot application and want to introduce an apsect. As I need "if()" pointcut designators, I need more than Spring AOP. But:
- I don't explicitly define my Aspect as a Spring Aspect, yet it seems to be handled by Spring
-If I define an aspect in a ".js" file, it is not found by the weaver.
I followed the instruction in https://www.baeldung.com/aspectj very closely. Basically I use a runtime weaver plugin for my maven project and register the aspect in an "aop.xml" file.
How can I separate the life cylce of my aspect from Spring?
package myApp;
public aspect MyAspect {
pointcut prepInFile() :
execution(public void MySpringBootApp.execute());
after(): MyMethod() {
System.out.println("*** I am here ***");
}
}
<aspectj>
<aspects>
<aspect name="myApp.MyAspect"/>
<weaver options="-verbose -showWeaveInfo">
<include within="myApp"/>
</weaver>
</aspects>
</aspectj>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<configuration>
<argLine>
-javaagent:"${settings.localRepository}"/org/aspectj/
aspectjweaver/${aspectj.version}/
aspectjweaver-${aspectj.version}.jar
</argLine>
<useSystemClassLoader>true</useSystemClassLoader>
<forkMode>always</forkMode>
</configuration>
</plugin>
</plugins>
</build>
First of all, AspectJ source files are not JavaScript, i.e. they don't have a *.js file extension, rather *.aj.
Then, instead of #EnableAspectJAutoProxy (which activates proxy-based Spring AOP) you need to to what is describes in the Spring manual section of AspectJ, e.g. use #EnableLoadTimeWeaving as described in the section about load-time weaving.
Please also note that native syntax AspectJ files need to be compiled by the AspectJ compiler. For that you want to use the AspectJ Maven plugin.

Adding Spring (Boot?) to existing RESTEasy JAX-RS application

I have an existing Maven project based on JAX-RS using RESTEasy. It works great! It creates a WAR that I deploy to Tomcat running on Ubuntu. It's clean and follows the latest standards. The POM is simple:
...
<packaging>war</packaging>
...
<dependencies>
...
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>3.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-servlet-initializer</artifactId>
<version>3.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>3.1.0.Final</version>
</dependency>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
...
I don't need any web.xml because I'm using the latest Java EE annotations:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/")
public class MyRESTApplication extends Application {
final FooResource fooResource = new FooResourceService();
...
#Override
public Set<Object> getSingletons() {
return ImmutableSet.of(fooResource);
}
}
This is all simple and it's working so great! Now I just want a way to easily change FooResource implementations based on the profile --- in other words, wire my singletons. That's where Spring comes in, right? And I've been told that Spring Boot makes Spring even more awesome, and you can use it with anything, and it gives you an actuator that allows you to gain real-time inside on the health of your system.
Unfortunately all the Spring Boot books and tutorials seem to think I'm starting with one of their quick-start applications. But I already have a great, simple application. I just want to:
Get my application wiring, based on profiles, from an external configuration file (not annotations) via Spring.
Get whatever other goodness comes from Spring Boot, because apparently it is awesome and will completely transform my application.
How do I add Spring (or Spring Boot) to this simple little JAX-RS application?
we solved it that way, that we created a singleton spring bean, let's call it ServiceStartupClass, where we register all JAX-RS services.
Here some code snippet how we start our services:
import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;
#Component
#ApplicationPath("/")
public class ServiceStartupClass extends ResourceConfig {
#PostConstruct
public void startup() {
register(FooResource.class);
...
}
}
If you need any further help, let me know

Cannot start an OSGI bundle that embed spring boot in liferay 7

I want to develop a standalone bundle that implement a service using spring boot and spring data jpa (without web).
The bundle aims to create a spring context to facilitate the creation of Repository, and in teh bundle activator, I create the spring boot application, get an implementation of service that use the injected repository and this service will be registered as an OSGI service.
The bundle will be deployed on Liferay 7 so there is no ready bundles to help exporting packages (for jpa ...), to make simpler the idea is to have a standalone bundle that embed all dependencies in the bundle classpath (no package to import from outside the bundle)
Is there any sample that can help ? and is that a good idea ?
The problem was, when trying to start the bundle, it fails with "Caused by: java.lang.ClassNotFoundException: org.osgi.framework.BundleActivator .."
The following classes are simplified sampel to demonstates the problem (normally it must be a separate bundle that define and export the api that will be implemented by the bundle in question, but in this sample this is a unique bundle with 4 classes)
1/ The bundle activator class
import java.util.Hashtable;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.springframework.context.ApplicationContext;
public class Activator implements BundleActivator
{
#Override
public void start(BundleContext context) throws Exception
{
ApplicationContext springCtx = SpringFramework.getContext();
UserDao dao = springCtx.getBean(UserDao.class);
userDaoReg = bc.registerService(UserDao.class.getName(), dao, new Hashtable());
}
#Override
public void stop(BundleContext context) throws Exception
{
/** **/
}
}
2/ class to launch the spring boot application
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
#SpringBootApplication
public class SpringFramework {
private static ConfigurableApplicationContext context;
public static void main(String[] args) {
context = SpringApplication.run(SpringFramework.class);
}
public static ConfigurableApplicationContext getContext()
{
if (context == null) {
context = SpringApplication.run(SpringFramework.class);
}
return context;
}
}
3/ The UserDao to be registered as a service
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserDao extends JpaRepository<User, Integer>
{
}
4/ and a simple JPA Entity class "User"
And these are dependencies and plugin used in the pom.xml
<dependencies>
<!--OSGI dependencies-->
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<scope>provided</scope>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--persistence-api -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.3.0.RELEASE</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
<configuration>
<instructions>
<Import-Package>!*</Import-Package>
<Bundle-Activator>hello.Activator</Bundle-Activator>
<Embed-Dependency>*</Embed-Dependency>
<!--<Embed-Transitive>true</Embed-Transitive>-->
</instructions>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
This is the generated manifest in the jar
Manifest-Version: 1.0
Bundle-SymbolicName: test-spring-boot-no-web
Archiver-Version: Plexus Archiver
Built-By: XXX
Bnd-LastModified: 1475774161783
Bundle-ManifestVersion: 2
Embed-Dependency: *
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
Spring-Boot-Version: 1.3.0.RELEASE
Tool: Bnd-3.2.0.201605172007
Main-Class: org.springframework.boot.loader.JarLauncher
Embedded-Artifacts: org.osgi.core-6.0.0.jar;g="org.osgi";a="org.osgi.c
ore";v="6.0.0",slf4j-api-1.7.13.jar;g="org.slf4j";a="slf4j-api";v="1.
7.13",spring-boot-starter-1.3.0.RELEASE.jar;g="org.springframework.bo
ot";a="spring-boot-starter";v="1.3.0.RELEASE",spring-boot-starter-dat
a-jpa-1.3.0.RELEASE.jar;g="org.springframework.boot";a="spring-boot-s
tarter-data-jpa";v="1.3.0.RELEASE",persistence-api-1.0.2.jar;g="javax
.persistence";a="persistence-api";v="1.0.2",javax.transaction-api-1.2
.jar;g="javax.transaction";a="javax.transaction-api";v="1.2"
Export-Package: hello;version="1.0.0"
Bundle-Name: spring-boot-no-web
Bundle-Version: 1.0.0.SNAPSHOT
Bundle-ClassPath: .,org.osgi.core-6.0.0.jar,slf4j-api-1.7.13.jar,sprin
g-boot-starter-1.3.0.RELEASE.jar,spring-boot-starter-data-jpa-1.3.0.R
ELEASE.jar,persistence-api-1.0.2.jar,javax.transaction-api-1.2.jar
Bundle-Activator: hello.Activator
Start-Class: hello.SpringFramework
Created-By: Apache Maven Bundle Plugin
Build-Jdk: 1.8.0_101
I saw 2 questions in your post so I'll try to answer those:
Is there any sample that can help ?
I don't think so! What you are trying to do seems weird to me. See below for details.
... is that a good idea ?
You are saying you want "standalone bundle" that "will be deployed on Liferay 7"! It may be you just picked the wrong words but the way you state it, you are trying to have mutually exclusive things.
There is no such thing as "standalone bundle". I assume you mean standalone java application (executable Jar file that has the OSGi framework embedded). You can build such applications in a number of different ways. For example there is excellent tutorial how to do this from EnRoute. You can not however deploy such executable jar as it typically is not a OSGi bundle. While technically you can make it a bundle, you may run into all kinds of issues due to the embeded runtime and dependencies.
In Liferay 7 (and any outher product that has OSGi container) you can run a bundle in the runtime environment the product defines. The bundle must be resolvable at runtime. It may have all it's dependencies embedded but that defeats the purpose of modularity unless it provides something to outher bundles (which does not seem to be your case).
From that perspective what you are trying to do seems to be a bad idea. Moreover Spring Boot is a framework to build stand alone java applications and as such have it's own assumptions. Making it work inside an OSGi container is likely not a trivial task (if at all possible)
Perhaps better idea would be to have some bundles providing the business logic only. Then you could deploy those bundles in Liferay and laverage Liferay's capabilities to serve REST servces. You could use the exact same bundles to cunstruct standalone application that uses Spring or CXF or something else.
Notes about your code
your main class is org.springframework.boot.loader.JarLauncher. If you run this jar it will start Spring which likely will be totally unaware of OSGI runtime.
you have org.osgi.core-6.0.0.jar is your bundle's classpath with is basically the OSGi's runtime. This will cause issues if deployed into a OSGi runtime and my be the reason why you see
Caused by: java.lang.ClassNotFoundException: org.osgi.framework.BundleActivator ..

aspectj maven joinpoint advised but advice not called

Before marking this question as a duplicate, please read through, as
I've gone through a number of posts on SO and other places but have
still failed to find a solution to my problem.
I'm trying to implement a project in Spring + AspectJ and as the title says, I can see the aspectj maven plugin applying the advice but it isn't actually called.
I'm trying to apply an advice based on an annotation.
Following is the annotation class:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface LogThis {
String name();
int id();
int eventID();
}
This annotation has been applied to a method which is being called from a js script on the front end through an ajax call. The method being called is as follows:
#RequestMapping(value = "/handle", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.NO_CONTENT)
#LogThis(name = "name", ID = 12345, eventID = 12345)
public void create(#RequestBody TodoDTO todo, HttpServletRequest req) {
//perform some action
}
The advice is being applied to the LogThis annotation and the advice is as follows:
#Pointcut("#annotation(LogThis)")
public void genericPointcut() {
}
#Pointcut("execution(* *(..))")
public void atExecution() {
}
#Async
#Before("genericPointcut() && atExecution()")
public void logBefore(JoinPoint joinPoint) throws NoSuchMethodException, SecurityException {
// Perform logging
}
The annotation class and the aspect are in the same package while the class which is being advised is in a different package but everything is in the same project.
I've configured my maven pom.xml file as follows (only relevant portions shown):
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<configuration>
<showWeaveInfo>true</showWeaveInfo>
<source>${java.source-target.version}</source>
<target>${java.source-target.version}</target>
<Xlint>ignore</Xlint>
<complianceLevel>${java.source-target.version}</complianceLevel>
<encoding>${project.build.sourceEncoding}</encoding>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.2</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
The properties are:
<properties>
<sonar.language>java</sonar.language>
<java.source-target.version>1.7</java.source-target.version>
<aspectj.version>1.8.7</aspectj.version>
</properties>
The aspectj dependencies are:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
Spring version being used is 4.1.3.RELEASE.
The spring config entry for my aspect is as follows:
<aop:aspectj-autoproxy />
<bean id="logAspect" class="somep2.LoggingAspect" />
On running mvn clean install the build succeeds and the following log entry is present w.r.t aspectj:
[INFO] --- aspectj-maven-plugin:1.8:compile (default) # myproj ---
[INFO] Showing AJC message detail for messages of types: [error, warning, fail]
[INFO] Join point 'method-execution(void somep.SomeC.create(param1, param2))' in Type 'somep.SomeC' (SomeC.java:63) advised by before advice from 'somep2.LoggingAspect' (LoggingAspect.java:36)
[INFO]
[INFO] --- aspectj-maven-plugin:1.8:test-compile (default) # myproj ---
[WARNING] No sources found skipping aspectJ compile
According to the logs, the join point was found and advised but the logBefore() method is never called when the create() method is called. I am certain of this because I'm writing to a file using a FileWriter but nothing is being written.
Deployment details:
This project is built as part of another project which creates an ear file which is then deployed on JBoss 6.3
I've tried numerous approaches but nothing has worked. Please let me know what I'm missing.
Any help is appreciated.
I finally got it to work. Turns out I was complicating things too much. There was no need for the aspectj compiler. All that I had to do was tell spring that my aspect was a component as well so that it could inject my advice.
So here's the complete list of changes:
Removed the aspectj-maven-plugin from pom.xml. It is not required.
Removed the aspectj-tools and spring-aop dependencies and added the aspectj-weaver dependency to pom.xml
Removed the bean entry for my aspect from the spring config.
Annotated my aspect class with #Component and made sure that it was being scanned as part of the Spring component scan.
Hope this helps someone else as well.
PS. I still do not completely understand why this worked so if someone has an explanation, please comment.

Spring 4 Java Config Transactions Proxy and Aspecj

I am creating a new project that uses aspectj transactions. It also uses legacy jars that contain services that are using the proxy method where an interface is required.
I am using java config and when I set
#EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
Then I get the following exception thrown with accessing the proxy style services from the legacy libs:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
If I change to:
#EnableTransactionManagement(mode=AdviceMode.PROXY)
Then I don't get the problem but I can't then use the aspectj style transactions in my new project.
I've tried adding two #EnableTransactionManagement annotations with each adviceMode, but that is not allowed.
Here is the annotated class
#EnableWebMvc
#Configuration
#ComponentScan("com.mydomain")
#EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
public class ApplicationConfig extends WebMvcConfigurerAdapter {
...
I've also added the aspectj maven plugin to the legacy project in the hope that it would handle the weaving at compile time and thus aspectj transactions would work. But this has not solved the problem.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<showWeaveInfo>true</showWeaveInfo>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
Is it possible to have spring deal with both advice modes? How would I do this?
Or is there another way around this problem.
The problem was with the aspectj config on the legacy project.
When I ran mvn compile it became apparent. I had to add the dependency:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
That got it working when compiled using maven, but I it would still not work in eclipse. I had to right click on the legacy project in eclipse:
Configure>Convert to Aspectj Project
Then I could deploy from eclipse and I had aspectj transactional support in the legacy jars.

Resources