/*
* JBoss, the OpenSource EJB server
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.ejb;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.MalformedURLException;
import java.security.Principal;
import java.util.Map;
import java.util.Iterator;
import java.util.Hashtable;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.Collection;
import java.util.Enumeration;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.InitialContext;
import javax.naming.LinkRef;
import javax.naming.NamingException;
import javax.naming.NameNotFoundException;
import javax.transaction.TransactionManager;
import org.jboss.logging.Logger;
import org.jboss.system.EJBSecurityManager;
import org.jboss.system.RealmMapping;
import org.jboss.metadata.BeanMetaData;
import org.jboss.metadata.EnvEntryMetaData;
import org.jboss.metadata.EjbRefMetaData;
import org.jboss.metadata.ResourceRefMetaData;
import org.jboss.metadata.ApplicationMetaData;
import org.jnp.interfaces.Naming;
import org.jnp.interfaces.java.javaURLContextFactory;
import org.jnp.server.NamingServer;
/**
* This is the base class for all EJB-containers in JBoss. A Container
* functions as the central hub of all metadata and plugins. Through
this
* the container plugins can get hold of the other plugins and any
metadata they need.
*
* The ContainerFactory creates instances of subclasses of this class
and calls the appropriate
* initialization methods.
*
* A Container does not perform any significant work, but instead
delegates to the plugins to provide for
* all kinds of algorithmic functionality.
*
* @see ContainerFactory
* @author Rickard �berg ([EMAIL PROTECTED])
* @author <a href="[EMAIL PROTECTED]">Marc Fleury</a>
* @version $Revision: 1.33 $
*/
public abstract class Container
{
// Constants -----------------------------------------------------
// Attributes ----------------------------------------------------
// This is the application that this container is a part of
protected Application application;
// This is the classloader of this container. All classes and resources
that
// the bean uses will be loaded from here. By doing this we make the
bean re-deployable
protected ClassLoader classLoader;
// This is the new metadata. it includes information from both ejb-jar
and jboss.xml
// the metadata for the application can be accessed trough
metaData.getApplicationMetaData()
protected BeanMetaData metaData;
// This is the EnterpriseBean class
protected Class beanClass;
// This is the TransactionManager
protected TransactionManager tm;
// This is the SecurityManager
protected EJBSecurityManager sm;
// This is the realm mapping
protected RealmMapping rm;
// This is a cache for method permissions
private HashMap methodPermissionsCache = new HashMap();
// Public --------------------------------------------------------
/**
* Sets a transaction manager for this container.
*
* @see javax.transaction.TransactionManager
* @param tm
*/
public void setTransactionManager(TransactionManager tm)
{
this.tm = tm;
}
public TransactionManager getTransactionManager()
{
return tm;
}
public void setSecurityManager(EJBSecurityManager sm)
{
this.sm = sm;
}
public EJBSecurityManager getSecurityManager()
{
return sm;
}
public void setRealmMapping(RealmMapping rm)
{
this.rm = rm;
}
public RealmMapping getRealmMapping()
{
return rm;
}
/**
* Sets the application deployment unit for this container. All the bean
* containers within the same application unit share the same instance.
*
* @param app application for this container
*/
public void setApplication(Application app)
{
if (app == null)
throw new IllegalArgumentException("Null application");
application = app;
}
public Application getApplication()
{
return application;
}
/**
* Sets the class loader for this container. All the classes and
resources
* used by the bean in this container will use this classloader.
*
* @param cl
*/
public void setClassLoader(ClassLoader cl)
{
this.classLoader = cl;
}
/**
* Returns the classloader for this container.
*
* @return
*/
public ClassLoader getClassLoader()
{
return classLoader;
}
/**
* Sets the meta data for this container. The meta data consists of the
* properties found in the XML descriptors.
*
* @param metaData
*/
public void setBeanMetaData(BeanMetaData metaData)
{
this.metaData = metaData;
}
/**
* Returns the metadata of this container.
*
* @return metaData;
*/
public BeanMetaData getBeanMetaData()
{
return metaData;
}
/**
* Returns the permissions for a method. (a set of roles)
*
* @return assemblyDescriptor;
*/
public Set getMethodPermissions( Method m, boolean home )
{
Set permissions = (Set) methodPermissionsCache.get( m );
if (permissions == null)
{
permissions = getBeanMetaData().getMethodPermissions(m.getName(),
m.getParameterTypes(), !home);
methodPermissionsCache.put(m, permissions);
}
return permissions;
}
/**
* Returns the bean class instance of this container.
*
* @return instance of the Enterprise bean class
*/
public Class getBeanClass()
{
return beanClass;
}
/**
* The ContainerFactory calls this method. The ContainerFactory has set
all the
* plugins and interceptors that this bean requires and now proceeds to
initialize
* the chain. The method looks for the standard classes in the URL, sets
up
* the naming environment of the bean. The concrete container classes
should
* override this method to introduce implementation specific
initialization behaviour.
*
* @exception Exception if loading the bean class failed
(ClassNotFoundException)
* or setting up "java:" naming environment
failed (DeploymentException)
*/
public void init()
throws Exception
{
// Acquire classes from CL
beanClass = classLoader.loadClass(metaData.getEjbClass());
// Setup "java:" namespace
setupEnvironment();
}
/**
* A default implementation of starting the container service (no-op).
The concrete
* container classes should override this method to introduce
implementation specific
* start behaviour.
*
* @exception Exception an exception that occured during start
*/
public void start()
throws Exception
{
}
/**
* A default implementation of stopping the container service (no-op).
The concrete
* container classes should override this method to introduce
implementation specific
* stop behaviour.
*/
public void stop()
{
}
/**
* A default implementation of destroying the container service (no-op).
The concrete
* container classes should override this method to introduce
implementation specific
* destroy behaviour.
*/
public void destroy()
{
}
/**
* This method is called by the ContainerInvoker when a method call
comes in on the Home object.
*
* The Container forwards this call to the interceptor chain for further
processing.
*
* @param mi the object holding all info about this invocation
* @return the result of the home invocation
* @exception Exception
*/
public abstract Object invokeHome(MethodInvocation mi)
throws Exception;
/**
* This method is called by the ContainerInvoker when a method call
comes in on an EJBObject.
*
* The Container forwards this call to the interceptor chain for further
processing.
*
* @param id the id of the object being invoked. May be null
if stateless
* @param method the method being invoked
* @param args the parameters
* @return the result of the invocation
* @exception Exception
*/
public abstract Object invoke(MethodInvocation mi)
throws Exception;
// Protected -----------------------------------------------------
abstract Interceptor createContainerInterceptor();
// Private -------------------------------------------------------
/*
* setupEnvironment
*
* This method sets up the naming environment of the bean.
* We create the java: namespace with properties, EJB-References, and
* DataSource ressources.
*
*/
private void setupEnvironment()
throws DeploymentException
{
try
{
// Since the BCL is already associated with this thread we can
start using the java: namespace directly
Context ctx = (Context) new InitialContext().lookup("java:comp");
ctx = ctx.createSubcontext("env");
// Bind environment properties
{
Iterator enum = getBeanMetaData().getEnvironmentEntries();
while(enum.hasNext())
{
EnvEntryMetaData entry = (EnvEntryMetaData)enum.next();
if (entry.getType().equals("java.lang.Integer"))
{
bind(ctx, entry.getName(), new Integer(entry.getValue
()));
}
else if (entry.getType().equals("java.lang.Long"))
{
bind(ctx, entry.getName(), new Long(entry.getValue()));
}
else if (entry.getType().equals("java.lang.Double"))
{
bind(ctx, entry.getName(), new Double(entry.getValue()));
}
else if (entry.getType().equals("java.lang.Float"))
{
bind(ctx, entry.getName(), new Float(entry.getValue()));
}
else if (entry.getType().equals("java.lang.Byte"))
{
bind(ctx, entry.getName(), new Byte(entry.getValue()));
}
else if (entry.getType().equals("java.lang.Short"))
{
bind(ctx, entry.getName(), new Short(entry.getValue()));
}
else if (entry.getType().equals("java.lang.Boolean"))
{
bind(ctx, entry.getName(), new Boolean(entry.getValue
()));
}
else
{
// Unknown type
// Default is string
bind(ctx, entry.getName(), entry.getValue());
}
}
}
// Bind EJB references
{
Iterator enum = getBeanMetaData().getEjbReferences();
while(enum.hasNext())
{
EjbRefMetaData ref = (EjbRefMetaData)enum.next();
Logger.debug("Binding an EJBReference "+ref.getName());
if (ref.getLink() != null)
{
// Internal link
Logger.debug("Binding "+ref.getName()+" to internal JNDI
source: "+ref.getLink());
if (getApplication().getContainer(ref.getLink()) == null)
throw new DeploymentException ("Bean "+ref.getLink()+"
not found within this application.");
bind(ctx, ref.getName(), new LinkRef(getApplication
().getContainer(ref.getLink()).getBeanMetaData().getJndiName()));
// bind(ctx, ref.getName(), new
Reference(ref.getHome(), new StringRefAddr("Container",ref.getLink()),
getClass().getName()+".EjbReferenceFactory", null));
// bind(ctx, ref.getName(), new
LinkRef(ref.getLink()));
}
else
{
// External link
if (ref.getJndiName() == null)
{
throw new DeploymentException("ejb-ref "+ref.getName()
+", expected either ejb-link in ejb-jar.xml or jndi-name in jboss.xml");
}
Logger.debug("Binding "+ref.getName()+" to external JNDI
source: "+ref.getJndiName());
bind(ctx, ref.getName(), new LinkRef(ref.getJndiName()));
}
}
}
// Bind resource references
{
Iterator enum = getBeanMetaData().getResourceReferences();
// let's play guess the cast game ;) New metadata should fix
this.
ApplicationMetaData application = getBeanMetaData
().getApplicationMetaData();
while(enum.hasNext())
{
ResourceRefMetaData ref = (ResourceRefMetaData)enum.next();
String resourceName = ref.getResourceName();
String finalName =
application.getResourceByName(resourceName);
if (finalName == null)
{
// the application assembler did not provide a resource
manager
// if the type is javax.sql.Datasoure use the default one
if (ref.getType().equals("javax.sql.DataSource"))
{
// Go through JNDI and look for DataSource - use the
first one
Context dsCtx = new InitialContext();
try
{
// Check if it is available in JNDI
dsCtx.lookup("java:/DefaultDS");
finalName = "java:/DefaultDS";
} catch (Exception e)
{
Logger.debug(e);
}
}
// Default failed? Warn user and move on
// POTENTIALLY DANGEROUS: should this be a critical
error?
if (finalName == null)
{
Logger.warning("No resource manager found for "
+ref.getResourceName());
continue;
}
}
if (ref.getType().equals("javax.sql.DataSource"))
{
// Datasource bindings
bind(ctx, ref.getRefName(), new LinkRef(finalName));
}
else if (ref.getType().equals("java.net.URL"))
{
// URL bindings
try
{
bind(ctx, ref.getRefName(), new URL(finalName));
} catch (MalformedURLException e)
{
throw new NamingException("Malformed URL:"
+e.getMessage());
}
}
else
{
// Resource Manager bindings
Logger.debug("Binding resource manager "+finalName+ "
with JDNI ENC " +ref.getRefName());
bind(ctx, ref.getRefName(), new LinkRef(finalName));
}
}
}
} catch (NamingException e)
{
Logger.exception(e);
e.getRootCause().printStackTrace();
throw new DeploymentException("Could not set up environment", e);
}
}
/**
* Bind a value to a name in a JNDI-context, and create any missing
subcontexts
*
* @param ctx
* @param name
* @param val
* @exception NamingException
*/
private void bind(Context ctx, String name, Object val)
throws NamingException
{
// Bind val to name in ctx, and make sure that all intermediate
contexts exist
Name n = ctx.getNameParser("").parse(name);
while (n.size() > 1)
{
String ctxName = n.get(0);
try
{
ctx = (Context)ctx.lookup(ctxName);
} catch (NameNotFoundException e)
{
ctx = ctx.createSubcontext(ctxName);
}
n = n.getSuffix(1);
}
ctx.bind(n.get(0), val);
}
//JACK STUFF
/*
//PersistenceManager pm;
ContainerPlugin pm;
//public void setPersistenceManager(PersistenceManager mgr) {
public void setPersistenceManager(ContainerPlugin mgr) {
pm = mgr;
}
//public PersistenceManager getPersistenceManager(){
public ContainerPlugin getPersistenceManager(){
return pm;
}
*/
ContainerInvoker ci;
public void setContainerInvoker(ContainerInvoker invoker){
ci = invoker;
}
public ContainerInvoker getContainerInvoker(){
return ci;
}
InstanceCache ic;
public void setInstanceCache(InstanceCache cache){
ic = cache;
}
public InstanceCache getInstanceCache(){
return ic;
}
InstancePool ip;
public void setInstancePool(InstancePool pool){
ip = pool;
}
public InstancePool getInstancePool(){
return ip;
}
public abstract void addInterceptor(Interceptor interceptor);
}