blob: 1ecdbc285bea04d5520eeffdd45f1200e5f10932 [file] [log] [blame]
/**
* 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.camel.component.jmx;
import java.util.Hashtable;
import javax.management.MalformedObjectNameException;
import javax.management.NotificationFilter;
import javax.management.ObjectName;
import org.apache.camel.Consumer;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.impl.DefaultEndpoint;
import org.apache.camel.util.ObjectHelper;
/**
* Endpoint that describes a connection to an mbean.
* <p/>
* The component can connect to the local platform mbean server with the following URI:
* <p/>
* <code>jmx://platform?options</code>
* <p/>
* A remote mbean server url can be provided following the initial JMX scheme like so:
* <p/>
* <code>jmx:service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi?options</code>
* <p/>
* You can append query options to the URI in the following format, ?options=value&option2=value&...
*
* @author markford
*/
public class JMXEndpoint extends DefaultEndpoint {
// error messages as constants so they can be asserted on from unit tests
protected static final String ERR_PLATFORM_SERVER = "Monitor type consumer only supported on platform server.";
protected static final String ERR_THRESHOLD_LOW = "ThresholdLow must be set when monitoring a gauge attribute.";
protected static final String ERR_THRESHOLD_HIGH = "ThresholdHigh must be set when monitoring a gauge attribute.";
protected static final String ERR_GAUGE_NOTIFY = "One or both of NotifyHigh and NotifyLow must be true when monitoring a gauge attribute.";
protected static final String ERR_STRING_NOTIFY = "One or both of NotifyDiffer and NotifyMatch must be true when monitoring a string attribute.";
protected static final String ERR_STRING_TO_COMPARE = "StringToCompare must be specified when monitoring a string attribute.";
protected static final String ERR_OBSERVED_ATTRIBUTE = "Observed attribute must be specified";
/**
* URI Property: [monitor types only] The attribute to observe for the monitor bean.
*/
private String mObservedAttribute;
/**
* URI Property: [monitor types only] The frequency to poll the bean to check the monitor.
*/
private long mGranularityPeriod;
/**
* URI Property: [monitor types only] The type of monitor to create. One of string, gauge, counter.
*/
private String mMonitorType;
/**
* URI Property: [counter monitor only] Initial threshold for the monitor. The value must exceed this before notifications are fired.
*/
private int mInitThreshold;
/**
* URI Property: [counter monitor only] The amount to increment the threshold after it's been exceeded.
*/
private int mOffset;
/**
* URI Property: [counter monitor only] The value at which the counter is reset to zero
*/
private int mModulus;
/**
* URI Property: [counter + gauge monitor only] If true, then the value reported in the notification is the difference from the threshold as opposed to the value itself.
*/
private boolean mDifferenceMode;
/**
* URI Property: [gauge monitor only] If true, the gauge will fire a notification when the high threshold is exceeded
*/
private boolean mNotifyHigh;
/**
* URI Property: [gauge monitor only] If true, the gauge will fire a notification when the low threshold is exceeded
*/
private boolean mNotifyLow;
/**
* URI Property: [gauge monitor only] Value for the gauge's high threshold
*/
private Double mThresholdHigh;
/**
* URI Property: [gauge monitor only] Value for the gauge's low threshold
*/
private Double mThresholdLow;
/**
* URI Property: [string monitor only] If true, the string monitor will fire a notification when the string attribute differs from the string to compare.
*/
private boolean mNotifyDiffer;
/**
* URI Property: [string monitor only] If true, the string monitor will fire a notification when the string attribute matches the string to compare.
*/
private boolean mNotifyMatch;
/**
* URI Property: [string monitor only] Value for the string monitor's string to compare.
*/
private String mStringToCompare;
/**
* URI Property: Format for the message body. Either "xml" or "raw". If xml, the notification is serialized to xml. If raw, then the raw java object is set as the body.
*/
private String mFormat = "xml";
/**
* URI Property: credentials for making a remote connection
*/
private String mUser;
/**
* URI Property: credentials for making a remote connection
*/
private String mPassword;
/**
* URI Property: The domain for the mbean you're connecting to
*/
private String mObjectDomain;
/**
* URI Property: The name key for the mbean you're connecting to. This value is mutually exclusive with the object properties that get passed.
*/
private String mObjectName;
/**
* URI Property: Reference to a bean that implements the NotificationFilter.
*/
private NotificationFilter mNotificationFilter;
/**
* URI Property: Value to handback to the listener when a notification is received. This value will be put in the message header with the key "jmx.handback"
*/
private Object mHandback;
/**
* URI Property: properties for the object name. These values will be used if the objectName param is not set
*/
private Hashtable<String, String> mObjectProperties;
/**
* cached object name that was built from the objectName param or the hashtable
*/
private ObjectName mJMXObjectName;
/**
* server url comes from the remaining endpoint
*/
private String mServerURL;
public JMXEndpoint(String aEndpointUri, JMXComponent aComponent) {
super(aEndpointUri, aComponent);
}
public Consumer createConsumer(Processor aProcessor) throws Exception {
// validate that all of the endpoint is configured properly
if (getMonitorType() != null) {
if (!isPlatformServer()) {
throw new IllegalArgumentException(ERR_PLATFORM_SERVER);
}
if (ObjectHelper.isEmpty(getObservedAttribute())) {
throw new IllegalArgumentException(ERR_OBSERVED_ATTRIBUTE);
}
if (getMonitorType().equals("string")) {
if (ObjectHelper.isEmpty(getStringToCompare())) {
throw new IllegalArgumentException(ERR_STRING_TO_COMPARE);
}
if (!isNotifyDiffer() && !isNotifyMatch()) {
throw new IllegalArgumentException(ERR_STRING_NOTIFY);
}
} else if (getMonitorType().equals("gauge")) {
if (!isNotifyHigh() && !isNotifyLow()) {
throw new IllegalArgumentException(ERR_GAUGE_NOTIFY);
}
if (getThresholdHigh() == null) {
throw new IllegalArgumentException(ERR_THRESHOLD_HIGH);
}
if (getThresholdLow() == null) {
throw new IllegalArgumentException(ERR_THRESHOLD_LOW);
}
}
return new JMXMonitorConsumer(this, aProcessor);
} else {
// shouldn't need any other validation.
return new JMXConsumer(this, aProcessor);
}
}
public Producer createProducer() throws Exception {
throw new UnsupportedOperationException("producing JMX notifications is not supported");
}
public boolean isSingleton() {
return false;
}
public String getFormat() {
return mFormat;
}
public void setFormat(String aFormat) {
mFormat = aFormat;
}
public boolean isXML() {
return "xml".equals(getFormat());
}
public boolean isPlatformServer() {
return "platform".equals(getServerURL());
}
public String getUser() {
return mUser;
}
public void setUser(String aUser) {
mUser = aUser;
}
public String getPassword() {
return mPassword;
}
public void setPassword(String aPassword) {
mPassword = aPassword;
}
public String getObjectDomain() {
return mObjectDomain;
}
public void setObjectDomain(String aObjectDomain) {
mObjectDomain = aObjectDomain;
}
public String getObjectName() {
return mObjectName;
}
public void setObjectName(String aObjectName) {
if (getObjectProperties() != null) {
throw new IllegalArgumentException("Cannot set both objectName and objectProperties");
}
mObjectName = aObjectName;
}
protected String getServerURL() {
return mServerURL;
}
protected void setServerURL(String aServerURL) {
mServerURL = aServerURL;
}
public NotificationFilter getNotificationFilter() {
return mNotificationFilter;
}
public void setNotificationFilter(NotificationFilter aFilterRef) {
mNotificationFilter = aFilterRef;
}
public Object getHandback() {
return mHandback;
}
public void setHandback(Object aHandback) {
mHandback = aHandback;
}
public Hashtable<String, String> getObjectProperties() {
return mObjectProperties;
}
/**
* Setter for the ObjectProperties is either called by reflection when
* processing the URI or manually by the component.
* <p/>
* If the URI contained a value with a reference like "objectProperties=#myHashtable"
* then the Hashtable will be set in place.
* <p/>
* If there are extra properties that begin with "key." then the component will
* create a Hashtable with these values after removing the "key." prefix.
*/
public void setObjectProperties(Hashtable<String, String> aObjectProperties) {
if (getObjectName() != null) {
throw new IllegalArgumentException("Cannot set both objectName and objectProperties");
}
mObjectProperties = aObjectProperties;
}
protected ObjectName getJMXObjectName() throws MalformedObjectNameException {
if (mJMXObjectName == null) {
ObjectName on = buildObjectName();
setJMXObjectName(on);
}
return mJMXObjectName;
}
protected void setJMXObjectName(ObjectName aCachedObjectName) {
mJMXObjectName = aCachedObjectName;
}
public String getObservedAttribute() {
return mObservedAttribute;
}
public void setObservedAttribute(String aObservedAttribute) {
mObservedAttribute = aObservedAttribute;
}
public long getGranularityPeriod() {
return mGranularityPeriod;
}
public void setGranularityPeriod(long aGranularityPeriod) {
mGranularityPeriod = aGranularityPeriod;
}
public String getMonitorType() {
return mMonitorType;
}
public void setMonitorType(String aMonitorType) {
mMonitorType = aMonitorType;
}
public int getInitThreshold() {
return mInitThreshold;
}
public void setInitThreshold(int aInitThreshold) {
mInitThreshold = aInitThreshold;
}
public int getOffset() {
return mOffset;
}
public void setOffset(int aOffset) {
mOffset = aOffset;
}
public int getModulus() {
return mModulus;
}
public void setModulus(int aModulus) {
mModulus = aModulus;
}
public boolean isDifferenceMode() {
return mDifferenceMode;
}
public void setDifferenceMode(boolean aDifferenceMode) {
mDifferenceMode = aDifferenceMode;
}
public boolean isNotifyHigh() {
return mNotifyHigh;
}
public void setNotifyHigh(boolean aNotifyHigh) {
mNotifyHigh = aNotifyHigh;
}
public boolean isNotifyLow() {
return mNotifyLow;
}
public void setNotifyLow(boolean aNotifyLow) {
mNotifyLow = aNotifyLow;
}
public Double getThresholdHigh() {
return mThresholdHigh;
}
public void setThresholdHigh(Double aThresholdHigh) {
mThresholdHigh = aThresholdHigh;
}
public Double getThresholdLow() {
return mThresholdLow;
}
public void setThresholdLow(Double aThresholdLow) {
mThresholdLow = aThresholdLow;
}
public boolean isNotifyDiffer() {
return mNotifyDiffer;
}
public void setNotifyDiffer(boolean aNotifyDiffer) {
mNotifyDiffer = aNotifyDiffer;
}
public boolean isNotifyMatch() {
return mNotifyMatch;
}
public void setNotifyMatch(boolean aNotifyMatch) {
mNotifyMatch = aNotifyMatch;
}
public String getStringToCompare() {
return mStringToCompare;
}
public void setStringToCompare(String aStringToCompare) {
mStringToCompare = aStringToCompare;
}
private ObjectName buildObjectName() throws MalformedObjectNameException {
ObjectName objectName;
if (getObjectProperties() == null) {
StringBuilder sb = new StringBuilder(getObjectDomain()).append(':').append("name=").append(getObjectName());
objectName = new ObjectName(sb.toString());
} else {
objectName = new ObjectName(getObjectDomain(), getObjectProperties());
}
return objectName;
}
}