Dropwizard Counter Not Retaining value in Spring Boot App - spring-boot

I am trying to register the # of spring boot apps I've in my pvt cloud environment. Logic is to use Counter metric to increment during startUp and decrement during shut down. All the different deployments will publish to the same metricPreFix(--assumption). Following is the graph I get in Graphite:
#application.properties
spring.metrics.export.delay-millis=100
Why do I see the value to come down to 0 even when the app is running? I have tried with 2 different implementations with same result. Can someone please point out the gap in my understanding? PFB the code
#Component
public class AppStartupBean implements CommandLineRunner {
private static final String appMetricName = "MyApp.currentCount.GraphOne";
private static final String metricName = "MyApp.currentCount.GraphTwo";
#Autowired
DropwizardMetricServices dwMetricService;
#Autowired
private MetricRegistry registry;
#Override
public void run(String... arg0) throws Exception {
dwMetricService.increment(appMetricName);
Counter counter = registry.counter(metricName);
counter.inc();
}
}

The configuration for DropwizardMetricServices was wrong. I was using
#Bean
public DropwizardMetricServices dwMetricService(MetricRegistry registry) {
return new DropwizardMetricServices(registry);
}
Instead we should just #Autowire DropwizardMetricServices as needed. PFB
When Dropwizard metrics are in use, the default CounterService and
GaugeService are replaced with a DropwizardMetricServices, which is a
wrapper around the MetricRegistry (so you can #Autowired one of those
services and use it as normal).

Related

Spring Bookt Kafka ABSwitchCluster

I couldn't find any example to swtich between kafka cluster .
Anyone has implmeneted this class ABSwitchCluster from Spring Kafka.
https://docs.spring.io/spring-kafka/reference/html/
I tried with below code, but its not switching cluster.
#RestController
public class ApacheKafkaWebController {
#Autowired
ConsumerKakfaConfiguration configuration;
#Autowired
private KafkaListenerEndpointRegistry registry;
#Autowired
private ABSwitchCluster switcher;
#GetMapping(value = "/switch")
public String producer() {
registry.stop();
switcher.secondary();
registry.start();
return "switched!";
}
}
and swticher bean here:
#Bean
public ABSwitchCluster switcher() {
return new ABSwitchCluster("127.0.0.1:9095", "127.0.0.1:9096");
}
Could you please tell me am I missing anything here?, still its running in 9095 port.
See this answer and this test.
Basically, you switch the cluster and reset the connections by stopping and starting listener containers and resetting the producer factory.

Passing an external property to JUnit's extension class

My Spring Boot project uses JUnit 5. I'd like to setup an integration test which requires a local SMTP server to be started, so I implemented a custom extension:
public class SmtpServerExtension implements BeforeAllCallback, AfterAllCallback {
private GreenMail smtpServer;
private final int port;
public SmtpServerExtension(int port) {
this.port = port;
}
#Override
public void beforeAll(ExtensionContext extensionContext) {
smtpServer = new GreenMail(new ServerSetup(port, null, "smtp")).withConfiguration(GreenMailConfiguration.aConfig().withDisabledAuthentication());
smtpServer.start();
}
#Override
public void afterAll(ExtensionContext extensionContext) {
smtpServer.stop();
}
}
Because I need to configure the server's port I register the extension in the test class like this:
#SpringBootTest
#AutoConfigureMockMvc
#ExtendWith(SpringExtension.class)
#ActiveProfiles("test")
public class EmailControllerIT {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Value("${spring.mail.port}")
private int smtpPort;
#RegisterExtension
// How can I use the smtpPort annotated with #Value?
static SmtpServerExtension smtpServerExtension = new SmtpServerExtension(2525);
private static final String RESOURCE_PATH = "/mail";
#Test
public void whenValidInput_thenReturns200() throws Exception {
mockMvc.perform(post(RESOURCE_PATH)
.contentType(APPLICATION_JSON)
.content("some content")
).andExpect(status().isOk());
}
}
While this is basically working: How can I use the smtpPort annotated with #Value (which is read from the test profile)?
Update 1
Following your proposal I created a custom TestExecutionListener.
public class CustomTestExecutionListener implements TestExecutionListener {
#Value("${spring.mail.port}")
private int smtpPort;
private GreenMail smtpServer;
#Override
public void beforeTestClass(TestContext testContext) {
smtpServer = new GreenMail(new ServerSetup(smtpPort, null, "smtp")).withConfiguration(GreenMailConfiguration.aConfig().withDisabledAuthentication());
smtpServer.start();
};
#Override
public void afterTestClass(TestContext testContext) {
smtpServer.stop();
}
}
The listener is registered like this:
#TestExecutionListeners(value = CustomTestExecutionListener.class, mergeMode = MERGE_WITH_DEFAULTS)
When running the test the listener gets called but smtpPort is always 0, so it seems as if the #Value annotation is not picked up.
I don't think you should work with Extensions here, or in general, any "raw-level" JUnit stuff (like lifecycle methods), because you won't be able to access the application context from them, won't be able to execute any custom logic on beans and so forth.
Instead, take a look at Spring's test execution listeners abstraction
With this approach, GreenMail will become a bean managed by spring (probably in a special configuration that will be loaded only in tests) but since it becomes a bean it will be able to load the property values and use #Value annotation.
In the test execution listener you'll start the server before the test and stop after the test (or the whole test class if you need that - it has "hooks" for that).
One side note, make sure you mergeMode = MergeMode.MERGE_WITH_DEFAULTS as a parameter to #TestExecutionListeners annotation, otherwise some default behaviour (like autowiring in tests, dirty context if you have it, etc) won't work.
Update 1
Following Update 1 in the question. This won't work because the listener itself is not a spring bean, hence you can't autowire or use #Value annotation in the listener itself.
You can try to follow this SO thread that might be helpful, however originally I meant something different:
Make a GreenMail a bean by itself:
#Configuration
// since you're using #SpringBootTest annotation - it will load properties from src/test/reources/application.properties so you can put spring.mail.port=1234 there
public class MyTestMailConfig {
#Bean
public GreenMail greenMail(#Value(${"spring.mail.port"} int port) {
return new GreenMail(port, ...);
}
}
Now this configuration can be placed in src/test/java/<sub-package-of-main-app>/ so that in production it won't be loaded at all
Now the test execution listener could be used only for running starting / stopping the GreenMail server (as I understood you want to start it before the test and stop after the test, otherwise you don't need these listeners at all :) )
public class CustomTestExecutionListener implements TestExecutionListener {
#Override
public void beforeTestClass(TestContext testContext) {
GreenMail mailServer =
testContext.getApplicationContext().getBean(GreenMail.class);
mailServer.start();
}
#Override
public void afterTestClass(TestContext testContext) {
GreenMail mailServer =
testContext.getApplicationContext().getBean(GreenMail.class);
mailServer.stop();
}
}
Another option is autowiring the GreenMail bean and using #BeforeEach and #AfterEach methods of JUnit, but in this case you'll have to duplicate this logic in different Test classes that require this behavour. Listeners allow reusing the code.

Alfresco Process Services with TaskListener #Autowired issue

I am using Alfresco Process Services and have created a created a spring boot project for custom logic like TaskListeners and Delegations. I am creating the jar file from this maven project and copying it into webapps/activiti-app/WEB-INF/lib folder.
I have a simple TaskListener as below which is getting called on Task start. But the #Autowired variables are always null.
package com.activiti.extension.bean;
#Component("myTaskListener")
public class MyTaskListener implements TaskListener {
#Autowired
UserService userService;
#Override
public void notify(DelegateTask task) {
logger.info("userService: " +userService); // Always prints null
}
Finally I was able to make it work. I was putting the task listener in the class field of the Task properties with full package name. Now I am putting Delegate expression like ${myTaskListener} and it worked...
Thank you all for your time and help
This is because your your MyTaskListener is annotated as #Component or at least being ignored by spring during init. for auto-wiring capabilities spring requires this annotation (or similar to this) under the provided #ComponentScan packages to consider the class as a bean otherwise it will take as a normal java class and hence the #autowired is of no use in your case.
This below code is worked for me
#Component
public class MyTaskListener implements TaskListener {
public static UserService getUserServiceObject() {
return SpringApplicationContextHolder.getApplicationContext().getBean(UserService.class);
}
#Override
public void notify(DelegateTask delegateTask) {
//UserService Object, It is not null now
getUserServiceObject();
}
}
#Component
public class SpringApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
There is also one more way to get to your custom service "UserService" using Alfresco Spring Application context.
First access ServiceRegistry (registry used for accessing Alfresco Services but also any other custom service):
ServiceRegistry serviceRegistry = (ServiceRegistry) Context.getProcessEngineConfiguration().getBeans().get(ActivitiConstants.SERVICE_REGISTRY_BEAN_KEY);
Then get custom service UserService:
QName qname = QName.createQName("UserService");
UserService userService = (UserService) serviceRegistry.getService(qname);

#EnableRedisHttpSession + Spring Boot ignoring server.session.timeout on application.yml

I have a project with Spring Boot 1.3.3 [another stuff] and Redis configurated to manage sessions, i.e., #EnableRedisHttpSession. The application works well and stores the information on Redis regularly.
The problem that I'm facing is that, different from what documentation says, whether I define or not a server.session.timeout, the Redis always is using the default value for its annotation attribute (maxInactiveIntervalInSeconds) that is: 1800
Here, the documentation that I followed: http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-session.html
I've also tried the approach defined by #rwinch here https://github.com/spring-projects/spring-session/issues/110 but also without success.
Updating ......
My configuration file as requested:
#First attempt (server.session.timeout) following the Spring documentation mentioned
server:
session:
timeout: 10
spring:
#session timeout under spring (as mentioned by M Deinum in comment - unfortunately doesnt work)
session:
timeout: 10
redis:
host: 192.168.99.101
port: 6379
Beside that, I've also tried to implement a SessionListener that was in charge of setting the timeout (something like this):
public class SessionListener implements HttpSessionListener {
#Value(value = "${server.session.timeout}")
private int timeout;
#Override
public void sessionCreated(HttpSessionEvent event) {
if(event!=null && event.getSession()!=null){
event.getSession().setMaxInactiveInterval(timeout);
}
}
...
It still didn't result in a correct scenario. I'm really racking my brain :|
Please guys, am I missing some point? Does anyone else have faced it?
Thanks in advance.
Another solution:
#EnableRedisHttpSession
public class HttpSessionConfig {
#Value("${server.session.timeout}")
private Integer maxInactiveIntervalInMinutes;
#Inject
private RedisOperationsSessionRepository sessionRepository;
#PostConstruct
private void afterPropertiesSet() {
sessionRepository.setDefaultMaxInactiveInterval(maxInactiveIntervalInMinutes * 60);
}
In this way you use the default configuration, and just add your timeout. So you maintain the default HttpSessionListener, and you don't need to use an ApplicationListener to set the time out, just one time, in the application lifecycle.
Well, just in case someone is facing the same situation, we have 2 ways to workaround:
I. Implement the following:
#EnableRedisHttpSession
public class Application {
//some other codes here
#Value("${spring.session.timeout}")
private Integer maxInactiveIntervalInSeconds;
#Bean
public RedisOperationsSessionRepository sessionRepository( RedisConnectionFactory factory) {
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(factory);
sessionRepository.setDefaultMaxInactiveInterval(maxInactiveIntervalInSeconds);
return sessionRepository;
}
Unfortunately, I had to implement a listener in order to perform additional actions when a session expires. And, when you define a RedisOperationsSessionRepository, you don't have a HttpSessionListener anymore (instead of it, you have a SessionMessageListener, as described here: http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository). Because of this question, the 2nd approach was required.
II. To overcome the problem:
#EnableRedisHttpSession
public class Application implements ApplicationListener{
#Value("${spring.session.timeout}")
private Integer maxInactiveIntervalInSeconds;
#Autowired
private RedisOperationsSessionRepository redisOperation;
#Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
redisOperation.setDefaultMaxInactiveInterval(maxInactiveIntervalInSeconds);
}
}
...
Assuming that none of them are the desirable out-of-box setup, at least they allow me to continue in my PoC.
#EnableRedisHttpSession(maxInactiveIntervalInSeconds = 60)
You can remove EnableRedisHttpSession annotation, instead, set the property:
spring.session.store-type=redis
Both spring.session.timeout and server.servlet.session.timeout will work. Please note spring.session.timeout will override server.servlet.session.timeout per my test.
Extend RedisHttpSessionConfiguration and do init in #PostConstruct method.
#Configuration
public class HttpSessionConfig extends RedisHttpSessionConfiguration {
#Value("${spring.session.timeout}")
private Integer sessionTimeoutInSec;
#Value("${spring.session.redis.namespace}")
private String sessionRedisNamespace;
#Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
#PostConstruct
public void initConfig() throws Exception {
this.setMaxInactiveIntervalInSeconds(sessionTimeoutInSec);
this.setRedisNamespace(sessionRedisNamespace);
}
}

Spring job launcher not able to access services

Hi Still learning hope someone can fill in the blanks
I have JSVC quartz spring batch system that has been running for over a year. The Job launcher needs to connect to 2 spring services that successfully run in other parts of the system. Each service has a number of sql repositories or services injected into it.
Please make note of package declarations. for the application context entry.
package com.mycompany.business.services.impl;
....
#Service
public class BatchProcessService {
private final DomainSepcificRepository1 rep1;
...
private final DomainSepcificRepositoryN repN;
#Inject
public BatchProcessService(Final final DomainSepcificRepository1 rep1,..., final DomainSepcificRepositoryN repN) {
// injected values assigned to instance variables.
}
public List <...> findByCriteria(.....)(
.....
}
}
and
package com.mycompany.business.services.impl;
....
#Service
public class EmailNotificationServiceImpl implements EmailNotificationService {
private UserService userService;
private final MailMessage mailMessage;
private final MailTransport mailTransport;
#Inject
public EmailNotificationServiceImpl(final UserService userService, final MailMessage mailMessage, final MailTransport mailTransport) {
this.userService = userService;
this.mailMessage = mailMessage;
this.mailTransport = mailTransport;
}
.....
public void notifySupportStaff(....){
.....
}
}
In my application context xml file, there is the following line that should allow my job launcher to see and instantiate the above services. I think "base-package=" specifies the packages to look for #services, #components and #repositories that can be injected.
<context:component-scan base-package="com.mycompany.common.batch, com.mycompany.batch, com.mycompany.business.services" >
<context:exclude-filter type="assignable" expression="com.mycompany.common.batch.scheduler.service.MyCompanySchedulerService"/>
</context:component-scan>
I think that #Component should allow this class to see the services and instaniate them and any dependancies (other services) they have.
For some reason the jsvc system only wants to invoke class below with the NO arg constructor. and it is not injecting the 2 services.
My unit tests are able to test the method using the service only if I provide a 2 argument constructor for MOCK services.
lll
Any thoughts on why batch system cannot inject the dependencies?
package com.mycompany.batch.scheduler;
....
#Inject
private BatchProcessService batchProcessService;
#Inject
private EmailNotificationService emailNotificationService;
#Component
public class MyCompanySchedulerJobLauncher extends SchedulerJobLauncher {
public MyCompanySchedulerJobLauncher() {
super();
}
// #Inject
public MyCompanySchedulerJobLauncher(final BatchProcessService batchProcessService, final EmailNotificationService emailNotificationService) {
super();
this.batchProcessService = batchProcessService;
this.emailNotificationService = emailNotificationService;
}
#Override
public int processJob(final JobExecutionContext context) throws JobRestartException, JobExecutionAlreadyRunningException, ParseException {
......
if(batchProcessSerive.findByCriteria(....).size() == 0) {
emailNotificationService.notifySupport(...)
}
}
Well Don't I feel silly.
The problem was that at the point where I was assuming I could/would inject dependancies. The application context was private. Once I made my application context protected and to get the services. all worked

Resources