This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new 259c3c83d1 [SYNCOPE-1105] Generating random UUID as operation key, 
storing into MDC and displaying via log4j's PatternLayout
259c3c83d1 is described below

commit 259c3c83d1ecc804b7fd2bc4a4587eb68288841f
Author: Francesco Chicchiriccò <ilgro...@apache.org>
AuthorDate: Wed Jan 4 16:43:55 2023 +0100

    [SYNCOPE-1105] Generating random UUID as operation key, storing into MDC 
and displaying via log4j's PatternLayout
---
 .../core/rest/cxf/IdRepoRESTCXFContext.java        | 33 ++++++------
 .../syncope/core/rest/cxf/MDCInInterceptor.java    | 58 ++++++++++++++++++++++
 .../core/rest/cxf/RestServiceExceptionMapper.java  |  6 ++-
 ....java => ThreadLocalCleanupOutInterceptor.java} | 20 +++++---
 .../core/provisioning/api/job/JobDelegate.java     |  2 +
 .../java/job/AbstractInterruptableJob.java         |  1 -
 .../java/job/AbstractSchedTaskJobDelegate.java     | 13 +++++
 .../java/job/report/DefaultReportJobDelegate.java  | 13 +++++
 docker/core/src/main/resources/log4j2.xml          |  2 +-
 fit/core-reference/src/main/resources/log4j2.xml   |  2 +-
 10 files changed, 119 insertions(+), 31 deletions(-)

diff --git 
a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java
 
b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java
index 51755161c8..dd0d1de78e 100644
--- 
a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java
+++ 
b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java
@@ -28,7 +28,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ThreadPoolExecutor;
-import javax.servlet.ServletRequestListener;
+import javax.validation.Validator;
 import org.apache.cxf.Bus;
 import org.apache.cxf.endpoint.Server;
 import org.apache.cxf.jaxrs.ext.ContextProvider;
@@ -138,7 +138,6 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Qualifier;
 import 
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import 
org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -193,15 +192,15 @@ public class IdRepoRESTCXFContext {
 
     @ConditionalOnMissingBean
     @Bean
-    public BeanValidationProvider validationProvider() {
-        return new BeanValidationProvider();
+    public MDCInInterceptor mdcInInterceptor() {
+        return new MDCInInterceptor();
     }
 
     @ConditionalOnMissingBean
     @Bean
-    public JAXRSBeanValidationInInterceptor validationInInterceptor(final 
BeanValidationProvider validationProvider) {
+    public JAXRSBeanValidationInInterceptor validationInInterceptor(final 
Validator validator) {
         JAXRSBeanValidationInInterceptor validationInInterceptor = new 
JAXRSBeanValidationInInterceptor();
-        validationInInterceptor.setProvider(validationProvider);
+        validationInInterceptor.setProvider(new 
BeanValidationProvider(validator));
         return validationInInterceptor;
     }
 
@@ -220,6 +219,12 @@ public class IdRepoRESTCXFContext {
         return gzipOutInterceptor;
     }
 
+    @ConditionalOnMissingBean
+    @Bean
+    public ThreadLocalCleanupOutInterceptor threadLocalCleanupOutInterceptor() 
{
+        return new ThreadLocalCleanupOutInterceptor();
+    }
+
     @ConditionalOnMissingBean
     @Bean
     public RestServiceExceptionMapper restServiceExceptionMapper(final 
Environment env) {
@@ -317,9 +322,11 @@ public class IdRepoRESTCXFContext {
             final JacksonXMLProvider xmlProvider,
             final JacksonJsonProvider jsonProvider,
             final DateParamConverterProvider dateParamConverterProvider,
+            final MDCInInterceptor mdcInInterceptor,
             final JAXRSBeanValidationInInterceptor validationInInterceptor,
             final GZIPInInterceptor gzipInInterceptor,
             final GZIPOutInterceptor gzipOutInterceptor,
+            final ThreadLocalCleanupOutInterceptor 
threadLocalCleanupOutInterceptor,
             final OpenApiFeature openapiFeature,
             final Bus bus,
             final ApplicationContext ctx,
@@ -351,11 +358,9 @@ public class IdRepoRESTCXFContext {
                 addDomainFilter,
                 addETagFilter));
 
-        restContainer.setInInterceptors(List.of(
-                gzipInInterceptor,
-                validationInInterceptor));
+        restContainer.setInInterceptors(List.of(mdcInInterceptor, 
validationInInterceptor, gzipInInterceptor));
 
-        restContainer.setOutInterceptors(List.of(gzipOutInterceptor));
+        restContainer.setOutInterceptors(List.of(gzipOutInterceptor, 
threadLocalCleanupOutInterceptor));
 
         restContainer.setFeatures(List.of(openapiFeature));
 
@@ -363,14 +368,6 @@ public class IdRepoRESTCXFContext {
         return restContainer.create();
     }
 
-    @ConditionalOnMissingBean
-    @Bean
-    public ServletListenerRegistrationBean<ServletRequestListener> 
listenerRegistrationBean() {
-        ServletListenerRegistrationBean<ServletRequestListener> bean = new 
ServletListenerRegistrationBean<>();
-        bean.setListener(new ThreadLocalCleanupListener());
-        return bean;
-    }
-
     @ConditionalOnMissingBean
     @Bean
     public AccessTokenService accessTokenService(final AccessTokenLogic 
accessTokenLogic) {
diff --git 
a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/MDCInInterceptor.java
 
b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/MDCInInterceptor.java
new file mode 100644
index 0000000000..5117610665
--- /dev/null
+++ 
b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/MDCInInterceptor.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.rest.cxf;
+
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+import org.apache.cxf.phase.Phase;
+import org.apache.syncope.core.provisioning.api.job.JobDelegate;
+import org.apache.syncope.core.spring.security.SecureRandomUtils;
+import org.slf4j.MDC;
+
+/**
+ * Populate MDC with sensible information, for the current thread.
+ *
+ * MDC is then cleared up by {@link 
org.apache.syncope.core.rest.cxf.ThreadLocalCleanupOutInterceptor}
+ */
+public class MDCInInterceptor extends AbstractPhaseInterceptor<Message> {
+
+    // just same value as 
org.apache.cxf.ext.logging.event.LogEvent.KEY_EXCHANGE_ID
+    protected static final String KEY_EXCHANGE_ID = "exchangeId";
+
+    public MDCInInterceptor() {
+        super(Phase.PRE_INVOKE);
+    }
+
+    @Override
+    public void handleMessage(final Message message) throws Fault {
+        Exchange exchange = message.getExchange();
+
+        // this ensures we are not duplicating nor conflicting with
+        // 
org.apache.cxf.ext.logging.AbstractLoggingInterceptor.createExchangeId(Message)
+        String exchangeId = (String) exchange.get(KEY_EXCHANGE_ID);
+        if (exchangeId == null) {
+            exchangeId = SecureRandomUtils.generateRandomUUID().toString();
+            exchange.put(KEY_EXCHANGE_ID, exchangeId);
+        }
+
+        MDC.put(JobDelegate.OPERATION_ID, exchangeId);
+    }
+}
diff --git 
a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
 
b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
index d2690d0c34..8f2d253cf8 100644
--- 
a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
+++ 
b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java
@@ -320,7 +320,9 @@ public class RestServiceExceptionMapper implements 
ExceptionMapper<Exception> {
             message = env.getProperty("errMessage." + UNIQUE_MSG_KEY);
         }
 
-        return Optional.ofNullable(message)
-                .orElseGet(() -> (ex.getCause() == null) ? ex.getMessage() : 
ex.getCause().getMessage());
+        return Optional.ofNullable(message).
+                orElseGet(() -> Optional.ofNullable(ex.getCause()).
+                map(Throwable::getMessage).
+                orElseGet(() -> ex.getMessage()));
     }
 }
diff --git 
a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/ThreadLocalCleanupListener.java
 
b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/ThreadLocalCleanupOutInterceptor.java
similarity index 70%
rename from 
core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/ThreadLocalCleanupListener.java
rename to 
core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/ThreadLocalCleanupOutInterceptor.java
index e6c5c39983..39b893a690 100644
--- 
a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/ThreadLocalCleanupListener.java
+++ 
b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/ThreadLocalCleanupOutInterceptor.java
@@ -18,27 +18,31 @@
  */
 package org.apache.syncope.core.rest.cxf;
 
-import javax.servlet.ServletRequestEvent;
-import javax.servlet.ServletRequestListener;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+import org.apache.cxf.phase.Phase;
 import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
 import org.identityconnectors.common.l10n.CurrentLocale;
 import 
org.identityconnectors.framework.impl.api.local.ThreadClassLoaderManager;
+import org.slf4j.MDC;
 
 /**
- * Remove any known thread-local variable when the servlet request is 
destroyed.
+ * Removes any known thread-local variable.
  */
-public class ThreadLocalCleanupListener implements ServletRequestListener {
+public class ThreadLocalCleanupOutInterceptor extends 
AbstractPhaseInterceptor<Message> {
 
-    @Override
-    public void requestInitialized(final ServletRequestEvent sre) {
-        // nothing to do while setting up this request (and thread)
+    public ThreadLocalCleanupOutInterceptor() {
+        super(Phase.POST_INVOKE);
     }
 
     @Override
-    public void requestDestroyed(final ServletRequestEvent sre) {
+    public void handleMessage(final Message message) throws Fault {
         FormatUtils.clear();
 
         ThreadClassLoaderManager.clearInstance();
         CurrentLocale.clear();
+
+        MDC.clear();
     }
 }
diff --git 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobDelegate.java
 
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobDelegate.java
index 329cad2845..9addc1b2e0 100644
--- 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobDelegate.java
+++ 
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobDelegate.java
@@ -23,6 +23,8 @@ package org.apache.syncope.core.provisioning.api.job;
  */
 public interface JobDelegate {
 
+    String OPERATION_ID = "operation.id";
+
     void interrupt();
 
     boolean isInterrupted();
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java
index a858d2385b..3889ee56ae 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractInterruptableJob.java
@@ -36,7 +36,6 @@ public abstract class AbstractInterruptableJob implements 
InterruptableJob {
         public boolean isInterrupted() {
             return false;
         }
-
     };
 
     public JobDelegate getDelegate() {
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
index cbace864bd..29d422d5cb 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
@@ -35,11 +35,13 @@ import 
org.apache.syncope.core.provisioning.api.job.JobManager;
 import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
 import 
org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
+import org.apache.syncope.core.spring.security.SecureRandomUtils;
 import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobExecutionException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.transaction.annotation.Transactional;
@@ -131,6 +133,13 @@ public abstract class AbstractSchedTaskJobDelegate<T 
extends SchedTask> implemen
             return;
         }
 
+        boolean manageOperationId = Optional.ofNullable(MDC.get(OPERATION_ID)).
+                map(operationId -> false).
+                orElseGet(() -> {
+                    MDC.put(OPERATION_ID, 
SecureRandomUtils.generateRandomUUID().toString());
+                    return true;
+                });
+
         String executor = 
Optional.ofNullable(context.getMergedJobDataMap().getString(JobManager.EXECUTOR_KEY)).
                 orElse(securityProperties.getAdminUser());
         TaskExec<SchedTask> execution = 
taskUtilsFactory.getInstance(taskType).newTaskExec();
@@ -181,6 +190,10 @@ public abstract class AbstractSchedTaskJobDelegate<T 
extends SchedTask> implemen
                 result,
                 task,
                 null);
+
+        if (manageOperationId) {
+            MDC.remove(OPERATION_ID);
+        }
     }
 
     /**
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java
index 1004593e9e..2ec4777791 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/DefaultReportJobDelegate.java
@@ -46,9 +46,11 @@ import 
org.apache.syncope.core.provisioning.api.event.JobStatusEvent;
 import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate;
 import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
 import org.apache.syncope.core.spring.implementation.ImplementationManager;
+import org.apache.syncope.core.spring.security.SecureRandomUtils;
 import org.quartz.JobExecutionException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.transaction.annotation.Transactional;
 import org.xml.sax.helpers.AttributesImpl;
@@ -135,6 +137,13 @@ public class DefaultReportJobDelegate implements 
ReportJobDelegate {
             return;
         }
 
+        boolean manageOperationId = Optional.ofNullable(MDC.get(OPERATION_ID)).
+                map(operationId -> false).
+                orElseGet(() -> {
+                    MDC.put(OPERATION_ID, 
SecureRandomUtils.generateRandomUUID().toString());
+                    return true;
+                });
+
         // 1. create execution
         ReportExec execution = entityFactory.newEntity(ReportExec.class);
         execution.setStatus(ReportExecStatus.STARTED);
@@ -240,6 +249,10 @@ public class DefaultReportJobDelegate implements 
ReportJobDelegate {
             execution.setMessage(reportExecutionMessage.toString());
             execution.setEnd(OffsetDateTime.now());
             reportExecDAO.save(execution);
+
+            if (manageOperationId) {
+                MDC.remove(OPERATION_ID);
+            }
         }
     }
 }
diff --git a/docker/core/src/main/resources/log4j2.xml 
b/docker/core/src/main/resources/log4j2.xml
index 770fc91c7b..91ab9b6611 100644
--- a/docker/core/src/main/resources/log4j2.xml
+++ b/docker/core/src/main/resources/log4j2.xml
@@ -22,7 +22,7 @@ under the License.
   <appenders>
 
     <Console name="console" target="SYSTEM_OUT" follow="true">
-      <PatternLayout pattern="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd 
HH:mm:ss.SSS}} %highlight{${LOG_LEVEL_PATTERN:-%5p}}{FATAL=red blink, 
ERROR=red, WARN=yellow bold, INFO=green, DEBUG=green bold, TRACE=blue} 
[%11.11t] %style{%-60.60c{60}}{cyan} : 
%m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
+      <PatternLayout pattern="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd 
HH:mm:ss.SSS}} %notEmpty{[operation.id=%X{operation.id}] 
}%highlight{${LOG_LEVEL_PATTERN:-%5p}}{FATAL=red blink, ERROR=red, WARN=yellow 
bold, INFO=green, DEBUG=green bold, TRACE=blue} [%11.11t] 
%style{%-60.60c{60}}{cyan} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
     </Console>
 
   </appenders>
diff --git a/fit/core-reference/src/main/resources/log4j2.xml 
b/fit/core-reference/src/main/resources/log4j2.xml
index cdd8cc39ba..4ab455fe53 100644
--- a/fit/core-reference/src/main/resources/log4j2.xml
+++ b/fit/core-reference/src/main/resources/log4j2.xml
@@ -24,7 +24,7 @@ under the License.
                              
filePattern="${log.directory}/core-%d{yyyy-MM-dd}.log.gz"
                              immediateFlush="false" append="true">
       <PatternLayout>
-        <pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
+        <pattern>%d{HH:mm:ss.SSS} %notEmpty{[operation.id=%X{operation.id}] 
}%-5level %logger - %msg%n</pattern>
       </PatternLayout>
       <Policies>
         <TimeBasedTriggeringPolicy/>

Reply via email to