blob: bf0121ea02ef76dee7101e8d973307d183e2ddb3 [file] [log] [blame]
/*
* =========================================================================
* Copyright (c) 2002-2012 VMware, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. VMware products are covered by
* more patents listed at http://www.vmware.com/go/patents.
* All Rights Reserved.
* ========================================================================
*/
package com.gemstone.gemfire.mgmt.DataBrowser.query.internal;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.gemstone.gemfire.internal.GemFireVersion;
import com.gemstone.gemfire.mgmt.DataBrowser.query.IntrospectionResult;
import com.gemstone.gemfire.mgmt.DataBrowser.utils.LogUtil;
/**
* Helper class through which everything needed for PDX support is available.
* <pre>
* 1. Whether PDX is available
* 2. Whether given type is assignable to PdxInstance
* 3. Whether given object is instance of PdxInstance
* <pre>
* Using reflection, isolates loading of GemFire 6.6 types/classes used for
* PdxInstance from the rest of the code so that Data Browser can be used with
* earlier GemFire versions.
*
* @author abhishek
* @since 1.3 (GemFire 6.6)
*/
public class PdxHelper {
/* Initial GemFire version since when PDX support started */
private static final double PDX_SUPPORT_START_VERSION = 6.6;
private static final double PDX_FIELDTYPE_MADE_PUBLIC_MINOR_VERSION = 6.2; //read it in continuation with 6.6 i.e. 6.6 MAJOR.MINOR & 6.2 MINOR.SUBMINOR
/* Class Name to use to load the PdxInstance type (java.lang.Class) */
private static final String PDXINSTANCE_CLASS_NAME = "com.gemstone.gemfire.pdx.PdxInstance";
/* Class Name of the class used for IntrospectionResult for objects of type
* PdxInstance. This class has to use internal GemFire types introduced in
* GemFire 6.6, hence this is also accessed using reflection though it's a
* Data Browser class */
private static final String PDXINSTANCE_INTROSPECTION_RESULT_CLASS_NAME = "com.gemstone.gemfire.mgmt.DataBrowser.query.internal.PdxTypesIntrospectionResult";
private static final String PDXINSTANCE_FIELDTYPE66_CLASS_NAME = "com.gemstone.gemfire.pdx.internal.FieldType";
private static final String PDXINSTANCE_FIELDTYPE662_CLASS_NAME = "com.gemstone.gemfire.pdx.FieldType";
/* PdxHelper instance class */
private static final PdxHelper instance = new PdxHelper();
/* Class used for PdxInstance type */
private Class<?> pdxInstanceClass;
/* Class used for IntrospectionResult for PdxInstance type */
private Class<? extends IntrospectionResult> pdxIntrospectionResultClass;
/* Caching constructor of IntrospectionResult implementation used for
* PdxInstance type. Specifically: PdxTypesIntrospectionResult(Object, String) */
private Constructor<? extends IntrospectionResult> pdxIntrospectionResultConstructor;
/* Caching method of IntrospectionResult implementation used for retrieving
* PdxInfoType for a PdxInstance type. Specifically: getPdxInfoType(Object)
* & clearKnownPdxInfo()*/
private Method getPdxInfoTypeMethod;
private Method clearKnownPdxInfoMethod;
/* boolean indicating whether PDX is supported or not */
private boolean pdxSupported = false;
private double gemfireMajorMinorVersion = 6.0; //oldest supported by Data Browser
private double gemfireMinorSubMinorVersion = 0.0; //oldest supported by Data Browser
private String fieldTypeClassName;
/**
* Checks whether PDX support is available. If PDX support is available,
* initializes components required for introspecting PdxInstance
*/
private PdxHelper() {
String version = GemFireVersion.getGemFireVersion();
if(version.indexOf("Beta") > 0){
version = version.substring(0, version.indexOf("Beta"));
}
String[] split = version.split("\\.");
if (split.length >= 2) {
String majorMinor = split[0] + "." + split[1];
gemfireMajorMinorVersion = Double.valueOf(majorMinor);
this.pdxSupported = gemfireMajorMinorVersion >= PDX_SUPPORT_START_VERSION;
}
if (this.pdxSupported) {
if (split.length > 2) {
String minorSubMinor = split[1] + "." + split[2];
gemfireMinorSubMinorVersion = Double.valueOf(minorSubMinor);
}
fieldTypeClassName = PDXINSTANCE_FIELDTYPE662_CLASS_NAME;
//for versions prior to 6.6.2
if (gemfireMajorMinorVersion == PDX_SUPPORT_START_VERSION &&
gemfireMinorSubMinorVersion < PDX_FIELDTYPE_MADE_PUBLIC_MINOR_VERSION) {
fieldTypeClassName = PDXINSTANCE_FIELDTYPE66_CLASS_NAME;
}
initPdxIntrospectionSupport();
}
}
/**
* Initializes local (Data Browser side) support for introspecting PdxInstance
* types.
*/
private void initPdxIntrospectionSupport() {
try {
pdxInstanceClass = Class.forName(PDXINSTANCE_CLASS_NAME);
pdxIntrospectionResultClass = (Class<? extends IntrospectionResult>) Class.forName(PDXINSTANCE_INTROSPECTION_RESULT_CLASS_NAME);
} catch (ClassNotFoundException e) {
pdxInstanceClass = null;
pdxIntrospectionResultClass = null;
LogUtil.error("This GemFire version supports Pdx but could not load/find required classes.", e);
}
if (pdxIntrospectionResultClass != null) {
try {
pdxIntrospectionResultConstructor = pdxIntrospectionResultClass.getConstructor(new Class[] {Object.class, String.class});
getPdxInfoTypeMethod = pdxIntrospectionResultClass.getDeclaredMethod("getPdxInfoType", new Class[] {Object.class});
clearKnownPdxInfoMethod = pdxIntrospectionResultClass.getDeclaredMethod("clearKnownPdxTypeInfo", new Class[0]);
} catch (SecurityException e) {
pdxIntrospectionResultConstructor = null;
getPdxInfoTypeMethod = null;
clearKnownPdxInfoMethod = null;
LogUtil.error("This GemFire version supports Pdx but could not access required class elements.", e);
} catch (NoSuchMethodException e) {
pdxIntrospectionResultConstructor = null;
getPdxInfoTypeMethod = null;
clearKnownPdxInfoMethod = null;
LogUtil.error("This GemFire version supports Pdx but could not find required class elements.", e);
}
}
LogUtil.fine("Versions Info: "+gemfireMajorMinorVersion + "," + gemfireMinorSubMinorVersion + ", field type class : "+ fieldTypeClassName);
}
/**
* @return singleton for PdxHelper
*/
public static PdxHelper getInstance() {
return instance;
}
/**
* @return whether the GemFire version supports PDX
*/
public boolean isPdxSupported() {
return this.pdxSupported;
}
/**
* Checks whether PDX support is available & whether the given instance is of
* PdxInstance GemFire type
*
* @param other
* Object to be checked whether it's of PdxInstance GemFire type
* @return true if PDX support is available & given object is of type
* PdxInstance, false otherwise
*/
public boolean isPdxInstance(Object other) {
if (!isPdxSupported()) {
return false;
}
return pdxInstanceClass.isInstance(other);
}
/**
* Checks whether PDX support is available & whether the given Class/type is
* same as or sub-type of PdxInstance GemFire type
*
* @param klass
* Class/type to be checked whether it is same as or sub-type of
* PdxInstance GemFire type
* @return true if PDX support is available & given object is same as or
* sub-type of PdxInstance GemFire type, false otherwise
*/
public boolean isPdxInstanceType(Class<?> klass) {
if (!isPdxSupported()) {
return false;
}
return pdxInstanceClass.isAssignableFrom(klass);
}
/**
* Wraps the method PdxTypesIntrospectionResult.getPdxInfoType(Object) which
* is used to Create or return existing PdxInfoType for the given object. If
* given object - 'pdxInstanceObj' - is not of the type PdxInstanceImpl OR if
* can call to PdxTypesIntrospectionResult.getPdxInfoType() using reflection
* fails, it returns null.
*
* @param pdxInstanceObj
* PdxInstance for which PdxInfoType is to be retrieved
* @return new or existing PdxInfoType for the given object.
* @throws PdxIntrospectionException
* if fails to invoke method -
* PdxTypesIntrospectionResult.getPdxInfoType
*/
public PdxInfoType getPdxInfoType(Object pdxInstanceObj) throws PdxIntrospectionException {
PdxInfoType pdxInfoType = null;
try {
pdxInfoType = (PdxInfoType) getPdxInfoTypeMethod.invoke(pdxIntrospectionResultClass, new Object[] {pdxInstanceObj});
} catch (IllegalArgumentException e) {
throw new PdxIntrospectionException(e);
} catch (IllegalAccessException e) {
throw new PdxIntrospectionException(e);
} catch (InvocationTargetException e) {
throw new PdxIntrospectionException(e);
}
return pdxInfoType;
}
/**
* Wraps creation of new PdxTypesIntrospectionResult objects.
*
* @param pdxInstanceObj
* PdxInstance for which PdxTypesIntrospectionResult is needed
* @return new PdxTypesIntrospectionResult object for a given pdxInstanceObj
* @throws PdxIntrospectionException
* if fails to create PdxTypesIntrospectionResult using reflection
* @throws IllegalArgumentException
* if given object is not a PdxInstance
*/
public IntrospectionResult getPdxMetaInfo(Object pdxInstanceObj) throws PdxIntrospectionException {
IntrospectionResult pdxIntrospectionResult = null;
try {
pdxIntrospectionResult = pdxIntrospectionResultConstructor.newInstance(new Object[] {pdxInstanceObj, fieldTypeClassName});
} catch (IllegalArgumentException e) {
throw new PdxIntrospectionException(e);
} catch (InstantiationException e) {
throw new PdxIntrospectionException(e);
} catch (IllegalAccessException e) {
throw new PdxIntrospectionException(e);
} catch (InvocationTargetException e) {
throw new PdxIntrospectionException(e);
}
return pdxIntrospectionResult;
}
/**
* Clears cached known Pdx Instance type Information.
* Wraps call to PdxTypesIntrospectionResult.clearKnownPdxTypeInfo()
*/
public void clearKnownPdxTypeInfo() {
try {
clearKnownPdxInfoMethod.invoke(pdxIntrospectionResultClass, new Object[0]);
} catch (IllegalArgumentException e) {
LogUtil.warning("Could not clear cached known Pdx Type information.", e);
} catch (IllegalAccessException e) {
LogUtil.warning("Could not clear cached known Pdx Type information.", e);
} catch (InvocationTargetException e) {
LogUtil.warning("Could not clear cached known Pdx Type information.", e);
}
}
}