/* * 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.sling.event.impl.jobs.console; import java.io.PrintWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.felix.inventory.Format; import org.apache.felix.inventory.InventoryPrinter; import org.apache.sling.discovery.InstanceDescription; import org.apache.sling.event.impl.jobs.JobConsumerManager; import org.apache.sling.event.impl.jobs.config.InternalQueueConfiguration; import org.apache.sling.event.impl.jobs.config.JobManagerConfiguration; import org.apache.sling.event.impl.jobs.config.TopologyCapabilities; import org.apache.sling.event.jobs.JobManager; import org.apache.sling.event.jobs.Queue; import org.apache.sling.event.jobs.QueueConfiguration; import org.apache.sling.event.jobs.ScheduleInfo; import org.apache.sling.event.jobs.ScheduledJobInfo; import org.apache.sling.event.jobs.Statistics; import org.apache.sling.event.jobs.TopicStatistics; import org.osgi.framework.Constants; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; /** * This is a inventory plugin displaying the active queues, some statistics * and the configurations. * @since 3.2 */ @Component(service={InventoryPrinter.class}, property = { Constants.SERVICE_VENDOR + "=The Apache Software Foundation", InventoryPrinter.NAME + "=slingjobs", InventoryPrinter.TITLE + "=Sling Jobs", InventoryPrinter.FORMAT + "=TEXT", InventoryPrinter.FORMAT + "=JSON", InventoryPrinter.WEBCONSOLE + ":Boolean=false" }) public class InventoryPlugin implements InventoryPrinter { @Reference private JobManager jobManager; @Reference private JobManagerConfiguration configuration; @Reference private JobConsumerManager jobConsumerManager; /** * Format an array. */ private String formatArrayAsText(final String[] array) { if ( array == null || array.length == 0 ) { return ""; } return Arrays.toString(array); } private String formatType(final QueueConfiguration.Type type) { switch ( type ) { case ORDERED : return "Ordered"; case TOPIC_ROUND_ROBIN : return "Topic Round Robin"; case UNORDERED : return "Parallel"; } return type.toString(); } /** Default date format used. */ private final DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSS yyyy-MMM-dd"); /** * Format a date */ private synchronized String formatDate(final long time) { if ( time == -1 ) { return "-"; } final Date d = new Date(time); return dateFormat.format(d); } /** * Format time (= duration) */ private String formatTime(final long time) { if ( time == 0 ) { return "-"; } if ( time < 1000 ) { return time + " ms"; } else if ( time < 1000 * 60 ) { return time / 1000 + " secs"; } final long min = time / 1000 / 60; final long secs = (time - min * 1000 * 60); return min + " min " + secs / 1000 + " secs"; } /** * @see org.apache.felix.inventory.InventoryPrinter#print(java.io.PrintWriter, org.apache.felix.inventory.Format, boolean) */ @Override public void print(final PrintWriter pw, final Format format, final boolean isZip) { if ( format.equals(Format.TEXT) ) { printText(pw); } else if ( format.equals(Format.JSON) ) { printJson(pw); } } private void printText(final PrintWriter pw) { pw.println("Apache Sling Job Handling"); pw.println("-------------------------"); String topics = this.jobConsumerManager.getTopics(); if ( topics == null ) { topics = ""; } Statistics s = this.jobManager.getStatistics(); pw.println("Overall Statistics"); pw.printf("Start Time : %s%n", formatDate(s.getStartTime())); pw.printf("Local topic consumers: %s%n", topics); pw.printf("Last Activated : %s%n", formatDate(s.getLastActivatedJobTime())); pw.printf("Last Finished : %s%n", formatDate(s.getLastFinishedJobTime())); pw.printf("Queued Jobs : %s%n", s.getNumberOfQueuedJobs()); pw.printf("Active Jobs : %s%n", s.getNumberOfActiveJobs()); pw.printf("Jobs : %s%n", s.getNumberOfJobs()); pw.printf("Finished Jobs : %s%n", s.getNumberOfFinishedJobs()); pw.printf("Failed Jobs : %s%n", s.getNumberOfFailedJobs()); pw.printf("Cancelled Jobs : %s%n", s.getNumberOfCancelledJobs()); pw.printf("Processed Jobs : %s%n", s.getNumberOfProcessedJobs()); pw.printf("Average Processing Time : %s%n", formatTime(s.getAverageProcessingTime())); pw.printf("Average Waiting Time : %s%n", formatTime(s.getAverageWaitingTime())); pw.println(); pw.println("Topology Capabilities"); final TopologyCapabilities cap = this.configuration.getTopologyCapabilities(); if ( cap == null ) { pw.print("No topology information available !"); } else { final Map<String, List<InstanceDescription>> instanceCaps = cap.getInstanceCapabilities(); for(final Map.Entry<String, List<InstanceDescription>> entry : instanceCaps.entrySet()) { final StringBuilder sb = new StringBuilder(); for(final InstanceDescription id : entry.getValue()) { if ( sb.length() > 0 ) { sb.append(", "); } if ( id.isLocal() ) { sb.append("local"); } else { sb.append(id.getSlingId()); } } pw.printf("%s : %s%n", entry.getKey(), sb.toString()); } } pw.println(); pw.println("Scheduled Jobs"); final Collection<ScheduledJobInfo> infos = this.jobManager.getScheduledJobs(); if ( infos.size() == 0 ) { pw.print("No jobs currently scheduled"); } else { for(final ScheduledJobInfo info : infos) { pw.println("Schedule"); pw.printf("Job Topic< : %s%n", info.getJobTopic()); pw.print("Schedules : "); boolean first = true; for(final ScheduleInfo si : info.getSchedules() ) { if ( !first ) { pw.print(", "); } first = false; switch ( si.getType() ) { case YEARLY : pw.printf("YEARLY %s %s : %s:%s", si.getMonthOfYear(), si.getDayOfMonth(), si.getHourOfDay(), si.getMinuteOfHour()); break; case MONTHLY : pw.printf("MONTHLY %s : %s:%s", si.getDayOfMonth(), si.getHourOfDay(), si.getMinuteOfHour()); break; case WEEKLY : pw.printf("WEEKLY %s : %s:%s", si.getDayOfWeek(), si.getHourOfDay(), si.getMinuteOfHour()); break; case DAILY : pw.printf("DAILY %s:%s", si.getHourOfDay(), si.getMinuteOfHour()); break; case HOURLY : pw.printf("HOURLY %s", si.getMinuteOfHour()); break; case CRON : pw.printf("CRON %s", si.getExpression()); break; default : pw.printf("AT %s", si.getAt()); } } pw.println(); pw.println(); } } pw.println(); boolean isEmpty = true; for(final Queue q : this.jobManager.getQueues()) { isEmpty = false; pw.printf("Active JobQueue: %s %s%n", q.getName(), q.isSuspended() ? "(SUSPENDED)" : ""); s = q.getStatistics(); final QueueConfiguration c = q.getConfiguration(); pw.println("Statistics"); pw.printf("Start Time : %s%n", formatDate(s.getStartTime())); pw.printf("Last Activated : %s%n", formatDate(s.getLastActivatedJobTime())); pw.printf("Last Finished : %s%n", formatDate(s.getLastFinishedJobTime())); pw.printf("Queued Jobs : %s%n", s.getNumberOfQueuedJobs()); pw.printf("Active Jobs : %s%n", s.getNumberOfActiveJobs()); pw.printf("Jobs : %s%n", s.getNumberOfJobs()); pw.printf("Finished Jobs : %s%n", s.getNumberOfFinishedJobs()); pw.printf("Failed Jobs : %s%n", s.getNumberOfFailedJobs()); pw.printf("Cancelled Jobs : %s%n", s.getNumberOfCancelledJobs()); pw.printf("Processed Jobs : %s%n", s.getNumberOfProcessedJobs()); pw.printf("Average Processing Time : %s%n", formatTime(s.getAverageProcessingTime())); pw.printf("Average Waiting Time : %s%n", formatTime(s.getAverageWaitingTime())); pw.printf("Status Info : %s%n", q.getStateInfo()); pw.println("Configuration"); pw.printf("Type : %s%n", formatType(c.getType())); pw.printf("Topics : %s%n", formatArrayAsText(c.getTopics())); pw.printf("Max Parallel : %s%n", c.getMaxParallel()); pw.printf("Max Retries : %s%n", c.getMaxRetries()); pw.printf("Retry Delay : %s ms%n", c.getRetryDelayInMs()); pw.printf("Priority : %s%n", c.getThreadPriority()); pw.println(); } if ( isEmpty ) { pw.println("No active queues."); pw.println(); } for(final TopicStatistics ts : this.jobManager.getTopicStatistics()) { pw.printf("Topic Statistics - %s%n", ts.getTopic()); pw.printf("Last Activated : %s%n", formatDate(ts.getLastActivatedJobTime())); pw.printf("Last Finished : %s%n", formatDate(ts.getLastFinishedJobTime())); pw.printf("Finished Jobs : %s%n", ts.getNumberOfFinishedJobs()); pw.printf("Failed Jobs : %s%n", ts.getNumberOfFailedJobs()); pw.printf("Cancelled Jobs : %s%n", ts.getNumberOfCancelledJobs()); pw.printf("Processed Jobs : %s%n", ts.getNumberOfProcessedJobs()); pw.printf("Average Processing Time : %s%n", formatTime(ts.getAverageProcessingTime())); pw.printf("Average Waiting Time : %s%n", formatTime(ts.getAverageWaitingTime())); pw.println(); } pw.println("Apache Sling Job Handling - Job Queue Configurations"); pw.println("----------------------------------------------------"); this.printQueueConfiguration(pw, this.configuration.getQueueConfigurationManager().getMainQueueConfiguration()); final InternalQueueConfiguration[] configs = this.configuration.getQueueConfigurationManager().getConfigurations(); for(final InternalQueueConfiguration c : configs ) { this.printQueueConfiguration(pw, c); } } private void printQueueConfiguration(final PrintWriter pw, final InternalQueueConfiguration c) { pw.printf("Job Queue Configuration: %s%n", c.getName()); pw.printf("Valid : %s%n", c.isValid()); pw.printf("Type : %s%n", formatType(c.getType())); pw.printf("Topics : %s%n", formatArrayAsText(c.getTopics())); pw.printf("Max Parallel : %s%n", c.getMaxParallel()); pw.printf("Max Retries : %s%n", c.getMaxRetries()); pw.printf("Retry Delay : %s ms%n", c.getRetryDelayInMs()); pw.printf("Priority : %s%n", c.getThreadPriority()); pw.printf("Ranking : %s%n", c.getRanking()); pw.println(); } private void printJson(final PrintWriter pw) { pw.println("{"); Statistics s = this.jobManager.getStatistics(); pw.println(" \"statistics\" : {"); pw.printf(" \"startTime\" : %s,%n", s.getStartTime()); pw.printf(" \"startTimeText\" : \"%s\",%n", formatDate(s.getStartTime())); pw.printf(" \"lastActivatedJobTime\" : %s,%n", s.getLastActivatedJobTime()); pw.printf(" \"lastActivatedJobTimeText\" : \"%s\",%n", formatDate(s.getLastActivatedJobTime())); pw.printf(" \"lastFinishedJobTime\" : %s,%n", s.getLastFinishedJobTime()); pw.printf(" \"lastFinishedJobTimeText\" : \"%s\",%n", formatDate(s.getLastFinishedJobTime())); pw.printf(" \"numberOfQueuedJobs\" : %s,%n", s.getNumberOfQueuedJobs()); pw.printf(" \"numberOfActiveJobs\" : %s,%n", s.getNumberOfActiveJobs()); pw.printf(" \"numberOfJobs\" : %s,%n", s.getNumberOfJobs()); pw.printf(" \"numberOfFinishedJobs\" : %s,%n", s.getNumberOfFinishedJobs()); pw.printf(" \"numberOfFailedJobs\" : %s,%n", s.getNumberOfFailedJobs()); pw.printf(" \"numberOfCancelledJobs\" : %s,%n", s.getNumberOfCancelledJobs()); pw.printf(" \"numberOfProcessedJobs\" : %s,%n", s.getNumberOfProcessedJobs()); pw.printf(" \"averageProcessingTime\" : %s,%n", s.getAverageProcessingTime()); pw.printf(" \"averageProcessingTimeText\" : \"%s\",%n", formatTime(s.getAverageProcessingTime())); pw.printf(" \"averageWaitingTime\" : %s,%n", s.getAverageWaitingTime()); pw.printf(" \"averageWaitingTimeText\" : \"%s\"%n", formatTime(s.getAverageWaitingTime())); pw.print(" }"); final TopologyCapabilities cap = this.configuration.getTopologyCapabilities(); if ( cap != null ) { pw.println(","); pw.println(" \"capabilities\" : ["); final Map<String, List<InstanceDescription>> instanceCaps = cap.getInstanceCapabilities(); final Iterator<Map.Entry<String, List<InstanceDescription>>> iter = instanceCaps.entrySet().iterator(); while ( iter.hasNext() ) { final Map.Entry<String, List<InstanceDescription>> entry = iter.next(); final List<String> instances = new ArrayList<>(); for(final InstanceDescription id : entry.getValue()) { if ( id.isLocal() ) { instances.add("local"); } else { instances.add(id.getSlingId()); } } pw.println(" {"); pw.printf(" \"topic\" : \"%s\",%n", entry.getKey()); pw.printf(" \"instances\" : %s%n", formatArrayAsJson(instances.toArray(new String[instances.size()]))); if ( iter.hasNext() ) { pw.println(" },"); } else { pw.println(" }"); } } pw.print(" ]"); } boolean first = true; for(final Queue q : this.jobManager.getQueues()) { pw.println(","); if ( first ) { pw.println(" \"queues\" : ["); first = false; } pw.println(" {"); pw.printf(" \"name\" : \"%s\",%n", q.getName()); pw.printf(" \"suspended\" : %s,%n", q.isSuspended()); s = q.getStatistics(); pw.println(" \"statistics\" : {"); pw.printf(" \"startTime\" : %s,%n", s.getStartTime()); pw.printf(" \"startTimeText\" : \"%s\",%n", formatDate(s.getStartTime())); pw.printf(" \"lastActivatedJobTime\" : %s,%n", s.getLastActivatedJobTime()); pw.printf(" \"lastActivatedJobTimeText\" : \"%s\",%n", formatDate(s.getLastActivatedJobTime())); pw.printf(" \"lastFinishedJobTime\" : %s,%n", s.getLastFinishedJobTime()); pw.printf(" \"lastFinishedJobTimeText\" : \"%s\",%n", formatDate(s.getLastFinishedJobTime())); pw.printf(" \"numberOfQueuedJobs\" : %s,%n", s.getNumberOfQueuedJobs()); pw.printf(" \"numberOfActiveJobs\" : %s,%n", s.getNumberOfActiveJobs()); pw.printf(" \"numberOfJobs\" : %s,%n", s.getNumberOfJobs()); pw.printf(" \"numberOfFinishedJobs\" : %s,%n", s.getNumberOfFinishedJobs()); pw.printf(" \"numberOfFailedJobs\" : %s,%n", s.getNumberOfFailedJobs()); pw.printf(" \"numberOfCancelledJobs\" : %s,%n", s.getNumberOfCancelledJobs()); pw.printf(" \"numberOfProcessedJobs\" : %s,%n", s.getNumberOfProcessedJobs()); pw.printf(" \"averageProcessingTime\" : %s,%n", s.getAverageProcessingTime()); pw.printf(" \"averageProcessingTimeText\" : \"%s\",%n", formatTime(s.getAverageProcessingTime())); pw.printf(" \"averageWaitingTime\" : %s,%n", s.getAverageWaitingTime()); pw.printf(" \"averageWaitingTimeText\" : \"%s\"%n", formatTime(s.getAverageWaitingTime())); pw.print(" },"); final QueueConfiguration c = q.getConfiguration(); pw.printf(" \"stateInfo\" : \"%s\",%n", q.getStateInfo()); pw.println(" \"configuration\" : {"); pw.printf(" \"type\" : \"%s\",%n", c.getType()); pw.printf(" \"topics\" : \"%s\",%n", formatArrayAsJson(c.getTopics())); pw.printf(" \"maxParallel\" : %s,%n", c.getMaxParallel()); pw.printf(" \"maxRetries\" : %s,%n", c.getMaxRetries()); pw.printf(" \"retryDelayInMs\" : %s,%n", c.getRetryDelayInMs()); pw.printf(" \"priority\" : \"%s\"%n", c.getThreadPriority()); pw.println(" }"); pw.print(" }"); } if ( !first ) { pw.print(" ]"); } first = true; for(final TopicStatistics ts : this.jobManager.getTopicStatistics()) { pw.println(","); if ( first ) { pw.println(" \"topicStatistics\" : ["); first = false; } pw.println(" {"); pw.printf(" \"topic\" : \"%s\",%n", ts.getTopic()); pw.printf(" \"lastActivatedJobTime\" : %s,%n", ts.getLastActivatedJobTime()); pw.printf(" \"lastActivatedJobTimeText\" : \"%s\",%n", formatDate(ts.getLastActivatedJobTime())); pw.printf(" \"lastFinishedJobTime\" : %s,%n", ts.getLastFinishedJobTime()); pw.printf(" \"lastFinishedJobTimeText\" : \"%s\",%n", formatDate(ts.getLastFinishedJobTime())); pw.printf(" \"numberOfFinishedJobs\" : %s,%n", ts.getNumberOfFinishedJobs()); pw.printf(" \"numberOfFailedJobs\" : %s,%n", ts.getNumberOfFailedJobs()); pw.printf(" \"numberOfCancelledJobs\" : %s,%n", ts.getNumberOfCancelledJobs()); pw.printf(" \"numberOfProcessedJobs\" : %s,%n", ts.getNumberOfProcessedJobs()); pw.printf(" \"averageProcessingTime\" : %s,%n", ts.getAverageProcessingTime()); pw.printf(" \"averageProcessingTimeText\" : \"%s\",%n", formatTime(ts.getAverageProcessingTime())); pw.printf(" \"averageWaitingTime\" : %s,%n", ts.getAverageWaitingTime()); pw.printf(" \"averageWaitingTimeText\" : \"%s\"%n", formatTime(ts.getAverageWaitingTime())); pw.print(" }"); } if ( !first ) { pw.print(" ]"); } pw.println(","); pw.println(" \"configurations\" : ["); this.printQueueConfigurationJson(pw, this.configuration.getQueueConfigurationManager().getMainQueueConfiguration()); final InternalQueueConfiguration[] configs = this.configuration.getQueueConfigurationManager().getConfigurations(); for(final InternalQueueConfiguration c : configs ) { pw.println(","); this.printQueueConfigurationJson(pw, c); } pw.println(); pw.println(" ]"); pw.println("}"); } private void printQueueConfigurationJson(final PrintWriter pw, final InternalQueueConfiguration c) { pw.println(" {"); pw.printf(" \"name\" : \"%s\",%n", c.getName()); pw.printf(" \"valid\" : %s,%n", c.isValid()); pw.printf(" \"type\" : \"%s\",%n", c.getType()); pw.printf(" \"topics\" : %s,%n", formatArrayAsJson(c.getTopics())); pw.printf(" \"maxParallel\" : %s,%n", c.getMaxParallel()); pw.printf(" \"maxRetries\" : %s,%n", c.getMaxRetries()); pw.printf(" \"retryDelayInMs\" : %s,%n", c.getRetryDelayInMs()); pw.printf(" \"priority\" : \"%s\",%n", c.getThreadPriority()); pw.printf(" \"ranking\" : %s%n", c.getRanking()); pw.print(" }"); } /** * Format an array. */ private String formatArrayAsJson(final String[] array) { if ( array == null || array.length == 0 ) { return "[]"; } final StringBuilder sb = new StringBuilder("["); boolean first = true; for(final String s : array) { if ( !first ) { sb.append(", "); } first = false; sb.append("\""); sb.append(s); sb.append("\""); } sb.append("]"); return sb.toString(); } }