org.glassfish.gmbal.impl.ManagedObjectManagerImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of webservices-rt Show documentation
Show all versions of webservices-rt Show documentation
This module contains the Metro runtime code.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2007-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.gmbal.impl ;
import java.util.ResourceBundle ;
import java.util.Map ;
import java.util.HashMap ;
import java.util.WeakHashMap ;
import java.util.List ;
import java.util.ArrayList ;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.io.IOException ;
import java.io.Serializable;
import java.lang.annotation.Annotation ;
import java.lang.management.ManagementFactory ;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collection;
import javax.management.MBeanServer ;
import javax.management.JMException ;
import javax.management.ObjectName ;
import javax.management.MBeanAttributeInfo;
import org.glassfish.gmbal.AMXMBeanInterface;
import org.glassfish.gmbal.AMXClient;
import org.glassfish.gmbal.GmbalMBean ;
import org.glassfish.gmbal.ManagedObject ;
import org.glassfish.gmbal.Description ;
import org.glassfish.gmbal.IncludeSubclass ;
import org.glassfish.gmbal.InheritedAttribute ;
import org.glassfish.gmbal.InheritedAttributes ;
import org.glassfish.gmbal.AMXMetadata;
import org.glassfish.gmbal.ManagedAttribute;
import org.glassfish.gmbal.ManagedObjectManager;
import org.glassfish.gmbal.ManagedData;
import org.glassfish.gmbal.generic.Pair ;
import org.glassfish.gmbal.generic.Algorithms ;
import org.glassfish.gmbal.generic.MethodMonitor;
import org.glassfish.gmbal.generic.MethodMonitorFactory;
import org.glassfish.gmbal.generic.DumpIgnore;
import org.glassfish.gmbal.generic.ObjectUtility;
import org.glassfish.gmbal.generic.Predicate;
import org.glassfish.gmbal.generic.UnaryFunction;
import org.glassfish.gmbal.generic.FacetAccessor ;
import org.glassfish.gmbal.generic.FacetAccessorImpl;
import org.glassfish.gmbal.generic.DelayedObjectToString;
import org.glassfish.gmbal.typelib.EvaluatedClassAnalyzer;
import org.glassfish.gmbal.typelib.EvaluatedClassDeclaration;
import org.glassfish.gmbal.typelib.EvaluatedDeclaration;
import org.glassfish.gmbal.typelib.EvaluatedFieldDeclaration;
import org.glassfish.gmbal.typelib.EvaluatedMethodDeclaration;
import org.glassfish.gmbal.typelib.EvaluatedType;
import org.glassfish.gmbal.typelib.TypeEvaluator;
import org.glassfish.external.amx.AMX;
import org.glassfish.external.statistics.AverageRangeStatistic ;
import org.glassfish.external.statistics.BoundaryStatistic;
import org.glassfish.external.statistics.BoundedRangeStatistic;
import org.glassfish.external.statistics.CountStatistic;
import org.glassfish.external.statistics.RangeStatistic;
import org.glassfish.external.statistics.Statistic;
import org.glassfish.external.statistics.TimeStatistic;
import org.glassfish.external.statistics.StringStatistic;
import org.glassfish.gmbal.generic.ClassAnalyzer;
import static org.glassfish.gmbal.generic.Algorithms.* ;
/* Implementation notes:
* XXX Test attribute change notification.
*/
public class ManagedObjectManagerImpl implements ManagedObjectManagerInternal {
// Used in MBeanSkeleton
@AMXMetadata
static class DefaultAMXMetadataHolder { }
private static final AMXMetadata DEFAULT_AMX_METADATA =
DefaultAMXMetadataHolder.class.getAnnotation(AMXMetadata.class);
private static ObjectUtility myObjectUtil =
new ObjectUtility(true, 0, 4)
.useToString( EvaluatedType.class )
.useToString( ManagedObjectManager.class ) ;
private static final class StringComparator implements Serializable,
Comparator {
private static final long serialVersionUID = 8274851916877850245L;
public int compare(String o1, String o2) {
return - o1.compareTo( o2 ) ;
}
} ;
private static Comparator REV_COMP = new StringComparator() ;
// All finals should be initialized in this order in the private constructor
@DumpIgnore
private final MethodMonitor mm ;
private final String domain ;
private final MBeanTree tree ;
private final Map skeletonMap ;
private final Map typeConverterMap ;
private final Map> addedAnnotations ;
private final MBeanSkeleton amxSkeleton ;
private final Set amxAttributeNames ;
// All non-finals should be initialized in this order in the init() method.
private boolean rootCreated ;
private ResourceBundle resourceBundle ;
private MBeanServer server ;
private ManagedObjectManager.RegistrationDebugLevel regDebugLevel ;
private boolean runDebugFlag ;
private boolean jmxRegistrationDebugFlag ;
// Maintain the list of typePrefixes in reversed sorted order, so that
// we strip the longest prefix first.
private final SortedSet typePrefixes = new TreeSet(
REV_COMP ) ;
private boolean stripPackagePrefix = false ;
private ManagedObjectManagerImpl( final String domain,
final ObjectName rootParentName ) {
this.mm = MethodMonitorFactory.makeStandard( getClass() ) ;
this.domain = domain ;
this.tree = new MBeanTree( this, domain, rootParentName, AMX.TYPE_KEY ) ;
this.skeletonMap =
new WeakHashMap() ;
this.typeConverterMap = new WeakHashMap() ;
this.addedAnnotations =
new HashMap>() ;
final EvaluatedClassDeclaration ecd =
(EvaluatedClassDeclaration)TypeEvaluator.getEvaluatedType(
AMXMBeanInterface.class ) ;
this.amxAttributeNames = new HashSet() ;
this.amxSkeleton = getSkeleton( ecd ) ;
for (MBeanAttributeInfo mbi : amxSkeleton.getMBeanInfo().getAttributes()) {
amxAttributeNames.add( mbi.getName() ) ;
}
}
@ManagedData
@Description( "The Statistic model and its sub-models specify the data"
+ " models which are requried to be used to provide the performance data"
+ " described by the specific attributes in the Stats models" )
@InheritedAttributes( {
@InheritedAttribute( methodName="getName",
description = "The name of this Statistic" ),
@InheritedAttribute( methodName="getUnit",
description = "The unit of measurement for this Statistic" ),
@InheritedAttribute( methodName="getDescription",
description = "A human-readable description of the Statistic" ),
@InheritedAttribute( methodName="getStartTime",
description = "The time of the first measurement represented as a long" ),
@InheritedAttribute( methodName="getLastSampleTime",
description = "The time of the first measurement represented as a long")
} )
public interface DummyStatistic { }
@ManagedData
@Description( "Specifies standard timing measurements")
@InheritedAttributes( {
@InheritedAttribute( methodName="getCount",
description = "Number of times the operation was invoked since "
+ "the beginning of this measurement" ),
@InheritedAttribute( methodName="getMaxTime",
description = "The maximum amount of time taken to complete one invocation "
+ "of this operation since the beginning of this measurement" ),
@InheritedAttribute( methodName="getMinTime",
description = "The minimum amount of time taken to complete one invocation "
+ "of this operation since the beginning of this measurement" ),
@InheritedAttribute( methodName="getTotalTime",
description = "The total amount of time taken to complete every invocation "
+ "of this operation since the beginning of this measurement" )
} )
public interface DummyTimeStatistic extends DummyStatistic { }
@ManagedData
@Description( "Specifies standard measurements of the upper and lower "
+ "limits of the value of an attribute" )
@InheritedAttributes( {
@InheritedAttribute( methodName = "getUpperBound",
description = "The upper limit of the value of this attribute" ),
@InheritedAttribute( methodName = "getLowerBound",
description = "The lower limit of the value of this attribute" )
} )
public interface DummyBoundaryStatistic extends DummyStatistic {}
@ManagedData
@Description( "Specifies standard count measurements" )
@InheritedAttributes( {
@InheritedAttribute( methodName = "getCount",
description = "The count since the last reset" )
} )
public interface DummyCountStatistic {}
@ManagedData
@Description( "Specifies standard measurements of the lowest and highest values"
+ " an attribute has held as well as its current value" )
@InheritedAttributes( {
@InheritedAttribute( methodName = "getHighWaterMark",
description = "The highest value this attribute has held since"
+ " the beginninYg of the measurement" ),
@InheritedAttribute( methodName = "getLowWaterMark",
description = "The lowest value this attribute has held since"
+ " the beginninYg of the measurement" ),
@InheritedAttribute( methodName = "getCurrent",
description = "The current value of this attribute" )
} )
public interface DummyRangeStatistic {}
@ManagedData
@Description( "Adds an average to the range statistic")
@InheritedAttributes( {
@InheritedAttribute( methodName = "getAverage",
description =
"The average value of this attribute since its last reset")
})
public interface DummyAverageRangeStatistic {}
@ManagedData
@Description( "Provides standard measurements of a range that has fixed limits" )
public interface DummyBoundedRangeStatistic extends
DummyBoundaryStatistic, DummyRangeStatistic {}
@ManagedData
@Description( "Custom statistic type whose value is a string")
@InheritedAttributes( {
@InheritedAttribute(
methodName="getCurrent",
description="Returns the String value of the statistic" )
} )
public interface DummyStringStatistic extends DummyStatistic { }
List> statsData = list(
pair( (Class)DummyStringStatistic.class,
(Class)StringStatistic.class ),
pair( (Class)DummyTimeStatistic.class,
(Class)TimeStatistic.class ),
pair( (Class)DummyStatistic.class,
(Class)Statistic.class ),
pair( (Class)DummyBoundaryStatistic.class,
(Class)BoundaryStatistic.class ),
pair( (Class)DummyBoundedRangeStatistic.class,
(Class)BoundedRangeStatistic.class ),
pair( (Class)DummyCountStatistic.class,
(Class)CountStatistic.class ),
pair( (Class)DummyRangeStatistic.class,
(Class)RangeStatistic.class ),
pair( (Class)DummyAverageRangeStatistic.class,
(Class)AverageRangeStatistic.class )
) ;
private void addAnnotationIfNotNull( AnnotatedElement elemement,
Annotation annotation ) {
if (annotation != null) {
addAnnotation(elemement, annotation);
}
}
private void initializeStatisticsSupport() {
for (Pair pair : statsData) {
Class dummy = pair.first() ;
Class real = pair.second() ;
addAnnotationIfNotNull( real, dummy.getAnnotation( ManagedData.class ) ) ;
addAnnotationIfNotNull( real, dummy.getAnnotation( Description.class ) ) ;
addAnnotationIfNotNull( real, dummy.getAnnotation( InheritedAttributes.class ) ) ;
}
}
private void init() {
this.server = AccessController.doPrivileged(
new PrivilegedAction() {
public MBeanServer run() {
return ManagementFactory.getPlatformMBeanServer() ;
}
} ) ;
rootCreated = false ;
resourceBundle = null ;
regDebugLevel = ManagedObjectManager.RegistrationDebugLevel.NONE ;
runDebugFlag = false ;
jmxRegistrationDebugFlag = false ;
tree.clear() ;
skeletonMap.clear() ;
typeConverterMap.clear() ;
addedAnnotations.clear() ;
mm.clear() ;
initializeStatisticsSupport() ;
}
public ManagedObjectManagerImpl( final String domain ) {
this( domain, null ) ;
init() ;
}
public ManagedObjectManagerImpl( final ObjectName rootParentName ) {
this( rootParentName.getDomain(), rootParentName ) ;
init() ;
}
public void close() throws IOException {
// Can be called anytime
mm.enter( registrationDebug(), "close" ) ;
try {
init() ;
} finally {
mm.exit( registrationDebug() ) ;
}
}
private synchronized void checkRootNotCreated( String methodName ) {
if (rootCreated) {
throw Exceptions.self.createRootCalled(methodName) ;
}
}
private synchronized void checkRootCreated( String methodName ) {
if (!rootCreated) {
throw Exceptions.self.createRootNotCalled(methodName) ;
}
}
public synchronized void suspendJMXRegistration() {
mm.clear() ;
// Can be called anytime
tree.suspendRegistration() ;
}
public synchronized void resumeJMXRegistration() {
mm.clear() ;
// Can be called anytime
tree.resumeRegistration();
}
public synchronized void stripPackagePrefix() {
mm.clear() ;
checkRootNotCreated("stripPackagePrefix");
stripPackagePrefix = true ;
}
@Override
public String toString( ) {
// Can be called anytime
return "ManagedObjectManagerImpl[domain=" + domain + "]" ;
}
public synchronized ObjectName getRootParentName() {
checkRootCreated("getRootParentName");
return tree.getRootParentName() ;
}
@ManagedObject
@AMXMetadata( type="gmbal-root", isSingleton=true)
@Description( "Dummy class used when no root is specified" )
private static class Root {
// No methods: will simply implement an AMXMBeanInterface container
@Override
public String toString() {
return "GmbalDefaultRoot" ;
}
}
public synchronized GmbalMBean createRoot() {
return createRoot( new Root() ) ;
}
public synchronized GmbalMBean createRoot(Object root) {
return createRoot( root, null ) ;
}
public synchronized GmbalMBean createRoot(Object root, String name) {
mm.clear() ;
checkRootNotCreated( "createRoot" ) ;
GmbalMBean result ;
try {
// Assume successful create, so that AMXMBeanInterface checks that
// back through getRootParentName will succeed.
rootCreated = true ;
result = tree.setRoot( root, name ) ;
if (result == null) {
rootCreated = false ;
}
} catch (RuntimeException exc) {
rootCreated = false ;
throw exc ;
}
return result ;
}
public synchronized Object getRoot() {
mm.clear() ;
// Can be called anytime.
return tree.getRoot() ;
}
private synchronized MBeanSkeleton getSkeleton( EvaluatedClassDeclaration cls ) {
// can be called anytime, otherwise we can't create the root itself!
mm.enter( registrationDebug(), "getSkeleton", cls ) ;
try {
MBeanSkeleton result = skeletonMap.get( cls ) ;
boolean newSkeleton = result == null ;
if (newSkeleton) {
mm.info( registrationDebug(), "Skeleton not found" ) ;
Pair pair =
getClassAnalyzer( cls, ManagedObject.class ) ;
EvaluatedClassAnalyzer ca = pair.second() ;
EvaluatedClassDeclaration annotatedClass = pair.first() ;
mm.info( registrationFineDebug(), "Annotated class for skeleton is",
annotatedClass ) ;
if (annotatedClass == null) {
throw Exceptions.self.managedObjectAnnotationNotFound(
cls.name() ) ;
}
MBeanSkeleton skel = new MBeanSkeleton( cls, ca, this ) ;
if (amxSkeleton == null) {
// Can't compose amxSkeleton with itself!
result = skel ;
} else {
result = amxSkeleton.compose( skel ) ;
}
skeletonMap.put( cls, result ) ;
}
mm.info(registrationFineDebug() || (registrationDebug() && newSkeleton),
"Skeleton", new DelayedObjectToString( result, myObjectUtil ) ) ;
return result ;
} finally {
mm.exit( registrationDebug() ) ;
}
}
public synchronized TypeConverter getTypeConverter( EvaluatedType type ) {
// Can be called anytime
mm.enter( registrationFineDebug(), "getTypeConverter", type ) ;
TypeConverter result = null;
try {
boolean newTypeConverter = false ;
result = typeConverterMap.get( type ) ;
if (result == null) {
mm.info( registrationFineDebug(), "Creating new TypeConverter" ) ;
// Store a TypeConverter impl that throws an exception when
// acessed. Used to detect recursive types.
typeConverterMap.put( type,
new TypeConverterImpl.TypeConverterPlaceHolderImpl( type ) ) ;
result = TypeConverterImpl.makeTypeConverter( type, this ) ;
// Replace recursion marker with the constructed implementation
typeConverterMap.put( type, result ) ;
newTypeConverter = true ;
}
mm.info(registrationFineDebug() ||
(registrationDebug() && newTypeConverter), "result",
myObjectUtil.objectToString( result ) ) ;
} finally {
mm.exit( registrationFineDebug(), result ) ;
}
return result ;
}
private static Field getDeclaredField( final Class> cls,
final String name )
throws PrivilegedActionException, NoSuchFieldException {
SecurityManager sman = System.getSecurityManager() ;
if (sman == null) {
return cls.getDeclaredField( name ) ;
} else {
return AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Field run() throws Exception {
return cls.getDeclaredField( name ) ;
}
}
) ;
}
}
private String getAMXTypeFromField( Class> cls, String fieldName ) {
try {
final Field fld = getDeclaredField(cls, fieldName);
if (Modifier.isFinal(fld.getModifiers())
&& Modifier.isStatic(fld.getModifiers())
&& fld.getType().equals(String.class)) {
AccessController.doPrivileged(new PrivilegedAction