/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. 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.html
* or glassfish/bootstrap/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.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [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 com.sun.enterprise.management.support;
import java.util.List;
import java.util.Set;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;
import java.util.Iterator;
import java.io.IOException;
import java.lang.reflect.Proxy;
import javax.management.ObjectName;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.MBeanServerNotification;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.NotCompliantMBeanException;
import javax.management.relation.MBeanServerNotificationFilter;
import com.sun.appserv.management.base.XTypes;
import com.sun.appserv.management.base.Util;
import com.sun.appserv.management.util.jmx.stringifier.NotificationStringifier;
import com.sun.appserv.management.util.jmx.JMXUtil;
import com.sun.appserv.management.util.jmx.MBeanProxyHandler;
import com.sun.appserv.management.util.jmx.MBeanServerConnectionSource;
import com.sun.appserv.management.util.jmx.stringifier.StringifierRegistryIniter;
import com.sun.appserv.management.util.misc.ExceptionUtil;
import com.sun.appserv.management.util.misc.GSetUtil;
import com.sun.appserv.management.util.stringifier.StringifierRegistryIniterImpl;
import com.sun.appserv.management.util.stringifier.StringifierRegistryImpl;
import com.sun.appserv.management.DomainRoot;
import com.sun.appserv.management.client.ProxyFactory;
import com.sun.enterprise.management.support.TypeInfos;
import com.sun.enterprise.management.DomainRootImpl;
import com.sun.enterprise.admin.common.ObjectNames;
import com.sun.enterprise.util.FeatureAvailability;
/**
Implements loading of all MBean API MBeans WITHIN the DAS (Domain Admin Server).
*/
public final class Loader extends LoaderBase
{
private boolean mQueuedAll;
private List<LoaderOfOld> mLoaders;
private Map<ObjectName,ObjectName> mOldToNewObjectNames;
private LoaderRegThread mRegThread;
private final Set<String> JMX_DOMAINS_OF_INTEREST;
private final DeferredRegistrationThread mDeferredRegistrationThread;
public
Loader()
{
JMX_DOMAINS_OF_INTEREST =
GSetUtil.newUnmodifiableStringSet( ObjectNames.kDefaultIASDomainName );
mOldToNewObjectNames =
Collections.synchronizedMap( new HashMap<ObjectName,ObjectName>() );
mQueuedAll = false;
mLoaders = null;
mRegThread = null;
mDeferredRegistrationThread = new DeferredRegistrationThread();
mDeferredRegistrationThread.start();
}
protected void
preRegisterHook()
{
mRegThread = new LoaderRegThread( this, mLogger );
mRegThread.start();
}
protected final void
postRegisterHook()
{
super.postRegisterHook();
FeatureAvailability.getInstance().registerFeature(
FeatureAvailability.AMX_LOADER_FEATURE, getObjectName() );
}
public void
handleNotification(
final Notification notifIn,
final Object handback)
{
final String type = notifIn.getType();
if ( notifIn instanceof MBeanServerNotification )
{
final MBeanServerNotification notif = (MBeanServerNotification)notifIn;
final ObjectName objectName = notif.getMBeanName();
if ( type == MBeanServerNotification.REGISTRATION_NOTIFICATION )
{
if ( JMXUtil.toString(objectName).equals( "amx:j2eeType=J2EEServer,name=server" ) )
{
debug( "Loader.handleNotification: REGISTER for: " + JMXUtil.toString(objectName) );
}
}
else if ( type == MBeanServerNotification.UNREGISTRATION_NOTIFICATION )
{
//debug( "Loader.handleNotification: UNREGISTER for: " + JMXUtil.toString(objectName) );
}
if ( JMX_DOMAINS_OF_INTEREST.contains( objectName.getDomain() ) &&
shouldSync( objectName ) )
{
final boolean register =
type == MBeanServerNotification.REGISTRATION_NOTIFICATION;
// put it at the end of the registration or unregistration queue
mRegThread.enqueue( register, objectName );
}
}
}
private static final long WAIT_THRESHOLD_MILLIS = 5 * 1000; // 5 seconds
// might not be needed; see dependent code
private static final boolean WAIT_FOR_REGISTRATION = false;
public void
handleMBeanRegistered( final ObjectName oldObjectName )
throws InstanceNotFoundException
{
trace( "handleMBeanRegistered: " + oldObjectName );
if ( shouldSync( oldObjectName ) )
{
final long start = now();
if ( WAIT_FOR_REGISTRATION )
{
while ( ! getMBeanServer().isRegistered( oldObjectName ) &&
(now() - start) < WAIT_THRESHOLD_MILLIS )
{
mySleep( 50 );
debug( "SLEPT for 50ms waiting for " + oldObjectName );
}
}
if ( ! getMBeanServer().isRegistered( oldObjectName ) )
{
trace( "Loader.handleMBeanRegistered: not found: " + JMXUtil.toString(oldObjectName) );
throw new InstanceNotFoundException( JMXUtil.toString(oldObjectName) );
}
try
{
sync( oldObjectName );
}
catch( Exception e )
{
final Throwable rootCause = ExceptionUtil.getRootCause( e );
if ( rootCause instanceof DeferRegistrationException )
{
mDeferredRegistrationThread.defer( oldObjectName );
}
}
}
}
/**
Cascaded MBeans are problematic because some of them get registered before
MBeans that contain them. For most MBeans this is a non-issue, but the workaround
code in LoaderOfOldMonitor.MyOldTypes.oldTypeToJ2EEType() can't deal with this situation,
thus this facility exists to allow retry of problematically-registered MBeans.
The need will go away if the underlying monitoring MBeans are fixed so that the workaround
code is no longer needed.
*/
private final class DeferredRegistrationThread extends Thread
{
private final List<DeferredItem> mDeferredItems;
private boolean mSleeping;
private final class DeferredItem
{
public long mStartMillis;
public final ObjectName mObjectName;
public
DeferredItem( final ObjectName objectName )
{
mObjectName = objectName;
mStartMillis = now();
}
};
private final long RETRY_INTERVAL_MILLIS = 4000;
private final long MAX_DELAY_MILLIS = 1 * 30 * 1000;
public
DeferredRegistrationThread( )
{
mDeferredItems = Collections.synchronizedList( new ArrayList<DeferredItem>() );
mSleeping = false;
}
private synchronized void
internalDefer( final DeferredItem item )
{
// even though it's a synchronized List, we still need to do this because
// takeAllItems() needs to be able to make an array the clearing the list,
// which is two operations.
synchronized( mDeferredItems )
{
mDeferredItems.add( item );
}
}
public synchronized void
defer( final ObjectName oldObjectName )
{
logFine( "Deferring registration for " + quote( oldObjectName ) );
internalDefer( new DeferredItem( oldObjectName ) );
if ( mSleeping )
{
this.interrupt();
}
}
private DeferredItem[]
takeAllItems()
{
synchronized( mDeferredItems )
{
final DeferredItem[] items = new DeferredItem[ mDeferredItems.size() ];
mDeferredItems.toArray( items );
mDeferredItems.clear();
return( items );
}
}
private ObjectName
retry( final ObjectName oldObjectName )
{
return sync( oldObjectName );
}
private void
retryItem( final DeferredItem item)
{
final ObjectName oldObjectName = item.mObjectName;
final String prefix = "DeferredRegistrationThread.retryItem: ";
final long elapsed = now() - item.mStartMillis;
try
{
final ObjectName result = retry( oldObjectName );
final String msg = prefix + "deferred registration SUCCEEDED after " +
elapsed + " milliseconds for " + quote( oldObjectName ) +
", amx ObjectName = " + quote( result );
getMBeanLogger().info( msg );
}
catch( Throwable t )
{
final Throwable rootCause = ExceptionUtil.getRootCause( t );
if ( rootCause instanceof DeferRegistrationException )
{
if ( elapsed < MAX_DELAY_MILLIS )
{
logWarning( prefix +
"deferred registration RETRY failed after " +
elapsed + " milliseconds for " + quote( oldObjectName ) + " (DEFERRING AGAIN)" );
internalDefer( item );
}
else
{
logWarning( prefix +
"Deferred registration FAILED for " + quote( oldObjectName ) +
"after deferral of " + elapsed + " ms, ignoring MBean." );
}
}
else
{
logWarning( prefix +
"Deferred registration FAILED for " + quote( oldObjectName ) +
"due to Exception of type " + rootCause.getClass().getName() );
}
}
}
private void
checkList()
{
final DeferredItem[] items = takeAllItems();
logFine( "DeferredRegistrationThread.checkList: numItems = " + items.length );
for( int i = 0; i < items.length; ++i )
{
final DeferredItem item = items[ i ];
if ( getMBeanServer().isRegistered( item.mObjectName ) )
{
retryItem( item );
}
else
{
logInfo(
"DeferredRegistrationThread.checkList: " +
"MBean is no longer registered: " + quote( item.mObjectName ) );
}
}
}
public void
run()
{
while ( true )
{
try
{
getMBeanLogger().fine( "DeferredRegistrationThread.run: CHECKING LIST@" + now() );
checkList();
// force a delay for efficiency in batching deferred items
final long sleepMillis = mDeferredItems.size() == 0 ? 60 * 1000 : RETRY_INTERVAL_MILLIS;
getMBeanLogger().fine( "DeferredRegistrationThread.run: SLEEPING FOR: " + sleepMillis + "@" + now() );
mSleeping = true;
final boolean interrupted = mySleep( sleepMillis );
mSleeping = false;
}
catch( Throwable t )
{
getMBeanLogger().warning( "DeferredRegistrationThread.run: caught Throwable:\n" +
ExceptionUtil.getStackTrace( t ) );
}
}
}
}
public void
handleMBeanUnregistered( final ObjectName oldObjectName )
throws InstanceNotFoundException, MBeanRegistrationException
{
trace( "handleMBeanUnregistered: " + oldObjectName );
final ObjectName newObjectName =
mOldToNewObjectNames.remove( oldObjectName );
if ( newObjectName != null && getMBeanServer().isRegistered( newObjectName ) )
{
debug( "unregistering: " + newObjectName + " corresponding to " + oldObjectName );
getMBeanServer().unregisterMBean( newObjectName );
}
}
synchronized ObjectName
registerNew(
final Object impl,
final ObjectName implObjectName,
final ObjectName oldObjectName )
throws MBeanRegistrationException,
InstanceAlreadyExistsException, NotCompliantMBeanException
{
//debug( "registering: ", JMXUtil.toString(implObjectName), " corresponding to ", JMXUtil.toString(oldObjectName) );
final ObjectName resultName =
getMBeanServer().registerMBean( impl, implObjectName ).getObjectName();
mOldToNewObjectNames.put( oldObjectName, resultName );
return( resultName );
}
protected ObjectName
loadSystemInfo( final MBeanServer server )
throws NotCompliantMBeanException, MBeanRegistrationException,
InstanceAlreadyExistsException
{
final BootUtil bootUtil = BootUtil.getInstance();
final SystemInfoImpl systemInfo = new SystemInfoImpl( server, bootUtil );
final ObjectName tempName = mObjectNames.getSingletonObjectName( systemInfo.J2EE_TYPE );
final ObjectName objectName = mServer.registerMBean( systemInfo, tempName ).getObjectName();
debug( "loaded SystemInfo" );
return( objectName );
}
private void
addLoaders()
{
assert( getMBeanServer() != null );
assert( getMBeanLogger() != null );
final List<LoaderOfOld> loaders = new ArrayList<LoaderOfOld>();
loaders.add( new LoaderOfOldConfig( this ) );
loaders.add( new LoaderOfOld77( this ) );
loaders.add( new LoaderOfOldMonitor( this ) );
mLoaders = Collections.unmodifiableList( loaders );
}
private boolean
shouldSync( final ObjectName oldObjectName )
{
final String jmxDomain = oldObjectName.getDomain();
final boolean applicable = ObjectNames.kDefaultIASDomainName.equals( jmxDomain );
return applicable ? (findLoaderOfOld( oldObjectName ) != null) : false;
}
private LoaderOfOld
findLoaderOfOld( final ObjectName candidate )
{
LoaderOfOld oldLoader = null;
for( final LoaderOfOld loo : mLoaders )
{
if ( loo.shouldSync( candidate ) )
{
oldLoader = loo;
break;
}
}
return( oldLoader );
}
private final Object CONFIG_SYNC = new Object();
public final ObjectName
sync( final ObjectName oldObjectName )
{
if ( ! mStarted )
{
throw new IllegalStateException();
}
if ( ! shouldSync( oldObjectName ) )
{
throw new IllegalArgumentException( oldObjectName.toString() );
}
ObjectName result = null;
/*
Synchronize for config; ConfigFactory can call sync() explicitly, and there
is a race condition that can attempt duplicate registration with the
one to be done or already done or in progress by handleMBeanRegistered().
*/
final String category = oldObjectName.getKeyProperty( "category" );
if ( category != null && category.equals( "config" ) )
{
synchronized( CONFIG_SYNC )
{
result = _sync( oldObjectName );
}
}
else
{
result = _sync( oldObjectName );
}
return result;
}
private ObjectName
_sync( final ObjectName oldObjectName )
{
if ( ! getMBeanServer().isRegistered( oldObjectName ) )
{
throw new RuntimeException( new InstanceNotFoundException() );
}
// out of order registration can result in trying to register the same MBean
// more than once.
synchronized( this )
{
ObjectName result = mOldToNewObjectNames.get( oldObjectName );
if ( result == null )
{
try
{
final LoaderOfOld loaderOfOld = findLoaderOfOld( oldObjectName );
if ( loaderOfOld != null )
{
result = loaderOfOld.syncWithOld( oldObjectName );
if ( result == null )
{
throw new IllegalArgumentException( oldObjectName.toString() );
}
}
}
catch( Exception e )
{
final String msg = ExceptionUtil.toString( e );
debug( msg );
getMBeanLogger().warning( msg );
if ( e instanceof RuntimeException )
{
throw (RuntimeException)e;
}
else
{
throw new RuntimeException( e );
}
}
}
return( result );
}
}
private void
queueAll()
{
for( final LoaderOfOld oldLoader : mLoaders )
{
final List<ObjectName> oldObjectNames = oldLoader.findAllOld();
mRegThread.enqueue( true, oldObjectNames );
getMBeanLogger().fine( "Loader: Queued " + oldObjectNames.size() +
" MBeans for loader " + oldLoader.getClass().getName() );
}
}
protected void
startHook()
{
super.startHook();
addLoaders();
queueAll();
mQueuedAll = true;
}
public boolean
isStarted( )
{
return super.isStarted() && mQueuedAll &&
mRegThread.isQueueEmpty();
}
public final void
waitAll()
{
mRegThread.waitAll();
debug( "waitAll() DONE" );
}
protected Object
createDomainRoot()
{
return new DomainRootImpl();
}
public boolean
isDAS()
{
return true;
}
public ObjectName
resyncAMXMBean( final ObjectName amx )
throws InstanceNotFoundException, MBeanRegistrationException
{
if ( ! getMBeanServer().isRegistered( amx ) )
{
throw new InstanceNotFoundException();
}
if ( ! getAMXJMXDomainName().equals( amx.getDomain() ) )
{
throw new IllegalArgumentException( "" + amx );
}
debug( "resyncAMXMBean: looking for matching delegate MBean" );
ObjectName old = null;
for( final ObjectName oldTemp : mOldToNewObjectNames.keySet() )
{
if ( mOldToNewObjectNames.get( oldTemp ).equals( amx ) )
{
old = oldTemp;
debug( "resyncAMXMBean: found matching delegate MBean: " + old );
break;
}
}
if ( old == null )
{
throw new IllegalArgumentException( "" + amx );
}
debug( "resyncAMXMBean: removing mapping from: " + old + " TO " + amx );
mOldToNewObjectNames.remove( old );
debug( "resyncAMXMBean: unregistering: " + amx );
getMBeanServer().unregisterMBean( amx );
debug( "resyncAMXMBean: handleMBeanRegistered: " + amx );
handleMBeanRegistered( old );
final ObjectName newAMX = mOldToNewObjectNames.get( old );
assert( newAMX != null );
debug( "resyncAMXMBean: new ObjectName: " + newAMX );
return newAMX;
}
}
|