/**
* EasyBeans
* Copyright (C) 2006-2008 Bull S.A.S.
* Contact: easybeans@ow2.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* --------------------------------------------------------------------------
* $Id: EasyBeansResourceAdapter.java 5369 2010-02-24 14:58:19Z benoitf $
* --------------------------------------------------------------------------
*/
package org.ow2.easybeans.server.ra;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.resource.ResourceException;
import javax.resource.spi.ActivationSpec;
import javax.resource.spi.BootstrapContext;
import javax.resource.spi.ResourceAdapterInternalException;
import javax.resource.spi.endpoint.MessageEndpointFactory;
import javax.rmi.PortableRemoteObject;
import javax.transaction.xa.XAResource;
import org.ow2.easybeans.container.mdb.helper.JOnAS4ResourceAdapterFinder;
import org.ow2.easybeans.container.mdb.helper.MDBResourceAdapterHelper;
import org.ow2.easybeans.deployer.JOnASDeployer;
import org.ow2.easybeans.security.propagation.context.SecurityCurrent;
import org.ow2.easybeans.security.propagation.jonas.JOnASSecurityCurrent;
import org.ow2.easybeans.server.EasyBeans;
import org.ow2.easybeans.server.Embedded;
import org.ow2.easybeans.server.EmbeddedException;
import org.ow2.easybeans.util.loader.ClassUtils;
import org.ow2.util.ee.deploy.api.deployer.DeployerException;
import org.ow2.util.ee.deploy.api.deployer.IDeployerManager;
import org.ow2.util.ee.deploy.impl.deployer.DeployerManager;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;
/**
* Defines a resource adapter which allow to embed the EJB3 server in Application Server.
* application server.
* @author Florent Benoit
*/
public class EasyBeansResourceAdapter implements javax.resource.spi.ResourceAdapter {
/**
* Default XML file.
*/
public static final String DEFAULT_XML_FILE = "org/ow2/easybeans/server/ra/easybeans-ra.xml";
/**
* JOnAS ADM interface.
*/
public static final String JONAS_ADM_ITF = "org.objectweb.jonas.adm.AdmInterface";
/**
* Logger.
*/
private static Log logger = LogFactory.getLog(EasyBeansResourceAdapter.class);
/**
* Embedded instance.
*/
private Embedded embedded = null;
/**
* Starts the embedded server.
* @param ctx - a bootstrap context containing references to useful
* facilities that could be used by a resource adapter instance.
* @throws ResourceAdapterInternalException indicates bootstrap failure.
*/
public void start(final BootstrapContext ctx) throws ResourceAdapterInternalException {
// user configuration ?
URL xmlConfigurationURL = Thread.currentThread().getContextClassLoader().getResource(EasyBeans.USER_XML_FILE);
if (xmlConfigurationURL == null) {
xmlConfigurationURL = Thread.currentThread().getContextClassLoader().getResource(DEFAULT_XML_FILE);
logger.info(
"No user-defined configuration file named ''{0}'' found in classpath. Using default settings from ''{1}''",
EasyBeans.USER_XML_FILE, xmlConfigurationURL);
}
// Add the configuration URL to the existing list
this.embedded = new Embedded();
this.embedded.getServerConfig().getConfigurationURLs().add(xmlConfigurationURL);
// Use JOnAS deployer
IDeployerManager deployerManager = new DeployerManager();
this.embedded.setDeployerManager(deployerManager);
try {
JOnASDeployer deployer = new JOnASDeployer();
deployer.setEmbedded(this.embedded);
deployerManager.register(deployer);
} catch (DeployerException e) {
throw new ResourceAdapterInternalException("Cannot register the JOnAS deployer", e);
}
// Update configuration
String jBase = System.getProperty("jonas.base");
if (jBase == null) {
throw new ResourceAdapterInternalException("No JONAS_BASE found, cannot continue.");
}
File workDir = new File(jBase + File.separator + Embedded.DEFAULT_DEPLOY_DIRECTORY);
workDir.mkdir();
// Add deploy directory
this.embedded.getServerConfig().addDeployDirectory(workDir);
// Use JOnAS 4.x MDB Helper
MDBResourceAdapterHelper.setResourceAdapterFinder(new JOnAS4ResourceAdapterFinder());
// Use of JOnAS 4.x security model
SecurityCurrent.setSecurityCurrent(new JOnASSecurityCurrent());
// start of the embedded object will be delayed until JOnAS is started.
// This is because Datasource could be deployed after this adapter and
// then datasource won't be found.
new EmbeddedStarter().start();
}
/**
* Stopp the embedded server.
*/
public void stop() {
try {
this.embedded.stop();
} catch (EmbeddedException e) {
throw new IllegalStateException("Cannot stop the embedded server", e);
} finally {
this.embedded = null;
}
}
/**
* This is called during the activation of a message endpoint.
* @param endpointFactory - a message endpoint factory instance.
* @param spec an activation spec JavaBean instance.
* @throws ResourceException - indicates message endpoint activation
* rejection due to incorrect activation setup information.
*/
public void endpointActivation(final MessageEndpointFactory endpointFactory, final ActivationSpec spec)
throws ResourceException {
}
/**
* This is called when a message endpoint is deactivated. The instances
* passed as arguments to this method call should be identical to those
* passed in for the corresponding endpointActivation call. This causes the
* resource adapter to stop delivering messages to the message endpoint. Any
* exception thrown by this method is ignored. After this method call, the
* endpoint is deemed inactive.
* @param endpointFactory a message endpoint factory instance.
* @param spec an activation spec JavaBean instance.
*/
public void endpointDeactivation(final MessageEndpointFactory endpointFactory, final ActivationSpec spec) {
}
/**
* This method is called by the application server during crash recovery.
* @param specs an array of ActivationSpec JavaBeans each of which
* corresponds to an deployed endpoint application that was active
* prior to the system crash.
* @return an array of XAResource objects each of which represents a unique
* resource manager.
* @throws ResourceException generic exception if operation fails due to an
* error condition.
*/
public XAResource[] getXAResources(final ActivationSpec[] specs) throws ResourceException {
return null;
}
/**
* Gets the embedded object.
* @return embedded server.
*/
protected Embedded getEmbedded() {
return this.embedded;
}
/**
* This inner class will wait until the JOnAS server is ready and start the
* embedded server.
* @author Florent Benoit
*/
public class EmbeddedStarter extends Thread {
/**
* Sleep time for this waiter thread (1 second).
*/
private static final int SLEEP_TIME = 1000;
/**
* Logger.
*/
private Log logger = LogFactory.getLog(EmbeddedStarter.class);
/**
* Wait until JOnAS is started.<br>
* Then, start embedded object.
*/
@Override
public void run() {
boolean embeddedStarted = false;
while (!embeddedStarted) {
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {
throw new RuntimeException("Thread fail to sleep");
}
if (jonasIsReady()) {
try {
getEmbedded().start();
} catch (EmbeddedException e) {
this.logger.error("Cannot start the embedded server", e);
}
embeddedStarted = true;
}
}
}
/**
* @return true if the JOnAS server is ready, else false
*/
private boolean jonasIsReady() {
String jonasName = System.getProperty("jonas.name", "jonas");
// Get Adm object
String admName = jonasName + "_Adm";
Context initialContext = null;
try {
initialContext = new InitialContext();
} catch (NamingException e) {
this.logger.error("Cannot get an initial context", e);
return false;
}
Object adm = null;
try {
adm = initialContext.lookup(admName);
} catch (NamingException e) {
this.logger.error("No {0} object found in the initial context", admName, e);
return false;
}
// Cast object into Adm object
// Load Adm interface
Class<?> admItfClass;
try {
admItfClass = ClassUtils.forName(JONAS_ADM_ITF);
} catch (ClassNotFoundException e) {
this.logger.error("Cannot load the class ''{0}''", JONAS_ADM_ITF, e);
return false;
}
adm = PortableRemoteObject.narrow(adm, admItfClass);
// Try to see if there is a method getServerState on this object
Method getStateMethod;
String methodName = "getServerState";
try {
getStateMethod = adm.getClass().getMethod(methodName);
} catch (SecurityException e) {
this.logger.error("Cannot get method name {0}", methodName, e);
return false;
} catch (NoSuchMethodException e) {
this.logger.error("Cannot get method name {0}", methodName, e);
return false;
}
if (getStateMethod == null) {
this.logger.warn("No method getStateMethod() found on object {0}", adm);
return false;
}
Integer state = null;
try {
state = (Integer) getStateMethod.invoke(adm);
} catch (IllegalArgumentException e) {
this.logger.error("Cannot call method {0} on object {1}.", methodName, adm, e);
return false;
} catch (IllegalAccessException e) {
this.logger.error("Cannot call method {0} on object {1}.", methodName, adm, e);
return false;
} catch (InvocationTargetException e) {
this.logger.error("Cannot call method {0} on object {1}.", methodName, adm, e);
return false;
}
// check the status
if (state.intValue() == 1) {
this.logger.info("JOnAS has been started, EasyBeans is now starting.");
// JOnAS is ready, can start embedded !
return true;
}
return false;
}
}
}