blob: e49491e8478b293ca7984a0601b7e00f4c12e939 [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.geode.management.internal;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.management.ObjectName;
import org.apache.logging.log4j.Logger;
import org.apache.geode.DataSerializable;
import org.apache.geode.DataSerializer;
import org.apache.geode.internal.DataSerializableFixedID;
import org.apache.geode.internal.Version;
import org.apache.geode.internal.logging.LogService;
/**
* Central component for federation It consists of an Object State as well as some meta data for the
* Object being federated.
*
*
*/
public class FederationComponent
implements java.io.Serializable, DataSerializable, DataSerializableFixedID {
private static final Logger logger = LogService.getLogger();
private static final String THIS_COMPONENT = FederationComponent.class.getName();
private static final long serialVersionUID = 3123549507449088591L;
/**
* Name of the MBean. This name will be replicated at Managing Node
*/
private String objectName;
/**
* Name if the interface class . It will determine the interface for MBean at Managing Node side
*/
private String interfaceClassName;
/**
* Flag to determine if MBean emits notification or not.
*/
private boolean notificationEmitter;
/**
* This Map holds the object state as property-value Every component should be serializable
*/
private Map<String, Object> objectState = new HashMap<String, Object>();
private transient Map<String, Method> getterMethodMap;
private transient Object mbeanObject;
private transient Class mbeanInterfaceClass;
private transient Map<String, Object> oldObjectState = new HashMap<String, Object>();
private final transient Map<Method, OpenMethod> methodHandlerMap = OpenTypeUtil.newMap();
private transient boolean prevRefreshChangeDetected = false;
/**
*
* @param objectName ObjectName of the MBean
* @param interfaceClass interface class of the MBean
* @param notificationEmitter specifies whether this MBean is going to emit notifications
*/
public FederationComponent(Object object, ObjectName objectName, Class interfaceClass,
boolean notificationEmitter) {
this.objectName = objectName.toString();
this.interfaceClassName = interfaceClass.getCanonicalName();
this.mbeanInterfaceClass = interfaceClass;
this.notificationEmitter = notificationEmitter;
this.mbeanObject = object;
getterMethodMap = new HashMap<String, Method>();
initGetters(interfaceClass);
}
public FederationComponent() {}
// Introspect the mbeanInterface and initialize this object's maps.
//
private void initGetters(Class<?> mbeanInterface) {
final Method[] methodArray = mbeanInterface.getMethods();
for (Method m : methodArray) {
String name = m.getName();
String attrName = "";
if (name.startsWith("get")) {
attrName = name.substring(3);
} else if (name.startsWith("is") && m.getReturnType() == boolean.class) {
attrName = name.substring(2);
}
if (attrName.length() != 0 && m.getParameterTypes().length == 0
&& m.getReturnType() != void.class) { // For Getters
m.setAccessible(true);
getterMethodMap.put(attrName, m);
methodHandlerMap.put(m, OpenMethod.from(m));
}
}
}
/**
* gets the Canonical name of the MBean interface
*
* @return mbean interface class name
*/
public String getMBeanInterfaceClass() {
return interfaceClassName;
}
/**
* True if this MBean is a notification emitter.
*
* @return whether its a notification emitter or not
*/
public boolean isNotificationEmitter() {
return notificationEmitter;
}
/**
* This method will get called from Management Thread. This will dynamically invoke the MBeans
* getter methods and set them in ObjectState Map.
*
* In Future releases we can implement the delta propagation here
*
* @return true if the refresh detects that the state changed. It will return false if two
* consecutive refresh calls results in no state change. This indicates to the
* LocalManager whether to send the MBean state to Manager or not.
*/
public boolean refreshObjectState(boolean keepOldState) {
boolean changeDetected = false;
Object[] args = null;
if (keepOldState) {
oldObjectState.putAll(objectState);
}
for (Map.Entry<String, Method> gettorMethodEntry : getterMethodMap.entrySet()) {
String property = gettorMethodEntry.getKey();
Object propertyValue = null;
try {
Method m = gettorMethodEntry.getValue();
propertyValue = m.invoke(mbeanObject, args);
// To Handle open types in getter values
OpenMethod op = methodHandlerMap.get(m);
propertyValue = op.toOpenReturnValue(propertyValue);
} catch (Exception e) {
propertyValue = null;
if (logger.isTraceEnabled()) {
logger.trace(e.getMessage());
}
}
Object oldValue = objectState.put(property, propertyValue);
if (!changeDetected) {
if (propertyValue != null) {
if (!propertyValue.equals(oldValue)) {
changeDetected = true;
}
} else { // new value is null
if (oldValue != null) {
changeDetected = true;
}
}
}
}
boolean retVal = prevRefreshChangeDetected || changeDetected;
prevRefreshChangeDetected = changeDetected;
return retVal;
}
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof FederationComponent) {
FederationComponent anotherFedComp = (FederationComponent) anObject;
if (anotherFedComp.interfaceClassName.equals(this.interfaceClassName)
&& anotherFedComp.notificationEmitter == this.notificationEmitter
&& anotherFedComp.objectState.equals(this.objectState)
&& anotherFedComp.objectName.equals(this.objectName))
return true;
}
return false;
}
public int hashCode() {
return objectName.hashCode();
}
/**
* Managing node will get Object state by calling this method
*
* @return value of the given property
*/
public Object getValue(String propertyName) {
return objectState.get(propertyName);
}
public String toString() {
if (Boolean.getBoolean("debug.Management")) {
return " ObjectName = " + objectName + ",InterfaceClassName = " + interfaceClassName
+ ", NotificationEmitter = " + notificationEmitter + ", ObjectState = "
+ objectState.toString();
} else {
return "ObjectName = " + objectName;
}
}
public Map<String, Object> getObjectState() {
return objectState;
}
public Map<String, Object> getOldState() {
return oldObjectState;
}
@Override
public void fromData(DataInput in) throws IOException, ClassNotFoundException {
this.notificationEmitter = DataSerializer.readPrimitiveBoolean(in);
this.interfaceClassName = DataSerializer.readString(in);
this.objectState = DataSerializer.readHashMap(in);
this.objectName = DataSerializer.readString(in);
}
@Override
public void toData(DataOutput out) throws IOException {
DataSerializer.writePrimitiveBoolean(this.notificationEmitter, out);
DataSerializer.writeString(this.interfaceClassName, out);
DataSerializer.writeHashMap((HashMap<?, ?>) objectState, out);
DataSerializer.writeString(this.objectName, out);
}
@Override
public int getDSFID() {
return DataSerializableFixedID.MGMT_FEDERATION_COMPONENT;
}
public Object getMBeanObject() {
return mbeanObject;
}
public Class getInterfaceClass() {
return mbeanInterfaceClass;
}
@Override
public Version[] getSerializationVersions() {
// TODO Auto-generated method stub
return null;
}
}