Java spring problem concerning JPA repositories and autowire - java

I am an oldtimer, but fairly new to spring and friends (and maybe i am getting old and rusty, dont be hard on me) . I have question which is very, very, similar to the one found in the Unable to Field identify bean named 'entityManagerFactory' as using Auto-wiring annotation to Repository and it currently beats me... I am unable to find an answer as to why the springboot application (which is a silly little thing i do on my spare time) doesnt seem to be able to autowire jpa repositiories as it should. I have (to the best of my knowledge) followed the instructions in the linked issue above (and removed any known direct hibernate dependencies. I am enclosing my pom file in this public question as well.
<?xml version="1.0" encoding="UTF-8"?>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>se.pointsofinterest</groupId>
<artifactId>poi-restlayer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>restlayer</name>
<description>This module contains the rest layer for the application</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!--dependency>
<groupId>se.pointsofinterest</groupId>
<artifactId>dblayer</artifactId>
<version>1.0.0</version>
</dependency-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.3.1.RELEASE</version>
</dependency>
<!--dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency-->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
<version>1.4.200</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Possibly suspect depedencies below! -->
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<version>2.0.4.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>4.13</version>
</dependency>
</dependencies>
<repositories>
<!-- Main maven repository -->
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<!-- Repository where to store our local artifacts (an azure artifacts)! -->
<repository>
<id>joakimhansson</id>
<url>https://pkgs.dev.azure.com/joakimhansson/_packaging/joakimhansson/maven/v1</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
As far as I know I have no dependencies on hibernate in this pom.
I have a database layer which contains;
domain (all the entities)
repository (all relevant repository references)
Service which contains a service layer (which defines somewhat higher business logic for the data handling).
This is, to the best of my abilities to see very analogue to the issue referenced above.
The error i get is;
***************************
APPLICATION FAILED TO START
***************************
Description:
Field mapLocationRepository in se.poi.restlayer.dblayer.services.MapLocationService required a bean named 'entityManagerFactory' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
I..e the autowire function does not work.
My application configuration;
package se.poi.restlayer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import se.poi.dblayer.domain.*;
import se.poi.dblayer.repositories.AddressRepository;
import se.poi.dblayer.repositories.LinksRepository;
import se.poi.dblayer.repositories.MapLocationRepository;
import se.poi.dblayer.repositories.TagDefinitionsRepository;
/**
* * #author Joakim Hansson, 2020
*
*/
#Slf4j
#SpringBootApplication(scanBasePackages = {"se.poi.dblayer.repositories", "se.poi.dblayer.services"})
/*#Import(value={ConfigurationDbLayer.class})
#ComponentScan(basePackages={
"se.poi.dblayer",
"se.poi.dblayer.domain",
"se.poi.dblayer.repositories",
"se.poi.dblayer.services"})
*/
#EntityScan(basePackageClasses = {
Address.class,
Links.class,
MapLocation.class,
MapLocationTagDefinitionsRelation.class,
TagDefinitions.class
})
#EnableJpaRepositories(basePackageClasses = {
AddressRepository.class,
LinksRepository.class,
MapLocationRepository.class,
TagDefinitionsRepository.class})
#ComponentScan(basePackages={
"se.poi.restlayer.dblayer",
"se.poi.restlayer.dblayer.domain",
"se.poi.restlayer.dblayer.repositories",
"se.poi.restlayer.dblayer.services"})
public class Application {
public static void main (String[] args) {
log.info("Starting the main backend for the end customer for us.");
log.info("------------------------------------------------------");
//new BeanConfigurator();
//AutowireCapableBeanFactory f = context.getContext().getAutowireCapableBeanFactory();
//f.autowireBean(new AddressRepository());
SpringApplication.run(Application.class, args);
}
}
The repository;
package se.poi.restlayer.dblayer.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import se.poi.dblayer.domain.Address;
#Repository
//#Component
public interface AddressRepository extends JpaRepository<Address, Long>{
}
The Service;
package se.poi.restlayer.dblayer.services;
import java.util.List;
import java.util.Optional;
import javax.annotation.PostConstruct;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import lombok.extern.slf4j.Slf4j;
import se.poi.dblayer.domain.Address;
import se.poi.dblayer.domain.Links;
import se.poi.dblayer.domain.MapLocation;
import se.poi.dblayer.domain.TagDefinitions;
import se.poi.dblayer.repositories.AddressRepository;
import se.poi.dblayer.repositories.LinksRepository;
import se.poi.dblayer.repositories.MapLocationRepository;
import se.poi.dblayer.repositories.TagDefinitionsRepository;
//import org.springframework.web.context.annotation.ApplicationScope;
/**
* Demo backend that accepts up to 100 fishing spots. Data is shared with all
* users.
*/
#Slf4j
//#Service
#Component
public class MapLocationService {
//private List<MapLocation> spots = new ArrayList<MapLocation>();
#Autowired(required = true)
MapLocationRepository mapLocationRepository;
#Autowired(required = true)
TagDefinitionsRepository tagDefinitionsRepository;
#Autowired (required = true)
LinksRepository linksRepository;
#Autowired (required = true)
AddressRepository addressRepository;
public void checkRepositoryStatus () {
log.warn("checkRepositoryStatus");
if (mapLocationRepository == null) {
log.warn("Repository is == NULL!");
} else if (tagDefinitionsRepository == null) {
log.warn("tagDefnitionsRepository == NULL!!");
}
}
public void setMapLocationRepository (MapLocationRepository repository) {
this.mapLocationRepository = repository;
}
public Repository getMapLocationRepository() {
return (Repository) mapLocationRepository;
}
#PostConstruct
private void init() {
log.info("init called!");
}
/**
* Retrieves a list of map locations to the caller. As the
* map location is lazilly loaded the caller needs to instantiate each object
* using #link getManagedMapLocation
*
* #return list of map locations.
*/
#Transactional
public List<MapLocation> getAll() {
log.info("getAll");
//return Collections.unmodifiableList(spots);
return mapLocationRepository.findAll();
}
#Transactional
public MapLocation getManagedMapLocation (MapLocation mapLocation) {
Optional<MapLocation>mapLocationResponse = mapLocationRepository.findById(mapLocation.getId());
mapLocation = mapLocationResponse.get();
mapLocation = getAllLinks(mapLocation);
mapLocation = getAllAddresses(mapLocation);
mapLocation = getAllTags(mapLocation);
return mapLocation;
}
#Transactional
public MapLocation getAllAddresses (MapLocation mapLocation) {
log.info("getAllAddresses called!");
mapLocation.getAddresses();
log.info("Retrieved (" + mapLocation.getAddresses().size() + ") objects in list!");
return mapLocation;
}
#Transactional
public MapLocation getAllLinks (MapLocation mapLocation) {
log.info("getAllLinks called!");
mapLocation.getLinks();
log.info("Retrieved (" + mapLocation.getLinks().size() + ") objects in list!");
return mapLocation;
}
#Transactional
public MapLocation getAllTags (MapLocation mapLocation) {
mapLocation.getTagDefinitions();
return mapLocation;
}
/**
* The spot object is a non managed object as returned by this service from
* the getAllFunction.
*
* #param spot
*/
#Transactional
public MapLocation addSpot(MapLocation spot) {
log.info("addSpot called!");
MapLocation mapLocation = mapLocationRepository.save(spot);
for (Links i : spot.getLinks()) {
log.info("links: " + i.getLink() + " id = " + i.getId());
i.setMaplocation(mapLocation);
linksRepository.save(i);
}
for (Address i : spot.getAddresses()) {
log.info("Address: " + i.getAddressline1() + " id = " + i.getId());
i.setMaplocation(mapLocation);
addressRepository.save(i);
}
for (TagDefinitions i : spot.getTagDefinitions()) {log.info("Tagdefinition: " + i.getTag());}
return mapLocation;
}
#Transactional
public void delete (MapLocation mapLocation) {
/* Implementaion */
log.info("delete on maplocation is called!");
for (Links i: mapLocation.getLinks()) {
log.info("Removing link (" + i.getId() + ")");
linksRepository.delete(i);
}
for(Address i : mapLocation.getAddresses()) {
log.info("Deleting address (" + i.getId() + ")");
addressRepository.delete(i);
}
log.info ("remove mapLocation.getId (" + mapLocation.getId() + ")");
mapLocationRepository.delete(mapLocation);
/* * * */
}
/**
*
* #param name Marker name, which should be used on the map.
* #param links the links associated with the marker
* #param address the address to the nearest street address
* #param latitude
* #param longitude
* #param tags the list of tag (in string form) for the marker.
* #return
*/
public MapLocation prepareSpot (Long id,
String name,
List<Links> links,
List<Address> addresses,
double latitude,
double longitude,
List<TagDefinitions> tagDefinitions) {
/* Implementation */
MapLocation mapLocation = new MapLocation();
mapLocation.setId (id);
mapLocation.setName (name);
mapLocation.setLinks (links);
mapLocation.setAddresses (addresses);
mapLocation.setLatitude (latitude);
mapLocation.setLongitude (longitude);
mapLocation.setTagDefinitions (tagDefinitions);
mapLocation.setAddresses (addresses);
mapLocation.setLinks (links);
/* * * */
return mapLocation;
}
}
And a suitable domain entity;
package se.poi.restlayer.dblayer.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Simple data object representing a marker on a map.
*/
#Entity
#Data
#Table(name="MAP_LOCATION")
#AllArgsConstructor
#NoArgsConstructor
public class MapLocation implements Serializable {
/**
*
*/
private static final long serialVersionUID = -590067472197846904L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(name="latitude", nullable = false)
private Double latitude;
#Column(name="longitude", nullable = false)
private Double longitude;
#Column(name="name", length = 128)
private String name;
#ManyToMany (fetch=FetchType.EAGER) //(mappedBy = "mapLocations")
#JoinTable(name="maplocations_tagdefinitions",
joinColumns= #JoinColumn(name="mapLocations"),
inverseJoinColumns = #JoinColumn(name="tagDefinitions"))
private List<TagDefinitions>tagDefinitions = new ArrayList<>();
#OneToMany(mappedBy="maplocation")
private List<Links> links;
#OneToMany(mappedBy="maplocation")
private List<Address> addresses;
public MapLocation(double latitude, double longitude, String name, List<TagDefinitions>tagDefinitions) {
this.latitude = latitude;
this.longitude = longitude;
this.name = name;
this.tagDefinitions = tagDefinitions;
/* * * */
}
}
The test that sets it off;
package se.poi.restlayer;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
//import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import lombok.extern.slf4j.Slf4j;
import se.poi.restlayer.controller.GetTagsController;
/**
*
* #author fmanh
*/
#RunWith(SpringRunner.class)
#WebMvcTest(GetTagsController.class)
#AutoConfigureRestDocs(outputDir="target/snippets")
#Slf4j
public class WebLayerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testGetTags() throws Exception {
log.info ("mockMvc == " + ((this.mockMvc==null)?"NULL":"INSTANTIATED"));
log.info("KALLE");
this.mockMvc.perform (get("/retrievealltags")); //.
//andExpect(status().isOk()).
//andExpect(content().json("")).
//andDo(print()).
//andDo(document("/retrievealltags"));
}
}
The application.properties
server.port=8080
# Ensure application is run in Vaadin 14/npm mode
vaadin.compatibilityMode = false
logging.level.org.atmosphere = warn
#
# Settings for the internal H2
#
#spring.datasource.url = jdbc:h2:file:~/test
#spring.datasource.driverClassName = org.h2.Driver
#spring.datasource.username = sa
##spring.datasource.password =
#spring.jpa.databse-platform = org.hibernate.dialect.H2Dialect
#spring.h2.console.enabled = true
#spring.h2.console.path = /h2-console
#hibernate.dialect = H2
#
# Set up the postgres database
#
spring.datasource.url = jdbc:postgresql://localhost:5432/postgres
spring.datasource.username = postgres
spring.datasource.password = d1d4a5baa55f4f70a90e12bc95473833
spring.jpa.database-platform = org.hibernate.dialect.PostgreSQL94Dialect
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
spring.jpa.hibernate.naming.implicit-strategy = org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
spring.jpa.properties.hibernate.format_sql = true
spring.jpa.properties.hibernate.generate_statistics = true
logging.level.org.hibernate.type=trace
logging.level.org.hibernate.stat=debug
#spring.jpa.hibernate.ddl-auto=none
# Following values available;
# validate, update, create, create-drop, none
#server.port = 8443
#server.ssl.key-store-type = PKCS12
#server.ssl.key-store = classpath:keystore.p12
#server.ssl.key-store-password = Pur3Life
#server.ssl.key-alias = tomcat
#security.require-ssl = true
Any help is appreciated! I have tried in vain to google it (maybe my google fu is not up to par, or I have missed something obvious), if so feel free to point it out. A tar archive of the softwar
e can be obtained if you wish (there is no secrets here). Please help me grow a bit in wisdom here!
EDIT!
I realised that my description is not complete: My solution contains a restlayer which containsa the following controller;
package se.poi.restlayer.controller;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import se.poi.dblayer.ConfigurationDbLayer;
import se.poi.dblayer.domain.Address;
import se.poi.dblayer.domain.Links;
import se.poi.dblayer.domain.MapLocation;
import se.poi.dblayer.domain.TagDefinitions;
import se.poi.dblayer.repositories.AddressRepository;
import se.poi.dblayer.services.MapLocationService;
import se.poi.dblayer.services.TagDefinitionsService;
import se.poi.restlayer.model.AddressObject;
import se.poi.restlayer.model.LinkObject;
import se.poi.restlayer.model.MapLocationList;
import se.poi.restlayer.model.MapLocationObject;
import se.poi.restlayer.model.TagDefinitionObject;
import se.poi.restlayer.model.TagDefinitionsList;
/**
* The
* #author Joakim Hansson
*/
#RestController
//#EnableJpaRepositories(basePackages={"se.poi.dblayer.repositories"})
//#EntityScan(basePackages={"se.poi.dblayer.domain"})
//#ComponentScan(basePackages={"se.poi.dblayer.services", "se.poi.dblayer.repositories"})
#Slf4j
//#Import(value={ConfigurationDbLayer.class})
public class GetTagsController {
//#Autowired
//AddressRepository a;
#Autowired
TagDefinitionsService tagDefinitionService;
//#Autowired
MapLocationService mapLocationService;
//private TagDefinitionsService tagDefinitionService = new TagDefinitionsService ();
//private MapLocationService mapLocationService = new MapLocationService ();
#GetMapping("/retrievealltags")
public TagDefinitionsList retrieveAllTags () {
/* Implementation */
if (tagDefinitionService==null) {log.error ("tagDefinitionsService: NULL!");}
List<TagDefinitions> list = tagDefinitionService.getAllTagDefinitionsInFull();
TagDefinitionsList tagDefinitionsList = new TagDefinitionsList();
ArrayList<TagDefinitionObject> tagDefinitions = new ArrayList<TagDefinitionObject>();
for (TagDefinitions item : list) {
TagDefinitionObject tagDefinition = new TagDefinitionObject ();
tagDefinition.setId (item.getId());
tagDefinition.setDescription (item.getDescription());
tagDefinition.setTag (item.getTag());
tagDefinition.setParentId (null);
tagDefinitions.add (tagDefinition);
}
tagDefinitionsList.setTagDefinitions(tagDefinitions);
/* * * */
return tagDefinitionsList;
}
#GetMapping("/retrieveMarkers")
public MapLocationList retrieveMarkers () {
/* Implementation */
// Retrieve all the data from the service...
List<MapLocation> l = mapLocationService.getAll();
// Convert to...
MapLocationList mapLocationList = new MapLocationList ();
ArrayList<MapLocationObject> ll = new ArrayList<MapLocationObject> ();
for (MapLocation item: l) {
MapLocationObject mapLocationObject = new MapLocationObject ();
mapLocationObject.setId (item.getId ());
mapLocationObject.setLatitude (item.getLatitude ());
mapLocationObject.setLongitude (item.getLongitude ());
mapLocationObject.setName (item.getName ());
mapLocationObject.setLinks (copyLinksList (item.getLinks ()));
mapLocationObject.setAddresses (copyAddressList (item.getAddresses ()));
ll.add (mapLocationObject);
}
/* * * */
return mapLocationList;
}
/* Private functions
* **********************************************************************/
/**
* Copies data from the database model to the rest API model.
*
* #param links
* #return
*/
private List<LinkObject> copyLinksList (List<Links>links) {
/* Implementation */
ArrayList<LinkObject> ll = new ArrayList<LinkObject> ();
for (Links item: links) {
LinkObject linkObject = new LinkObject();
linkObject.setId (item.getId());
linkObject.setLink (item.getLink());
ll.add(linkObject);
}
/* * * */
return ll;
}
/**
*
* #param address
* #return
*/
private List<AddressObject> copyAddressList (List<Address>address) {
/* Implementation */
ArrayList<AddressObject> ll = new ArrayList<AddressObject> ();
for (Address item: address) {
AddressObject addressObject = new AddressObject();
addressObject.setId (item.getId ());
addressObject.setAddressline1 (item.getAddressline1 ());
addressObject.setAddressline2 (item.getAddressline2 ());
addressObject.setAddressline3 (item.getAddressline3 ());
addressObject.setCity (item.getCity ());
addressObject.setPostcode (item.getPostcode ());
ll.add(addressObject);
}
/* * * */
return ll;
}
}
This means that the application tries to instantiate a controller which contains an autowire annotation on a dblayer service, and this dblayer service in turn contains an autowire annotation on a repository. It is this chain which goes south. Sorry for the omission.
EDIT;
I am now experimenting widly; autowire simply doesnt work. SIGH!. The funny part is that I can see in my log files that 4 jpa entities are found, but I simply cant get Autowired to work with those repositories... Google seams to point to that this is a common problem, but there doesnt seem to be a clear cut solution.

You are using different versions for SpringBoot and JPA (that is wrong, you always let SpringBoot pull versions for all SpringBoot starters) - remove this line
<version>2.3.1.RELEASE</version>
from JPA dependency so that Spring Boot pulls the correct version from parent and let me know if the problem is still there.

Related

annotation to generate source code for same file in java

I am trying to define a simple annotation and use it during compile time only to add a simple method to the consumed source file/class file but nothing is getting added.
I am using maven and java 1.8.
Below is the code and not sure where it is going wrong
Annotation class definition:
package com.test.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.SOURCE)
public #interface AddMethod {
}
Processor for the annotation
package com.test.annotations;
import com.google.auto.service.AutoService;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.stream.Collectors;
#SupportedAnnotationTypes("com.test.annotations.AddMethod")
#SupportedSourceVersion(SourceVersion.RELEASE_8)
#AutoService(Processor.class)
public class AddMethodProcessor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// get all the classes annotated with ToJsonString class
Collection<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(AddMethod.class);
annotatedElements.forEach(e -> {
// some code to get the class name
try {
writeBuilderFile(qualifiedClassName);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
});
return true;
}
private void writeBuilderFile(String qualifiedClassName) throws IOException {
String packageName = null;
int lastDot = qualifiedClassName.lastIndexOf('.');
if (lastDot > 0) {
packageName = qualifiedClassName.substring(0, lastDot);
}
String simpleClassName = qualifiedClassName.substring(lastDot + 1);
JavaFileObject generatedSourceFile = processingEnv.getFiler()
.createSourceFile(qualifiedClassName);
try (PrintWriter out = new PrintWriter(generatedSourceFile.openWriter())) {
if (packageName != null) {
out.print("package ");
out.print(packageName);
out.println(";");
out.println();
}
out.print("public class ");
out.print(simpleClassName);
out.println(" {");
out.println();
out.print(" public ");
out.print("String");
out.println(" testMethod() {");
out.println(" return \"this is a test method\" ");
out.println(" }");
out.println();
out.println("}");
}
}
}
After this, I am running mvn install to install the jar in my .m2 repository so that the consumer can use it.
consumer project:
pom.xml
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>add-method</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<forceJavacCompilerUse>true</forceJavacCompilerUse>
<generatedSourcesDirectory>${project.build.directory}
/generated-sources/</generatedSourcesDirectory>
<annotationProcessors>
<annotationProcessor>
com.test.annotations.AddMethodProcessor
</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
</plugins>
</build>
</project>
Test class using the annotation
package org.example;
import com.test.annotations.AddMethod;
#AddMethod
public class Test {
private String x;
}
On running build in intellij, I could not see the source file or class file having the auto generated method.

Surefire not running all tests

I am trying to use a combination of Surefire (or even Failsafe if I can configure it to work properly), to run my integration tests for a personal project. At present it will run 2/3 of my integration tests. The 3 classes are GameDbApplicationTests, UserDAOImplTest, & GameDAOImplTest. The first two will run, while the last one will not.
Logically it should be running all of the classes titled *Test, so I'm not sure what is going on here. I am capable of running each test class manually using IntelliJ, but the Maven test goal will not execute all of the test classes.
.\mvnw test output is included here
Note that the test lists GameDbApplicationTests & UserDAOImplTest, but not GameDAOImplTest for whatever reason.
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.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.StruckCroissant</groupId>
<artifactId>Game-DB</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Game-DB</name>
<description>Game Database Search Engine</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.vorburger.mariaDB4j</groupId>
<artifactId>mariaDB4j</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>23.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>8.0.5</version>
</plugin>
</plugins>
</build>
</project>
GameDAOImplTest
package com.StruckCroissant.GameDB.core.game;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import com.StruckCroissant.GameDB.TestDbConfig;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.junit.Test;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#SpringBootTest
#RunWith(SpringJUnit4ClassRunner.class)
#TestPropertySource(locations = "classpath:test.properties")
#ContextConfiguration(classes = {TestDbConfig.class, GameDAOImpl.class})
class GameDAOImplTest {
#Qualifier("db-game")
#Autowired
private GameDAOImpl underTest;
#Qualifier("testTemplate")
#Autowired
private JdbcTemplate jdbcTemplate;
#Test
public void shouldGetAllGames() {
// given
// nothing
// when
List<Game> allGames = underTest.selectAllGames();
// then
allGames.forEach((game) -> assertThat(game).isInstanceOf(Game.class));
assertThat(allGames.size()).isGreaterThanOrEqualTo(30250);
}
#Test
public void shouldGetGameById() {
// given
int[] validGames = {1, 2, 3, 4, 5};
int[] invalidGames = {-4, -56, -3, 879627};
List<Optional<Game>> validResult = new ArrayList<>();
List<Optional<Game>> invalidResult = new ArrayList<>();
// when
for (int gameInt : validGames) {
Optional<Game> currentGameOpt = underTest.selectGameById(gameInt);
validResult.add(currentGameOpt);
}
for (int gameInt : invalidGames) {
Optional<Game> currentGameOpt = underTest.selectGameById(gameInt);
invalidResult.add(currentGameOpt);
}
// then
validResult.forEach((gameOpt) -> assertThat(gameOpt).get().isInstanceOf(Game.class));
invalidResult.forEach((gameOpt) -> assertThat(gameOpt).isEmpty());
}
}
UserDAOImplTest
package com.StruckCroissant.GameDB.core.user;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.catchThrowable;
import com.StruckCroissant.GameDB.TestDbConfig;
import com.StruckCroissant.GameDB.core.game.Game;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.jdbc.JdbcTestUtils;
#SpringBootTest
#RunWith(SpringJUnit4ClassRunner.class)
#TestPropertySource(locations = "classpath:test.properties")
#ContextConfiguration(classes = {TestDbConfig.class, UserDAOImpl.class})
class UserDAOImplTest {
#Qualifier("db-user")
#Autowired
private UserDAOImpl underTest;
#Qualifier("testTemplate")
#Autowired
private JdbcTemplate jdbcTemplate;
#AfterEach
void tearDown() {
JdbcTestUtils.deleteFromTables(jdbcTemplate, "user");
}
#NotNull
private String insertDefaultUserDb() {
User user = new User("test_username", "test_password", UserRoleEnum.USER, false, true);
underTest.insertUser(user);
return "test_username";
}
#NotNull
private String insertUserDb(String username) {
User user = new User(username, "test_password", UserRoleEnum.USER, false, true);
underTest.insertUser(user);
return username;
}
#NotNull
private Integer getUidFromUserObj(User userFromDb) {
return userFromDb.getId().orElseThrow();
}
#NotNull
private Integer getTestUidFromUsernameDb(String test_username) {
return underTest
.selectUserByUsername(test_username)
.flatMap(User::getId)
.orElseThrow(() -> new IllegalStateException("User not found in DB"));
}
#Test
#Order(1)
void shouldInsertAndSelectNewUser() {
// given
String test_username = insertDefaultUserDb();
// when
boolean userExists = underTest.selectUserByUsername(test_username).isPresent();
// then
assertThat(userExists).isTrue();
}
#Test
void shouldGetUidFromuser() {
// given
String test_username = insertDefaultUserDb();
// when
int uid = underTest.selectUserByUsername(test_username).flatMap(User::getId).orElse(0);
// then
assertThat(uid).isNotEqualTo(0);
}
#Test
void shouldUpdateUser() {
// given
// Init user
String test_username = insertDefaultUserDb();
int initUidDb = getTestUidFromUsernameDb(test_username);
// Updated user
String new_username = "new_account_420_updated";
String new_password = "password_updated";
User updatedUser = new User(new_username, new_password, UserRoleEnum.USER, false, true);
// Updated user
underTest.updateUserById(initUidDb, updatedUser);
User updatedUserDb = getTestUserFromUidDb(initUidDb);
int updatedUidDb = getUidFromUserObj(updatedUserDb);
// then
assertThat(updatedUidDb).isEqualTo(initUidDb);
assertThat(updatedUserDb.getUsername()).isEqualTo(new_username);
assertThat(updatedUserDb.getPassword()).isEqualTo(new_password);
}
private User getTestUserFromUidDb(int initUidDb) {
return underTest
.selectUserById(initUidDb)
.orElseThrow(() -> new RuntimeException("User not found in database"));
}
#Test
void shouldDeleteUser() {
// given
String test_username = insertDefaultUserDb();
User userFromDb = underTest.selectUserByUsername(test_username).get();
int uidFromDb = getUidFromUserObj(userFromDb);
// when
underTest.deleteUserById(uidFromDb);
boolean userExists = underTest.selectUserByUsername(test_username).isPresent();
// then
assertThat(userExists).isFalse();
}
#Test
void shouldGetSavedGames() {
// given
int[] games = {1, 2, 3}; // Test games
String test_username = insertDefaultUserDb();
User userFromDb = underTest.selectUserByUsername(test_username).orElseThrow();
int uid = getUidFromUserObj(userFromDb);
// when
for (int gid : games) {
underTest.insertSavedGame(uid, gid);
}
// then
assertThat(underTest.selectSavedGames(uid).size()).isNotEqualTo(0);
underTest.selectSavedGames(uid).forEach(g -> assertThat(g).isExactlyInstanceOf(Game.class));
}
#Test
void shouldDeleteSavedGame() {
// given
int[] games = {1, 2, 3}; // Test games
String test_username = insertDefaultUserDb();
User userFromDb = underTest.selectUserByUsername(test_username).orElseThrow();
int uid = getUidFromUserObj(userFromDb);
// when
for (int gid : games) {
underTest.insertSavedGame(uid, gid);
}
underTest.deleteSavedGame(uid, games[0]);
underTest.deleteSavedGame(uid, games[1]);
underTest.deleteSavedGame(uid, games[2]);
// then
assertThat(underTest.selectSavedGames(uid).size()).isEqualTo(0);
}
#Test
void shouldDetectNonUniqueUser() {
// given
String test_username = insertDefaultUserDb();
User userFromDb = underTest.selectUserByUsername(test_username).orElseThrow();
// when
boolean userIsUnique = underTest.userIsUnique(userFromDb);
// then
assertThat(userIsUnique).isFalse();
}
#Test
void shouldSelectAllUsers() {
// given
String test_username = insertUserDb("test_username1");
String test_username2 = insertUserDb("test_username2");
String test_username3 = insertUserDb("test_username3");
// when
List<User> users = underTest.selectAllUsers();
// then
assertThat(users.size()).isEqualTo(3);
users.forEach(
(User u) -> {
assertThat(u).isExactlyInstanceOf(User.class);
assertThat(u.getUsername()).isIn(test_username, test_username2, test_username3);
});
}
#Test
void shouldThrowOnNullId() {
// given
User user = new User("test_username", "test_password", UserRoleEnum.USER, false, true);
// when
Throwable thrown = catchThrowable(() -> underTest.userIsUnique(user));
// then
assertThat(thrown).isExactlyInstanceOf(IllegalArgumentException.class);
}
}
GameDbApplicationTests
package com.StruckCroissant.GameDB;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
class GameDbApplicationTests {
#Test
void contextLoads() {}
}
TestDbConfig
package com.StruckCroissant.GameDB;
import ch.vorburger.exec.ManagedProcessException;
import ch.vorburger.mariadb4j.DB;
import ch.vorburger.mariadb4j.DBConfiguration;
import ch.vorburger.mariadb4j.DBConfigurationBuilder;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.core.JdbcTemplate;
#Configuration
#PropertySource("classpath:test.properties")
public class TestDbConfig {
#Bean
public DBConfiguration dbConfig() {
DBConfigurationBuilder config = DBConfigurationBuilder.newBuilder();
//Port not set to allow new instances to be generated for batch of tests
config.setSecurityDisabled(true);
return config.build();
}
#Bean
public DataSource dataSource(
#Autowired DBConfiguration dbConfig,
#Value("${test.datasource.name}") String databaseName,
#Value("${test.datasource.username}") String datasourceUsername,
#Value("${test.datasource.username}") String datasourcePassword,
#Value("${test.datasource.driver-class-name}") String datasourceDriver)
throws ManagedProcessException {
DB db = DB.newEmbeddedDB(dbConfig);
db.start();
db.createDB(databaseName, "root", "");
db.source("db/init/schema.sql", databaseName);
DBConfiguration dbConfiguration = db.getConfiguration();
return DataSourceBuilder.create()
.driverClassName(datasourceDriver)
.url(dbConfiguration.getURL(databaseName))
.username(datasourceUsername)
.password(datasourcePassword)
.build();
}
#Bean("testTemplate")
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
I was able to resolve the issue by myself. The problem was due to inadvertent usage of both JUnit4 and Junit5.
I was using the #AfterEach annotation in my test classes, which is defined by Junit5 and therefore imported into the test classes. I was also adding JUnit5 via the spring-boot-starter-test dependency. After removing all refrences to JUnit5 the issue is resolved.

Apache Camel Context start failure

I am pretty new to Spring Boot, Apache Camel and the ActiveMQ broker. I am trying to create an application which will send a message to a queue which I am hosting locally using Camel for routing.
POM:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>2.22.0</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-activemq</artifactId>
<version>3.2.0</version>
</dependency>
MsgRouteBuilder:
public void configure() throws Exception {
from("direct:firstRoute")
.setBody(constant("Hello"))
.to("activemq:queue:myQueue");
}
application.yaml:
activemq:
broker-url: tcp://localhost:61616
user: meAd
password: meAd
MainApp.java:
package me.ad.myCamel;
import org.apache.camel.CamelContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import me.ad.myCamel.router.MessageRouteBuilder;
#SpringBootApplication
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
#EnableAspectJAutoProxy(proxyTargetClass = true)
#EnableCaching
public class MeAdApp implements CommandLineRunner {
private static final Logger LOG = LoggerFactory.getLogger(MeAdApp.class);
public static void main(String[] args) {
try {
SpringApplication.run(MeAdApp.class, args);
} catch (Exception ex) {
LOG.error(ex.getMessage(), ex);
}
}
#Override
public void run(String... args) throws Exception {
LOG.info("Starting MeAdApp...");
}
}
MyController.java :
#GetMapping(value = "/routing")
public boolean sendToMyQueue() {
sendMyInfo.startRouting();
return true;
}
SendMyInfo.java :
MsgRouteBuilder routeBuilder = new MsgRouteBuilder();
CamelContext ctx = new DefaultCamelContext();
public void startRouting(){
try {
ctx.addRoutes(routeBuilder);
ctx.start();
Thread.sleep(5 * 60 * 1000);
ctx.stop();
}
catch (Exception e) {
e.printStackTrace();
}
}
So, whenever I call my rest end point: /routing, I get the error:
java.lang.NoSuchMethodError: org.apache.camel.RuntimeCamelException.wrapRuntimeException(Ljava/lang/Throwable;)Ljava/lang/RuntimeException;`
Can anybody please point me to the right direction as to why I am getting this error? Any help is greatly appreciated .
You need to have the components of the same version. If you are using camel-core with 3.2.0, use camel-activemq 3.2.0. And, since you are using spring-boot, you can make use of the starter dependencies. Just add these and you are good to go.
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-activemq-starter</artifactId>
<version>3.2.0</version>
</dependency>

How do #Poller-s work in Spring Integration?

I am building an implementation of Sprint Integration with two PollableChannels:
Regular channel
Error channel
Messages are polled from the regular channel and processed. If there is an error during processing (e.g., an external service is unavailable), the message is sent into the error channel. From the error channel it is re-queued onto the regular channel, and the cycle continues until the message is successfully processed.
The idea is to poll the error channel infrequently, to give the processor some time to (hopefully) recover.
I have simulated this workflow in the following test:
package com.stackoverflow.questions.sipoller;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Poller;
import org.springframework.integration.annotation.Router;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.MessageChannels;
import org.springframework.messaging.Message;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.MessageBuilder;
import static org.awaitility.Awaitility.await;
import static org.awaitility.Durations.FIVE_MINUTES;
import static org.awaitility.Durations.ONE_HUNDRED_MILLISECONDS;
#SpringBootTest
class SiPollerApplicationTests {
private final static Logger LOG = LoggerFactory.getLogger(SiPollerApplicationTests.class);
private final static String QUEUE_CHANNEL_REGULAR = "queueChannelRegular";
private final static String QUEUE_CHANNEL_ERROR = "queueChannelError";
private final static String POLLER_PERIOD_REGULAR = "500"; // 0.5 second
private final static String POLLER_PERIOD_ERROR = "3000"; // 3 seconds
private final static AtomicInteger NUMBER_OF_ATTEMPTS = new AtomicInteger();
private final static AtomicInteger NUMBER_OF_SUCCESSES = new AtomicInteger();
private final static List<Instant> ATTEMPT_INSTANTS = Collections.synchronizedList(new ArrayList<>());
#Autowired
#Qualifier(QUEUE_CHANNEL_REGULAR)
private PollableChannel channelRegular;
#Test
void testTimingOfMessageProcessing() {
channelRegular.send(MessageBuilder.withPayload("Test message").build());
await()
.atMost(FIVE_MINUTES)
.with()
.pollInterval(ONE_HUNDRED_MILLISECONDS)
.until(
() -> {
if (NUMBER_OF_SUCCESSES.intValue() == 1) {
reportGaps();
return true;
}
return false;
}
);
}
private void reportGaps() {
List<Long> gaps = IntStream
.range(1, ATTEMPT_INSTANTS.size())
.mapToObj(
i -> Duration
.between(
ATTEMPT_INSTANTS.get(i - 1),
ATTEMPT_INSTANTS.get(i)
)
.toMillis()
)
.collect(Collectors.toList());
LOG.info("Gaps between attempts (in ms): {}", gaps);
}
#Configuration
#EnableIntegration
#Import(SiPollerApplicationTestEndpoint.class)
static class SiPollerApplicationTestConfig {
#Bean(name = QUEUE_CHANNEL_REGULAR)
public PollableChannel queueChannelRegular() {
return MessageChannels.queue(QUEUE_CHANNEL_REGULAR).get();
}
#Bean(name = QUEUE_CHANNEL_ERROR)
public PollableChannel queueChannelError() {
return MessageChannels.queue(QUEUE_CHANNEL_ERROR).get();
}
#Router(
inputChannel = QUEUE_CHANNEL_ERROR,
poller = #Poller(fixedRate = POLLER_PERIOD_ERROR)
)
public String retryProcessing() {
return QUEUE_CHANNEL_REGULAR;
}
}
#MessageEndpoint
static class SiPollerApplicationTestEndpoint {
#Autowired
#Qualifier(QUEUE_CHANNEL_ERROR)
private PollableChannel channelError;
#ServiceActivator(
inputChannel = QUEUE_CHANNEL_REGULAR,
poller = #Poller(fixedRate = POLLER_PERIOD_REGULAR)
)
public void handleMessage(Message<String> message) {
// Count and time attempts
int numberOfAttempts = NUMBER_OF_ATTEMPTS.getAndIncrement();
ATTEMPT_INSTANTS.add(Instant.now());
// First few times - refuse to process message and bounce it into
// error channel
if (numberOfAttempts < 5) {
channelError.send(message);
return;
}
// After that - process message
NUMBER_OF_SUCCESSES.getAndIncrement();
}
}
}
The pom.xml dependencies are:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
</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.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.0.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Note the configuration for Pollers:
private final static String POLLER_PERIOD_REGULAR = "500"; // 0.5 second
private final static String POLLER_PERIOD_ERROR = "3000"; // 3 seconds
The regular channel is supposed to be polled once in half a second, and the error channel — once in three seconds.
The test simulates outages during message processing: the first five attempts to process the message are rejected. Also, the test records the Instant of every processing attempt. In the end, on my machine, the test outputs:
Gaps between attempts (in ms): [1, 0, 0, 0, 0]
In other words, the message is re-tried almost immediately after each failure.
It seems to me that I fundamentally misunderstand how Pollers work in Spring Integration. So my questions are:
Why is there such a dissonance between the poller configuration and the actual frequency of polling.
Does Spring Integration provide a way to implement the pattern I have described?
There are two settings that can affect this behavior.
QueueChannel pollers will drain the queue by default; setMaxMessagesPerPoll(1) to only receive one message each poll.
Also, by default, the QueueChannel default timeout is 1 second (1000ms).
So the first poll may be sooner than you think; set it to 0 to immediately exit if there are no messages present in the queue.

Can't Access Atmosphere MetaBroadcaster in Spring Boot Controller

I have a simple chat application set up using Spring Boot (1.3.2) and Atmosphere (2.4.2).
Here is my WebSocketConfigurer:
package com.chat.shared.websocket;
import java.util.Collections;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.apache.catalina.Context;
import org.apache.tomcat.websocket.server.WsSci;
import org.atmosphere.cpr.AtmosphereFramework;
import org.atmosphere.cpr.AtmosphereServlet;
import org.atmosphere.cpr.MetaBroadcaster;
import org.springframework.boot.context.embedded.ServletContextInitializer;
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.chat.privatechat.ChatChannel;
#Configuration
public class WebSocketConfigurer implements ServletContextInitializer {
#Bean
public TomcatEmbeddedServletContainerFactory tomcatContainerFactory() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.setTomcatContextCustomizers(Collections.singletonList(tomcatContextCustomizer()));
return factory;
}
#Bean
public TomcatContextCustomizer tomcatContextCustomizer() {
return new TomcatContextCustomizer() {
#Override
public void customize(Context context) {
context.addServletContainerInitializer(new WsSci(), null);
}
};
}
#Bean
public AtmosphereServlet atmosphereServlet() {
return new AtmosphereServlet();
}
#Bean
public AtmosphereFramework atmosphereFramework() {
return atmosphereServlet().framework();
}
#Bean
public MetaBroadcaster metaBroadcaster() {
AtmosphereFramework framework = atmosphereFramework();
return framework.metaBroadcaster();
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
configureAtmosphere(atmosphereServlet(), servletContext);
}
private void configureAtmosphere(AtmosphereServlet servlet, ServletContext servletContext) {
ServletRegistration.Dynamic reg = servletContext.addServlet("atmosphereServlet", servlet);
reg.setInitParameter("org.atmosphere.cpr.packages", ChatChannel.class.getPackage().getName());
reg.setInitParameter("org.atmosphere.cpr.broadcasterClass", "org.atmosphere.plugin.hazelcast.HazelcastBroadcaster");
reg.setInitParameter("org.atmosphere.cpr.broadcaster.maxProcessingThreads", "10");
reg.setInitParameter("org.atmosphere.cpr.broadcaster.maxAsyncWriteThreads", "10");
reg.setInitParameter("org.atmosphere.interceptor.HeartbeatInterceptor.clientHeartbeatFrequencyInSeconds", "10");
servletContext.addListener(new org.atmosphere.cpr.SessionSupport());
reg.addMapping("/chat/*");
reg.setLoadOnStartup(0);
reg.setAsyncSupported(true);
}
}
And here is how I'm currently leveraging it in the ChatChannel:
package com.chat.privatechat;
import com.chat.privatechat.DTOs.ChatMessageDTO;
import com.chat.shared.localmessagebus.LocalMessage;
import com.chat.shared.localmessagebus.LocalMessageBus;
import org.atmosphere.config.service.Disconnect;
import org.atmosphere.config.service.Get;
import org.atmosphere.config.service.ManagedService;
import org.atmosphere.config.service.PathParam;
import org.atmosphere.config.service.Ready;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResourceEvent;
import org.atmosphere.cpr.AtmosphereResourceFactory;
import org.atmosphere.cpr.BroadcasterFactory;
import org.atmosphere.cpr.MetaBroadcaster;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import javax.inject.Inject;
#ManagedService(path = "/chat/{channel: [a-zA-Z][a-zA-Z_0-9]*}")
public class ChatChannel {
#PathParam("channel")
private String channelUuid;
#Inject
private BroadcasterFactory factory;
#Inject
private AtmosphereResourceFactory resourceFactory;
#Inject
private MetaBroadcaster metaBroadcaster;
#Get
public void init(AtmosphereResource resource) {
resource.getResponse().setCharacterEncoding(StandardCharsets.UTF_8.name());
}
#Ready
public void onReady(final AtmosphereResource resource) {
String userId = resource.getRequest().getHeader("userId");
System.out.println("User " + userId + " has connected.");
}
#Disconnect
public void onDisconnect(AtmosphereResourceEvent event) {
String userId = event.getResource().getRequest().getHeader("userId");
System.out.println("User " + userId + " has disconnected");
}
#org.atmosphere.config.service.Message(encoders = MessageEncoderDecoder.class, decoders = MessageEncoderDecoder.class)
public ChatMessageDTO onMessage(ChatMessageDTO chatMessage) throws IOException {
LocalMessageBus.manager().send(new LocalMessage<ChatMessageDTO>(chatMessage));
return chatMessage;
}
}
This setup works great (users in a conversation are connected to a "channel" and the messages are sent/received immediately. LocalMessageBus is a simple message bus that will eventually be replaced by a proper message broker).
Although I don't have a use case for this, I went to set up a MetaBroadcaster in my ChatController to see if I could broadcast messages from there. Unfortunately, I am not able to properly inject/reference the MetaBroadcaster as it is always null. Here's the important bits of the ChatController:
package com.chat.privatechat;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import com.chat.privatechat.DTOs.ChatMessageDTO;
import com.chat.privatechat.DTOs.ChatSessionInitializationDTO;
import com.chat.privatechat.DTOs.EstablishedChatSessionDTO;
import com.chat.shared.http.JSONResponseHelper;
import com.chat.user.UserService;
import com.chat.user.exceptions.IsSameUserException;
import com.chat.user.exceptions.UserNotFoundException;
import com.chat.user.strategies.UserRetrievalByIdStrategy;
#Controller
public class ChatController {
#Autowired
private ChatService chatService;
#Autowired
private BeanFactory beanFactory;
#Autowired
private UserService userService;
#Inject
private MetaBroadcaster metaBroadcaster;
#RequestMapping(value="/api/chat/session", method=RequestMethod.PUT, produces="application/json", consumes="application/json")
public ResponseEntity<String> establishChatSession(#RequestBody ChatSessionInitializationDTO initialChatSession) throws IsSameUserException, BeansException, UserNotFoundException {
...
}
#RequestMapping(value="/api/chat/session/{channelUuid}", method=RequestMethod.GET, produces="application/json")
public ResponseEntity<String> getExistingChatSessionMessages(#PathVariable("channelUuid") String channelUuid) {
...
}
}
Injecting/Autowiring MetaBroadcaster nor bringing in metaBroadcaster bean from the BeanFactory work. I've searched and searched and searched without a good solution. It seems like the bean in not accessible in this Spring Controller context and I'm running out of ideas.
Thanks you for any input!
NOTE: These are the Atmosphere deps I have:
<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-runtime</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-spring</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>atmosphere-javascript</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-hazelcast</artifactId>
<version>2.4.2</version>
</dependency>

Categories

Resources