I am trying to use spring-boot and gradle to create a simple CRUD application as a PoC. At this point, I am getting this error when I am attempting to load the front website-
2019-02-11 14:54:48.915 WARN 5704 --- [nio-8080-exec-3] o.s.web.servlet.PageNotFound : No mapping for GET /WEB-INF/jsp/welcome.jsp
I am using spring boot and gradle by specifying in the controller class, and the application properties and build gradle script but still I get this debug statement when I go to my welcome.jsp and the now corresponding 404 error.
I've already looked through a lot of tutorials, but a lot of them are in maven. At this point, I know I need to specify the path in the controller and specify the resources in the properties and build scripts. I have even tried creating a specific mvcconfiguration but I don't think I need one.
build.gradle
buildscript {
ext {
springBootVersion = '2.1.2.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}}
apply plugin: 'eclipse'
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.bill'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
}
ext {
set('springCloudServicesVersion', '2.1.0.RELEASE')
set('springCloudVersion', 'Greenwich.RELEASE')
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-actuator'
/*implementation 'io.pivotal.spring.cloud:spring-cloud-services-starter-config-client'
/*implementation 'io.pivotal.spring.cloud:spring-cloud-services-starter-service-registry'
implementation 'org.springframework.cloud:spring-cloud-starter'
/*implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-ribbon'
*/
runtimeOnly 'mysql:mysql-connector-java'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "io.pivotal.spring.cloud:spring-cloud-services-dependencies:${springCloudServicesVersion}"
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
application.properties
spring.mvc.view.prefix:/WEB-INF/jsp/
spring.mvc.view.suffix:.jsp
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/mh1
spring.datasource.username=root
spring.datasource.password=root
MainController.java
package com.javainuse;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.javainuse.BankAccountApplication;
import com.javainuse.User;
import com.javainuse.UserDAO;
import com.javainuse.UserForm;
#Controller
public class MainController {
#Autowired
UserDAO userDAO;
#RequestMapping("/welcome")
public ModelAndView viewHome() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("welcome");
return modelAndView;
}
#RequestMapping("/users")
public String viewUsers(Model model) {
List<User> list = UserDAO.getUsers();
model.addAttribute("users", list);
return "usersPage";
}
#RequestMapping("/registerSuccessful")
public String viewRegisterSuccessful() {
return "registerSuccessfulPage";
}
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String viewRegister(Model model) {
UserForm form = new UserForm();
model.addAttribute("userForm", form);
return "registerPage";
}
public String saveRegister( Model model, //
#ModelAttribute("userForm") #Validated UserForm userForm, //
BindingResult result, //
final RedirectAttributes redirectAttributes) {
// Validate result
if (result.hasErrors()) {
return "registerPage";
}
User newUser= null;
try {
newUser = userDAO.createUser(userForm);
}
// Other error!!
catch (Exception e) {
model.addAttribute("errorMessage", "Error: " + e.getMessage());
return "registerPage";
}
redirectAttributes.addFlashAttribute("newUser", newUser);
return "redirect:/registerSuccessful";
}
}
MvcConfiguration.java
package com.javainuse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#ComponentScan(basePackages="...package name...")
#EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter{
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
2019-02-11 14:54:48.915 WARN 5704 --- [nio-8080-exec-3] o.s.web.servlet.PageNotFound : No mapping for GET /WEB-INF/jsp/welcome.jsp
Related
Swagger Configuration not working
I use swagger with springboot , i am getting error404 when i hit this
url http://localhost:8080/swagger-ui.html. I am new to swagger I have
no idea how to solve it. I create separate configuration file
Failed to load resource: the server responded with a status of 404 ()
Here all the documentation details
UserCongig.kt
package com.main.swaggerdemo.config
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import springfox.documentation.builders.PathSelectors
import springfox.documentation.builders.RequestHandlerSelectors
import springfox.documentation.spi.DocumentationType
import springfox.documentation.spring.web.plugins.Docket
import springfox.documentation.swagger2.annotations.EnableSwagger2
#Configuration
#EnableSwagger2
class UserConfig {
#Bean
fun postApi(): Docket {
return Docket(DocumentationType.SWAGGER_2)
.select()
.paths(PathSelectors.ant("/api/*"))
.apis(RequestHandlerSelectors.basePackage("com.main.swaggerdemo"))
.build()
}
}
UserController.kt
package com.main.swaggerdemo.controller
import com.main.swaggerdemo.entity.User
import com.main.swaggerdemo.model.req.ReqUser
import com.main.swaggerdemo.model.response.RespUser
import com.main.swaggerdemo.repo.UserRepo
import io.swagger.annotations.ApiOperation
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
#RestController
class UserController {
#Autowired
private lateinit var userRepo: UserRepo
#GetMapping("/signup")
#ApiOperation(value = "create new user")
fun signupUser(#ModelAttribute request: ReqUser): ResponseEntity<*> {
val newUser = User(name = request.name, country = request.country,
email = request.email, password = request.password)
userRepo.save(newUser)
val resUser = RespUser(newUser.id, newUser.name, newUser.country, newUser.email)
return ResponseEntity(resUser, HttpStatus.OK)
}
#GetMapping("/findByEmail/{email}")
#ApiOperation(value = "find the user")
fun getUserByEmail(#PathVariable("email") email: String): ResponseEntity<*> {
val curentUser = userRepo.findByEmail(email)
if (curentUser != null) {
val userData = userRepo.findByEmail(email)
return ResponseEntity(userData, HttpStatus.OK)
}
return ResponseEntity("No data found", HttpStatus.NOT_FOUND)
}
}
build-gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.4.5"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
kotlin("jvm") version "1.4.32"
kotlin("plugin.spring") version "1.4.32"
kotlin("plugin.jpa") version "1.4.32"
}
group = "com.main"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("io.springfox:springfox-swagger2:3.0.0")
implementation("io.springfox:springfox-swagger-ui:3.0.0")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
runtimeOnly("mysql:mysql-connector-java")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
I have added following changes for my service with swagger. You can try the same.
#Configuration
#EnableSwagger2
class UserConfig {
#Bean
public Docket apiDocumentationV1() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.paths(PathSelectors.ant("/v1/**"))
.build()
.apiInfo(createMetaData())
.useDefaultResponseMessages(false);
}
private ApiInfo createMetaData() {
return new ApiInfoBuilder()
.title("Test Service APIs")
.description("API to maintain test service.")
.version("1.0.0")
.build();
}
}
I am trying to run a standalone MVC + JPA application with embedded tomcat
But the application fails with the following error, tho I dont see any obvious reason for this failure. I believe this has something to do with the way Tomcat deals with class loaders
I am not sure how spring boot gets around this problem
Appreciate if anyone can shed light on why this error is thrown
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepositoryImpl': Initialization of bean failed; nested exception is java.lang.IllegalAccessError: class com.acme.$Proxy53 cannot access its superinterface com.acme.UserRepository
... 36 more
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:584)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:400)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:291)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:103)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4661)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5131)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1382)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1372)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:907)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:831)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1382)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1372)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:907)
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:423)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:933)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.startup.Tomcat.start(Tomcat.java:398)
at com.acme.EmbeddedTomcatWebAppWithoutBoot.main(EmbeddedTomcatWebAppWithoutBoot.java:65)
Caused by: java.lang.IllegalAccessError: class com.acme.$Proxy53 cannot access its superinterface com.acme.UserRepository
at java.lang.reflect.Proxy.defineClass0(Native Method)
at java.lang.reflect.Proxy.access$300(Proxy.java:228)
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:642)
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
at java.lang.reflect.WeakCache.get(WeakCache.java:127)
at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
at org.springframework.aop.framework.JdkDynamicAopProxy.getProxy(JdkDynamicAopProxy.java:123)
at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:473)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:352)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:301)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:434)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1749)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:576)
... 36 more
Sources used
EmbeddedTomcatWebAppWithoutBoot.java
package com.acme;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.repository.config.RepositoryConfiguration;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.SharedEntityManagerCreator;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Properties;
import static java.lang.String.valueOf;
public class EmbeddedTomcatWebAppWithoutBoot {
private static final int PORT = 8080;
public static void main(String[] args) throws LifecycleException {
String appBase = ".";
Tomcat tomcat = new Tomcat();
tomcat.setBaseDir(createTempDir());
tomcat.setPort(PORT);
tomcat.getHost().setAppBase(appBase);
tomcat.addWebapp("", ".");
tomcat.getConnector(); // Trigger the creation of the default connector
tomcat.start();
ClassLoader classLoader = findContext(tomcat).getLoader().getClassLoader();
Thread.currentThread().setContextClassLoader(classLoader);
tomcat.getServer().await();
}
private static Context findContext(Tomcat tomcat) {
for (Container child : tomcat.getHost().findChildren()) {
if (child instanceof Context) {
return (Context) child;
}
}
throw new IllegalStateException("The host does not contain a Context");
}
// based on AbstractEmbeddedServletContainerFactory
private static String createTempDir() {
try {
File tempDir = File.createTempFile("tomcat.", "." + PORT);
tempDir.delete();
tempDir.mkdir();
tempDir.deleteOnExit();
return tempDir.getAbsolutePath();
} catch (IOException ex) {
throw new RuntimeException(
"Unable to create tempDir. java.io.tmpdir is set to " + System.getProperty("java.io.tmpdir"),
ex
);
}
}
}
class MainWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(DataAccessConfiguration.class, UserRepositoryImpl.class);
servletContext.addListener(new ContextLoaderListener(rootContext));
AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
servletAppContext.register(WebMvcConfiguration.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet(servletAppContext);
ServletRegistration.Dynamic servletRegistration = servletContext.addServlet("dispatcher", dispatcherServlet);
servletRegistration.setLoadOnStartup(1);
servletRegistration.addMapping("/");
}
}
#EnableWebMvc
#Configuration
#Import(UserResource.class)
class WebMvcConfiguration {}
#RestController
class UserResource {
private final UserRepository userRepository;
public UserResource(UserRepository userRepository) {
this.userRepository = userRepository;
}
#GetMapping
ResponseEntity<List<User>> get() {
return ResponseEntity.ok().body(userRepository.findAllUsers());
}
}
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
class UserRequest {
private String name;
private String authorityName;
}
#Configuration
#EnableTransactionManagement
class DataAccessConfiguration {
#Bean
DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.setName("sample")
.build();
}
#Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource);
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
factoryBean.setJpaVendorAdapter(vendorAdapter);
factoryBean.setPackagesToScan(EmbeddedTomcatWebAppWithoutBoot.class.getPackage().getName());
// set som extra properties hibernate
Properties jpaProperties = new Properties();
jpaProperties.setProperty("hibernate.show_sql", "true");
jpaProperties.setProperty("hibernate.format_sql", "true");
jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
jpaProperties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
factoryBean.setJpaProperties(jpaProperties);
return factoryBean;
}
#Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
#Bean
EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
return SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory);
}
#Bean
PersistenceExceptionTranslationPostProcessor postProcessor() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
interface UserRepository {
List<User> findAllUsers();
}
#Repository
#Transactional
class UserRepositoryImpl implements UserRepository {
private final EntityManager entityManager;
public UserRepositoryImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
public List<User> findAllUsers() {
return entityManager.createQuery("from User", User.class)
.getResultList();
}
}
#Entity
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#ToString
class User {
#Id
#GeneratedValue
private int id;
private String name;
}
build.gradle
ext {
set('spring-boot.version', '2.1.2.RELEASE')
// copied from above
set('spring.version', '5.1.4.RELEASE')
// async logging
set('lmax-disruptor.version', '3.4.2')
// misc utils
set('unexceptional.version', '1.0.0')
set('mapstruct.version', '1.3.0.Final')
// tooling
set('jetbrains-annotations.version', '16.0.2')
}
group 'com.acme'
version '1.0.0-SNAPSHOT'
apply plugin: 'java-library'
sourceCompatibility = 1.8
repositories {
mavenCentral()
jcenter()
maven {
url 'https://repo.spring.io/libs-milestone'
}
}
configurations {
springBom
developmentOnly
// we want to make sure the dependencies bom ia available everywhere so that the dependencies can be resolved across all configurations
compileOnly.extendsFrom(springBom)
annotationProcessor.extendsFrom(springBom)
testAnnotationProcessor.extendsFrom(springBom)
api.extendsFrom(springBom)
implementation.extendsFrom(springBom)
runtimeClasspath {
extendsFrom developmentOnly
}
// lets inherit everything
testCompileOnly.extendsFrom(compileOnly)
}
dependencies {
//*** bill of materials
springBom platform("org.springframework.boot:spring-boot-dependencies:${project.'spring-boot.version'}")
implementation 'org.springframework:spring-context'
implementation 'org.springframework.data:spring-data-jpa'
implementation 'org.hibernate:hibernate-core'
implementation 'com.h2database:h2'
// web
implementation 'org.springframework:spring-web'
implementation 'org.springframework:spring-webmvc'
// jackson for json serialization
implementation 'com.fasterxml.jackson.core:jackson-databind'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8' // new java 8 classes like stream, OPtional
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' // new java date & time api
implementation 'com.fasterxml.jackson.module:jackson-module-parameter-names' // custom modules within jackson
// embedded server
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
// log4j2
implementation 'org.apache.logging.log4j:log4j-core'
implementation 'org.apache.logging.log4j:log4j-slf4j-impl'
implementation 'org.apache.logging.log4j:log4j-jcl'
implementation 'org.apache.logging.log4j:log4j-jul'
implementation "com.lmax:disruptor:${project.'lmax-disruptor.version'}"
// unexceptional for making sure code is not super polluted
implementation "io.earcam:io.earcam.unexceptional:${project.'unexceptional.version'}"
// junit
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testImplementation 'org.junit.jupiter:junit-jupiter-engine'
testImplementation 'org.junit.jupiter:junit-jupiter-params'
testImplementation 'org.mockito:mockito-junit-jupiter'
// test misc
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.hamcrest:hamcrest-library'
testImplementation 'org.mockito:mockito-junit-jupiter'
// NOTE: Due to a bug as of now mapstruct needs to go before lombok
// https://github.com/mapstruct/mapstruct/issues/1581
// maspstruct
implementation "org.mapstruct:mapstruct:${project.'mapstruct.version'}"
implementation "org.mapstruct:mapstruct:${project.'mapstruct.version'}"
annotationProcessor "org.mapstruct:mapstruct-processor:${project.'mapstruct.version'}"
testAnnotationProcessor "org.mapstruct:mapstruct-processor:${project.'mapstruct.version'}"
// lombok
compileOnly('org.projectlombok:lombok')
annotationProcessor('org.projectlombok:lombok')
}
configurations {
all {
// we need to exclude logging to avoid keeping both slf4j & log4j2 both in the same location
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
// `spring-boot-starter-test` is pulling older version of junit. Lets just ignore it
exclude group: 'junit', module: 'junit'
}
}
The complete runnable gradle project can be be found here
The creation of the proxy is failing because the interface that defines the proxy's API, UserRepository, is package-private, access to a package-private type is prohibited across class loaders and the class loader being used to create the proxy is not the same as the class loader that was used to load UserRepository.
There are a few different ways that you can fix the problem, including:
Make UserRepository public
Update MainWebAppInitializer to call rootContext.setClassLoader(getClass().getClassLoader())
The second option is closer to what Spring Boot does and ensures that the class loader that loaded MainWebAppInitializer (and UserRepository) is used for proxy creation.
Problem: Rule(InterestRate.drl) getting fired in standalone java code and giving me accurate result
package com.test.drool.config;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import com.test.drool.facts.InterestRate;
public class RunSampleInterestRate {
public static void main(String[] args) {
// TODO Auto-generated method stub
KieContainer container= KieServices.Factory.get().getKieClasspathContainer();
InterestRate interestRate=new InterestRate();
interestRate.setLender("RBL");
System.out.println("printing session object before inserting"+interestRate.toString());
KieSession kieSession=container.newKieSession("ksession-rules");
kieSession.insert(interestRate);
kieSession.fireAllRules();
System.out.println(interestRate.getRate());
}
}
gives me expected 12.5 as interest rate.
Problem:I have to integrate this in rest service and I have been trying to test same logic under rest environment and it is not giving me expected results.After firing rules,service
always returns default value 0.0.My Environment is Spring-boot and drool is 6.5.0 final.
POJO:
package com.test.drool.facts;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown=true)
public class InterestRate {
#Override
public String toString() {
return "InterestRate [lender=" + lender + ", principal=" + principal + ", store=" + store
+ ", dealer=" + dealer + ", rate=" + rate + "]";
}
private String lender;
private String principal;
private String store;
private String dealer;
private double rate;
public double getRate() {
return rate;
}
public void setRate(double rate) {
this.rate = rate;
}
public String getLender() {
return lender;
}
public void setLender(String lender) {
this.lender = lender;
}
public String getPrincipal() {
return principal;
}
public void setPrincipal(String principal) {
this.principal = principal;
}
public String getStore() {
return store;
}
public void setStore(String store) {
this.store = store;
}
public String getDealer() {
return dealer;
}
public void setDealer(String dealer) {
this.dealer = dealer;
}
}
Bean Config
package com.test.drool.config;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class DroolDependencyConfig {
#Bean(name="kieContainer")
public KieContainer kieContainer() {
return KieServices.Factory.get().getKieClasspathContainer();
}
}
Controller:
package com.test.drool.controllers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.test.drool.facts.InterestRate;
import com.test.drool.service.RuleExecuteService;
#RestController
#RequestMapping(value="/rule")
public class RuleExecuteController {
#Autowired
private RuleExecuteService executeService;
private static Logger logger=LoggerFactory.getLogger(RuleExecuteController.class);
#PostMapping(value = "/execute", consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Double> getInterestRate(#RequestBody InterestRate interestRate){
logger.info(String.format("logging Request Object %s",interestRate.toString()));
return new ResponseEntity<Double>(executeService.executeRule(interestRate),HttpStatus.OK);
}
}
RuleServiceImpl:
package com.test.drool.service.impl;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.test.drool.facts.InterestRate;
import com.test.drool.service.RuleExecuteService;
#Service
public class RuleExecutorServiceImpl implements RuleExecuteService {
private KieContainer kieContainer;
private static org.slf4j.Logger logger=LoggerFactory.getLogger(RuleExecutorServiceImpl.class);
#Autowired
public RuleExecutorServiceImpl(KieContainer kieContainer) {
this.kieContainer=kieContainer;
}
#Override
public double executeRule(InterestRate interestRate) {
logger.info("firing up session and executing rules");
KieSession kieSession= kieContainer.newKieSession("ksession-rules");
logger.info("Printing object before inserting in session"+interestRate.toString());
kieSession.insert(interestRate);
kieSession.fireAllRules();
System.out.println("returning values from rule execution"+">>>"+interestRate.getRate());
return interestRate.getRate();
}
}
DRL file:
package com.test.drool.facts
rule "Interest Rate"
when
$interestrate := InterestRate(lender.equals("RBL"))
then
$interestrate.setRate(12.30);
end
Gradle dependency:
dependencies {
compile "org.kie:kie-spring:${droolsVersion}"
compile "org.kie:kie-api:${droolsVersion}"
compile "org.drools:drools-core:${droolsVersion}"
compile "org.drools:drools-compiler:${droolsVersion}"
//compile('org.springframework.cloud:spring-cloud-starter-eureka')
compile('org.springframework.boot:spring-boot-starter-actuator')
compile ('org.springframework.boot:spring-boot-starter-web')
compile ('org.springframework.boot:spring-boot-starter-data-rest')
compile ('org.springframework.boot:spring-boot-devtools')
testCompile('org.springframework.boot:spring-boot-starter-test')
compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.2'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.0'
}
I don't know the reason why it works but we kept minimum spring drool in our gradle file.All me and my colleague did was to have only kie-spring and drool-compiler in gradle and it worked like a charm.We also need to exclude some modules of spring since spring boot 1.5 does not work with spring 3.2 on which kie depends.For future spring boot drool development,here is gradle. Just paste it and build and code as you will code for any other spring-boot project.
repositories {
mavenCentral()
maven {
name 'jboss'
url 'http://repository.jboss.org/nexus/content/groups/public-jboss'
}
}
buildscript {
ext {
springBootVersion = '1.5.3.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
version = '0.0.1'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
dependencyManagement {
imports {
mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Camden.SR6'
}
}
dependencies {
compile ('org.kie:kie-spring:6.5.0.Final'){
exclude group:'org.springframework', module: 'spring-core'
exclude group:'org.springframework', module: 'spring-tx'
exclude group:'org.springframework', module: 'spring-beans'
exclude group:'org.springframework', module: 'spring-context'
}
compile "org.drools:drools-compiler:6.5.0.Final"
compile ('org.springframework.boot:spring-boot-starter-web')
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.0'
}
task createFolder{
def configDir = new File(project.buildDir.path+"/libs", "config")
def keystoreDir = new File(project.buildDir.path+"/libs", "keystore")
def logDir = new File(project.buildDir.path+"/libs", "log")
def libDir = new File(project.buildDir.path+"/libs", "lib")
if(!logDir.exists()){
logDir.mkdirs()
}
delete configDir
delete libDir
delete keystoreDir
libDir.mkdirs()
configDir.mkdirs()
keystoreDir.mkdirs()
}
//copy config
task copyConfig(type: Copy) {
into project.buildDir.path+"/libs/config"
from "config"
}
//copy keystore
task copyKeystore(type: Copy) {
into project.buildDir.path+"/libs/keystore"
from "keystore"
}
//copy dependencies
task copyRuntimeLibs(type: Copy) {
into project.buildDir.path+"/libs/lib"
from configurations.compile
}
task bundleAll(type: Jar){
dependsOn 'createFolder', 'copyRuntimeLibs', 'copyConfig', 'copyKeystore'
manifest {
def manifestClasspath = configurations.compile.collect { "lib/" + it.getName() }.join(' ')
attributes 'Implementation-Title': 'rule-service',
'Implementation-Version': version,
'Main-Class': 'com.test.drool.starte.RuleStarter',
'Class-Path': manifestClasspath
}
baseName=project.name
from { (configurations.compile - configurations.compile).collect { it.isDirectory() ? it : zipTree(it) } }
with jar
exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA'
}
task zip(type: Zip){
dependsOn 'bundleAll'
from 'build/libs'
}
Investigating Springfox and Swagger UI, but I am facing an issue. I am using the Spring Boot REST example project as the foundation for my PoC. I'm running JDK 8 and the project leverages Gradle.
First, here are the file contents for the project:
build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.7.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
jar {
baseName = 'gs-rest-service'
version = '0.1.0'
}
repositories {
mavenCentral()
jcenter()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("io.springfox:springfox-swagger2:2.2.2")
compile("io.springfox:springfox-swagger-ui:2.2.2")
testCompile("junit:junit")
}
task wrapper(type: Wrapper) {
gradleVersion = '2.3'
}
GreetingController.java
package hello;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
Greeting.java
package hello;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
Application.java
package hello;
import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.schema.AlternateTypeRules.newRule;
import java.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.async.DeferredResult;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.schema.WildcardType;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import com.fasterxml.classmate.TypeResolver;
#SpringBootApplication
#EnableSwagger2
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Autowired
private TypeResolver typeResolver;
#Bean
public Docket greetingApi() {
return new Docket(DocumentationType.SPRING_WEB)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.pathMapping("/")
.directModelSubstitute(LocalDate.class, String.class)
.genericModelSubstitutes(ResponseEntity.class)
.alternateTypeRules(newRule(typeResolver.resolve(DeferredResult.class,
typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
typeResolver.resolve(WildcardType.class)))
.useDefaultResponseMessages(false)
.globalResponseMessage(RequestMethod.GET,
newArrayList(new ResponseMessageBuilder()
.code(500)
.message("500 message")
.responseModel(new ModelRef("Error"))
.build()))
.enableUrlTemplating(true);
}
}
Here is the issue I am facing. When I build and run the application, I can successfully navigate to the Swagger UI page (http://localhost:8080/swagger-ui.html). When I expand greeting-controller, I see the different methods and expand "get /greeting{?name}". The Get section has the following content:
Response Class (Status 200)
Model
{
"content": "string",
"id": 0
}
Response Content Type: */*
Parameters
parameter = name, value = World, parameter type = query, data type = string
When I click the "Try It Out" button, I see the following:
curl = curl -X GET --header "Accept: */*" "http://localhost:8080/greeting{?name}?name=World"
request url = http://localhost:8080/greeting{?name}?name=World
repsonse body = {
"timestamp": 1446418006199,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/greeting%7B"
}
response code = 404
response headers = {
"server": "Apache-Coyote/1.1",
"content-type": "application/json;charset=UTF-8",
"transfer-encoding": "chunked",
"date": "Sun, 01 Nov 2015 22:46:46 GMT"
}
At first glance, it looks like for some reason that Springfox/Swagger is not correctly replacing the placeholder for {?name}. My question is, how do I configure it to do so, if that is in fact the issue, so that I can successfully test out the service from the Swagger UI page?
In your Application class changing the enableUrlTemplating to false will fix your problem.
#Bean
public Docket greetingApi() {
return new Docket(DocumentationType.SPRING_WEB)
//...
.enableUrlTemplating(false);
}
Just a little bit of background on that flag. That flag is to support RFC 6570 without which operations that differ only by query string parameters will not show up correctly per spec. In the next iteration of the swagger spec there are plans to address that issue. That is the reason for enableUrlTemplating to be marked as an incubating feature.
I´m trying to execute an app using spring boot but I got the following error message:"Circular view path [index]: would dispatch back to the current handler URL [/test/index] again". I already see some similar post but I didn't have success.
Below you can see the error message:
Circular view path [index]: would dispatch back to the current handler URL [/test/index] again
build.gradle
buildscript {
repositories {
mavenCentral()
maven { url "http://repo.spring.io/snapshot" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.1.BUILD-SNAPSHOT")
}
}
apply plugin: 'java'
apply plugin: 'eclipse-wtp'
apply plugin: 'spring-boot'
apply plugin: 'war'
repositories {
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
mavenCentral()
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile 'org.springframework:spring-webmvc:3.2.2.RELEASE'
compile 'jstl:jstl:1.2'
providedRuntime 'org.apache.tomcat.embed:tomcat-embed-jasper'
}
WebMvcConfig.java
package com.hellomvc.test.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#EnableWebMvc
#Configuration
class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("WEB-INF/pages/");
resolver.setSuffix(".jsp");
resolver.setCache(false);
resolver.setOrder(2);
return resolver;
}
}
AppInitializer
package com.hellomvc.test.initializer;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
public class AppInitializer implements WebApplicationInitializer {
private static final String CONFIG_LOCATION = "com.hellomvc.test.config";
private static final String MAPPING_URL = "/";
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(MAPPING_URL);
}
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation(CONFIG_LOCATION);
return context;
}
}
Controller:
package com.hellomvc.test.controller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
#RestController
#Configuration
#ComponentScan("com.hello.spring.example.controller")
#EnableAutoConfiguration
public class HelloController {
#RequestMapping(value = "/test/{name}", method = RequestMethod.GET)
public ModelAndView welcome(#PathVariable("name") String name) {
ModelAndView model = new ModelAndView();
model.setViewName("index");
model.addObject("name", name);
return model;
}
public static void main(String[] args) {enter code here
SpringApplication.run(HelloController.class, args);
}
}
Thanks for your help.
Edgardo