I have a job that looks like this:
#Named
public class MyCamelRouteBuilder extends RouteBuilder {
private static final String JOB_NAME = "abc";
private static final String JOB_METHOD_NAME = "xyz";
private final MyJob myJob;
#Inject
public MyCamelRouteBuilder(MyJob myJob) {
super();
this.myJob = myJob;
}
#Override
public void configure() {
fromF("direct:%s", JOB_NAME)
.routeId(JOB_NAME)
.bean(myJob, JOB_METHOD_NAME)
.end();
fromF("master:some_name_1/some_name_2:scheduler:%s?delay=%s", JOB_NAME, 1234)
.routeId("JobTimer")
.toF("direct:%s", JOB_NAME)
.end();
}
}
A very simplified version of the job class:
#Named
public class MyJob {
private MyJob() {}
}
public void xyz() {
}
}
This does work and it does gets triggered as expected.
The problem starts here:
Now, I also want to create a REST controller that will be able to trigger the exact same job. Something like this:
#Named
#RestController
#RequestMapping
#Validated
public class MyController {
private static final String JOB_NAME = "abc";
private final ProducerTemplate producerTemplate;
#Inject
public MyController(
ProducerTemplate producerTemplate
) {
this.producerTemplate = producerTemplate;
}
#PostMapping(path = "/my_endpoint")
public String run() throws Exception {
producerTemplate.requestBody("direct:" + JOB_NAME);
return "ok";
}
}
But once it reaches this line, the job is not triggered and the request call keeps hanging.
producerTemplate.requestBody("direct:" + JOB_NAME);
Any ideas?
The fix for my problem:
#Named
#RestController
#RequestMapping
#Validated
public class MyController {
private static final String JOB_NAME = "abc";
#Produce("direct:" + JOB_NAME)
private final ProducerTemplate producerTemplate;
private final CamelContext context;
#Inject
public MyController(
ProducerTemplate producerTemplate, CamelContext context
) {
this.producerTemplate = producerTemplate;
this.context = context;
}
#PostMapping(path = "/my_endpoint")
public String run() throws Exception {
Exchange exchange = new DefaultExchange(context);
producerTemplate.send(exchange);
return "ok";
}
}
Related
I have a final class Constants, which holds some final data.
#Component
public final class Constants {
public final String TOKEN;
public final String HOST;
public final String TELEGRAM;
public Constants(#Value("${myapp.bot-token}") String token,
#Value("${myapp.host}") String host) {
this.TOKEN = token;
this.HOST = host;
this.TELEGRAM = "https://api.telegram.org/bot" + TOKEN;
}
}
The problem is that, when I want to use a variable as #PostMapping path, I faced this error:
Attribute value must be constant
#RestController
#RequestMapping
public class Controller {
private final Constants constants;
#Autowired
public Controller(Constants constants) {
this.constants = constants;
}
#PostMapping(constants.TOKEN)// Problem is here
public ResponseEntity<?> getMessage(#RequestBody String payload) {
return new ResponseEntity<HttpStatus>(HttpStatus.OK);
}
}
I've tried to load TOKEN in my controller class but faced the same issue.
#RestController
#RequestMapping
public class Controller {
#Value("${myapp.bot-token}") String token
private String token;
#PostMapping(token)// Problem is here
public ResponseEntity<?> getMessage(#RequestBody String payload) {
return new ResponseEntity<HttpStatus>(HttpStatus.OK);
}
}
When I do something like this the problem will gone. But I don't want to declare my token in source-code.
#RestController
#RequestMapping
public class Controller {
private final String TOKEN = "SOME-TOKEN";
#PostMapping(TOKEN)// No problem
public ResponseEntity<?> getMessage(#RequestBody String payload) {
return new ResponseEntity<HttpStatus>(HttpStatus.OK);
}
}
Can anyone please give me a solution to this?
Try to paste string with property path inside #PostMapping annotation. Like this
#GetMapping(value = "${app.path}")
public String hello() {
return "hello";
}
You can only use a constant (i.e. a final static variable) as the parameter for an annotation.
Example:
#Component
class Constants {
public final static String FACEBOOK = "facebook";
}
#RestController
class Controller {
#PostMapping(Constants.FACEBOOK)
public ResponseEntity<ResponseBody> getMessage(#RequestBody String payload) {
return new ResponseEntity<>(HttpStatus.OK);
}
}
You must use builder pattern(use Lombok for ease) and freeze the value that you are getting from the properties and then use that in your program.
I have a spring-boot application.
I have entity:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Document(COLLECTION_NAME)
public class PersonEntity {
public static final String COLLECTION_NAME = "person_info";
private static final String PERSON_NAME = "person_name";
#Id
private PersonId id;
#Field(name = PERSON_NAME)
private String personName;
#Indexed(name = "ttl_index", expireAfterSeconds=20)
private LocalDateTime date;
}
I have a repository interface:
public interface PersonRepository {
void saveWithTtl(PersonEntity entity);
}
The repository implementation:
#Slf4j
#Repository
public class PersonRepositoryImpl implements PersonRepository {
private final int expireAfterSeconds;
private final ReactiveMongoTemplate mongoTemplate;
public PersonRepositoryImpl(#Value("${ttl.index}") int expireAfterSeconds,
ReactiveMongoTemplate mongoTemplate) {
this.expireAfterSeconds = expireAfterSeconds;
this.mongoTemplate = mongoTemplate;
}
#Override
public void saveWithTtl(PersonEntity entity) {
mongoTemplate.indexOps(PersonEntity.class)
.ensureIndex(new Index().on(PersonEntity.CREATED_AT, ASC)
.expire(expireAfterSeconds)).subscribe(result -> log.info("Ttl index has been created: {}", result));
mongoTemplate.save(entity).subscribe(result -> log.info("Entity has been saved: {}", result));
}
}
And, finally, I have test that does not work:
#DataMongoTest
#Testcontainers
public class PersonRepositoryIT {
#Autowired
private ReactiveMongoTemplate mongoTemplate;
#Autowired
private PersonRepository repository;
#Container
private static MongoDbContainer mongoDbContainer = new MongoDbContainer();
#AfterEach
void cleanUp() {
repository.deleteAll();
}
#DynamicPropertySource
static void registerMongoProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongoDbContainer::getReplicaSetUrl);
}
#Test
public void shouldCreateAndDeleteRecordsAfterDelay_whenSaveWithTtl_givenDefinedTll() {
//given
PersonEntity givenEntity = PersonEntity.builder().createdAt(LocalDateTime.now())
.personName("Joe")
.id(PERSON_ID).build();
//when
repository.saveWithTtl(givenEntity);
//then
StepVerifier.create(mongoTemplate.estimatedCount(PersonEntity.COLLECTION_NAME))
.expectNext(1L)
.verifyComplete();
}
}
On expectNext it fails coz it returns 0 and not 1.
mongoTemplate.estimatedCount returns 0
When I test the repository from Postman (repo is calling inside service), it creates the document in MongoDB wil ttl index, as expected.
In test fonfig I have set the ${ttl.index} to 20.
What am I doing wrong?
I don't know if it is to late, but I had the same problem today.
I Found your question looking for an answer for my problem hahahaha.
This snipped worked for me:
#Container
public static MongoDBContainer container = new MongoDBContainer(DockerImageName.parse("mongo:6"));
#DynamicPropertySource
static void mongoDbProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", container::getReplicaSetUrl);
}
#Bean
public ReactiveMongoTemplate reactiveMongoTemplate() throws Exception {
container.start();
ConnectionString connectionString = new ConnectionString(container.getReplicaSetUrl());
MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.build();
MongoClient mongoClient = MongoClients.create(mongoClientSettings);
return new ReactiveMongoTemplate(mongoClient,"test");
}
Apparently ReactiveMongoTemplate is not being injected by default, then I created my own Bean an it worked
Note: I already look at and tried some approaches on SO e.g. How to test Spring's declarative caching support on Spring Data repositories?, but as most of them old, I cannot make them work properly and I need a solution with the latest library versions. So, I would be appreciated if you have a look at the question and help.
#Service
#EnableCaching
#RequiredArgsConstructor
public class DemoServiceImpl implements DemoService {
private static final String CACHE_NAME = "demoCache";
private final LabelRepository labelRepository;
private final LabelTranslatableRepository translatableRepository;
private final LanguageService languageService;
#Override
public LabelDTO findByUuid(UUID uuid) {
final Label label = labelRepository.findByUuid(uuid)
.orElseThrow(() -> new EntityNotFoundException("Not found."));
final List<LabelTranslatable> translatableList = translatableRepository.findAllByEntityUuid(uuid);
return new LabelDTO(Pair.of(label.getUuid(), label.getKey()), translatableList);
}
}
I created the following Unit Test to test caching for the nethod above:
#EnableCaching
#ImportAutoConfiguration(classes = {
CacheAutoConfiguration.class,
RedisAutoConfiguration.class
})
#ExtendWith(MockitoExtension.class)
class TextLabelServiceImpl_deneme_Test {
#Autowired
private CacheManager cacheManager;
#InjectMocks
private LabelService labelService;
#Mock
private LabelRepository labelRepository;
#Mock
private LabelTranslatableRepository translatableRepository;
#Test
void test_Cache() {
UUID uuid = UUID.randomUUID();
final TextLabel textLabel = new TextLabel();
textLabel.setId(1);
textLabel.setKey("key1");
TextLabelTranslatable textLabelTranslatable = new TextLabelTranslatable();
textLabelTranslatable.setEntityUuid(uuid);
textLabelTranslatable.setLanguage(SupportedLanguage.fr);
textLabelTranslatable.setValue("value1");
final List<TextLabelTranslatable> translatableList = new ArrayList<>();
translatableList.add(textLabelTranslatable);
when(labelRepository.findByUuid(uuid)).thenReturn(Optional.of(textLabel));
when(translatableRepository.findAllByEntityUuid(uuid)).thenReturn(translatableList);
TextLabelDTO result1 = labelService.findByUuid(uuid);
TextLabelDTO result2 = labelService.findByUuid(uuid);
assertEquals(result1, result2);
Mockito.verify(translatableRepository, Mockito.times(1)).findAllByEntityUuid(uuid);
}
I am not sure if there is a missing part in my test, but at the last line (Mockito.verify()), it returns 2 instead of 1 that means caching not works. But it is working properly and there is a problem in my test I think. How should I complete the unit test to check the caching properly?
You need to annotate the service class method with #Cacheable. Try to follow the code in this tutorial. The following test code works as expected
#Import({CacheConfig.class, DemoServiceImpl.class})
#ExtendWith(SpringExtension.class)
#EnableCaching
#ImportAutoConfiguration(classes = {
CacheAutoConfiguration.class,
RedisAutoConfiguration.class
})
class DemoServiceImplTest {
#MockBean
private LabelRepository labelRepository;
#Autowired
private DemoServiceImpl demoService;
#Autowired
private CacheManager cacheManager;
#TestConfiguration
static class EmbeddedRedisConfiguration {
private final RedisServer redisServer;
public EmbeddedRedisConfiguration() {
this.redisServer = new RedisServer();
}
#PostConstruct
public void startRedis() {
redisServer.start();
}
#PreDestroy
public void stopRedis() {
this.redisServer.stop();
}
}
#Test
void givenRedisCaching_whenFindItemById_thenItemReturnedFromCache() {
UUID id = UUID.randomUUID();
Label aLabel = new Label(id, "label");
Mockito.when(labelRepository.findById(id)).thenReturn(Optional.of(aLabel));
Label labelCacheMiss = demoService.findByUuid(id);
Label labelCacheHit = demoService.findByUuid(id);
Mockito.verify(labelRepository, Mockito.times(1)).findById(id);
}
}
With this service class code:
#Service
#RequiredArgsConstructor
#EnableCaching
public class DemoServiceImpl {
public static final String CACHE_NAME = "demoCache";
private final LabelRepository labelRepository;
#Cacheable(value = CACHE_NAME)
public Label findByUuid(UUID uuid) {
return labelRepository.findById(uuid)
.orElseThrow(() -> new EntityNotFoundException("Not found."));
}
}
And this CacheConfig:
#Configuration
public class CacheConfig {
#Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
return (builder) -> builder
.withCacheConfiguration(DemoServiceImpl.CACHE_NAME,
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)));
}
#Bean
public RedisCacheConfiguration cacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(60))
.disableCachingNullValues()
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer()));
}
}
In CrossSellOffersServiceAdapter class, this statement:
crossSellOffersConnectBDS.getBDSCustomerInfo(channelId, customerId, cinSuffix,
countryCode);
Should return the value as it is mocked. But it is returning null value in CrossSellOffersServiceAdapterTest class.
public class CrossSellOffersServiceAdapter implements CrossSellOffersService {
#Autowired
private CrossSellOffersConnectBDS crossSellOffersConnectBDS;
#Autowired
private CrossSellOffersConnectCMP crossSellOffersConnectCMP;
#Autowired
private BDSCustomerHoldings bdsCustomerHoldings;
private static final Logger LOGGER = LoggerFactory.getLogger(CrossSellOffersServiceAdapter.class);
#Override
public Offers getApplicableOffers(String channelId, String customerId, String cinSuffix, String countryCode,
String interactionPoint, String sessionId, Integer numberOfOffers) throws CrossSellOffersException {
bdsCustomerHoldings = crossSellOffersConnectBDS.getBDSCustomerInfo(channelId, customerId, cinSuffix,
countryCode);
CMPOffer cmpOffer = crossSellOffersConnectCMP.getCMPOffers(bdsCustomerHoldings, interactionPoint, sessionId,
numberOfOffers);
Offers offers = getOffers(cmpOffer);
return offers;
}
}
public class CrossSellOffersServiceAdapterTest {
#InjectMocks
private CrossSellOffersServiceAdapter crossSellOffersService;
#Mock
private CrossSellOffersConnectBDSAdapter crossSellOffersConnectBDS;
#Mock
private CrossSellOffersConnectCMPAdapter crossSellOffersConnectCMP;
#Mock
private RestTemplate restTemplate;
#Mock
OffersRequest offersRq;
#Mock
private BDSRequest bdsRequest ;
#Mock
private BDSCustomerHoldings bdsResponse;
#Test
public void getApplicableOffersTest() throws CrossSellOffersException {
Mockito.when(crossSellOffersConnectBDS.getBDSCustomerInfo("MBSG", "S9718016D", "00", "SG")).thenReturn(sampleBDSResponse());
Mockito.when(crossSellOffersConnectCMP.getCMPOffers(bdsResponse, "NEW_CC_ADDON", "IBOXS007", 1)).thenReturn(CrossSellOffersConnectCMPAdapterTest.sampleCMPOffer());
Offers offers = crossSellOffersService.getApplicableOffers("MBSG", "IBOXS007", "00", "SG","NEW_CC_ADDON", "S9718016D", 1);
assertNotNull(offers, "response is not null");
}
}
Think you are missing the mockito init:
import org.mockito.MockitoAnnotations;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
I have a Rest API Created with Spring Framework 3.2.4. I am trying to write test cases in the WebApplicationContext. I see from the server log that the xml configuration files are loaded but it fails while executing the TestContextManager.
ROR org.springframework.test.context.TestContextManager- Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener#4c980278] to prepare test instance
Here is my Test Class:
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class })
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {"file:WebContent/WEB-INF/applicationContext.xml","file:WebContent/WEB-INF/spring-security.xml","file:WebContent/WEB-INF/spring-servlet.xml"})
public class AppControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setUp() {
//Mockito.reset();//awesome_service
mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
}
#Test
public void testGetSignupForm() throws Exception {
this.mockMvc.perform(get("/apps"))
.andExpect(status().isOk());
}
}
Here is my Controller
#Controller
#RequestMapping(value = "/apps")
public class AppController
{
#Resource(name = "appAPIService")
private AppAPIService appAPIService;
#Resource(name = "applicationVersionService")
private ApplicationVersionService applicationVersionService;
#Resource(name = "applicationService")
private ApplicationService applicationService;
#Autowired
Validator validator;
private static final Logger LOGGER = LoggerFactory.getLogger(AppController.class);
#InitBinder
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
binder.registerCustomEditor(Model.class, "model", new PropertyEditorSupport() {
#Override
public void setAsText(String text) {
BasicDBObject obj = (BasicDBObject)JSON.parse(text);
Model model = new Model();
model.setPrice(obj.getInt(ApplicationFields.MODEL_PRICE,0));
model.setTrial(obj.getInt(ApplicationFields.MODEL_TRIAL,0));
model.setType(obj.getString(ApplicationFields.MODEL_TYPE));
setValue(model);
}
});
}
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<String> find(HttpServletRequest request, #RequestParam(value="query", required=false) String queryString, #RequestParam(value="sort", required=false) String sortString, #RequestParam(value="pageNumber", required=false) Integer pageNumber, #RequestParam(value="limit", required=false) Integer limit, #RequestParam(value="userId", required=false) String hostUserId)
{
ObjectId marketplaceId = null;
try
{
marketplaceId = AuthenticationUtils.getUserId();
BasicDBObject query;
try
{
query = FormatUtils.toJsonObject(queryString);
}
catch(Exception e)
{
return ResponseUtils.badRequest("query", "Invalid query: " + queryString);
}
BasicDBObject sort;
try
{
sort = FormatUtils.toJsonObject(sortString);
}
catch(Exception e)
{
return ResponseUtils.badRequest("sort", "Invalid sort: " + sortString);
}
return appAPIService.find(marketplaceId, query, sort, pageNumber, limit, hostUserId);
}
catch (Exception e)
{
String message = "Unable to find apps";
ToStringBuilder builder = new ToStringBuilder(message);
LoggingUtils.addToBuilder(builder, request);
builder.append("marketplaceId", AuthenticationUtils.getUserId());
LOGGER.error(builder.toString(), e);
return ResponseUtils.internalError();
}
}
....
}