How to add custom MeterRegisty for Spring Boot 2 - java

I am currently exporting Actuator metrics for my Spring Boot Webflux project to DataDog with 10 seconds interval. I would like to add another exporter for one of our internal system that is not in the list of supported backends. Looking at the implementation from DataDogMeterRegistry I came up with the following.
public interface ExternalConfig extends StepRegistryConfig {
ExternalConfig DEFAULT = k -> null;
#Override
default String prefix() {
return "vwexternal";
}
}
#Slf4j
public class ExternalMeterRegistry extends StepMeterRegistry {
public ExternalMeterRegistry() {
this(ExternalConfig.DEFAULT, Clock.SYSTEM);
}
public ExternalMeterRegistry(StepRegistryConfig config, Clock clock) {
super(config, clock);
}
#Override
protected void publish() {
log.info("HERE");
}
#Override
protected TimeUnit getBaseTimeUnit() {
return TimeUnit.MILLISECONDS;
}
}
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
Metrics.addRegistry(new ExternalMeterRegistry());
}
}
However this is not working since no logs are printed.
My question is how can I add and implement another MeterRegistry for Spring Boot Micrometer?

You need to start the publishing. Compare with the LoggingMeterRegistry
In your constructor something like:
start(new NamedThreadFactory("vw-metrics-publisher"))

Related

Using a constraint with a parmeterized constructor in Spring Boot application

I have a Spring Boot application where an interface has a constraint:
#Constraint(
validatedBy = {
MyValidator.class
})
public #interface MyInterface {
...
}
I'm using Togglz to enable/disable some features and one class where I want to implement some Togglz code is in MyValidator.
public class MyValidator
implements MyInterface<
MyInterface, TDLDetails> {
private FeatureManager featureManager;
public static final Feature FEATURE_ONE =
new NamedFeature("FEATURE_ONE ");
public MyValidator(FeatureManager featureManager) {
this.featureManager = featureManager;
}
#Override
public void initialize(MyInterface arg0) {}
#Override
public boolean isValid(TDLDetails tdlDetails, ConstraintValidatorContext ctx)
{
if (!featureManager.isActive(FEATURE_ONE)) {
if (tdlDetails.getType().equals(TDLType.ANA)) {
return (tdlDetails.getPlaceOfIssue() != null);
}
}
return true;
}
}
Am I wrong to have the parameterized constructor? It seems I need it for Togglz but I'm not sure how it should be used by #Constraint if it takes a parameter. What's the correct way to do this?

Java Spring : How to get a detail info for entity in real time using WebSocket?

I implemented a Spring Boot Application with WebSocket. This application show a list of entities (BaseStatistica) in real time with summary info (count of answered, no-answered and busy calls).
I would like to extend this application so it is possibly to click on the name of entity,move to other page and watch a more detail (ExtendedStatistica) info about calls counts grouped by gateways and trunks for this entity, also in real time.
My code :
WebSocketConfiguration.java
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/socket")
.setAllowedOrigins("*")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
MonitoringController.java
#PostMapping("/queue_info/")
public void getQueueInfo() throws JSONException {
List<QueueInfo> queueInfoList = new ArrayList<>();
template.convertAndSend("/topic/queue_info", queueInfoList);
}
BaseStatistica.java
public class BaseStatistica {
private String queueNum;
private int gotAnsweredCalls;
private int gotNoAnswerCalls;
private int gotBusyCalls;
}
ExtendedStatistica.java
public class ExtendedStatistica extends BaseStatistica {
private String gatewayName;
private String trunkName;
}
and client side - Angular 7
app.component.ts
export class AppComponent {
queueInfoList: String[]=[];
constructor(private webSocketService: WebSocketService) {
let stompClientQueueInfo = this.webSocketService.connect();
stompClientQueueInfo.connect({}, frame => {
stompClientQueueInfo.subscribe('/topic/queue_info', response => {
this.queueInfoList = JSON.parse(response.body);
})
});
}
}
websocket.service.ts
export class WebSocketService {
constructor() { }
connect() {
let socket = new SockJs(`http://localhost:8081/websocket-backend/socket`);
let stompClient = Stomp.over(socket);
return stompClient;
}
}
I guess that here it is possible somehow change the structure of entity classes, but I'm interested in the ability to view detailed information on the fly. Is it possible to create Websocket endpoints dynamically?

Setting 'relaxedQueryChars' for embedded Tomcat

How can I set relaxedQueryChars for Spring Boot embedded Tomcat?
The connector attribute described here, but Spring Boot documentation has no such parameter listed.
How to set Tomcat's Connector attributes in general?
I am not sure if you can do this with properties file. I believe this should work
#Component
public class MyTomcatWebServerCustomizer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
#Override
public void customize(Connector connector) {
connector.setAttribute("relaxedQueryChars", "yourvaluehere");
}
});
}
}
If you are using Spring Boot 2.x then you need to use WebSeerverFactoryCustomizer as given below.
#Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory>
containerCustomizer(){
return new EmbeddedTomcatCustomizer();
}
private static class EmbeddedTomcatCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
connector.setAttribute("relaxedPathChars", "<>[\\]^`{|}");
connector.setAttribute("relaxedQueryChars", "<>[\\]^`{|}");
});
}
}
I did this as a working solution for me:
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer(){
return new MyCustomizer();
}
private static class MyCustomizer implements EmbeddedServletContainerCustomizer {
#Override
public void customize(ConfigurableEmbeddedServletContainer factory) {
if(factory instanceof TomcatEmbeddedServletContainerFactory) {
customizeTomcat((TomcatEmbeddedServletContainerFactory) factory);
}
}
void customizeTomcat(TomcatEmbeddedServletContainerFactory factory) {
factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
connector.setAttribute("relaxedPathChars", "<>[\\]^`{|}");
connector.setAttribute("relaxedQueryChars", "<>[\\]^`{|}");
});
}
}
The simplest method is to configure the server (add a line to application.properties).
You can add something like this:
server.tomcat.relaxed-path-chars=<,>,etc
Spring Documentation Comma-separated list of additional unencoded characters that should be allowed in URI paths. Only "< > [ \ ] ^ ` { | }" are allowed.*

Use #PathParam(javax.websocket.server.PathParam) in WebSocketConfigurer for Spring Boot application

I have created a spring boot application in which I want to use Web Sockets. When I am using it without parameters its working fine. Below is the code without the parameters
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ABC(), "/getABC").setAllowedOrigins("*");
registry.addHandler(new XYZ(), "/getXYZ").setAllowedOrigins("*");
}
}
But now I need to pass a parameter to it using #PathParam. I am not able to use it in this configuration like
registry.addHandler(new XYZ(), "/getXYZ{someId}").setAllowedOrigins("*");
My Handler code:
public class XYZ extends TextWebSocketHandler {
static List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
String someId;
public XYZ() {
}
public XYZ(#PathParam(value = "someId") String someId) {
this.someId= someId;
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// the messages will be broadcasted to all users.
sessions.add(session);
}
}
I think there is some problem with the syntax, try using
public XYZ(#PathParam("someId") String someId)

Spring Boot - Alternative to Time.schedule?

I want to do something like javascript's setInterval(function, interval)/setTimeout(function, timeout) in Spring Boot.
I found the #Scheduled annotation that has the fixedRate argument, but as an annotation I cannot change the rate dynamically (Or can I?)
For now I am using java.util.Timer, but I would rather use Spring. Is there a way?
Can I get a Scheduler instance and work with it dynamically?
thanks!
You may use a Trigger which lets you dynamically control the next execution. You need to implement SchedulingConfigurer, another answer covers exactly this:
Scheduling a job with Spring programmatically (with fixedRate set dynamically)
EDIT to answer comments:
nextExecutionTime is called on and on and on... The next time the task (and nextExecutionTime) is called is defined by this:
nextExecutionTime.setTime(lastActualExecutionTime != null ? lastActualExecutionTime : new Date());
nextExecutionTime.add(Calendar.MILLISECOND, numberOfMillisecondsBeforeCallingTheTask);
All you need to do is have this numberOfMillisecondsBeforeCallingTheTask value changed.
Example:
#RestController
public class MyController {
public static int triggerDelay = 1000;
#RequestMapping("/changetrigger/{val}")
public void test(#PathVariable int val){
this.triggerDelay = val;
}
}
#SpringBootApplication
#EnableScheduling
public class Launcher implements SchedulingConfigurer{
public static void main(String[] args){
new SpringApplicationBuilder() //
.sources(Launcher.class)//
.run(args);
}
#Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(100);
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
;
taskRegistrar.addTriggerTask(new TriggerTask(new Runnable() {
#Override
public void run() {
System.out.println("blah");
System.out.println(System.currentTimeMillis());
}
}, new Trigger() {
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
Calendar nextExecutionTime = new GregorianCalendar();
nextExecutionTime.setTime(new Date());
nextExecutionTime.add(Calendar.MILLISECOND, MyController.triggerDelay);
System.out.println(System.currentTimeMillis());
return nextExecutionTime.getTime();
}}));
}
}
Notice how the dynamic value MyController.triggerDelay is used for the next execution. So if you change the number, the next execution time will be changed. You'll see if you put a breakpoint inside nextExecutionTime.
You can use #Scheduled(fixedRateString = "${spring.boot.schedule.rate}") for your case, where the spring.boot.schedule.rate is the external properties in application.properties
spring.boot.schedule.rate=5000
Misunderstand the question, above is just the externalize the properties.
For the dynamic solution, maybe this should be work, using the spEL in the annonation:
#Service
public class ScheduledService {
#Autowired
private FixRateProperty fixRateProperty;
#Scheduled(fixedRateString = "#{fixRateProperty.fixRate}")
private void reportCurrentTime() {
System.out.println(new Date());;
}
}
This is the FixRateProperty
#Component
public class FixRateProperty {
private Long fixRate = 500L;
public Long getFixRate() {
return fixRate;
}
public void setFixRate(Long fixRate) {
this.fixRate = fixRate;
}
}
so you can externalize the rate in the properties or set the fixRate somewhere.
Found a solution that works for my case.
In Main.java:
#SpringBootApplication
#ConfigurationProperties
#EnableScheduling
public class Main {
#Bean
ThreadPoolTaskScheduler taskScheduler() {
return new ThreadPoolTaskScheduler();
}
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
In Service.java (Called from a rest controller):
#Service
public class Service {
private static final Logger log = LoggerFactory.getLogger(Service.class);
private final TaskScheduler scheduler;
#Autowired
public Service(TaskScheduler scheduler) {
this.scheduler = scheduler;
}
public void startTask(int inteval) {
scheduler.schedule(() -> log.info("Doing work"), triggerContext -> {
if (some_condition) {
ZonedDateTime now = ZonedDateTime.now();
return Date.from(now.plusSeconds(interval).toInstant());
} else {
// Stop the execution
return null;
}
});
}
}
This solution works, but I'm not sure it is the correct way.
You are welcome to comment below, and I might change the solution if I get a suggestion I find helpful.

Categories