/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.sun.enterprise.ee.server;
import com.sun.enterprise.server.*;
import com.sun.enterprise.config.ConfigContext;
import com.sun.enterprise.config.serverbeans.Applications;
import com.sun.enterprise.config.serverbeans.J2eeApplication;
import com.sun.enterprise.config.serverbeans.ServerBeansFactory;
import com.sun.enterprise.ee.server.group.Barrier;
import com.sun.enterprise.ee.server.group.Message;
import com.sun.enterprise.ee.server.group.core.ServerMessageRuntime;
import com.sun.enterprise.loader.EJBClassPathUtils;
import com.sun.enterprise.server.ApplicationServer;
import com.sun.enterprise.server.ServerContext;
import com.sun.enterprise.server.ApplicationLoadEventListener;
import com.sun.enterprise.server.event.ApplicationEvent;
import com.sun.enterprise.server.event.EarLifecycleListener;
import com.sun.enterprise.server.event.EjbContainerEvent;
import com.sun.enterprise.tools.verifier.tests.app.EARFileUsesClassPath;
import com.sun.logging.LogDomains;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* ApplicationLoaderEventListener implementation. This is the basic entry
* point for Ear Lifecycle notification.
*
* @author Binod.
*/
public class AppLoaderEventListener implements EarLifecycleListener {
private static final String LISTENER = "EAR-Loader-Listener";
private static Logger __logger = LogDomains.getLogger(LogDomains.LOADER_LOGGER);
ServerMessageRuntime smrt = null;
static long timeout = 1000 * 30;
static final String EARLCTIMEOUT = "com.sun.enterprise.loaderevent.timeout";
static {
try {
String strTimeout = System.getProperty(EARLCTIMEOUT);
if (strTimeout != null)
timeout = Long.valueOf(strTimeout).longValue();
}catch (Exception e) {
__logger.log(Level.SEVERE, e.getMessage(), e);
}
}
public void handleEvent(ApplicationEvent event) {
// There is only one instance of AppLoaderEventListener. So, in essence,
// smrt is a singleton.
// FIX ME: May be create this lazily?
if (smrt == null)
smrt = ServerMessageRuntime.create(LISTENER, __logger);
if (event.getEventType() == ApplicationEvent.AFTER_APPLICATION_LOAD) {
ApplicationLoadEventListener loaderListener = __handleEvent(event);
String appName = event.getApplication().getRegistrationName();
if (loaderListener != null) {
MessageCallBackImpl impl =
new MessageCallBackImpl(loaderListener);
impl.setLoading(true);
ClassLoader toSet = loaderListener.getClass().getClassLoader();
ClassLoader saved = setContextClassLoader(toSet);
// Invoke the per-instance listener.
loaderListener.handleLoad();
setContextClassLoader(saved);
//During load, aggregate the results. So, all the instances
//will send the notification to the elected instnace. So, the
//route is ALLTOONE.
Barrier b = getServerMessageRuntime().initiateMessaging(appName,
Message.Route.ALLTOONE,impl);
if (b != null)
b.start(timeout);
}
return;
}
if (event.getEventType() == ApplicationEvent.BEFORE_APPLICATION_UNLOAD){
ApplicationLoadEventListener loaderListener = __handleEvent(event);
String appName = event.getApplication().getRegistrationName();
if (loaderListener != null) {
MessageCallBackImpl impl =
new MessageCallBackImpl(loaderListener);
// In case of unload, an ONETOALL message should be sent so that
// all instances will wait for clusterwide uload to complete.
// FIX ME. Would ALLTOONE be better here also?
getServerMessageRuntime().initiateMessagingAndWait(appName,
Message.Route.ONETOALL,impl);
ClassLoader toSet = loaderListener.getClass().getClassLoader();
ClassLoader saved = setContextClassLoader(toSet);
loaderListener.handleUnLoad();
setContextClassLoader(saved);
//Lets make sure that message runtime doesnt keep this app any more.
getServerMessageRuntime().purge(appName);
}
return;
}
if (event.getEventType() == ApplicationEvent.APPLICATION_LOADFAIL) {
ApplicationLoadEventListener loaderListener = __handleEvent(event);
String appName = event.getApplication().getRegistrationName();
if (loaderListener != null) {
MessageCallBackImpl impl =
new MessageCallBackImpl(loaderListener);
impl.setFailure(true);
ClassLoader toSet = loaderListener.getClass().getClassLoader();
ClassLoader saved = setContextClassLoader(toSet);
// Invoke the per-instance listener.
loaderListener.handleLoadFailure();
setContextClassLoader(saved);
//ALLTOONE message to aggregate failure messages.
Barrier b = getServerMessageRuntime().initiateMessaging
(appName+"__fail", Message.Route.ALLTOONE, impl);
if (b != null)
b.start(timeout);
}
return;
}
}
ServerMessageRuntime getServerMessageRuntime() {
return smrt;
}
private ApplicationLoadEventListener __handleEvent(ApplicationEvent event) {
String appName = event.getApplication().getRegistrationName();
ConfigContext ctx = event.getConfigContext();
if (ctx == null) {
ctx = ApplicationServer.getServerContext().getConfigContext();
}
Applications apps = null;
try {
apps = ServerBeansFactory.getApplicationsBean(ctx);
} catch (Exception e) {
return null;
}
J2eeApplication app = apps.getJ2eeApplicationByName(appName);
if (app == null )
return null;
if (__logger.isLoggable(Level.FINE)) {
__logger.fine("The application load event for " + appName);
}
String loc = app.getLocation();
//Try to retrieve the loader listener classname from manifest.
Manifest mf = EJBClassPathUtils.getManifest(loc);
String loaderListener = null;
if (mf != null) {
Attributes ars = mf.getMainAttributes();
if (ars != null) {
loaderListener = ars.getValue(LISTENER);
}
}
if (loaderListener == null) {
if (__logger.isLoggable(Level.FINE)) {
__logger.fine("Loader Listener is null for" + appName);
}
return null;
}
if (__logger.isLoggable(Level.FINE)) {
__logger.fine("Loader Listener is available for " + appName);
}
Object listenerObject = null;
try {
listenerObject = event.getApplication().getClassLoader().
loadClass(loaderListener).newInstance();
} catch (Exception e) {
__logger.log(Level.WARNING, e.getMessage(), e);
return null;
}
return ApplicationLoadEventListener.class.cast(listenerObject);
}
/**
* Utility method for setting the context classloader.
*/
static ClassLoader setContextClassLoader(ClassLoader newClassLoader) {
// Can reference only final local variables from dopriveleged block
final ClassLoader classLoaderToSet = newClassLoader;
final Thread currentThread = Thread.currentThread();
ClassLoader originalClassLoader = currentThread.getContextClassLoader();
if (classLoaderToSet != originalClassLoader) {
if (System.getSecurityManager() == null) {
currentThread.setContextClassLoader(classLoaderToSet);
} else {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public java.lang.Object run() {
currentThread.setContextClassLoader
(classLoaderToSet);
return null;
}
}
);
}
}
return originalClassLoader;
}
}