Difficulty to configure Activiti into a Springboot application api - spring-boot

First of all is it a viable thing to embed Activiti into an API type application for use within that application or should Activiti be run standalone?
The error below is due to bean definition but I'm not sure where the beans should be defined and how - if thats correct approach for version 6. Our standards with Springhboot 2 is to annotate beans in java rather than xml context
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-04-10 21:17:43.924 ERROR 19516 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Field runtimeService in ma.cvmeeting.workflow.WorkflowApplication$MyrestController required a bean of type 'org.activiti.engine.RuntimeService' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.activiti.engine.RuntimeService' in your configuration.
Process finished with exit code 0
code:
import org.activiti.engine.RuntimeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
public class WorkflowApplication {
public static void main(String[] args) {
SpringApplication.run(WorkflowApplication.class, args);
}
#RestController
public static class MyrestController{
#Autowired
private RuntimeService runtimeService;
#GetMapping("/start-process")
public String startProcess() {
runtimeService.startProcessInstanceByKey("Potulerauneoffre");
return "Process started. Number of currently running"
+ "process instances = "
+ runtimeService.createProcessInstanceQuery().count();
}
}
pom.xml:
<project>
<groupId>ma.cvmeeting</groupId>
<artifactId>workflow</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>workflow</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>7-201802-EA</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.h2database</groupId>
<artifactId>h2database</artifactId>
<version>1.0.20061217</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

There are two ways to initialize the engine when you embed it in your spring based application:
1.) let spring initialize it for you so you can use all the engine services right away without need of any configuration. this requires activiti-spring-boot-starter as dependency.
2.) You initialize engine by your self and provide the services beans from #Configuration class. for this you will require only activiti-engine core as dependency
The reason your application cannot find the RuntimeService because you are trying the second approach add the below dependency in your pom.xml and remove the engine one
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
</dependency>
you should follow documentation for more help.

We recommend activiti 7 core if you are planning to use spring boot 2.x and the use of the new APIs. This is great time if you want to get involved with the new APIs and project initiatives

You could write a #Configuration class and define Activiti services, like this :
#Configuration
public class ActivityConfig {
#Autowired
DataSource dataSource;
#Bean
public DataSourceTransactionManager getTransactionManager() {
return new DataSourceTransactionManager(dataSource);
}
#Bean
public ProcessEngineConfigurationImpl getProcessEngineConfiguration() {
SpringProcessEngineConfiguration res = new SpringProcessEngineConfiguration();
res.setDataSource(dataSource);
res.setTransactionManager(getTransactionManager());
return res;
}
#Bean
public ProcessEngineFactoryBean getProcessEngine() {
ProcessEngineFactoryBean res = new ProcessEngineFactoryBean();
res.setProcessEngineConfiguration(getProcessEngineConfiguration());
return res;
}
#Bean
public RepositoryService getRepositoryService() throws Exception {
return getProcessEngine().getObject().getRepositoryService();
}
#Bean
public FormService getFormService() throws Exception {
return getProcessEngine().getObject().getFormService();
}
#Bean
public TaskService getTaskService() throws Exception {
return getProcessEngine().getObject().getTaskService();
}
#Bean
public RuntimeService getRuntimeService() throws Exception {
return getProcessEngine().getObject().getRuntimeService();
}
#Bean
public HistoryService getHistoryService() throws Exception {
return getProcessEngine().getObject().getHistoryService();
}
#Bean
public IdentityService getIdentityService() throws Exception {
return getProcessEngine().getObject().getIdentityService();
}
}

Related

Error when adding custom revision in Hibernate envers

When I add custom revision entity, I start getting error:
2020-12-13 00:22:29.418 ERROR 80983 --- [ost-startStop-1] o.s.b.web.embedded.tomcat.TomcatStarter : Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'webSecurityConfig': Unsatisfied dependency expressed through field 'userDetailsService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userDetailsServiceImpl': Unsatisfied dependency expressed through field 'userRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Cannot create inner bean '(inner bean)#4384acd' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#4384acd': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: org/hibernate/resource/beans/spi/ManagedBeanRegistry
MyRevision:
package ...;
import org.hibernate.envers.DefaultRevisionEntity;
import org.hibernate.envers.RevisionEntity;
import javax.persistence.Entity;
#Entity
#RevisionEntity(MyRevisionListener.class)
public class MyRevision extends DefaultRevisionEntity {
private String username;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
}
MyRevisionListener:
package ...;
// import de.xxxxx.carorderprocess.models.User;
import org.hibernate.envers.RevisionListener;
// import org.springframework.security.core.Authentication;
// import org.springframework.security.core.context.SecurityContext;
// import org.springframework.security.core.context.SecurityContextHolder;
// import java.util.Optional;
public class MyRevisionListener implements RevisionListener {
#Override
public void newRevision(Object revisionEntity) {
/* String currentUser = Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getPrincipal)
.map(User.class::cast)
.map(User::getUsername)
.orElse("Unknown-User"); */
MyRevision audit = (MyRevision) revisionEntity;
audit.setUsername("dd");
}
}
WebSecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsServiceImpl userDetailsService;
UserDetailsServiceImpl:
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
UserRepository userRepository;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
return UserDetailsImpl.build(user);
}
}
UserRepository:
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
Boolean existsByUsername(String username);
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>de.xxxxxxx</groupId>
<artifactId>carorderprocess</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>carorderprocess</name>
<description>Demo project for Spring Boot</description>
<dependencyManagement>
<dependencies>
</dependencies>
</dependencyManagement>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-envers</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-envers</artifactId>
<version>5.4.25.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
I think your problem could be related with the different dependencies in your pom.xml.
Please, first, remove the spring-data-envers dependency, unless you are querying your audit tables you do not need it. Even in that case, you can use Envers on its own to obtain that information if required.
Be aware that, as indicated in the comments of the answer from Sunit, you will need to remove the attribute repositoryFactoryBeanClass, it could not longer take the value EnversRevisionRepositoryFactoryBean. But you probably still need to include the #EnableJpaRepositories annotation.
Although I initially indicated that you can let Spring Boot manage your versions, due to the one of spring-boot-starter-parent, the framework is providing you versions of hibernate-xxx similar to 5.2.17.Final.
But, as you indicated, you need to use the method forRevisionsOfEntityWithChanges for querying your audit entities. As you can see in the java docs, that method was introduced in AuditQueryCreator in version 5.3.
As a consequence, you need to provide the following dependency:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-envers</artifactId>
<version>5.3.20.Final</version>
</dependency>
But in addition you also need to provide a compatible version of both hibernate-entitymanager and hibernate-core:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.3.20.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.20.Final</version>
</dependency>
From what I understood from all the comments above, your requirement is
to use Envers Auditing
and use method forRevisionsOfEntityWithChanges to get list of all revisions with what changed in them
Please start by doing these
Remove dependency of spring-data-envers library.
Just keep library hibernate-envers - version 5.4.23.Final also worked for me
Remove repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class from #EnableJpaRepositories annotation
All Repository classes should only extend from JpaRespository and NOT from RevisionRepository. You dont need RevisionRepository
You should be able to get your application up and running now.
Now coming back to the question, how to get all revisions with changes using forRevisionsOfEntityWithChanges method.
Create an AuditConfiguration class like this, to create the AuditReader bean
#Configuration
public class AuditConfiguration {
private final EntityManagerFactory entityManagerFactory;
AuditConfiguration(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
#Bean
AuditReader auditReader() {
return AuditReaderFactory.get(entityManagerFactory.createEntityManager());
}
}
In your AuditRevisionEntity class, add following annotation. Without this the serialization of this class wont work. e.g
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class AuditRevisionEntity extends DefaultRevisionEntity {
In your entity class add option withModifiedFlag = true to #Audited annotation. Without this you cannot get entity revisions with all changes. e.g
#Audited(withModifiedFlag = true)
public class Customer {
Modify your database table for this entity audit table and fields *_mod. e.g if you have a customer table with fields name, age, address columns, then add columns name_mod, age_mod, address_mod to the customer_audit table
Last, add following code in your service method to get audit revisions with changes
#Autowired
private AuditReader auditReader;
public List<?> getRevisions(Long id) {
AuditQuery auditQuery = auditReader.createQuery()
.forRevisionsOfEntityWithChanges(Customer.class, true)
.add(AuditEntity.id().eq(id));
return auditQuery.getResultList();
}
I will try to post the same code in Github sometime today, so that you can take a look at working code.
Your code looks fine. But it may not be sufficient to identify the root cause.
Looking at the exception it is clear that application is failing since it is not able to find bean dependency
Could you try following
Check your library imports first in your build.gradle or pom.xml. Generally you should not require any other Hibernate library other than Spring Boot Data JPA and Hibernate Envers
Try removing/disabling the Hibernate Envers audit code and library dependencies and see if can you get your application up and running. This will help you identify if error is due to Hibernate Envers or if your application code has other issues.
If above does not works, then please provide more information
Which version of Spring Boot are you on
What libraries have you imported (build.gradle or maven pom file)
What other Configurations you have in your project - do you have any other JPA configuration file or any other custom configuration related to Hibernate or JPA
What annotations are on the main application class
Directory structure of your Repository class, and the directory on which you do component scan (in case you have overridden it)

Not able to inject #Service and #Contract dependency in my resource class

On base of the guide from this blog, Roll your own Auto Discovery with Jersey and HK2, I have the follow resource POJO:
#Path("Test")
public class TestResource {
#Inject
private TestService service;
#GET
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Set<Test> getTests() {
return service.getAllTests();
}
}
The TestService:
#Contract
public interface TestService {
public Set<Test> getAllTests();
}
The TestServiceImpl
#Service
public class TestServiceImpl implements TestService {
#Override
public Set<Test> getAllTests() {
Set<Test> tests = new HashSet<>();
Test c = new Test();
c.setName("test");
tests.add(c);
return tests;
}
}
The Jersey dependency in pom.xml is of version 2.25.1
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<!-- use the following artifactId if you don't need servlet 2.x compatibility -->
<!-- artifactId>jersey-container-servlet</artifactId -->
</dependency>
<dependency>
<groupId>org.glassfish.jersey.bundles</groupId>
<artifactId>jaxrs-ri</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2</artifactId>
<version>2.5.0-b36</version>
</dependency>
In order to make Jersey scan the #Service and #Contract classes automatically, I used the inhabitant-generator plugin with version 2.5.0-b36:
<plugin>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2-inhabitant-generator</artifactId>
<version>2.5.0-b36</version>
<executions>
<execution>
<goals>
<goal>generate-inhabitants</goal>
</goals>
</execution>
</executions>
</plugin>
There is the corresponding Feature implementation:
public class AutoServiceDiscovery implements Feature {
#Override
public boolean configure(FeatureContext context) {
ServiceLocator locator = ServiceLocatorProvider.getServiceLocator(context);
DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
Populator populator = dcs.getPopulator();
try {
populator.populate(new ClasspathDescriptorFileFinder(this.getClass().getClassLoader()),
new DuplicatePostProcessor());
} catch (IOException | MultiException ex) {
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
}
return true;
}
}
And it is indeeded registered through my ResourceConfig class:
#ApplicationPath("/*")
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
packages("resources");
register(new AutoServiceDiscovery());
}
}
However, I send request to the /test, got the following error:
MultiException has 3 exceptions. They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for
injection at SystemInjecteeImpl(requiredType=TestService,parent=TestResource,qualifiers=
{},position=-1,optional=false,self=false,unqualified=null,1947073589)
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of
rx.practice.ee.jaxrs.resources.TestResource errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on
rx.practice.ee.jaxrs.resources.TestResource
org.jvnet.hk2.internal.Collector.throwIfErrors(Collector.java:89)
org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:250)
org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:358)
org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:487)
org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:162)
...
Question: Anyone knows why the #Service class cannot be injected? I am using Tomcat server
After a couple of days research on the source code of inhabitat-generator, I figured out that in case of web application package,war, the locator file is not generated in META-INF/hk2-locator as demonstracted in the HK2 Inhabitant Generator office site in case of using jar as deployment package. The source code of AbstractInhabitantsGeneratorMojo.java told that in case of war, locator files are generated in hk2-locator, and this is not mentioned in the HK2 Inhabitant Generator office site.
However, when constructing the ClasspathDescriptorFileFinder without the directory names argument in the bootstrap class, AutoServiceDiscovery, it is only compatible with jar as deployment package, meaning it is only finding files in META-INF/hk2-locator.
So the better solution would be not to use inhabitant-generator plugin but the metadata-generator dependency, which is an annotation processor at compile time and, it is proved out-of-the-box.
If someone is persistent to using this plugin, he/she could create his/her own ClasspathDescriptorFileFinder so that it is able to find locator files from hk2-locator
Last but not least, I also tried to use the inhabitants-generator plugin's options to generate the locator files in hk2-locator, but this seems to be next to impossible as well

Spring batch with google spanner with no in memory database

I'm thinking to implement the spring batch job with Google Spanner as a database, but spring batch expects standard databases only, I don't want to do this with the in-memory database/external, I wanted to store all the job metadata in Google Spanner, how can I implement this? Is there any inputs from experts who have implemented with GCP spanner?
I referred this answer Data Source for GCP Spanner
got the below error
Caused by: java.lang.IllegalArgumentException: DatabaseType not found for product name: [Google Cloud Spanner]
at org.springframework.batch.support.DatabaseType.fromProductName(DatabaseType.java:84) ~[spring-batch-infrastructure-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.support.DatabaseType.fromMetaData(DatabaseType.java:123) ~[spring-batch-infrastructure-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.batch.core.repository.support.JobRepositoryFactoryBean.afterPropertiesSet(JobRepositoryFactoryBean.java:183) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.boot.autoconfigure.batch.BasicBatchConfigurer.createJobRepository(BasicBatchConfigurer.java:129) ~[spring-boot-autoconfigure-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at org.springframework.boot.autoconfigure.batch.BasicBatchConfigurer.initialize(BasicBatchConfigurer.java:97) ~[spring-boot-autoconfigure-2.2.2.RELEASE.jar:2.2.2.RELEASE]
... 26 common frames omitted
configuration code is below,
package io.spring.batchdemo.config;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.explore.support.JobExplorerFactoryBean;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
import org.springframework.boot.autoconfigure.batch.BasicBatchConfigurer;
import org.springframework.boot.autoconfigure.batch.BatchProperties;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
#Configuration
public class BatchConfig2 implements BatchConfigurer {
private final BatchProperties properties;
private PlatformTransactionManager transactionManager;
private final TransactionManagerCustomizers transactionManagerCustomizers;
private JobRepository jobRepository;
private JobLauncher jobLauncher;
private JobExplorer jobExplorer;
/**
* Create a new {#link BasicBatchConfigurer} instance.
* #param properties the batch properties
* #param dataSource the underlying data source
* #param transactionManagerCustomizers transaction manager customizers (or
* {#code null})
*/
protected BatchConfig2(BatchProperties properties,
TransactionManagerCustomizers transactionManagerCustomizers) {
this.properties = properties;
this.transactionManagerCustomizers = transactionManagerCustomizers;
}
#Override
public JobRepository getJobRepository() {
return this.jobRepository;
}
#Override
public PlatformTransactionManager getTransactionManager() {
return this.transactionManager;
}
#Override
public JobLauncher getJobLauncher() {
return this.jobLauncher;
}
#Override
public JobExplorer getJobExplorer() throws Exception {
return this.jobExplorer;
}
#PostConstruct
public void initialize() {
try {
this.transactionManager = buildTransactionManager();
this.jobRepository = createJobRepository();
this.jobLauncher = createJobLauncher();
this.jobExplorer = createJobExplorer();
}
catch (Exception ex) {
throw new IllegalStateException("Unable to initialize Spring Batch", ex);
}
}
protected JobExplorer createJobExplorer() throws Exception {
PropertyMapper map = PropertyMapper.get();
JobExplorerFactoryBean factory = new JobExplorerFactoryBean();
factory.setDataSource(spannerDataSource());
map.from(this.properties::getTablePrefix).whenHasText().to(factory::setTablePrefix);
factory.afterPropertiesSet();
return factory.getObject();
}
protected JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(getJobRepository());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
PropertyMapper map = PropertyMapper.get();
map.from(spannerDataSource()).to(factory::setDataSource);
map.from(this::determineIsolationLevel).whenNonNull().to(factory::setIsolationLevelForCreate);
map.from(this.properties::getTablePrefix).whenHasText().to(factory::setTablePrefix);
map.from(this::getTransactionManager).to(factory::setTransactionManager);
factory.afterPropertiesSet();
factory.setDatabaseType("spanner");//which datatype to set?, here is the error,Caused by: java.lang.IllegalArgumentException: DatabaseType not found for product name: [Google Cloud Spanner]
return factory.getObject();
}
/**
* Determine the isolation level for create* operation of the {#link JobRepository}.
* #return the isolation level or {#code null} to use the default
*/
protected String determineIsolationLevel() {
return null;
}
protected PlatformTransactionManager createTransactionManager() {
return new DataSourceTransactionManager(spannerDataSource());
}
private PlatformTransactionManager buildTransactionManager() {
PlatformTransactionManager transactionManager = createTransactionManager();
if (this.transactionManagerCustomizers != null) {
this.transactionManagerCustomizers.customize(transactionManager);
}
return transactionManager;
}
#Bean
public DataSource spannerDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.google.cloud.spanner.jdbc.JdbcDriver");
dataSource.setUrl("jdbc:cloudspanner:/projects/suresh-project-261506/instances/suresh-spanner/databases/spanner?credentials=C:\\Users\\skengab\\AppData\\Roaming\\gcloud\\application_default_credentials.json");
return dataSource;
}
}
pom.xml is here
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>io.spring</groupId>
<artifactId>batch-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>batch-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-starter-data-spanner</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-starter-storage</artifactId>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner-jdbc</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Spring Batch only has official support for a fixed set of databases. Google Cloud Spanner is not one of them. If you want to use Spring Batch with Spanner, you will need to set the database type manually to one of the supported databases. Spring Batch will then generate queries and other commands based on that setting, meaning that there is a chance that you could get errors regarding queries that are not compatible with Spanner.
If you can live with the above limitations, I would recommend trying to set the database type to POSTGRES, i.e. change the following line from
factory.setDatabaseType("spanner");//which datatype to set?, here is the error,Caused by: java.lang.IllegalArgumentException: DatabaseType not found for product name: [Google Cloud Spanner]
Into:
factory.setDatabaseType("POSTGRES");

Spring Boot 2.1.0 with Jersey

Today i started a simple application spring boot application. Because i am starting from the scratch, i am using the latest version of SpringBoot: 2.1.0.RELEASE
I would like to use Jersey to use JAX-RS. I have this working for 1.3.6 Spring Boot version, but I am getting the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'requestContextFilter', defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
I can't understand where the problem could be because my application at this point is minimalist.
Apparently the bean 'requestContextFilter' is being configured twice but i have no idea where it is configured.
Here is my configuration:
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<start-class>pt.msoftware.userauthservice.App</start-class>
<java.version>1.8</java.version>
<docker.image.prefix>${user.name}</docker.image.prefix>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
SpringBoot application class
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
** Jersey Config**
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;
import pt.msoftware.userauthservice.rest.UserEndpoint;
import javax.ws.rs.ApplicationPath;
/**
* Created by marco on 31/10/2018.
*/
#Component
#ApplicationPath("/rest")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(UserEndpoint.class);
}
}
** Endpoint**
import org.springframework.stereotype.Component;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
/**
* Created by marco on 31/10/2018.
*/
#Component
#Path("/user")
public class UserEndpoint {
#GET
public String message() {
return "Hello";
}
}
Can someone spot what I am missing or what might be wrong with my code/config?
Thank you so much
It's a bug in Spring Boot. Thanks for bringing it to our attention. I've opened this issue to track the problem.
If you intend to only use Jersey and JAX-RS, you do not need to use spring-boot-starter-web. It's, essentially, a Spring MVC-based equivalent of spring-boot-starter-jersey. You can, therefore, avoid the problem you're seeing by removing the spring-boot-starter-web dependency from your application.
If you do want to use both Spring MVC and JAX-RS, you can enable bean definition overriding by adding spring.main.allow-bean-definition-overriding=true to your application.properties file in src/main/resources.

spring boot actuator for standalone spring application

I am trying to learn spring boot actuator. I have a simple basic application which runs via main method. It has not tomcat or anything. It has just one class as below
public class StartUp {
public static void main(String... args) throws InterruptedException {
ConfigurableApplicationContext ctx = SpringApplication.run(StartUp.class,
args);
StartUp mainObj = ctx.getBean(StartUp.class);
mainObj.init();
System.out.println("Application exited");
}
public void init() throws InterruptedException {
System.out.println("inside init method");
Thread.sleep(10 * 60 * 1000);
System.out.println("outside init method");
}
}
I have configured spring actuator as below in pom:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
</parent>
<dependencies>
<!-- [3] -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
I was trying to view the applications health and info, i have configured the below in application.properties
management.port=8091
management.address=127.0.0.1
management.security.enabled=false
endpoints.shutdown.enabled=true
info.app.name=Startup Dashboard
info.app.version=2.0-ALPHA
logging.file=dashboard.log
while trying the url : http://localhost:8091/info
it never gets resolved.
Is it not possible to configure actuator for standalone applications ?
Your application is not a Spring Boot App yet.
One has to use the #SpringBootApplication at the minimum to convert an app into one.
#SpringBootApplication
public class BootadminMsAlphaApplication {
public static void main(String[] args) {
SpringApplication.run(BootadminMsAlphaApplication.class, args);
}
}
is one such simplest example.
Without this, the spring-boot dependency in your classpath have not been asked to do its magical things to make your application smart. The all-in-one annotation brings spring-boot's auto-configuration into play, that configures a lot of good stuff for the app, including Actuators (if in classpath).
It looks like your application isn't a Spring Boot application. It needs to be.

Resources