| /* |
| * 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.DataSerializer; |
| import org.apache.geode.internal.serialization.DataSerializableFixedID; |
| import org.apache.geode.internal.serialization.DeserializationContext; |
| import org.apache.geode.internal.serialization.SerializationContext; |
| import org.apache.geode.internal.serialization.Version; |
| import org.apache.geode.logging.internal.log4j.api.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, 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, |
| DeserializationContext context) 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, |
| SerializationContext context) 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; |
| } |
| |
| |
| } |