PDA

View Full Version : Comments to this way of implementing Spring into ejbs


geira
Jan 1st, 2005, 02:48 PM
I wanted to have access to my POJO's and local interfaces to ejb's from other ejb's using one single Spring application context within the Jboss container..

I have created a Spring MBean,- running a standalone MBean service....



package no.aftenposten.framework.spring;

import org.jboss.naming.NonSerializableFactory;
import org.jboss.system.ServiceMBeanSupport;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlA pplicationContext;

import javax.naming.*;
import java.io.*;
import java.util.Properties;

/**
* JMX - MBean which initiates the Spring framework by reading the spring.xml, exposing
* the Spring ApplicationContext from withing the ejb-container via jndi lookup.
* see spring-service.xml
*
* <p/>
* User: regearne
* Date: 03.des.2004
* Time: 15:02:51
*
* @author regearne
* @version $Revision: 1.2 $Date: 2005/01/01 19:31:52 $
*/
public class SpringService extends ServiceMBeanSupport implements SpringServiceMBean {
private Properties properties;
private String jndiName;
private String propertiesFile;
private boolean useProperties;
private boolean usePropertiesFile;
ApplicationContext context = null;
private static final String SPRING_CONFIG_KEY = "spring.config";

/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
*
* Constructors.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
*/
public SpringService() {
usePropertiesFile = false;
propertiesFile = "";
useProperties = false;
properties = new Properties();
jndiName = "Spring";
}

/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
*
* Interface.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
*/
public void setJndiName(String jndiName) throws Exception {
String oldName = this.jndiName;
this.jndiName = jndiName;

if (super.getState() == STARTED){
try {
unbind(oldName);
} catch (NamingException ne) {
log.error(captureStackTrace(ne));

throw new Exception("Failed to unbind Spring - ", ne);
}

try {
rebind();
} catch (NamingException ne) {
log.error(captureStackTrace(ne));

throw new Exception("Failed to rebind Spring - ", ne);
}
}
}

/** Getter for jndiName
*
* @return
*/
public String getJndiName() {
return jndiName;
}

/** Getter for name
*
* @return
*/
public String getName() {
return "SpringService(" + jndiName + ")";
}

/** Setter for properties, - used when assign properties via the
* spring-service.xml
*
* @param properties
*/
public void setProperties(String properties) {
if (usePropertiesFile){
log.error("Must specify only one of 'Properties' or 'PropertiesFile'");
return;
}

useProperties = true;

try {
ByteArrayInputStream bais = new ByteArrayInputStream(properties.getBytes());
this.properties = new Properties();
this.properties.load(bais);
} catch (IOException ioe) {
// should not happen
}
}

/** Get properties
*
* @return
*/
public String getProperties() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
properties.store(baos, "");

return new String(baos.toByteArray());
} catch (IOException ioe) {
// should not happen
return "";
}
}

/** Setter for propertiesFile
*
* @param propertiesFile
*/
public void setPropertiesFile(String propertiesFile) {
if (useProperties){
log.error("Must specify only one of 'Properties' or 'PropertiesFile'");
return;
}
usePropertiesFile = true;
this.propertiesFile = propertiesFile;
}

public String getPropertiesFile() {
return propertiesFile;
}

public void createService() throws Exception {
}

public void destroyService() throws Exception {
log.info("Destroy SpringService(" + jndiName + ")...");
}

/** Starting the jmx-service
*
* @throws Exception
*/
public void startService() throws Exception {
log.info("Start SpringService(" + jndiName + ")...");
String configFile;
if ((configFile = properties.getProperty(SPRING_CONFIG_KEY)) != null){
if (new File(configFile).exists()){
log.info("Initializing Spring framework with " + configFile);
context = new FileSystemXmlApplicationContext(configFile);
log.info("Spring framework initialized");
} else {
throw new FileNotFoundException("File: "+configFile+"doesn't exist");
}
}

try {
rebind();
} catch (NamingException ne) {
log.error(captureStackTrace(ne));

throw new Exception("Failed to rebind Spring - ",
ne);
}
log.info("SpringService(" + jndiName + ") started.");
}

public void stopService() throws Exception {
log.info("Stop SpringService(" + jndiName + ")...");

log.info("Spring(" + jndiName + ") stopped.");
}

private String captureStackTrace(Throwable throwable) {
StringWriter sw = new StringWriter();
throwable.printStackTrace(new PrintWriter(sw, true));

return sw.toString();
}

private static Context createContext(Context rootCtx, Name name)
throws NamingException {
Context subctx = rootCtx;

for (int n = 0; n < name.size(); n++){
String atom = name.get(n);
try {
Object obj = subctx.lookup(atom);
subctx = (Context) obj;
} catch (NamingException e) {
// No binding exists, create a subcontext
subctx = subctx.createSubcontext(atom);
}
}
return subctx;
}

private void rebind() throws Exception {
InitialContext rootCtx = new InitialContext();

// Get the parent context into which we are to bind
Name fullName = rootCtx.getNameParser("").parse(jndiName);

Name parentName = fullName;

if (fullName.size() > 1){
parentName = fullName.getPrefix(fullName.size() - 1);
} else {
parentName = new CompositeName();
}

Context parentCtx = createContext(rootCtx, parentName);
Name atomName = fullName.getSuffix(fullName.size() - 1);
String atom = atomName.get(0);

// Scheduler scheduler = schedulerFactory.getScheduler();

NonSerializableFactory.rebind(parentCtx, atom, context);
}

private void unbind(String jndiName) throws NamingException {
Context rootCtx = new InitialContext();

Name fullName = rootCtx.getNameParser("").parse(jndiName);
Name atomName = fullName.getSuffix(fullName.size() - 1);
String atom = atomName.get(0);

rootCtx.unbind(jndiName);
NonSerializableFactory.unbind(atom);
}

}

// deployed the class within a jar into the jboss lib folder...

and a spring-service.xml into the deploy folder, firing up the the service into the Jboss....

<?xml version="1.0" encoding="UTF-8"?>

<server>
<classpath codebase="." archives="a-framework.jar"/>
<mbean code="no.aftenposten.framework.spring.SpringService"
name="user:service=SpringService,name=SpringService">
<attribute name="JndiName">Spring</attribute>
<attribute name="Properties">
spring.config=/jboss-3.2.2/server/default/deploy/spring.xml
</attribute>
</mbean>
</server>



This enables me through this static method from any ejb to access the Spring application context within the container...

public static Object getBean(String name) throws NamingException, ComponentNotAvailableException {
//Initializing the spring configuration
InitialContext jndiContext = new InitialContext();
ApplicationContext context;
if((context = (ApplicationContext) jndiContext.lookup("Spring")) != null){
return context.getBean(name);
} else {
throw new ComponentNotAvailableException("Could get Spring-bean-"+name+"," +
" due to missing BeanFactory (jndi-Spring)");
}
}


From my ejb, - the only code I need to access a Spring configured bean is:

HibernateDAO sf = (HibernateDAO) SpringBeanFactory.getBean("DAOImplementation");
sf.create(bean);


What about this way of implementing the Spring into ejb container. Have I overlooked something. I haven't found any standard Spring classes helping my to achieve the same solution, - which I would have preferred. Or should I solve it another way??


Geir

rstearns01
Jan 5th, 2005, 06:40 PM
See if this post helps any.
http://forum.springframework.org/showthread.php?t=11492
By using SingletonContext... all your beans should have access to the same Spring container.

Sergio
Jan 21st, 2005, 05:17 AM
Hey,

Thx for the code I was just about to implement the same thing to get a better beanFactory deployment on the JBoss server. The only difference is that my problem is another.

I already use the provided AbstractEjb methods from spring but i find a problem in the interaction with the EJB.

BeanFactoryLocator holds a counter that is incremented everytime someone requests a SingletonFactory. Whenever someone is interested in releasing its hold he can call the singleton method release() which in time, when the counter gets to 0 destroys the bean context forcing someone accesing the factory again to recreate the Bean Factory.

The above exposed (which i find perfect for some situations) is a real drawback when working with EJB in JBoss due to the fact that JBoss destroys and recreates the EJB (counter-- 0 destroy context, next access = create bean factory) everytime the EJB throws an unexpected runtime exception.

In this scenary i need another anchor to enforce the lifecicle i want for the spring context (full application container life, not ejb, that is the reason it is singleton) and the MBean deployment descriptor is just the perfect place for it :).