SLING-8665: Added sanitization for queue names.
diff --git a/src/main/java/org/apache/sling/event/impl/jobs/stats/GaugeSupport.java b/src/main/java/org/apache/sling/event/impl/jobs/stats/GaugeSupport.java
index 493c97a..a73dca8 100644
--- a/src/main/java/org/apache/sling/event/impl/jobs/stats/GaugeSupport.java
+++ b/src/main/java/org/apache/sling/event/impl/jobs/stats/GaugeSupport.java
@@ -23,9 +23,13 @@
 import org.apache.sling.event.jobs.Statistics;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
-/** Helper class that holds gauges for relevant queue statistics. */
+/**
+ * Helper class that holds gauges for relevant queue statistics.
+ */
 class GaugeSupport {
 
     static final String GAUGE_NAME_PREFIX = "event.jobs";
@@ -40,89 +44,130 @@
     static final String AVG_PROCESSING_TIME_METRIC_SUFFIX = ".averageProcessingTime";
 
     private final MetricRegistry metricRegistry;
-    private Map<String, Gauge> gaugeList = new HashMap<>();
+    private final Map<String, Gauge<Long>> gaugeList = new HashMap<>();
+    private final Set<String> gaugeMetricNames = new HashSet<>();
+    private final String queueName;
 
-    /** Create a new GaugeSupport instance for the global queue.
+    /**
+     * Create a new GaugeSupport instance for the global queue.
      *
      * @param globalQueueStats the global queueStats
-     * @param metricRegistry the (sling) metric registry */
-    GaugeSupport(final Statistics globalQueueStats, MetricRegistry metricRegistry) {
+     * @param metricRegistry   the (sling) metric registry
+     */
+    GaugeSupport(final Statistics globalQueueStats, final MetricRegistry metricRegistry) {
         this(null, globalQueueStats, metricRegistry);
     }
 
-    /** Creates a new GaugeSupport instance. Registers gauges for jobs (based on the queueStats).
+    /**
+     * Creates a new GaugeSupport instance. Registers gauges for jobs (based on the queueStats).
      *
-     * @param queueName name of the queue
-     * @param queueStats queueStats of that queue
-     * @param metricRegistry the (sling) metric registry */
-    GaugeSupport(String queueName, final Statistics queueStats, MetricRegistry metricRegistry) {
+     * @param queueName      name of the queue
+     * @param queueStats     queueStats of that queue
+     * @param metricRegistry the (sling) metric registry
+     */
+    GaugeSupport(final String queueName, final Statistics queueStats, final MetricRegistry metricRegistry) {
         this.metricRegistry = metricRegistry;
-        if (metricRegistry != null) {
-            gaugeList.put(getMetricName(queueName, FINISHED_METRIC_SUFFIX), new Gauge() {
+        this.queueName = getSanitizedQueueName(queueName);
+        if (metricRegistry != null && queueStats != null) {
+            gaugeList.put(FINISHED_METRIC_SUFFIX, new Gauge<Long>() {
 
-                public Object getValue() {
+                public Long getValue() {
                     return queueStats.getNumberOfFinishedJobs();
                 }
             });
-            gaugeList.put(getMetricName(queueName, CANCELLED_METRIC_SUFFIX), new Gauge() {
+            gaugeList.put(CANCELLED_METRIC_SUFFIX, new Gauge<Long>() {
 
-                public Object getValue() {
+                public Long getValue() {
                     return queueStats.getNumberOfCancelledJobs();
                 }
             });
-            gaugeList.put(getMetricName(queueName, FAILED__METRIC_SUFFIX), new Gauge() {
+            gaugeList.put(FAILED__METRIC_SUFFIX, new Gauge<Long>() {
 
-                public Object getValue() {
+                public Long getValue() {
                     return queueStats.getNumberOfFailedJobs();
                 }
             });
-            gaugeList.put(getMetricName(queueName, QUEUED_METRIC_SUFFIX), new Gauge() {
+            gaugeList.put(QUEUED_METRIC_SUFFIX, new Gauge<Long>() {
 
-                public Object getValue() {
+                public Long getValue() {
                     return queueStats.getNumberOfQueuedJobs();
                 }
             });
-            gaugeList.put(getMetricName(queueName, PROCESSED_METRIC_SUFFIX), new Gauge() {
+            gaugeList.put(PROCESSED_METRIC_SUFFIX, new Gauge<Long>() {
 
-                public Object getValue() {
+                public Long getValue() {
                     return queueStats.getNumberOfProcessedJobs();
                 }
             });
-            gaugeList.put(getMetricName(queueName, ACTIVE_METRIC_SUFFIX), new Gauge() {
+            gaugeList.put(ACTIVE_METRIC_SUFFIX, new Gauge<Long>() {
 
-                public Object getValue() {
+                public Long getValue() {
                     return queueStats.getNumberOfActiveJobs();
                 }
             });
-            gaugeList.put(getMetricName(queueName, AVG_WAITING_TIME_METRIC_SUFFIX), new Gauge() {
+            gaugeList.put(AVG_WAITING_TIME_METRIC_SUFFIX, new Gauge<Long>() {
 
-                public Object getValue() {
+                public Long getValue() {
                     return queueStats.getAverageWaitingTime();
                 }
             });
-            gaugeList.put(getMetricName(queueName, AVG_PROCESSING_TIME_METRIC_SUFFIX), new Gauge() {
+            gaugeList.put(AVG_PROCESSING_TIME_METRIC_SUFFIX, new Gauge<Long>() {
 
-                public Object getValue() {
+                public Long getValue() {
                     return queueStats.getAverageProcessingTime();
                 }
             });
-            for (Map.Entry<String, Gauge> entry : gaugeList.entrySet()) {
-                metricRegistry.register(entry.getKey(), entry.getValue());
-            }
         }
     }
 
-    private String getMetricName(String queueName, String metricSuffix) {
-        return GAUGE_NAME_PREFIX + (queueName != null ? "." + QUEUE_PREFIX + "." + queueName : "") + metricSuffix;
-    }
-
-    /** Unregisters all job gauges of the queue. */
+    /**
+     * Unregisters all job gauges of the queue.
+     */
     void shutdown() {
         if (metricRegistry != null) {
-            for (String metricName : gaugeList.keySet()) {
-                metricRegistry.remove(metricName);
+           for (String metricName : gaugeMetricNames) {
+                try {
+                    metricRegistry.remove(metricName);
+                } catch (RuntimeException e) {
+                    // ignore
+                }
             }
         }
     }
 
+    /**
+     * Initializes the metric registry with the gauges.
+     */
+    void initialize() {
+        for (Map.Entry<String, Gauge<Long>> entry : gaugeList.entrySet()) {
+            registerWithSuffix(entry.getKey(), 0, entry.getValue());
+        }
+    }
+
+    private void registerWithSuffix(String suffix, int count, Gauge<Long> value) {
+        try {
+            String metricName = getMetricName(queueName, count, suffix);
+            metricRegistry.register(metricName, value);
+            gaugeMetricNames.add(metricName);
+        } catch (IllegalArgumentException e) {
+            if (queueName != null) {
+                registerWithSuffix(suffix, count + 1, value);
+            }
+        }
+    }
+
+    private String getMetricName(final String queueName, int count, final String metricSuffix) {
+        String metricName = (queueName != null ? "." + QUEUE_PREFIX + "." + queueName : "");
+        if (count > 0) {
+            metricName = metricName + "_" + count;
+        }
+        return GAUGE_NAME_PREFIX + metricName + metricSuffix;
+    }
+
+    private String getSanitizedQueueName(String queueName) {
+        if (queueName == null) {
+            return null;
+        }
+        return queueName.replaceAll("[^a-zA-Z\\d]", "_").toLowerCase();
+    }
 }
diff --git a/src/main/java/org/apache/sling/event/impl/jobs/stats/StatisticsManager.java b/src/main/java/org/apache/sling/event/impl/jobs/stats/StatisticsManager.java
index 3700250..ee4400e 100644
--- a/src/main/java/org/apache/sling/event/impl/jobs/stats/StatisticsManager.java
+++ b/src/main/java/org/apache/sling/event/impl/jobs/stats/StatisticsManager.java
@@ -119,6 +119,8 @@
             queueStatistics.putIfAbsent(queueName, new StatisticsImpl());
             queueStats = (StatisticsImpl)queueStatistics.get(queueName);
             topicGauges.putIfAbsent(queueName, new GaugeSupport(queueName, queueStats, metricRegistry));
+            GaugeSupport gaugeSupport = topicGauges.get(queueName);
+            gaugeSupport.initialize();
         }
         return queueStats;
     }
@@ -197,12 +199,17 @@
 
     @Activate
     protected void activate() {
-        globalGauges = new GaugeSupport(globalStatistics, metricRegistry);
+        if (metricRegistry != null) {
+            globalGauges = new GaugeSupport(globalStatistics, metricRegistry);
+            globalGauges.initialize();
+        }
     }
 
     @Deactivate
     protected void deactivate() {
-        globalGauges.shutdown();
+        if (globalGauges != null) {
+            globalGauges.shutdown();
+        }
         for (GaugeSupport gaugeSupport : topicGauges.values()) {
             gaugeSupport.shutdown();
         }
diff --git a/src/test/java/org/apache/sling/event/impl/jobs/stats/StatisticsManagerTest.java b/src/test/java/org/apache/sling/event/impl/jobs/stats/StatisticsManagerTest.java
index 3c1937b..71ef1f7 100644
--- a/src/test/java/org/apache/sling/event/impl/jobs/stats/StatisticsManagerTest.java
+++ b/src/test/java/org/apache/sling/event/impl/jobs/stats/StatisticsManagerTest.java
@@ -28,6 +28,7 @@
 
 import static org.apache.sling.event.impl.jobs.stats.GaugeSupport.*;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 public class StatisticsManagerTest {
 
@@ -49,6 +50,15 @@
     }
 
     @Test
+    public void testActivateDeactivateWithMissingRegistry() {
+        statisticsManager = new StatisticsManager();
+        TestUtil.setFieldValue(statisticsManager, "globalStatistics", statistics);
+        statisticsManager.activate();
+        assertNull(TestUtil.getFieldValue(statisticsManager, "metricRegistry"));
+        statisticsManager.deactivate();
+    }
+
+    @Test
     public void testGlobalGaugesAreRemovedOnDeactivate() {
         statisticsManager.jobQueued(TEST_QUEUE_NAME, TEST_TOPIC);
         assertEquals("Less than 16 metrics present (8 global + 8 topic).",
@@ -69,10 +79,10 @@
         Statistics queueStatistics = statisticsManager.getQueueStatistics(TEST_QUEUE_NAME);
 
         assertEquals(1L, queueStatistics.getNumberOfActiveJobs());
-        assertEquals(1L, topicMetric.getValue());
-        assertEquals(1L, globalMetric.getValue());
-        assertEquals(queueStatistics.getAverageWaitingTime(), waitingTimeTopicMetric.getValue());
-        assertEquals(queueStatistics.getAverageWaitingTime(), waitingTimeGlobalMetric.getValue());
+        assertEquals((Long) 1L, topicMetric.getValue());
+        assertEquals((Long) 1L, globalMetric.getValue());
+        assertEquals((Long) queueStatistics.getAverageWaitingTime(), waitingTimeTopicMetric.getValue());
+        assertEquals((Long) queueStatistics.getAverageWaitingTime(), waitingTimeGlobalMetric.getValue());
     }
 
     @Test
@@ -82,13 +92,13 @@
         Gauge globalMetric = getGlobalMetric(QUEUED_METRIC_SUFFIX);
         Statistics queueStatistics = statisticsManager.getQueueStatistics(TEST_QUEUE_NAME);
         assertEquals(1L, queueStatistics.getNumberOfQueuedJobs());
-        assertEquals(1L, topicMetric.getValue());
-        assertEquals(1L, globalMetric.getValue());
+        assertEquals((Long) 1L, topicMetric.getValue());
+        assertEquals((Long) 1L, globalMetric.getValue());
 
         statisticsManager.jobDequeued(TEST_QUEUE_NAME, TEST_TOPIC);
         assertEquals(0L, queueStatistics.getNumberOfQueuedJobs());
-        assertEquals(0L, topicMetric.getValue());
-        assertEquals(0L, globalMetric.getValue());
+        assertEquals((Long) 0L, topicMetric.getValue());
+        assertEquals((Long) 0L, globalMetric.getValue());
     }
 
     @Test
@@ -98,8 +108,8 @@
         Gauge globalMetric = getGlobalMetric(CANCELLED_METRIC_SUFFIX);
         Statistics queueStatistics = statisticsManager.getQueueStatistics(TEST_QUEUE_NAME);
         assertEquals(1L, queueStatistics.getNumberOfCancelledJobs());
-        assertEquals(1L, topicMetric.getValue());
-        assertEquals(1L, globalMetric.getValue());
+        assertEquals((Long) 1L, topicMetric.getValue());
+        assertEquals((Long) 1L, globalMetric.getValue());
     }
 
     @Test
@@ -109,8 +119,8 @@
         Gauge globalMetric = getGlobalMetric(FAILED__METRIC_SUFFIX);
         Statistics queueStatistics = statisticsManager.getQueueStatistics(TEST_QUEUE_NAME);
         assertEquals(1L, queueStatistics.getNumberOfFailedJobs());
-        assertEquals(1L, topicMetric.getValue());
-        assertEquals(1L, globalMetric.getValue());
+        assertEquals((Long) 1L, topicMetric.getValue());
+        assertEquals((Long) 1L, globalMetric.getValue());
     }
 
     @Test
@@ -127,12 +137,32 @@
         Statistics queueStatistics = statisticsManager.getQueueStatistics(TEST_QUEUE_NAME);
 
         assertEquals(1L, queueStatistics.getNumberOfFinishedJobs());
-        assertEquals(1L, finishedTopicMetric.getValue());
-        assertEquals(1L, processedTopicMetric.getValue());
-        assertEquals(queueStatistics.getAverageProcessingTime(), processingTopicTimeMetric.getValue());
-        assertEquals(1L, finishedGlobalMetric.getValue());
-        assertEquals(1L, processedGlobalMetric.getValue());
-        assertEquals(queueStatistics.getAverageProcessingTime(), processingGlobalTimeMetric.getValue());
+        assertEquals((Long) 1L, finishedTopicMetric.getValue());
+        assertEquals((Long) 1L, processedTopicMetric.getValue());
+        assertEquals((Long) queueStatistics.getAverageProcessingTime(), processingTopicTimeMetric.getValue());
+        assertEquals((Long) 1L, finishedGlobalMetric.getValue());
+        assertEquals((Long) 1L, processedGlobalMetric.getValue());
+        assertEquals((Long) queueStatistics.getAverageProcessingTime(), processingGlobalTimeMetric.getValue());
+    }
+
+    @Test
+    public void testQueueWithSpecialCharsIsSanitized() {
+        String queueName = "Topic*With%Special/Chars";
+        String queueName2 = "topic$with?special§chars";
+        String queueName3 = "topic with<special>chars";
+        statisticsManager.jobQueued(queueName, TEST_TOPIC);
+        statisticsManager.jobQueued(queueName2, TEST_TOPIC);
+        statisticsManager.jobQueued(queueName3, TEST_TOPIC);
+
+        Gauge topicMetric = (Gauge) metricRegistry.getMetrics().get(GAUGE_NAME_PREFIX +
+                "." + QUEUE_PREFIX + ".topic_with_special_chars" + QUEUED_METRIC_SUFFIX);
+        Gauge topicMetric2 = (Gauge) metricRegistry.getMetrics().get(GAUGE_NAME_PREFIX +
+                "." + QUEUE_PREFIX + ".topic_with_special_chars_1" + QUEUED_METRIC_SUFFIX);
+        Gauge topicMetric3 = (Gauge) metricRegistry.getMetrics().get(GAUGE_NAME_PREFIX +
+                "." + QUEUE_PREFIX + ".topic_with_special_chars_2" + QUEUED_METRIC_SUFFIX);
+        assertEquals(1L, topicMetric.getValue());
+        assertEquals(1L, topicMetric2.getValue());
+        assertEquals(1L, topicMetric3.getValue());
     }
 
     private Gauge getTopicMetric(String metricSuffix) {