-
Notifications
You must be signed in to change notification settings - Fork 1
Listen events
Fabrice Daugan edited this page Nov 1, 2025
·
1 revision
Sometime you want to create a plugin aware of events in the applications. Some events:
- Application event
- API calls
- Error management
Application events are Spring related events such as: start and stop of the context.
To listner these event, implements the SpringApplicationRunListener class.
As exemple of this listenener, the plugin listener
This section cover the API event listener dirrectly with CXF.
Create a class like this, and each API call (succeed of failed) will trigger the filter(...) method.
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.core.UriInfo;
import jakarta.ws.rs.ext.Provider;
import lombok.extern.slf4j.Slf4j;
import org.apache.cxf.jaxrs.impl.AbstractPropertiesImpl;
import org.ligoj.bootstrap.core.resource.AbstractMapper;
import org.ligoj.bootstrap.resource.system.configuration.ConfigurationResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
/**
* A response filter able to transform Null results to 404 HTTP response when
*/
@Provider
@Slf4j
@Component
public class OnEventFilter extends AbstractMapper implements ContainerResponseFilter {
/**
* Ignored class from payload.
*/
private static final Class<?>[] IGNORED_CLASSES = {UriInfo.class, SecurityContext.class, ServletConfig.class,
ServletRequest.class, ServletResponse.class, InputStream.class, ApplicationContext.class};
@Autowired
private ConfigurationResource configurationResource;
/**
* Convert complex or Servlet like technical object to their class name only.
*/
private Object convertForPayload(Object parameter) {
if (Arrays.stream(IGNORED_CLASSES).anyMatch(c -> parameter != null && c.isAssignableFrom(parameter.getClass()))) {
// This parameter is dropped
return "<" + parameter.getClass().getSimpleName() + ">";
}
return parameter;
}
@Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) {
try {
var responseEntity = responseContext.getEntity();
var principal = requestContext.getSecurityContext().getUserPrincipal().getName();
var response = responseEntity == null ? null : responseEntity.toString();
var exchange = ((AbstractPropertiesImpl) requestContext).getMessage().getExchange();
@SuppressWarnings("unchecked") final var params = exchange.getInMessage().getContent(List.class).stream()
.map(this::convertForPayload).toList();
log.info("API event {} /{}, params={}, response={}, principal={}, status={}",
requestContext.getMethod(), requestContext.getUriInfo().getPath(), params, response, principal,
responseContext.getStatus());
} catch (final Exception e) {
// Log only errors without interrupting the main flow
log.warn("API event handling failed", e);
}
}
}If you want to trigger a long running task, you should use a deffered execution
void execute(final Runnable runnable) {
CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS).execute(runnable);
}As exemple of this usage, the hook management