-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(agentplugin): add support for JMC agent plugin (#103)
* Basic agent functionality (TODO: Method & field capturing xml model) * Added more classes for agent probe xml modelling, added support for deleting templates * Cleaning up localProbetemplateService, adding XML Validation * Cleanup, adding tests, implementing getTemplate * Remove extraneous non-nls tags, cleanup, rework probeTemplate.deserialize * Remove extraneous non-nls tags * clean up unnecessary catches, turn ProbeTemplateService into an interface, clean up getLocalTemplates * Renaming ProbeTemplateService * Adding invalid converter/relation key to exception message, rethrowing exceptions in AgentJMXHelper * running spotless
- Loading branch information
1 parent
3bcde70
commit ace15ef
Showing
14 changed files
with
1,871 additions
and
0 deletions.
There are no files selected for viewing
126 changes: 126 additions & 0 deletions
126
src/main/java/io/cryostat/core/agent/AgentJMXHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* | ||
* Copyright The Cryostat Authors | ||
* | ||
* The Universal Permissive License (UPL), Version 1.0 | ||
* | ||
* Subject to the condition set forth below, permission is hereby granted to any | ||
* person obtaining a copy of this software, associated documentation and/or data | ||
* (collectively the "Software"), free of charge and under any and all copyright | ||
* rights in the Software, and any and all patent rights owned or freely | ||
* licensable by each licensor hereunder covering either (i) the unmodified | ||
* Software as contributed to or provided by such licensor, or (ii) the Larger | ||
* Works (as defined below), to deal in both | ||
* | ||
* (a) the Software, and | ||
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if | ||
* one is included with the Software (each a "Larger Work" to which the Software | ||
* is contributed by such licensors), | ||
* | ||
* without restriction, including without limitation the rights to copy, create | ||
* derivative works of, display, perform, and distribute the Software and make, | ||
* use, sell, offer for sale, import, export, have made, and have sold the | ||
* Software and the Larger Work(s), and to sublicense the foregoing rights on | ||
* either these or other terms. | ||
* | ||
* This license is subject to the following condition: | ||
* The above copyright notice and either this complete permission notice or at | ||
* a minimum a reference to the UPL must be included in all copies or | ||
* substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
package io.cryostat.core.agent; | ||
|
||
import java.io.IOException; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
|
||
import javax.management.MBeanServerConnection; | ||
import javax.management.MalformedObjectNameException; | ||
import javax.management.ObjectName; | ||
|
||
import org.openjdk.jmc.rjmx.ConnectionException; | ||
import org.openjdk.jmc.rjmx.IConnectionHandle; | ||
|
||
public class AgentJMXHelper { | ||
|
||
private static final Logger logger = Logger.getLogger(AgentJMXHelper.class.getName()); | ||
private static final String AGENT_OBJECT_NAME = | ||
"org.openjdk.jmc.jfr.agent:type=AgentController"; | ||
private static final String DEFINE_EVENT_PROBES = "defineEventProbes"; | ||
private static final String RETRIEVE_EVENT_PROBES = "retrieveEventProbes"; | ||
private static final String RETRIEVE_CURRENT_TRANSFORMS = "retrieveCurrentTransforms"; | ||
|
||
private final IConnectionHandle connectionHandle; | ||
private final MBeanServerConnection mbsc; | ||
|
||
public AgentJMXHelper(IConnectionHandle connectionHandle) throws ConnectionException { | ||
this.connectionHandle = connectionHandle; | ||
mbsc = connectionHandle.getServiceOrDummy(MBeanServerConnection.class); | ||
} | ||
|
||
public IConnectionHandle getConnectionHandle() { | ||
return connectionHandle; | ||
} | ||
|
||
public MBeanServerConnection getMBeanServerConnection() { | ||
return mbsc; | ||
} | ||
|
||
public boolean isMXBeanRegistered() throws Exception { | ||
try { | ||
return mbsc.isRegistered(new ObjectName(AGENT_OBJECT_NAME)); | ||
} catch (MalformedObjectNameException | IOException e) { | ||
logger.log(Level.SEVERE, "Could not check if agent MXBean is registered", e); | ||
throw e; | ||
} | ||
} | ||
|
||
public String retrieveEventProbes() throws Exception { | ||
try { | ||
Object result = | ||
mbsc.invoke( | ||
new ObjectName(AGENT_OBJECT_NAME), | ||
RETRIEVE_EVENT_PROBES, | ||
new Object[0], | ||
new String[0]); | ||
|
||
return result.toString(); | ||
} catch (Exception e) { | ||
logger.log(Level.WARNING, "Could not retrieve event probes", e); | ||
throw e; | ||
} | ||
} | ||
|
||
public Object retrieveCurrentTransforms() throws Exception { | ||
try { | ||
Object result = | ||
mbsc.invoke( | ||
new ObjectName(AGENT_OBJECT_NAME), | ||
RETRIEVE_CURRENT_TRANSFORMS, | ||
new Object[0], | ||
new String[0]); | ||
return result; | ||
} catch (Exception e) { | ||
logger.log(Level.WARNING, "Could not retrieve current transforms", e); | ||
throw e; | ||
} | ||
} | ||
|
||
public void defineEventProbes(String xmlDescription) throws Exception { | ||
try { | ||
Object[] params = {xmlDescription}; | ||
String[] signature = {String.class.getName()}; | ||
mbsc.invoke(new ObjectName(AGENT_OBJECT_NAME), DEFINE_EVENT_PROBES, params, signature); | ||
} catch (Exception e) { | ||
logger.log(Level.WARNING, "Could not define event probes: " + xmlDescription, e); | ||
throw e; | ||
} | ||
} | ||
} |
227 changes: 227 additions & 0 deletions
227
src/main/java/io/cryostat/core/agent/CapturedValue.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
/* | ||
* Copyright The Cryostat Authors | ||
* | ||
* The Universal Permissive License (UPL), Version 1.0 | ||
* | ||
* Subject to the condition set forth below, permission is hereby granted to any | ||
* person obtaining a copy of this software, associated documentation and/or data | ||
* (collectively the "Software"), free of charge and under any and all copyright | ||
* rights in the Software, and any and all patent rights owned or freely | ||
* licensable by each licensor hereunder covering either (i) the unmodified | ||
* Software as contributed to or provided by such licensor, or (ii) the Larger | ||
* Works (as defined below), to deal in both | ||
* | ||
* (a) the Software, and | ||
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if | ||
* one is included with the Software (each a "Larger Work" to which the Software | ||
* is contributed by such licensors), | ||
* | ||
* without restriction, including without limitation the rights to copy, create | ||
* derivative works of, display, perform, and distribute the Software and make, | ||
* use, sell, offer for sale, import, export, have made, and have sold the | ||
* Software and the Larger Work(s), and to sublicense the foregoing rights on | ||
* either these or other terms. | ||
* | ||
* This license is subject to the following condition: | ||
* The above copyright notice and either this complete permission notice or at | ||
* a minimum a reference to the UPL must be included in all copies or | ||
* substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
package io.cryostat.core.agent; | ||
|
||
import java.net.URI; | ||
import java.net.URISyntaxException; | ||
import java.util.Locale; | ||
|
||
import org.w3c.dom.Document; | ||
import org.w3c.dom.Element; | ||
import org.w3c.dom.NodeList; | ||
|
||
/** | ||
* Abstraction class for parsing and handling captured values (method parameters, fields, | ||
* converters) | ||
*/ | ||
public class CapturedValue { | ||
|
||
private static final String DEFAULT_STRING_FIELD = ""; | ||
private static final Object DEFAULT_OBJECT_TYPE = null; | ||
private static final String CONVERTER_REGEX = | ||
"([a-zA-Z_$][a-zA-Z0-9_$]*\\.)*([a-zA-Z_$][a-zA-Z0-9_$]*)"; | ||
|
||
private static final String XML_TAG_CAPTURED_VALUE = "capturedvalue"; | ||
private static final String XML_TAG_NAME = "name"; | ||
private static final String XML_TAG_DESCRIPTION = "description"; | ||
private static final String XML_TAG_CONTENT_TYPE = "contenttype"; | ||
private static final String XML_TAG_RELATION_KEY = "relationkey"; | ||
private static final String XML_TAG_CONVERTER = "converter"; | ||
|
||
private String name; | ||
private String description; | ||
private ContentType contentType; | ||
private String relationKey; | ||
private String converter; | ||
|
||
enum ContentType { | ||
NONE, | ||
BYTES, | ||
TIMESTAMP, | ||
MILLIS, | ||
NANOS, | ||
TICKS, | ||
ADDRESS, | ||
OS_THREAD, | ||
JAVA_THREAD, | ||
STACK_TRACE, | ||
CLASS, | ||
PERCENTAGE | ||
} | ||
|
||
CapturedValue() { | ||
name = DEFAULT_STRING_FIELD; | ||
description = DEFAULT_STRING_FIELD; | ||
contentType = (ContentType) DEFAULT_OBJECT_TYPE; | ||
relationKey = DEFAULT_STRING_FIELD; | ||
converter = DEFAULT_STRING_FIELD; | ||
} | ||
|
||
CapturedValue(Element element) { | ||
this(); | ||
|
||
NodeList elements; | ||
elements = element.getElementsByTagName(XML_TAG_NAME); | ||
if (elements.getLength() != 0) { | ||
name = elements.item(0).getTextContent(); | ||
} | ||
|
||
elements = element.getElementsByTagName(XML_TAG_DESCRIPTION); | ||
if (elements.getLength() != 0) { | ||
description = elements.item(0).getTextContent(); | ||
} | ||
|
||
elements = element.getElementsByTagName(XML_TAG_CONTENT_TYPE); | ||
if (elements.getLength() != 0) { | ||
contentType = | ||
ContentType.valueOf( | ||
elements.item(0).getTextContent().toUpperCase(Locale.ENGLISH)); | ||
} | ||
|
||
elements = element.getElementsByTagName(XML_TAG_RELATION_KEY); | ||
if (elements.getLength() != 0) { | ||
relationKey = elements.item(0).getTextContent(); | ||
} | ||
|
||
elements = element.getElementsByTagName(XML_TAG_CONVERTER); | ||
if (elements.getLength() != 0) { | ||
converter = elements.item(0).getTextContent(); | ||
} | ||
} | ||
|
||
public Element buildElement(Document document) { | ||
Element element = document.createElement(XML_TAG_CAPTURED_VALUE); | ||
|
||
if (name != null && !name.isEmpty()) { | ||
Element nameElement = document.createElement(XML_TAG_NAME); | ||
nameElement.setTextContent(name); | ||
element.appendChild(nameElement); | ||
} | ||
|
||
if (description != null && !description.isEmpty()) { | ||
Element descriptionElement = document.createElement(XML_TAG_DESCRIPTION); | ||
descriptionElement.setTextContent(description); | ||
element.appendChild(descriptionElement); | ||
} | ||
|
||
if (contentType != null) { | ||
Element contentTypeElement = document.createElement(XML_TAG_CONTENT_TYPE); | ||
contentTypeElement.setTextContent(contentType.toString()); | ||
element.appendChild(contentTypeElement); | ||
} | ||
|
||
if (relationKey != null && !relationKey.isEmpty()) { | ||
Element relationKeyElement = document.createElement(XML_TAG_RELATION_KEY); | ||
relationKeyElement.setTextContent(relationKey); | ||
element.appendChild(relationKeyElement); | ||
} | ||
|
||
if (converter != null && !converter.isEmpty()) { | ||
Element converterElement = document.createElement(XML_TAG_CONVERTER); | ||
converterElement.setTextContent(converter); | ||
element.appendChild(converterElement); | ||
} | ||
|
||
return element; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public void setName(String name) { | ||
this.name = name; | ||
} | ||
|
||
public String getDescription() { | ||
return description; | ||
} | ||
|
||
public void setDescription(String description) { | ||
this.description = description; | ||
} | ||
|
||
public ContentType getContentType() { | ||
return contentType; | ||
} | ||
|
||
public void setContentType(ContentType contentType) { | ||
this.contentType = contentType; | ||
} | ||
|
||
public String getRelationKey() { | ||
return relationKey; | ||
} | ||
|
||
public void setRelationKey(String relationKey) { | ||
if (relationKey != null && !relationKey.isEmpty()) { | ||
relationKey = relationKey.trim(); | ||
try { | ||
new URI(relationKey); | ||
} catch (URISyntaxException e) { | ||
throw new IllegalArgumentException( | ||
"Relational Key has incorrect syntax: " + relationKey); | ||
} | ||
} | ||
|
||
this.relationKey = relationKey; | ||
} | ||
|
||
public String getConverter() { | ||
return converter; | ||
} | ||
|
||
public void setConverter(String converter) { | ||
if (converter != null && !converter.isEmpty()) { | ||
converter = converter.trim(); | ||
if (!converter.matches(CONVERTER_REGEX)) { | ||
throw new IllegalArgumentException("Converter has incorrect syntax: " + converter); | ||
} | ||
} | ||
|
||
this.converter = converter; | ||
} | ||
|
||
protected void copyContentToWorkingCopy(CapturedValue copy) { | ||
copy.name = name; | ||
copy.description = description; | ||
copy.contentType = contentType; | ||
copy.relationKey = relationKey; | ||
copy.converter = converter; | ||
} | ||
} |
Oops, something went wrong.