From 2336321faa240d3c4e6da5d5ec22062d74673a2b Mon Sep 17 00:00:00 2001 From: Joshua Matsuoka Date: Fri, 14 Apr 2023 17:12:17 -0400 Subject: [PATCH] chore(jmc-core): Upstream updates (#201) * Updating embedded JMC-core sources to bring them in line with upstream * Updating to JMC core 8.2.0, reworking InterruptibleReportGenerator for rules changes in core, fixing whitespace * fixing newline at end of file * Removing unnecessary loop * Reverting RuleEvaluation structure for client consumers * fix NPE due to null reportStats * include summary, explanation, solution, and settings in text result * use computed decimal score value if available * Porting over RulesToolkit.evaluateParallel --------- Co-authored-by: Andrew Azores --- pom.xml | 2 +- .../reports/InterruptibleReportGenerator.java | 264 ++++++++++---- .../internal/CommonConstraints.java | 3 +- .../model/xml/JFCXMLValidator.java | 6 +- .../ui/configuration/model/xml/XMLModel.java | 20 +- .../ui/model/EventConfiguration.java | 23 +- .../org/openjdk/jmc/jdp/client/JDPClient.java | 2 +- .../openjdk/jmc/jdp/common/Configuration.java | 3 +- .../openjdk/jmc/rjmx/ConnectionToolkit.java | 37 +- .../openjdk/jmc/rjmx/JVMSupportToolkit.java | 25 +- .../JMXRMISystemPropertiesProvider.java | 35 +- .../jmc/rjmx/internal/RJMXConnection.java | 328 +++++++++--------- .../jmc/rjmx/preferences/PreferencesKeys.java | 11 +- .../AbstractAttributeSubscription.java | 16 +- .../DefaultAttributeSubscriptionThread.java | 4 +- ...efaultNotificationSubscriptionManager.java | 4 +- .../internal/FileMRIMetadata.java | 14 +- .../jmc/ui/common/jvm/JVMDescriptor.java | 16 +- .../openjdk/jmc/ui/common/jvm/JVMType.java | 20 +- .../common/labelingrules/NameConverter.java | 4 +- .../security/PersistentCredentials.java | 13 +- .../security/SecurityManagerFactory.java | 14 +- .../InterruptibleReportGeneratorTest.java | 7 +- .../core/util/RuleFilterParserTest.java | 22 +- 24 files changed, 599 insertions(+), 294 deletions(-) diff --git a/pom.xml b/pom.xml index e6bd6934..26e21150 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ 11 ${java.version} ${java.version} - 7.1.1 + 8.2.0 3.11.0 3.0.0 diff --git a/src/main/java/io/cryostat/core/reports/InterruptibleReportGenerator.java b/src/main/java/io/cryostat/core/reports/InterruptibleReportGenerator.java index 8c43cfd0..ff240a58 100644 --- a/src/main/java/io/cryostat/core/reports/InterruptibleReportGenerator.java +++ b/src/main/java/io/cryostat/core/reports/InterruptibleReportGenerator.java @@ -45,9 +45,12 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; +import java.util.Queue; import java.util.Set; -import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -58,17 +61,25 @@ import org.openjdk.jmc.common.io.IOToolkit; import org.openjdk.jmc.common.item.IItemCollection; +import org.openjdk.jmc.common.unit.IQuantity; import org.openjdk.jmc.common.util.IPreferenceValueProvider; import org.openjdk.jmc.common.util.Pair; import org.openjdk.jmc.common.util.XmlToolkit; import org.openjdk.jmc.flightrecorder.CouldNotLoadRecordingException; import org.openjdk.jmc.flightrecorder.JfrLoaderToolkit; +import org.openjdk.jmc.flightrecorder.rules.DependsOn; +import org.openjdk.jmc.flightrecorder.rules.IResult; import org.openjdk.jmc.flightrecorder.rules.IRule; -import org.openjdk.jmc.flightrecorder.rules.Result; +import org.openjdk.jmc.flightrecorder.rules.ResultBuilder; +import org.openjdk.jmc.flightrecorder.rules.ResultProvider; +import org.openjdk.jmc.flightrecorder.rules.ResultToolkit; import org.openjdk.jmc.flightrecorder.rules.RuleRegistry; +import org.openjdk.jmc.flightrecorder.rules.Severity; +import org.openjdk.jmc.flightrecorder.rules.TypedResult; import org.openjdk.jmc.flightrecorder.rules.report.html.internal.HtmlResultGroup; import org.openjdk.jmc.flightrecorder.rules.report.html.internal.HtmlResultProvider; import org.openjdk.jmc.flightrecorder.rules.report.html.internal.RulesHtmlToolkit; +import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit; import io.cryostat.core.log.Logger; @@ -78,6 +89,7 @@ import jdk.jfr.Label; import jdk.jfr.Name; import org.apache.commons.io.input.CountingInputStream; +import org.apache.commons.lang3.StringUtils; import org.jsoup.Jsoup; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -126,9 +138,9 @@ public Future generateReportInterruptibly( // but calling our cancellable evaluate() method rather than the // RulesToolkit.evaluateParallel as explained further down. try { - Pair, Long> helperPair = + Pair, Long> helperPair = generateResultHelper(recording, predicate); - Collection results = helperPair.left; + Collection results = helperPair.left; List groups = loadResultGroups(); String html = @@ -142,12 +154,9 @@ public Future generateReportInterruptibly( int rulesEvaluated = results.size(); int rulesApplicable = results.stream() - .filter( - result -> - result.getScore() != Result.NOT_APPLICABLE) + .filter(result -> result.getSeverity() != Severity.NA) .collect(Collectors.toList()) .size(); - return new ReportResult( html, new ReportStats( @@ -177,17 +186,24 @@ public Future> generateEvalMapInterruptibly( return qThread.submit( () -> { try { - Collection results = + Collection results = generateResultHelper(recording, predicate).left; Map evalMap = new HashMap(); for (var eval : results) { + IQuantity scoreQuantity = eval.getResult(TypedResult.SCORE); + double score; + if (scoreQuantity != null) { + score = scoreQuantity.doubleValue(); + } else { + score = eval.getSeverity().getLimit(); + } evalMap.put( eval.getRule().getId(), new RuleEvaluation( - eval.getScore(), + score, eval.getRule().getName(), eval.getRule().getTopic(), - eval.getShortDescription())); + getTextDescription(eval))); } return evalMap; } catch (InterruptedException @@ -196,39 +212,157 @@ public Future> generateEvalMapInterruptibly( | CouldNotLoadRecordingException e) { return Map.of( e.getClass().toString(), - new RuleEvaluation(Double.NaN, "", "", e.getMessage())); + new RuleEvaluation( + Severity.NA.getLimit(), + e.getClass().getSimpleName(), + "report_failure", + e.getMessage())); } }); } - private Pair, Long> generateResultHelper( + private String getTextDescription(IResult result) { + StringBuilder sb = new StringBuilder(); + + if (StringUtils.isNotBlank(result.getSummary())) { + sb.append("Summary:\n"); + sb.append(ResultToolkit.populateMessage(result, result.getSummary(), false)); + sb.append("\n\n"); + } + + if (StringUtils.isNotBlank(result.getExplanation())) { + sb.append("Explanation:\n"); + sb.append(ResultToolkit.populateMessage(result, result.getExplanation(), false)); + sb.append("\n\n"); + } + + if (StringUtils.isNotBlank(result.getSolution())) { + sb.append("Solution:\n"); + sb.append(ResultToolkit.populateMessage(result, result.getSolution(), false)); + sb.append("\n\n"); + } + + if (!result.suggestRecordingSettings().isEmpty()) { + sb.append("Suggested settings:\n"); + for (var suggestion : result.suggestRecordingSettings()) { + sb.append( + String.format( + "%s %s=%s", + suggestion.getSettingFor(), + suggestion.getSettingName(), + suggestion.getSettingValue())); + } + } + + return sb.toString().strip(); + } + + private Pair, Long> generateResultHelper( InputStream recording, Predicate predicate) throws InterruptedException, IOException, ExecutionException, CouldNotLoadRecordingException { - List> resultFutures = new ArrayList<>(); + Collection rules = + RuleRegistry.getRules().stream().filter(predicate).collect(Collectors.toList()); + ResultProvider resultProvider = new ResultProvider(); + Map> resultFutures = new HashMap<>(); + Queue> futureQueue = new ConcurrentLinkedQueue<>(); + // Map using the rule name as a key, and a Pair containing the rule (left) and it's + // dependency (right) + Map> rulesWithDependencies = new HashMap<>(); + Map computedResults = new HashMap<>(); try (CountingInputStream countingRecordingStream = new CountingInputStream(recording)) { - Collection rules = - RuleRegistry.getRules().stream().filter(predicate).collect(Collectors.toList()); - resultFutures.addAll( - evaluate(rules, JfrLoaderToolkit.loadEvents(countingRecordingStream)).stream() - .map(executor::submit) - .collect(Collectors.toList())); - Collection results = new HashSet<>(); - for (Future future : resultFutures) { + IItemCollection items = JfrLoaderToolkit.loadEvents(countingRecordingStream); + for (IRule rule : rules) { + if (RulesToolkit.matchesEventAvailabilityMap(items, rule.getRequiredEvents())) { + if (hasDependency(rule)) { + IRule depRule = + rules.stream() + .filter(r -> r.getId().equals(getRuleDependencyName(rule))) + .findFirst() + .orElse(null); + rulesWithDependencies.put(rule.getId(), new Pair<>(rule, depRule)); + } else { + RunnableFuture resultFuture = + rule.createEvaluation( + items, + IPreferenceValueProvider.DEFAULT_VALUES, + resultProvider); + resultFutures.put(rule, resultFuture); + futureQueue.add(resultFuture); + } + } else { + resultFutures.put( + rule, + CompletableFuture.completedFuture( + ResultBuilder.createFor( + rule, IPreferenceValueProvider.DEFAULT_VALUES) + .setSeverity(Severity.NA) + .build())); + } + } + for (Entry> entry : rulesWithDependencies.entrySet()) { + IRule rule = entry.getValue().left; + IRule depRule = entry.getValue().right; + Future depResultFuture = resultFutures.get(depRule); + if (depResultFuture == null) { + resultFutures.put( + rule, + CompletableFuture.completedFuture( + ResultBuilder.createFor( + rule, IPreferenceValueProvider.DEFAULT_VALUES) + .setSeverity(Severity.NA) + .build())); + } else { + IResult depResult = null; + if (!depResultFuture.isDone()) { + ((Runnable) depResultFuture).run(); + try { + depResult = depResultFuture.get(); + resultProvider.addResults(depResult); + computedResults.put(depRule, depResult); + } catch (InterruptedException | ExecutionException e) { + logger.warn("Error retrieving results for rule: " + depResult); + } + } else { + depResult = computedResults.get(depRule); + } + if (depResult != null && shouldEvaluate(rule, depResult)) { + RunnableFuture resultFuture = + rule.createEvaluation( + items, + IPreferenceValueProvider.DEFAULT_VALUES, + resultProvider); + resultFutures.put(rule, resultFuture); + futureQueue.add(resultFuture); + } else { + resultFutures.put( + rule, + CompletableFuture.completedFuture( + ResultBuilder.createFor( + rule, + IPreferenceValueProvider.DEFAULT_VALUES) + .setSeverity(Severity.NA) + .build())); + } + } + } + RuleEvaluator re = new RuleEvaluator(futureQueue); + executor.submit(re); + Collection results = new HashSet(); + for (Future future : resultFutures.values()) { results.add(future.get()); } - long recordingSizeBytes = countingRecordingStream.getByteCount(); - return new Pair, Long>(results, recordingSizeBytes); + return new Pair, Long>( + results, countingRecordingStream.getByteCount()); } catch (InterruptedException | IOException | ExecutionException | CouldNotLoadRecordingException e) { - resultFutures.forEach( - f -> { - if (!f.isDone()) { - f.cancel(true); - } - }); + for (Future f : resultFutures.values()) { + if (!f.isDone()) { + f.cancel(true); + } + } logger.warn(e); throw e; } @@ -336,43 +470,40 @@ private List loadResultGroups(Element element) { return groups; } - private List> evaluate(Collection rules, IItemCollection items) { - List> callables = new ArrayList<>(rules.size()); - for (IRule rule : rules) { - RunnableFuture result = - rule.evaluate(items, IPreferenceValueProvider.DEFAULT_VALUES); - callables.add( - () -> { - logger.trace("Processing rule {}", rule.getName()); - ReportRuleEvalEvent evt = new ReportRuleEvalEvent(rule.getName()); - evt.begin(); + private static String getRuleDependencyName(IRule rule) { + DependsOn dependency = rule.getClass().getAnnotation(DependsOn.class); + Class dependencyType = dependency.value(); + return dependencyType.getSimpleName(); + } - try { - result.run(); - return result.get(); - } finally { - evt.end(); - if (evt.shouldCommit()) { - evt.commit(); - } - } - }); + private static boolean hasDependency(IRule rule) { + DependsOn dependency = rule.getClass().getAnnotation(DependsOn.class); + return dependency != null; + } + + /** Brought over from org.openjdk.jmc.flightrecorder.rules.jdk.util.RulesToolkit */ + private static boolean shouldEvaluate(IRule rule, IResult depResult) { + DependsOn dependency = rule.getClass().getAnnotation(DependsOn.class); + if (dependency != null) { + if (depResult.getSeverity().compareTo(dependency.severity()) < 0) { + return false; + } } - return callables; + return true; } private static class SimpleResultProvider implements HtmlResultProvider { - private Map> resultsByTopic = new HashMap<>(); + private Map> resultsByTopic = new HashMap<>(); private Set unmappedTopics; - public SimpleResultProvider(Collection results, List groups) { - for (Result result : results) { + public SimpleResultProvider(Collection results, List groups) { + for (IResult result : results) { String topic = result.getRule().getTopic(); if (topic == null) { // Magic string to denote null topic topic = ""; } - Collection topicResults = resultsByTopic.get(topic); + Collection topicResults = resultsByTopic.get(topic); if (topicResults == null) { topicResults = new HashSet<>(); resultsByTopic.put(topic, topicResults); @@ -395,15 +526,15 @@ private static void removeMappedTopics( } @Override - public Collection getResults(Collection topics) { + public Collection getResults(Collection topics) { Collection topics2 = topics; if (topics2.contains("")) { topics2 = new HashSet<>(topics); topics2.addAll(unmappedTopics); } - Collection results = new HashSet<>(); + Collection results = new HashSet<>(); for (String topic : topics2) { - Collection topicResults = resultsByTopic.get(topic); + Collection topicResults = resultsByTopic.get(topic); if (topicResults != null) { results.addAll(topicResults); } @@ -412,6 +543,22 @@ public Collection getResults(Collection topics) { } } + private static class RuleEvaluator implements Runnable { + private Queue> futureQueue; + + public RuleEvaluator(Queue> futureQueue) { + this.futureQueue = futureQueue; + } + + @Override + public void run() { + RunnableFuture resultFuture; + while ((resultFuture = futureQueue.poll()) != null) { + resultFuture.run(); + } + } + } + private static class SimpleResultGroup implements HtmlResultGroup { String name; String image = null; @@ -521,6 +668,7 @@ public static class ReportResult { ReportResult(String html) { this.html = html; + this.reportStats = new ReportStats(0, 0, 0); } ReportResult(String html, ReportStats reportStats) { diff --git a/src/main/java/org/openjdk/jmc/flightrecorder/configuration/internal/CommonConstraints.java b/src/main/java/org/openjdk/jmc/flightrecorder/configuration/internal/CommonConstraints.java index ca25f42e..f056dc0a 100644 --- a/src/main/java/org/openjdk/jmc/flightrecorder/configuration/internal/CommonConstraints.java +++ b/src/main/java/org/openjdk/jmc/flightrecorder/configuration/internal/CommonConstraints.java @@ -40,6 +40,7 @@ import static org.openjdk.jmc.common.unit.UnitLookup.BYTE; import static org.openjdk.jmc.common.unit.UnitLookup.DAY; import static org.openjdk.jmc.common.unit.UnitLookup.EPOCH_MS; +import static org.openjdk.jmc.common.unit.UnitLookup.EPOCH_NS; import static org.openjdk.jmc.common.unit.UnitLookup.HOUR; import static org.openjdk.jmc.common.unit.UnitLookup.MEMORY; import static org.openjdk.jmc.common.unit.UnitLookup.MINUTE; @@ -270,7 +271,7 @@ public IQuantity parsePersisted(String persistedValue) throws QuantityConversion } public final static IConstraint POSITIVE_TIMESPAN = new ComparableConstraint<>( - new TimePersisterBrokenSI(), SECOND.quantity(0), YEAR.quantity(200)); + new TimePersisterBrokenSI(), SECOND.quantity(0), EPOCH_NS.quantity(Long.MAX_VALUE)); public final static IConstraint PERIOD_V1 = new ComparableConstraint<>(new PeriodPersister(), NANOSECOND.quantity(1), YEAR.quantity(1)); public final static IConstraint PERIOD_V2 = new ComparableConstraint<>(new PeriodPersisterV2(), diff --git a/src/main/java/org/openjdk/jmc/flightrecorder/controlpanel/ui/configuration/model/xml/JFCXMLValidator.java b/src/main/java/org/openjdk/jmc/flightrecorder/controlpanel/ui/configuration/model/xml/JFCXMLValidator.java index 536d9df5..988adfbf 100644 --- a/src/main/java/org/openjdk/jmc/flightrecorder/controlpanel/ui/configuration/model/xml/JFCXMLValidator.java +++ b/src/main/java/org/openjdk/jmc/flightrecorder/controlpanel/ui/configuration/model/xml/JFCXMLValidator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -39,7 +39,6 @@ import java.net.URI; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; @@ -243,7 +242,7 @@ public static Set createQualifiedVariableSet( } } } - Collections.sort(variables); + variables.sort(null); return new LinkedHashSet<>(variables); } @@ -263,5 +262,4 @@ private static URI createTrailingSlashURI(String uri) { } return URI.create(uri + '/'); } - } diff --git a/src/main/java/org/openjdk/jmc/flightrecorder/controlpanel/ui/configuration/model/xml/XMLModel.java b/src/main/java/org/openjdk/jmc/flightrecorder/controlpanel/ui/configuration/model/xml/XMLModel.java index 544787ce..e7ef3654 100644 --- a/src/main/java/org/openjdk/jmc/flightrecorder/controlpanel/ui/configuration/model/xml/XMLModel.java +++ b/src/main/java/org/openjdk/jmc/flightrecorder/controlpanel/ui/configuration/model/xml/XMLModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -50,6 +50,7 @@ import java.util.Map; import java.util.Observable; import java.util.Stack; +import java.util.logging.Logger; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; @@ -259,9 +260,10 @@ public void saveToFile(File file) throws IOException { // NOTE: The pretty printer writes that the encoding is UTF-8, so we must make sure it is. // Ensure charset exists before opening file for writing. Charset charset = Charset.forName("UTF-8"); //$NON-NLS-1$ - Writer osw = new OutputStreamWriter(new FileOutputStream(file), charset); - if (writeTo(osw)) { - setDirty(false); + try (Writer osw = new OutputStreamWriter(new FileOutputStream(file), charset)) { + if (writeTo(osw)) { + setDirty(false); + } } } @@ -275,15 +277,12 @@ public void saveToFile(File file) throws IOException { * @return true iff the model was successfully written to the {@link Writer}. */ public boolean writeTo(Writer writer) { - PrintWriter pw = new PrintWriter(writer); - try { + try (PrintWriter pw = new PrintWriter(writer)) { PrettyPrinter pp = new PrettyPrinter(pw, m_validator.getElementsTooKeepOnOneLine()); pp.print(this); pw.flush(); // PrintWriter never throws any exceptions, so this is how we find out if something went wrong. return !pw.checkError(); - } finally { - IOToolkit.closeSilently(pw); } } @@ -321,8 +320,9 @@ public void checkErrors() { // NOTE: This will only keep one result per node, although many may have been found. m_resultLookup.put(r.getObject(), r); if (r.isError()) { - // FIXME: Get a logger when this is in a better bundle. - System.out.println(r.getObject() + ": " + r.getText()); //$NON-NLS-1$ + Logger logger = Logger + .getLogger("org.openjdk.jmc.flightrecorder.controlpanel.ui.configuration.model.xml"); //$NON-NLS-1$ + logger.severe(r.getObject() + ": " + r.getText()); //$NON-NLS-1$ } } } diff --git a/src/main/java/org/openjdk/jmc/flightrecorder/controlpanel/ui/model/EventConfiguration.java b/src/main/java/org/openjdk/jmc/flightrecorder/controlpanel/ui/model/EventConfiguration.java index e9610003..6bf0b6e0 100644 --- a/src/main/java/org/openjdk/jmc/flightrecorder/controlpanel/ui/model/EventConfiguration.java +++ b/src/main/java/org/openjdk/jmc/flightrecorder/controlpanel/ui/model/EventConfiguration.java @@ -111,18 +111,15 @@ public final class EventConfiguration implements IEventConfiguration { public static void validate(InputStream xmlStream, String streamName, SchemaVersion version) throws ParseException, IOException { - InputStream schemaStream = version.createSchemaStream(); - if (schemaStream != null) { - try { + try (InputStream schemaStream = version.createSchemaStream()) { + if (schemaStream != null) { SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); //$NON-NLS-1$ XMLModel.validate(xmlStream, streamName, schemaFactory.newSchema(new StreamSource(schemaStream))); - } catch (SAXException e) { - throw new IOException("Trouble parsing schema for version " + version, e); //$NON-NLS-1$ - } finally { - IOToolkit.closeSilently(schemaStream); + } else { + throw new IOException("Could not locate schema for version " + version); //$NON-NLS-1$ } - } else { - throw new IOException("Could not locate schema for version " + version); //$NON-NLS-1$ + } catch (SAXException e) { + throw new IOException("Trouble parsing schema for version " + version, e); //$NON-NLS-1$ } } @@ -140,7 +137,9 @@ public static XMLModel createModel(String xmlText) throws ParseException, IOExce } public static XMLModel createModel(File file) throws FileNotFoundException, IOException, ParseException { - return createModel(new FileInputStream(file)); + try (FileInputStream fis = new FileInputStream(file)) { + return createModel(fis); + } } public static XMLModel createModel(InputStream inStream) throws IOException, ParseException { @@ -542,8 +541,8 @@ public boolean equalSettings(IEventConfiguration other) { * We're doing this last as it might be expensive. Otherwise, we could just have called * equals() on the maps. */ - return ourOptions.keySet().size() == other.getEventOptions(ourOptions.emptyWithSameConstraints()) - .keySet().size(); + return ourOptions.keySet().size() == other.getEventOptions(ourOptions.emptyWithSameConstraints()).keySet() + .size(); } public Set getConfigEventTypes() { diff --git a/src/main/java/org/openjdk/jmc/jdp/client/JDPClient.java b/src/main/java/org/openjdk/jmc/jdp/client/JDPClient.java index 66882179..fb1182f4 100644 --- a/src/main/java/org/openjdk/jmc/jdp/client/JDPClient.java +++ b/src/main/java/org/openjdk/jmc/jdp/client/JDPClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * diff --git a/src/main/java/org/openjdk/jmc/jdp/common/Configuration.java b/src/main/java/org/openjdk/jmc/jdp/common/Configuration.java index 85599a3c..81ade62e 100644 --- a/src/main/java/org/openjdk/jmc/jdp/common/Configuration.java +++ b/src/main/java/org/openjdk/jmc/jdp/common/Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -43,6 +43,7 @@ public interface Configuration { public static final short DEFAULT_TTL = 0; public static final String DEFAULT_MULTICAST_ADDRESS = "224.0.23.178"; //$NON-NLS-1$ public static final int DEFAULT_MAX_HEART_BEAT_TIMEOUT = 12000; + public static final boolean DEFAULT_JDP_ENABLE_AUTO_DISCOVERY = false; /** * The multicast group to join. diff --git a/src/main/java/org/openjdk/jmc/rjmx/ConnectionToolkit.java b/src/main/java/org/openjdk/jmc/rjmx/ConnectionToolkit.java index c4d175c1..d9afc8ba 100644 --- a/src/main/java/org/openjdk/jmc/rjmx/ConnectionToolkit.java +++ b/src/main/java/org/openjdk/jmc/rjmx/ConnectionToolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -56,6 +56,7 @@ import org.openjdk.jmc.common.version.JavaVMVersionToolkit; import org.openjdk.jmc.common.version.JavaVersion; import org.openjdk.jmc.rjmx.internal.RJMXConnection; +import org.openjdk.jmc.ui.common.jvm.JVMDescriptor; /** * Toolkit providing utility methods to retrieve MBean proxy objects, invoke JMX operations and @@ -346,7 +347,6 @@ public static int getDefaultPort() { * otherwise. */ public static boolean isJRockit(IConnectionHandle connectionHandle) { - String vmName = getVMName(connectionHandle); return JavaVMVersionToolkit.isJRockitJVMName(vmName); } @@ -365,6 +365,38 @@ public static boolean isHotSpot(IConnectionHandle connectionHandle) { return vmName != null && JavaVMVersionToolkit.isHotspotJVMName(vmName); } + /** + * Returns {@code true} if the connection handle is associated with an Oracle built JVM, + * {@code false} otherwise. If the information is already present in the {@link JVMDescriptor}, + * this method will not cause any JMXRMI calls. If the information is lacking, an attempt will + * be made to look it up in the connected JVM. If the attempt fails, false will be returned. + * + * @return {@code true} if the connection handle describes an Oracle JVM, or {@code false} + * otherwise or if it could not be determined. + */ + public static boolean isOracle(IConnectionHandle handle) { + JVMDescriptor descriptor = handle.getServerDescriptor().getJvmInfo(); + // This should normally not happen for discovered JVMs, but users can create custom connections + String name = null; + if (descriptor != null) { + name = descriptor.getJvmName(); + } else { + // We try checking if connected + if (handle.isConnected()) { + MBeanServerConnection connection = handle.getServiceOrNull(MBeanServerConnection.class); + if (connection != null) { + try { + name = getRuntimeBean(connection).getVmName(); + } catch (IOException e) { + // Worst case we classify JVM name wrong + RJMXPlugin.getDefault().getLogger().log(Level.WARNING, "Could not check if Oracle JVM", e); + } + } + } + } + return name != null && (name.contains("Java HotSpot")); + } + /** * This will return true if the java version is above or equal the supplied value. (For example * 1.7.0_40). @@ -411,5 +443,4 @@ private static JavaVersion getJavaVersion(IConnectionHandle connectionHandle) { } return null; } - } diff --git a/src/main/java/org/openjdk/jmc/rjmx/JVMSupportToolkit.java b/src/main/java/org/openjdk/jmc/rjmx/JVMSupportToolkit.java index 7e65c395..a6c336dc 100644 --- a/src/main/java/org/openjdk/jmc/rjmx/JVMSupportToolkit.java +++ b/src/main/java/org/openjdk/jmc/rjmx/JVMSupportToolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -83,6 +83,27 @@ public static String[] checkConsoleSupport(IConnectionHandle connection) { return new String[0]; } + /** + * Checks if Flight Recorder is available for use + * + * @param connection + * @return If it is an Oracle JVM or there is a FlightRecorder VM option, then return true. + * Otherwise, return false. This is used for verifying JDK 8 JVMs that are not built + * with JFR enabled, e.g., OpenJDK 8 + */ + public static boolean hasFlightRecorder(IConnectionHandle connection) { + if (ConnectionToolkit.isOracle(connection)) { + return true; + } + MBeanServerConnection server = connection.getServiceOrNull(MBeanServerConnection.class); + try { + HotspotManagementToolkit.getVMOption(server, "FlightRecorder"); + return true; + } catch (Exception e) { // RuntimeMBeanException thrown if FlightRecorder is not present + return false; + } + } + /** * Checks if Flight Recorder is disabled. * @@ -136,7 +157,7 @@ public static String getNoFlightRecorderErrorMessage(IConnectionHandle handle, b } /** - * Returns information about whether to server denoted by the handle supports Flight Recorder + * Returns information about whether the server supports Flight Recorder. * * @param handle * the server to check diff --git a/src/main/java/org/openjdk/jmc/rjmx/internal/JMXRMISystemPropertiesProvider.java b/src/main/java/org/openjdk/jmc/rjmx/internal/JMXRMISystemPropertiesProvider.java index c8c9a09a..2e3f8137 100644 --- a/src/main/java/org/openjdk/jmc/rjmx/internal/JMXRMISystemPropertiesProvider.java +++ b/src/main/java/org/openjdk/jmc/rjmx/internal/JMXRMISystemPropertiesProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -56,4 +56,37 @@ public static void setup() { } } + public static void clearJMXRMISystemProperties() { + try { + Properties jmxRmiProperties = JMXRMIPreferences.getInstance().getProperties(); + if (jmxRmiProperties != null) { + for (String prop : jmxRmiProperties.stringPropertyNames()) { + System.clearProperty(prop); + } + } + } catch (Exception e) { + RJMXPlugin.getDefault().getLogger().log(Level.FINE, "Failed to remove JMXRMI SystemProperties", e); //$NON-NLS-1$ + } + } + + public static boolean isKeyStoreConfigured() { + try { + Properties jmxRmiProperties = JMXRMIPreferences.getInstance().getProperties(); + if (jmxRmiProperties != null) { + int totalPrefCnt = 0; + for (String prop : jmxRmiProperties.stringPropertyNames()) { + Object val = jmxRmiProperties.get(prop); + if (val != null && !val.toString().isEmpty()) { + ++totalPrefCnt; + } + } + if (totalPrefCnt == 4) { + return true; + } + } + } catch (Exception e) { + RJMXPlugin.getDefault().getLogger().log(Level.FINE, "Did not load jmxRmiProperties", e); //$NON-NLS-1$ + } + return false; + } } diff --git a/src/main/java/org/openjdk/jmc/rjmx/internal/RJMXConnection.java b/src/main/java/org/openjdk/jmc/rjmx/internal/RJMXConnection.java index 158168ff..c68bdc8c 100644 --- a/src/main/java/org/openjdk/jmc/rjmx/internal/RJMXConnection.java +++ b/src/main/java/org/openjdk/jmc/rjmx/internal/RJMXConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -44,6 +44,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import javax.management.AttributeNotFoundException; @@ -92,7 +93,8 @@ */ public class RJMXConnection implements Closeable, IMBeanHelperService { - public final static String KEY_SOCKET_FACTORY = "com.sun.jndi.rmi.factory.socket"; //$NON-NLS-1$ + public static final String KEY_SOCKET_FACTORY = "com.sun.jndi.rmi.factory.socket"; //$NON-NLS-1$ + public static final String KEY_JMXREMOTE_SSL = "com.sun.management.jmxremote.ssl"; //$NON-NLS-1$ /** * The default port JMX @@ -105,11 +107,12 @@ public class RJMXConnection implements Closeable, IMBeanHelperService { */ private static final long VALUE_RECALIBRATION_INTERVAL = 120000; private static final long REMOTE_START_TIME_UNDEFINED = -1; + private static final AtomicInteger CONNECTION_COUNTER = new AtomicInteger(); // The ConnectionDescriptor used to create this RJMXConnection private final IConnectionDescriptor m_connectionDescriptor; - private final IServerDescriptor m_serverDescriptor; + private final int m_connectionId = CONNECTION_COUNTER.getAndIncrement(); // The MBean server connection used for all local and remote communication. private volatile MCMBeanServerConnection m_server; @@ -216,10 +219,11 @@ public IConnectionDescriptor getConnectionDescriptor() { public void close() { synchronized (connectionStateLock) { if (isConnected()) { - m_server.dispose(); - tryRemovingListener(); - clearCollections(); + MCMBeanServerConnection tmpServer = m_server; m_server = null; + tryRemovingListener(tmpServer); + tmpServer.dispose(); + clearCollections(); if (m_jmxc != null) { try { m_jmxc.close(); @@ -241,9 +245,11 @@ private void clearCollections() { clearCache(); } - private void tryRemovingListener() { + private void tryRemovingListener(MCMBeanServerConnection tmpServer) { try { - ensureConnected().removeNotificationListener(MBeanServerDelegate.DELEGATE_NAME, m_registrationListener); + if (tmpServer != null) { + tmpServer.removeNotificationListener(MBeanServerDelegate.DELEGATE_NAME, m_registrationListener); + } } catch (Exception e) { RJMXPlugin.getDefault().getLogger().log(Level.WARNING, "Failed to remove unregistration listener! Lost connection?", e); //$NON-NLS-1$ @@ -270,6 +276,159 @@ public Set getMBeanNames() throws IOException { } } + @Override + public HashMap getMBeanInfos() throws IOException { + synchronized (m_cachedInfos) { + initializeMBeanInfos(); + return new HashMap<>(m_cachedInfos); + } + } + + @Override + public MBeanInfo getMBeanInfo(ObjectName mbean) + throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException { + synchronized (m_cachedInfos) { + MBeanInfo mbeanInfo = m_cachedInfos.get(mbean); + if (mbeanInfo == null) { + MBeanServerConnection server = ensureConnected(); + mbeanInfo = server.getMBeanInfo(mbean); + if (mbeanInfo != null) { + m_cachedInfos.put(mbean, mbeanInfo); + } + } + return mbeanInfo; + } + } + + @Override + public Object getAttributeValue(MRI attribute) throws AttributeNotFoundException, MBeanException, IOException, + InstanceNotFoundException, ReflectionException { + try { + MBeanServerConnection server = ensureConnected(); + return AttributeValueToolkit.getAttribute(server, attribute); + } catch (JMRuntimeException e) { + throw new MBeanException(e, e.getMessage()); + } + } + + public boolean connect() throws ConnectionException { + JVMDescriptor jvmInfo = getServerDescriptor().getJvmInfo(); + if (jvmInfo != null && jvmInfo.getJavaVersion() != null + && !new JavaVersion(jvmInfo.getJavaVersion()).isGreaterOrEqualThan(JavaVersionSupport.JDK_6)) { + throw new ConnectionException("Too low JDK Version. JDK 1.6 or higher is supported."); //$NON-NLS-1$ + } + synchronized (connectionStateLock) { + if (isConnected()) { + return false; + } + JMXServiceURL url; + try { + url = m_connectionDescriptor.createJMXServiceURL(); + } catch (IOException e1) { + throw new WrappedConnectionException(m_serverDescriptor.getDisplayName(), null, e1); + } + + try { + // Use same convention as Sun. localhost:0 means "VM, monitor thyself!" + String hostName = ConnectionToolkit.getHostName(url); + if (hostName != null && (hostName.equals("localhost")) //$NON-NLS-1$ + && ConnectionToolkit.getPort(url) == 0) { + m_server = new MCMBeanServerConnection(ManagementFactory.getPlatformMBeanServer()); + } else { + establishConnection(url, m_connectionDescriptor.getEnvironment()); + } + tryToAddMBeanNotificationListener(); + m_remoteStartTime = fetchServerStartTime(); + return true; + } catch (Exception e) { + m_server = null; + throw new WrappedConnectionException(m_serverDescriptor.getDisplayName(), url, e); + } + } + } + + @Override + public long getApproximateServerTime(long localTime) { + long startTime = System.currentTimeMillis(); + if ((startTime - m_lastRecalibration) > VALUE_RECALIBRATION_INTERVAL + && m_remoteStartTime != REMOTE_START_TIME_UNDEFINED) { + try { + /* + * FIXME: JMC-4270 - Server time approximation is not reliable. Since JDK-6523160, + * getUptime can no longer be used to derive the current server time. Find some + * other way to do this. + */ + long uptime = ConnectionToolkit.getRuntimeBean(ensureConnected()).getUptime(); + long returnTime = System.currentTimeMillis(); + long localTimeEstimate = (startTime + returnTime) / 2; + m_serverOffset = m_remoteStartTime + uptime - localTimeEstimate; + m_lastRecalibration = returnTime; + } catch (Exception e) { + RJMXPlugin.getDefault().getLogger().log(Level.SEVERE, "Could not recalibrate server offset", e); //$NON-NLS-1$ + } + } + return localTime + m_serverOffset; + } + + public void clearCache() { + synchronized (m_cachedInfos) { + m_cachedInfos.clear(); + m_cachedMBeanNames.clear(); + m_hasInitializedAllMBeans = false; + } + } + + @Override + public String toString() { + return "RJMX Connection " + m_connectionId + ": " + m_serverDescriptor.getDisplayName(); //$NON-NLS-1$ + } + + @Override + public void removeMBeanServerChangeListener(IMBeanServerChangeListener listener) { + m_mbeanListeners.remove(listener); + } + + @Override + public void addMBeanServerChangeListener(IMBeanServerChangeListener listener) { + m_mbeanListeners.add(listener); + } + + @Override + public Map> getMBeanMetadata(ObjectName mbean) { + return m_mbeanDataProvider.getMBeanData(mbean); + } + + /** + * Returns the IOperations available for the specified MBean. + * + * @param mbean + * the MBean for which to return the information. + * @return the operations that can be invoked on this mbean. + * @throws Exception + * if the connection failed or some other problem occurred when trying create + * operations. + */ + public Collection getOperations(ObjectName mbean) throws Exception { + MBeanServerConnection connection = ensureConnected(); + return MBeanOperationWrapper.createOperations(connection, mbean, + connection.getMBeanInfo(mbean).getOperations()); + } + + IMRIService getMRIService() { + return m_mbeanDataProvider; + } + + /** + * Returns the MBeanServerConnection. Yes, this breaks abstraction a bit, and should only be + * used by the MBeanBrowser. Everybody else should be using subscriptions anyway. + * + * @return the MBeanServerConnection currently in use by this connection. May be null if none is + * currently in use. + */ + MBeanServerConnection getMBeanServer() { + return m_server; + } + /** * Returns the bean information for the MBeans matching the domain and query. * @@ -372,77 +531,6 @@ private void initializeMBeanInfos() throws IOException { } } - @Override - public HashMap getMBeanInfos() throws IOException { - synchronized (m_cachedInfos) { - initializeMBeanInfos(); - return new HashMap<>(m_cachedInfos); - } - } - - @Override - public MBeanInfo getMBeanInfo(ObjectName mbean) - throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException { - synchronized (m_cachedInfos) { - MBeanInfo mbeanInfo = m_cachedInfos.get(mbean); - if (mbeanInfo == null) { - MBeanServerConnection server = ensureConnected(); - mbeanInfo = server.getMBeanInfo(mbean); - if (mbeanInfo != null) { - m_cachedInfos.put(mbean, mbeanInfo); - } - } - return mbeanInfo; - } - } - - @Override - public Object getAttributeValue(MRI attribute) throws AttributeNotFoundException, MBeanException, IOException, - InstanceNotFoundException, ReflectionException { - try { - MBeanServerConnection server = ensureConnected(); - return AttributeValueToolkit.getAttribute(server, attribute); - } catch (JMRuntimeException e) { - throw new MBeanException(e, e.getMessage()); - } - } - - public boolean connect() throws ConnectionException { - JVMDescriptor jvmInfo = getServerDescriptor().getJvmInfo(); - if (jvmInfo != null && jvmInfo.getJavaVersion() != null - && !new JavaVersion(jvmInfo.getJavaVersion()).isGreaterOrEqualThan(JavaVersionSupport.JDK_6)) { - throw new ConnectionException("Too low JDK Version. JDK 1.6 or higher is supported."); //$NON-NLS-1$ - } - synchronized (connectionStateLock) { - if (isConnected()) { - return false; - } - JMXServiceURL url; - try { - url = m_connectionDescriptor.createJMXServiceURL(); - } catch (IOException e1) { - throw new WrappedConnectionException(m_serverDescriptor.getDisplayName(), null, e1); - } - - try { - // Use same convention as Sun. localhost:0 means "VM, monitor thyself!" - String hostName = ConnectionToolkit.getHostName(url); - if (hostName != null && (hostName.equals("localhost")) //$NON-NLS-1$ - && ConnectionToolkit.getPort(url) == 0) { - m_server = new MCMBeanServerConnection(ManagementFactory.getPlatformMBeanServer()); - } else { - establishConnection(url, m_connectionDescriptor.getEnvironment()); - } - tryToAddMBeanNotificationListener(); - m_remoteStartTime = fetchServerStartTime(); - return true; - } catch (Exception e) { - m_server = null; - throw new WrappedConnectionException(m_serverDescriptor.getDisplayName(), url, e); - } - } - } - private long fetchServerStartTime() throws IOException { try { return ConnectionToolkit.getRuntimeBean(ensureConnected()).getStartTime(); @@ -485,40 +573,7 @@ private void connectJmxConnector(JMXServiceURL serviceURL, Map e JMXRMISystemPropertiesProvider.setup(); // According to javadocs, has to pass env here too (which mSA RMI took literally). m_jmxc.connect(env); - } - - @Override - public long getApproximateServerTime(long localTime) { - long startTime = System.currentTimeMillis(); - if ((startTime - m_lastRecalibration) > VALUE_RECALIBRATION_INTERVAL - && m_remoteStartTime != REMOTE_START_TIME_UNDEFINED) { - try { - /* - * FIXME: JMC-4270 - Server time approximation is not reliable. Since JDK-6523160, - * getUptime can no longer be used to derive the current server time. Find some - * other way to do this. - */ - long uptime = ConnectionToolkit.getRuntimeBean(ensureConnected()).getUptime(); - long returnTime = System.currentTimeMillis(); - long localTimeEstimate = (startTime + returnTime) / 2; - m_serverOffset = m_remoteStartTime + uptime - localTimeEstimate; - m_lastRecalibration = returnTime; - } catch (Exception e) { - RJMXPlugin.getDefault().getLogger().log(Level.SEVERE, "Could not recalibrate server offset", e); //$NON-NLS-1$ - } - } - return localTime + m_serverOffset; - } - - /** - * Returns the MBeanServerConnection. Yes, this breaks abstraction a bit, and should only be - * used by the MBeanBrowser. Everybody else should be using subscriptions anyway. - * - * @return the MBeanServerConnection currently in use by this connection. May be null if none is - * currently in use. - */ - MBeanServerConnection getMBeanServer() { - return m_server; + JMXRMISystemPropertiesProvider.clearJMXRMISystemProperties(); } /** @@ -537,51 +592,4 @@ private MBeanServerConnection ensureConnected() throws IOException { return server; } - public void clearCache() { - synchronized (m_cachedInfos) { - m_cachedInfos.clear(); - m_cachedMBeanNames.clear(); - m_hasInitializedAllMBeans = false; - } - } - - @Override - public String toString() { - return "RJMX Connection: " + m_serverDescriptor.getDisplayName(); //$NON-NLS-1$ - } - - @Override - public void removeMBeanServerChangeListener(IMBeanServerChangeListener listener) { - m_mbeanListeners.remove(listener); - } - - @Override - public void addMBeanServerChangeListener(IMBeanServerChangeListener listener) { - m_mbeanListeners.addIfAbsent(listener); - } - - @Override - public Map> getMBeanMetadata(ObjectName mbean) { - return m_mbeanDataProvider.getMBeanData(mbean); - } - - /** - * Returns the IOperations available for the specified MBean. - * - * @param mbean - * the MBean for which to return the information. - * @return the operations that can be invoked on this mbean. - * @throws Exception - * if the connection failed or some other problem occurred when trying create - * operations. - */ - public Collection getOperations(ObjectName mbean) throws Exception { - MBeanServerConnection connection = ensureConnected(); - return MBeanOperationWrapper.createOperations(connection, mbean, connection.getMBeanInfo(mbean).getOperations()); - } - - IMRIService getMRIService() { - return m_mbeanDataProvider; - } - } diff --git a/src/main/java/org/openjdk/jmc/rjmx/preferences/PreferencesKeys.java b/src/main/java/org/openjdk/jmc/rjmx/preferences/PreferencesKeys.java index 338a9bb4..148b0657 100644 --- a/src/main/java/org/openjdk/jmc/rjmx/preferences/PreferencesKeys.java +++ b/src/main/java/org/openjdk/jmc/rjmx/preferences/PreferencesKeys.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -51,12 +51,21 @@ public class PreferencesKeys { public static final String PROPERTY_MAIL_SERVER = "rjmx.smtp.server"; //$NON-NLS-1$ public static final String PROPERTY_MAIL_SERVER_PORT = "rjmx.smtp.server.port"; //$NON-NLS-1$ public static final String PROPERTY_MAIL_SERVER_SECURE = "rjmx.smtp.server.secure"; //$NON-NLS-1$ + public static final String PROPERTY_MAIL_SERVER_STARTTLS_ENCRYPTION = "rjmx.smtp.server.starttls.encryption"; //$NON-NLS-1$ public static final String PROPERTY_MAIL_SERVER_CREDENTIALS = "rjmx.smtp.server.credentials"; //$NON-NLS-1$ public static final String DEFAULT_MAIL_SERVER = "mail.example.org"; //$NON-NLS-1$ public static final int DEFAULT_MAIL_SERVER_PORT = 25; public static final boolean DEFAULT_MAIL_SERVER_SECURE = false; + public static final boolean DEFAULT_MAIL_SERVER_STARTTLS_ENCRYPTION = false; public static final String DEFAULT_MAIL_SERVER_USER = ""; //$NON-NLS-1$ public static final String DEFAULT_MAIL_SERVER_PASSWORD = ""; //$NON-NLS-1$ public static final String DEFAULT_MAIL_SERVER_CREDENTIALS = ""; //$NON-NLS-1$ + // Persistence + public static final String PROPERTY_PERSISTENCE_LOG_ROTATION_LIMIT_KB = "rjmx.services.persistence.log.rotation.limit"; //$NON-NLS-1$ + public static final long DEFAULT_PERSISTENCE_LOG_ROTATION_LIMIT_KB = 100; + public static final String PROPERTY_PERSISTENCE_DIRECTORY = "rjmx.services.persistence.directory"; //$NON-NLS-1$ + + public static final String PROPERTY_LIST_AGGREGATE_SIZE = "rjmx.internal.listAggregateSize"; //$NON-NLS-1$ + public static final int DEFAULT_LIST_AGGREGATE_SIZE = 40; } diff --git a/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/AbstractAttributeSubscription.java b/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/AbstractAttributeSubscription.java index b9fb76bb..a507513f 100644 --- a/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/AbstractAttributeSubscription.java +++ b/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/AbstractAttributeSubscription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -35,10 +35,10 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.Logger; -import org.openjdk.jmc.common.IPredicate; import org.openjdk.jmc.rjmx.IConnectionHandle; import org.openjdk.jmc.rjmx.subscription.IMBeanHelperService; import org.openjdk.jmc.rjmx.subscription.IMRIMetadata; @@ -60,7 +60,7 @@ public abstract class AbstractAttributeSubscription implements IMRISubscription private final IMRIMetadata m_attributeInfo; private MRIValueEvent m_lastEvent; private IUpdatePolicy m_updatePolicy; - private final IPredicate m_valueFilter; + private final Predicate m_valueFilter; /** * Constructor for AbstractAttribute. @@ -180,7 +180,7 @@ public MRIValueEvent getLastMRIValueEvent() { * the newly created event. */ protected void storeAndFireEvent(MRIValueEvent event) { - if (m_valueFilter != null && m_valueFilter.evaluate(event.getValue())) { + if (m_valueFilter != null && m_valueFilter.test(event.getValue())) { LOGGER.log(Level.INFO, "Subscription filtered out value: " + event.getValue()); //$NON-NLS-1$ return; } @@ -235,20 +235,20 @@ public String toString() { return getClass().getName() + '[' + getMRIMetadata() + ']'; } - private static IPredicate getValueFilter(IMRIMetadata metadata) { + private static Predicate getValueFilter(IMRIMetadata metadata) { // FIXME: We hard code the filters for now, but this should be handled along with content types String mri = metadata.getMRI().getQualifiedName(); if ("attribute://java.lang:type=OperatingSystem/ProcessCpuLoad".equals(mri) //$NON-NLS-1$ || "attribute://java.lang:type=OperatingSystem/SystemCpuLoad".equals(mri) //$NON-NLS-1$ || "attribute://java.lang:type=OperatingSystem/SystemLoadAverage".equals(mri)) { //$NON-NLS-1$ - return new IPredicate() { + return new Predicate() { @Override - public boolean evaluate(Object value) { + public boolean test(Object value) { return !(value instanceof Number) || ((Number) value).doubleValue() < 0; } }; } return null; } -} +} \ No newline at end of file diff --git a/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/DefaultAttributeSubscriptionThread.java b/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/DefaultAttributeSubscriptionThread.java index 831422f6..78917f2f 100644 --- a/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/DefaultAttributeSubscriptionThread.java +++ b/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/DefaultAttributeSubscriptionThread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -87,7 +87,7 @@ public class DefaultAttributeSubscriptionThread extends Thread { private boolean sendNulls; - private volatile boolean collectDebugInfo = false; + private volatile boolean collectDebugInfo; private Map subscriptionDebugInfo; public static class SubscriptionStats { diff --git a/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/DefaultNotificationSubscriptionManager.java b/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/DefaultNotificationSubscriptionManager.java index c54aaa09..9d1b85e5 100644 --- a/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/DefaultNotificationSubscriptionManager.java +++ b/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/DefaultNotificationSubscriptionManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -71,7 +71,7 @@ public final class DefaultNotificationSubscriptionManager { // private final IMBeanHelperService service; private final MBeanServerConnection mbeanServer; - private volatile boolean collectDebugInfo = false; + private volatile boolean collectDebugInfo; private Map subscriptionDebugInfo; /** diff --git a/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/FileMRIMetadata.java b/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/FileMRIMetadata.java index 1b437df1..b585579b 100644 --- a/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/FileMRIMetadata.java +++ b/src/main/java/org/openjdk/jmc/rjmx/subscription/internal/FileMRIMetadata.java @@ -39,16 +39,14 @@ import java.util.logging.Level; import java.util.logging.Logger; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - -import org.openjdk.jmc.common.io.IOToolkit; import org.openjdk.jmc.common.util.XmlToolkit; import org.openjdk.jmc.rjmx.subscription.IMRIMetadataProvider; import org.openjdk.jmc.rjmx.subscription.MRI; import org.openjdk.jmc.rjmx.subscription.MRI.Type; import org.openjdk.jmc.rjmx.subscription.MRIMetadataToolkit; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; /** * This class is used to read the default metadata from the mrimetadata.xml file. @@ -77,9 +75,7 @@ class FileMRIMetadata { static Map> readDefaultsFromFile() { FileMRIMetadata metadataLoader = new FileMRIMetadata(); - InputStream is = null; - try { - is = FileMRIMetadata.class.getResourceAsStream("mrimetadata.xml"); //$NON-NLS-1$ + try (InputStream is = FileMRIMetadata.class.getResourceAsStream("mrimetadata.xml")) { //$NON-NLS-1$ Document doc = XmlToolkit.loadDocumentFromStream(is); List elems = XmlToolkit.getChildElementsByTag(doc.getDocumentElement(), ELEMENT_METADATA_COLLECTION); @@ -97,8 +93,6 @@ static Map> readDefaultsFromFile() { LOGGER.log(Level.WARNING, "Tried reading mrimetadata.xml, but an exception occurred: " + e.getMessage() //$NON-NLS-1$ + "Extended information about attributes may not be available, " //$NON-NLS-1$ + "and the console will not operate optimally.", e); //$NON-NLS-1$ - } finally { - IOToolkit.closeSilently(is); } return metadataLoader.metadataMap; } diff --git a/src/main/java/org/openjdk/jmc/ui/common/jvm/JVMDescriptor.java b/src/main/java/org/openjdk/jmc/ui/common/jvm/JVMDescriptor.java index 6903aa73..4a1cff1d 100644 --- a/src/main/java/org/openjdk/jmc/ui/common/jvm/JVMDescriptor.java +++ b/src/main/java/org/openjdk/jmc/ui/common/jvm/JVMDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -38,6 +38,8 @@ public class JVMDescriptor { private final String javaVersion; private final JVMType jvmType; + private final String jvmName; + private final String jvmVendor; private final JVMArch jvmArch; private final String javaCommand; private final String jvmArguments; @@ -46,13 +48,15 @@ public class JVMDescriptor { private final Connectable connectable; public JVMDescriptor(String javaVersion, JVMType jvmType, JVMArch jvmArch, String javaCommand, String jvmArguments, - Integer pid, boolean debug, Connectable attachable) { + String jvmName, String jvmVendor, Integer pid, boolean debug, Connectable attachable) { super(); this.javaVersion = javaVersion; this.jvmType = jvmType; this.jvmArch = jvmArch; this.javaCommand = javaCommand; this.jvmArguments = jvmArguments; + this.jvmName = jvmName; + this.jvmVendor = jvmVendor; this.pid = pid; this.debug = debug; connectable = attachable; @@ -78,6 +82,14 @@ public String getJVMArguments() { return jvmArguments; } + public String getJvmName() { + return jvmName; + } + + public String getJvmVendor() { + return jvmVendor; + } + public Integer getPid() { return pid; } diff --git a/src/main/java/org/openjdk/jmc/ui/common/jvm/JVMType.java b/src/main/java/org/openjdk/jmc/ui/common/jvm/JVMType.java index a990f8e6..e1dd0ede 100644 --- a/src/main/java/org/openjdk/jmc/ui/common/jvm/JVMType.java +++ b/src/main/java/org/openjdk/jmc/ui/common/jvm/JVMType.java @@ -32,6 +32,8 @@ */ package org.openjdk.jmc.ui.common.jvm; +import org.openjdk.jmc.common.version.JavaVMVersionToolkit; + /** * Enum for the different JVMs. */ @@ -51,6 +53,22 @@ public enum JVMType { /** * The JVM is some other JVM. */ - OTHER + OTHER; + + /** + * Derive the {@link JVMType} from a jvm name, e.g. the system property java.vm.name. + * + * @param jvmName + * the jvm name to get the JVMType for. + * @return the {@link JVMType} for the jvm name. + */ + public static JVMType getJVMType(String jvmName) { + if (JavaVMVersionToolkit.isJRockitJVMName(jvmName)) { + return JVMType.JROCKIT; + } else if (JavaVMVersionToolkit.isHotspotJVMName(jvmName)) { + return JVMType.HOTSPOT; + } + return JVMType.OTHER; + } } diff --git a/src/main/java/org/openjdk/jmc/ui/common/labelingrules/NameConverter.java b/src/main/java/org/openjdk/jmc/ui/common/labelingrules/NameConverter.java index 70b996bf..68ee52d7 100644 --- a/src/main/java/org/openjdk/jmc/ui/common/labelingrules/NameConverter.java +++ b/src/main/java/org/openjdk/jmc/ui/common/labelingrules/NameConverter.java @@ -37,7 +37,9 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.logging.Level; +import org.openjdk.jmc.ui.common.CorePlugin; import org.openjdk.jmc.ui.common.jvm.JVMCommandLineToolkit; import org.openjdk.jmc.ui.common.jvm.JVMDescriptor; import org.openjdk.jmc.ui.common.resource.Resource; @@ -143,7 +145,7 @@ public Resource getImageResource(JVMDescriptor descriptor) { */ public void addNamingRule(NamingRule rule) { rules.add(rule); - Collections.sort(rules, COMPARATOR); + rules.sort(COMPARATOR); } /** diff --git a/src/main/java/org/openjdk/jmc/ui/common/security/PersistentCredentials.java b/src/main/java/org/openjdk/jmc/ui/common/security/PersistentCredentials.java index 77979100..fb0b506d 100644 --- a/src/main/java/org/openjdk/jmc/ui/common/security/PersistentCredentials.java +++ b/src/main/java/org/openjdk/jmc/ui/common/security/PersistentCredentials.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -32,6 +32,9 @@ */ package org.openjdk.jmc.ui.common.security; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * {@link ICredentials} stored in the {@link ISecurityManager}. The username and password are lazy * loaded on demand. @@ -41,6 +44,9 @@ public class PersistentCredentials implements ICredentials { private final String id; private String[] wrapped; + private static final Pattern PASSWORD_PATTERN = Pattern + .compile("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#(&)[{-}]:;',?/*~$^+=<>]).{8,20}$"); //$NON-NLS-1$ + public PersistentCredentials(String id) { this.id = id; } @@ -78,4 +84,9 @@ private String[] getCredentials() throws SecurityException { public String getExportedId() { return id; } + + public static boolean isPasswordValid(final String password) { + Matcher matcher = PASSWORD_PATTERN.matcher(password); + return matcher.matches(); + } } diff --git a/src/main/java/org/openjdk/jmc/ui/common/security/SecurityManagerFactory.java b/src/main/java/org/openjdk/jmc/ui/common/security/SecurityManagerFactory.java index 7d7ccaf1..fe600854 100644 --- a/src/main/java/org/openjdk/jmc/ui/common/security/SecurityManagerFactory.java +++ b/src/main/java/org/openjdk/jmc/ui/common/security/SecurityManagerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -32,14 +32,18 @@ */ package org.openjdk.jmc.ui.common.security; +import java.util.logging.Level; +import java.util.logging.Logger; + /** * This is the global security manager factory for Mission Control. You can only have one * SecurityManager, and it is initialized at start. It can not be changed once initialized. The only * way to change security manager is to set the system property - * org.openjdk.jmc.rjmx.security.manager=<class> before this factory class is instantiated. The - * class must implement ISecurityManager, and it must have a default constructor. + * org.openjdk.jmc.rjmx.security.manager=<class> before this factory class is instantiated. + * The class must implement ISecurityManager, and it must have a default constructor. */ public final class SecurityManagerFactory { + private final static Logger LOGGER = Logger.getLogger("org.openjdk.jmc.ui.common.security"); //$NON-NLS-1$ private static ISecurityManager instance; @@ -51,8 +55,8 @@ public final class SecurityManagerFactory { instance = (ISecurityManager) c.newInstance(); } } catch (Exception e) { - System.out.println("Could not create Security manager for className. Using default! Exception was:"); //$NON-NLS-1$ - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "Could not create Security manager for className. Using default! Exception was:", //$NON-NLS-1$ + e); } } diff --git a/src/test/java/io/cryostat/core/reports/InterruptibleReportGeneratorTest.java b/src/test/java/io/cryostat/core/reports/InterruptibleReportGeneratorTest.java index 966c1ee0..48519521 100644 --- a/src/test/java/io/cryostat/core/reports/InterruptibleReportGeneratorTest.java +++ b/src/test/java/io/cryostat/core/reports/InterruptibleReportGeneratorTest.java @@ -149,7 +149,7 @@ void shouldProduceReportWithFilteredRulesAndTopics() throws Exception { MatcherAssert.assertThat( report.get().getReportStats(), Matchers.not(Matchers.nullValue())); MatcherAssert.assertThat( - report.get().getReportStats().rulesEvaluated, Matchers.equalTo(10)); + report.get().getReportStats().rulesEvaluated, Matchers.equalTo(11)); } } @@ -180,10 +180,9 @@ void shouldProduceEvalMap() throws Exception { MatcherAssert.assertThat( entry.getKey(), Matchers.not(Matchers.emptyOrNullString())); MatcherAssert.assertThat( - entry.getValue().getScore(), Matchers.not(Matchers.notANumber())); + entry.getValue().getName(), Matchers.not(Matchers.emptyOrNullString())); MatcherAssert.assertThat( - entry.getValue().getDescription(), - Matchers.not(Matchers.emptyOrNullString())); + entry.getValue().getTopic(), Matchers.not(Matchers.emptyOrNullString())); } } } diff --git a/src/test/java/io/cryostat/core/util/RuleFilterParserTest.java b/src/test/java/io/cryostat/core/util/RuleFilterParserTest.java index e871fb8d..209483d9 100644 --- a/src/test/java/io/cryostat/core/util/RuleFilterParserTest.java +++ b/src/test/java/io/cryostat/core/util/RuleFilterParserTest.java @@ -38,6 +38,7 @@ package io.cryostat.core.util; import java.util.Collection; +import java.util.Map; import java.util.Set; import java.util.concurrent.RunnableFuture; import java.util.function.Predicate; @@ -47,8 +48,11 @@ import org.openjdk.jmc.common.item.IItemCollection; import org.openjdk.jmc.common.util.IPreferenceValueProvider; import org.openjdk.jmc.common.util.TypedPreference; +import org.openjdk.jmc.flightrecorder.rules.IResult; +import org.openjdk.jmc.flightrecorder.rules.IResultValueProvider; import org.openjdk.jmc.flightrecorder.rules.IRule; -import org.openjdk.jmc.flightrecorder.rules.Result; +import org.openjdk.jmc.flightrecorder.rules.TypedResult; +import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit.EventAvailability; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; @@ -139,8 +143,10 @@ private TestRule(String id, String topic) { } @Override - public RunnableFuture evaluate( - IItemCollection items, IPreferenceValueProvider valueProvider) { + public RunnableFuture createEvaluation( + IItemCollection items, + IPreferenceValueProvider valueProvider, + IResultValueProvider dependencyResults) { return null; } @@ -163,5 +169,15 @@ public String getName() { public String getTopic() { return topic; } + + @Override + public Map getRequiredEvents() { + return null; + } + + @Override + public Collection> getResults() { + return null; + } } }