/*
* JBoss, Home of Professional Open Source
* Copyright 2007, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This 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 (at your option) any later version.
*
* This software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.deployment;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.jboss.deployers.spi.DeploymentException;
import org.jboss.deployers.spi.deployer.DeploymentStages;
import org.jboss.deployers.spi.deployer.helpers.AbstractRealDeployer;
import org.jboss.deployers.structure.spi.DeploymentUnit;
import org.jboss.logging.Logger;
import org.jboss.metadata.client.jboss.JBossClientMetaData;
import org.jboss.metadata.ejb.jboss.JBossEnterpriseBeanMetaData;
import org.jboss.metadata.ejb.jboss.JBossEnterpriseBeansMetaData;
import org.jboss.metadata.ejb.jboss.JBossEntityBeanMetaData;
import org.jboss.metadata.ejb.jboss.JBossMetaData;
import org.jboss.metadata.ejb.jboss.JBossSessionBeanMetaData;
import org.jboss.metadata.ejb.spec.BusinessLocalsMetaData;
import org.jboss.metadata.ejb.spec.BusinessRemotesMetaData;
import org.jboss.metadata.javaee.spec.AnnotatedEJBReferenceMetaData;
import org.jboss.metadata.javaee.spec.AnnotatedEJBReferencesMetaData;
import org.jboss.metadata.javaee.spec.EJBLocalReferenceMetaData;
import org.jboss.metadata.javaee.spec.EJBLocalReferencesMetaData;
import org.jboss.metadata.javaee.spec.EJBReferenceMetaData;
import org.jboss.metadata.javaee.spec.EJBReferencesMetaData;
import org.jboss.metadata.javaee.spec.Environment;
import org.jboss.metadata.javaee.spec.MessageDestinationMetaData;
import org.jboss.metadata.javaee.spec.MessageDestinationReferenceMetaData;
import org.jboss.metadata.javaee.spec.MessageDestinationReferencesMetaData;
import org.jboss.metadata.javaee.spec.MessageDestinationsMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
/**
* A deployer which resolves references for parsing deployers
*
* @author Scott.Stark@jboss.org
* @version $Revision: 82920 $
*/
public class ReferenceMetaDataResolverDeployer extends AbstractRealDeployer
{
private static Logger log = Logger.getLogger(ReferenceMetaDataResolverDeployer.class);
/** */
private boolean failOnUnresolvedRefs;
/**
* A map of vfs paths to reference endpoint mappedNames
*/
private ConcurrentHashMap<String, String> mappedNameMap = new ConcurrentHashMap<String, String>();
public ReferenceMetaDataResolverDeployer()
{
setStage(DeploymentStages.POST_CLASSLOADER);
HashSet<String> inputs = new HashSet<String>();
inputs.add(JBossClientMetaData.class.getName());
inputs.add(JBossMetaData.class.getName());
inputs.add(JBossWebMetaData.class.getName());
super.setInputs(inputs);
}
/**
* We want to process the parent last
*/
@Override
public boolean isParentFirst()
{
return false;
}
/**
* Look for ejb, web or client metadata to resolve references without
* mapped names.
*/
public void internalDeploy(DeploymentUnit unit) throws DeploymentException
{
JBossMetaData ejbMetaData = unit.getAttachment(JBossMetaData.class);
JBossWebMetaData webMetaData = unit.getAttachment(JBossWebMetaData.class);
JBossClientMetaData clientMetaData = unit.getAttachment(JBossClientMetaData.class);
if(ejbMetaData == null && webMetaData == null && clientMetaData == null)
return;
// Create a map of the ejbs
dump(unit);
if(ejbMetaData != null)
{
JBossEnterpriseBeansMetaData beans = ejbMetaData.getEnterpriseBeans();
// Map the ejbs
this.mapEjbs(unit.getRelativePath(), beans);
// Process ejb references
List<String> unresolvedPaths = resolve(unit, beans);
if(unresolvedPaths != null && unresolvedPaths.size() > 0)
log.warn("Unresolved references exist in JBossMetaData: "+unresolvedPaths);
}
if(webMetaData != null)
{
// Process web app references
List<String> unresolvedPaths = new ArrayList<String>();
resolve(unit, webMetaData.getJndiEnvironmentRefsGroup(), unresolvedPaths);
if(unresolvedPaths != null && unresolvedPaths.size() > 0)
log.warn("Unresolved references exist in JBossWebMetaData: "+unresolvedPaths);
}
if(clientMetaData != null)
{
// Process client app references
List<String> unresolvedPaths = new ArrayList<String>();
resolve(unit, clientMetaData.getJndiEnvironmentRefsGroup(), unresolvedPaths);
if(unresolvedPaths != null && unresolvedPaths.size() > 0)
log.warn("Unresolved references exist in JBossClientMetaData: "+unresolvedPaths);
}
}
protected void mapEjbs(String vfsPath, JBossEnterpriseBeansMetaData beans)
{
if(beans == null || beans.size() == 0)
return;
for(JBossEnterpriseBeanMetaData bean : beans)
{
String ejbPath = vfsPath + "/" + bean.getEjbName();
// TODO: endpoints based on business interface, etc.
String mappedName = bean.getMappedName();
if(mappedName != null)
mappedNameMap.put(ejbPath, mappedName);
if(bean instanceof JBossSessionBeanMetaData)
{
JBossSessionBeanMetaData sbean = (JBossSessionBeanMetaData) bean;
BusinessLocalsMetaData locals = sbean.getBusinessLocals();
if(locals != null)
{
for(String local : locals)
{
// TODO: what is the correct key name
}
}
BusinessRemotesMetaData remotes = sbean.getBusinessRemotes();
if(remotes != null)
{
for(String remote : remotes)
{
// TODO: what is the correct key name
}
}
}
}
}
protected void resolve(DeploymentUnit unit, Environment env, List<String> unresolvedRefs)
{
if(env == null)
return;
EJBLocalReferencesMetaData localRefs = env.getEjbLocalReferences();
resolveEjbLocalRefs(unit, localRefs, unresolvedRefs);
AnnotatedEJBReferencesMetaData annotatedRefs = env.getAnnotatedEjbReferences();
resolveAnnotatedRefs(unit, annotatedRefs, unresolvedRefs);
EJBReferencesMetaData ejbRefs = env.getEjbReferences();
resolveEjbRefs(unit, ejbRefs, unresolvedRefs);
MessageDestinationReferencesMetaData msgRefs = env.getMessageDestinationReferences();
resolveMsgRefs(unit, msgRefs, unresolvedRefs);
// TODO, other references
}
protected List<String> resolve(DeploymentUnit unit, JBossEnterpriseBeansMetaData beans)
{
ArrayList<String> unresolvedRefs = new ArrayList<String>();
if(beans == null || beans.size() == 0)
return unresolvedRefs;
for(JBossEnterpriseBeanMetaData bean : beans)
{
Environment env = bean.getJndiEnvironmentRefsGroup();
resolve(unit, env, unresolvedRefs);
}
return unresolvedRefs;
}
protected void resolveEjbLocalRefs(DeploymentUnit unit, EJBLocalReferencesMetaData localRefs, List<String> unresolvedRefs)
{
if(localRefs == null)
return;
ArrayList<String> searched = new ArrayList<String>();
for(EJBLocalReferenceMetaData ref : localRefs)
{
String link = ref.getLink();
String target = findLocalEjbLink(unit, link, searched);
if(target == null)
unresolvedRefs.add(ref.getEjbRefName()+"/ejb-local-ref/"+link +" available: " + searched);
else
ref.setResolvedJndiName(target);
}
}
protected void resolveAnnotatedRefs(DeploymentUnit unit, AnnotatedEJBReferencesMetaData annotatedRefs, List<String> unresolvedRefs)
{
if(annotatedRefs == null)
return;
ArrayList<String> searched = new ArrayList<String>();
for(AnnotatedEJBReferenceMetaData ref : annotatedRefs)
{
String mappedName = ref.getMappedName();
if(mappedName == null || mappedName.length() == 0)
{
mappedName = null;
String link = ref.getLink();
String target = null;
if(link != null)
target = findEjbLink(unit, link, searched);
if(target == null)
unresolvedRefs.add(ref.getEjbRefName()+"/ejb-ref/"+link + " available: " + searched);
else
mappedName = target;
}
ref.setResolvedJndiName(mappedName);
}
}
protected void resolveEjbRefs(DeploymentUnit unit, EJBReferencesMetaData ejbRefs, List<String> unresolvedRefs)
{
if(ejbRefs == null)
return;
ArrayList<String> searched = new ArrayList<String>();
for(EJBReferenceMetaData ref : ejbRefs)
{
String mappedName = ref.getMappedName();
if(mappedName == null || mappedName.length() == 0)
{
mappedName = null;
String link = ref.getLink();
String target = null;
if(link != null)
target = findEjbLink(unit, link, searched);
if(target == null)
unresolvedRefs.add(ref.getEjbRefName()+"/ejb-ref/"+link + " available: " + searched);
else
mappedName = target;
}
ref.setResolvedJndiName(mappedName);
}
}
protected void resolveMsgRefs(DeploymentUnit unit, MessageDestinationReferencesMetaData msgRefs, List<String> unresolvedRefs)
{
if(msgRefs == null)
return;
ArrayList<MessageDestinationMetaData> searched = new ArrayList<MessageDestinationMetaData>();
for(MessageDestinationReferenceMetaData ref : msgRefs)
{
String mappedName = ref.getMappedName();
if(mappedName == null || mappedName.length() == 0)
{
String link = ref.getLink();
MessageDestinationMetaData target = null;
if(link != null)
target = findMessageDestination(unit, link, searched);
if(target == null)
unresolvedRefs.add(ref.getMessageDestinationRefName()+"/message-destination-ref/"+link + " available: " + searched);
else
mappedName = target.getMappedName();
}
ref.setResolvedJndiName(mappedName);
}
}
/**
* Resolves an <ejb-link> target for an <ejb-ref> entry and
* returns the name of the target in the JNDI tree.
*
* @param unit DeploymentUnit
* @param link Content of the <ejb-link> entry.
* @param searched the list of ejbs searched
*
* @return The JNDI Entry of the target bean; <code>null</code> if
* no appropriate target could be found.
*/
public static String findEjbLink(DeploymentUnit unit, String link, ArrayList<String> searched)
{
return resolveLink(unit, link, searched, false);
}
/**
* Resolves an <ejb-link> target for an <ejb-local-ref> entry
* and returns the name of the target in the JNDI tree.
*
* @param unit DeploymentUnit
* @param link Content of the <ejb-link> entry.
* @param searched the searched ejbs
*
* @return The JNDI Entry of the target bean; <code>null</code> if
* no appropriate target could be found.
*/
public static String findLocalEjbLink(DeploymentUnit unit, String link, ArrayList<String> searched)
{
return resolveLink(unit, link, searched, true);
}
/**
* Resolves a <message-destination> target for a <message-destination-link>
* entry and returns the name of the target in the JNDI tree.
*
* @param di DeploymentUnit
* @param link Content of the <message-driven-link> entry.
*
* @return The JNDI Entry of the target; <code>null</code> if
* no appropriate target could be found.
*/
public static MessageDestinationMetaData findMessageDestination(DeploymentUnit di, String link, ArrayList<MessageDestinationMetaData> searched)
{
return resolveMessageDestination(di, link, searched);
}
private static String resolveLink(DeploymentUnit di, String link, ArrayList<String> searched, boolean isLocal)
{
if (link == null)
{
return null;
}
if (log.isTraceEnabled())
{
log.trace("resolveLink( {" + di + "}, {" + link + "}, {" + isLocal + "}");
}
if (di == null)
{
// We should throw an IllegalArgumentException here probably?
return null;
}
if (link.indexOf('#') != -1)
{
// <ejb-link> is specified in the form path/file.jar#Bean
return resolveRelativeLink(di, link, searched, isLocal);
}
else
{
// <ejb-link> contains a Bean Name, scan the DeploymentUnit tree
DeploymentUnit top = di.getTopLevel();
return resolveAbsoluteLink(top, link, searched, isLocal);
}
}
private static String resolveRelativeLink(DeploymentUnit unit, String link, ArrayList<String> searched, boolean isLocal)
{
DeploymentUnit top = unit.getTopLevel();
String path = link.substring(0, link.indexOf('#'));
String ejbName = link.substring(link.indexOf('#') + 1);
if (log.isTraceEnabled())
{
log.trace("Resolving relative link: " + link);
}
DeploymentUnit targetUnit = findLinkPath(top, path);
if (targetUnit == null)
{
log.warn("Can't locate DeploymentUnit for target: " + path);
return null;
}
String linkTarget = null;
if (targetUnit.getAttachment(JBossMetaData.class) != null)
{
JBossMetaData appMD = targetUnit.getAttachment(JBossMetaData.class);
JBossEnterpriseBeanMetaData beanMD = appMD.getEnterpriseBean(ejbName);
if (beanMD != null)
{
linkTarget = getJndiName(beanMD, isLocal);
if (linkTarget == null)
{
if (isLocal)
log.warn("Unable to determine local jndi name for " + beanMD.getEjbName());
else
log.warn("Unable to determine jndi name for " + beanMD.getEjbName());
searched.add(beanMD.getEjbName());
}
}
else
{
log.warn("No Bean named '" + ejbName + "' found in '" + path + "'!");
for (JBossEnterpriseBeanMetaData md : appMD.getEnterpriseBeans())
searched.add(md.getEjbName());
}
}
else
{
log.warn("DeploymentUnit " + targetUnit + " is not an EJB .jar " + "file!");
searched.add(targetUnit.getName());
}
return linkTarget;
}
private static String resolveAbsoluteLink(DeploymentUnit unit, String link, ArrayList<String> searched, boolean isLocal)
{
if (log.isTraceEnabled())
{
log.trace("Resolving absolute link, di: " + unit);
}
String ejbName = null;
// Search all ejb DeploymentUnits
List<JBossMetaData> ejbMetaData = getAllAttachments(unit, JBossMetaData.class);
for(JBossMetaData ejbMD : ejbMetaData)
{
JBossEnterpriseBeanMetaData beanMD = ejbMD.getEnterpriseBean(link);
if (beanMD != null)
{
ejbName = getJndiName(beanMD, isLocal);
if (ejbName == null)
{
if (isLocal)
log.warn("Unable to determine local jndi name for " + beanMD.getEjbName());
else
log.warn("Unable to determine jndi name for " + beanMD.getEjbName());
searched.add(beanMD.getEjbName());
}
if (log.isTraceEnabled())
{
log.trace("Found Bean: " + beanMD + ", resolves to: " + ejbName);
}
return ejbName;
}
else if (log.isTraceEnabled())
{
// Dump the ejb module ejbNames
log.trace("No match for ejb-link: " + link + ", module names:");
for(JBossEnterpriseBeanMetaData md : ejbMD.getEnterpriseBeans())
{
String beanEjbName = getJndiName(md, isLocal);
searched.add(md.getEjbName());
log.trace("... ejbName: " + beanEjbName);
}
}
}
return ejbName;
}
private static <T> List<T> getAllAttachments(DeploymentUnit unit, Class<T> type)
{
ArrayList<T> attachments = new ArrayList<T>();
DeploymentUnit top = unit.getTopLevel();
getAllAttachments(top, type, attachments);
return attachments;
}
private static <T> void getAllAttachments(DeploymentUnit unit, Class<T> type, ArrayList<T> attachments)
{
T attachment = unit.getAttachment(type);
if(attachment != null)
attachments.add(attachment);
List<DeploymentUnit> children = unit.getChildren();
if(children != null)
for(DeploymentUnit child : children)
getAllAttachments(child, type, attachments);
}
private static String getJndiName(JBossEnterpriseBeanMetaData beanMD, boolean isLocal)
{
String jndiName = null;
if (isLocal)
{
// Validate that there is a local home associated with this bean
jndiName = beanMD.determineLocalJndiName();
if (jndiName == null)
{
log.warn("LocalHome jndi name requested for: '" + beanMD.getEjbName() + "' but there is no LocalHome class");
}
}
else
{
// FIXME the session.getHomeJndiName should be moved to the JBossSessionMetaData.determineJndiName()
// and these if statements replaced with md.determineJndiName()
if( beanMD.isEntity() )
{
JBossEntityBeanMetaData md = (JBossEntityBeanMetaData) beanMD;
jndiName = md.determineJndiName();
}
else if( beanMD.isSession())
{
JBossSessionBeanMetaData md = (JBossSessionBeanMetaData) beanMD;
jndiName = md.getHomeJndiName();
if(jndiName == null)
jndiName = md.determineJndiName();
}
}
return jndiName;
}
private static MessageDestinationMetaData resolveMessageDestination(DeploymentUnit di, String link, ArrayList<MessageDestinationMetaData> searched)
{
if (link == null)
return null;
if (log.isTraceEnabled())
log.trace("resolveLink( {" + di + "}, {" + link + "})");
if (di == null)
// We should throw an IllegalArgumentException here probably?
return null;
if (link.indexOf('#') != -1)
// link is specified in the form path/file.jar#Bean
return resolveRelativeMessageDestination(di, link, searched);
else
{
// link contains a Bean Name, scan the DeploymentUnit tree
DeploymentUnit top = di.getTopLevel();
return resolveAbsoluteMessageDestination(top, link, searched);
}
}
private static MessageDestinationMetaData resolveRelativeMessageDestination(DeploymentUnit unit, String link, ArrayList<MessageDestinationMetaData> searched)
{
String path = link.substring(0, link.indexOf('#'));
String destinationName = link.substring(link.indexOf('#') + 1);
if (log.isTraceEnabled())
{
log.trace("Resolving relative message-destination-link: " + link);
}
DeploymentUnit top = unit.getTopLevel();
DeploymentUnit targetUnit = findLinkPath(top, path);
if (targetUnit == null)
{
log.warn("Can't locate DeploymentUnit for target: " + path);
return null;
}
if (log.isTraceEnabled())
log.trace("Found appropriate DeploymentUnit: " + targetUnit);
MessageDestinationMetaData md = null;
MessageDestinationsMetaData mds = null;
if (targetUnit.getAttachment(JBossMetaData.class) != null)
{
JBossMetaData appMD = targetUnit.getAttachment(JBossMetaData.class);
mds = appMD.getAssemblyDescriptor().getMessageDestinations();
md = mds.get(destinationName);
}
if (targetUnit.getAttachment(JBossWebMetaData.class) != null)
{
JBossWebMetaData webMD = targetUnit.getAttachment(JBossWebMetaData.class);
mds = webMD.getMessageDestinations();
md = mds.get(destinationName);
}
if(md == null)
{
log.warn("DeploymentUnit " + targetUnit + " is not an EJB .jar " + "file!");
if(mds != null)
searched.addAll(mds);
}
return md;
}
private static MessageDestinationMetaData resolveAbsoluteMessageDestination(DeploymentUnit unit, String link, ArrayList<MessageDestinationMetaData> searched)
{
if (log.isTraceEnabled())
log.trace("Resolving absolute link, di: " + unit);
// Search current DeploymentUnit
MessageDestinationMetaData md = null;
MessageDestinationsMetaData mds = null;
if (unit.getAttachment(JBossMetaData.class) != null)
{
JBossMetaData appMD = unit.getAttachment(JBossMetaData.class);
mds = appMD.getAssemblyDescriptor().getMessageDestinations();
if(mds != null)
{
searched.addAll(mds);
md = mds.get(link);
if (md != null)
return md;
}
}
if (unit.getAttachment(JBossWebMetaData.class) != null)
{
JBossWebMetaData webMD = unit.getAttachment(JBossWebMetaData.class);
mds = webMD.getMessageDestinations();
if(mds != null)
{
searched.addAll(mds);
md = mds.get(link);
if (md != null)
return md;
}
}
// Search each subcontext
Iterator<DeploymentUnit> it = unit.getChildren().iterator();
while (it.hasNext())
{
DeploymentUnit child = it.next();
MessageDestinationMetaData mdMD = resolveAbsoluteMessageDestination(child, link, searched);
if (mdMD != null)
return mdMD;
}
// Not found
return null;
}
private static DeploymentUnit findLinkPath(DeploymentUnit top, String path)
{
List<DeploymentUnit> children = top.getChildren();
DeploymentUnit targetUnit = null;
if(children != null)
{
for(DeploymentUnit child : children)
{
String childPath = child.getRelativePath();
if(childPath.endsWith(path))
{
targetUnit = child;
}
}
}
if (targetUnit == null)
{
return null;
}
if (log.isTraceEnabled())
{
log.trace("Found appropriate DeploymentUnit: " + targetUnit);
}
return targetUnit;
}
private void dump(DeploymentUnit unit)
{
DeploymentUnit top = unit.getTopLevel();
StringBuffer tmp = new StringBuffer();
dump(top, tmp, 0);
log.debug("Processing unit:\n"+tmp);
}
private void dump(DeploymentUnit unit, StringBuffer tmp, int depth)
{
for(int n = 0; n < depth; n ++)
tmp.append('+');
tmp.append(unit.getRelativePath());
JBossMetaData metaData = unit.getAttachment(JBossMetaData.class);
if(metaData != null)
{
JBossEnterpriseBeansMetaData beans = metaData.getEnterpriseBeans();
if(beans != null)
{
for(JBossEnterpriseBeanMetaData bean : beans)
{
tmp.append(",ejbName=");
tmp.append(bean.getEjbName());
if(bean.getEjbClass() != null)
{
tmp.append(",ejbClass=");
tmp.append(bean.getEjbClass());
}
if(bean instanceof JBossSessionBeanMetaData)
{
JBossSessionBeanMetaData sbean = (JBossSessionBeanMetaData) bean;
if(sbean.getHome() != null)
{
tmp.append(",home=");
tmp.append(sbean.getHome());
}
if(sbean.getRemote() != null)
{
tmp.append(",remote=");
tmp.append(sbean.getRemote());
}
BusinessLocalsMetaData locals = sbean.getBusinessLocals();
if(locals != null)
{
tmp.append(",BusinessLocals: ");
tmp.append(locals);
}
BusinessRemotesMetaData remotes = sbean.getBusinessRemotes();
if(remotes != null)
{
tmp.append(",BusinessRemotes: ");
tmp.append(remotes);
}
}
}
}
}
tmp.append('\n');
List<DeploymentUnit> children = unit.getChildren();
if(children != null)
{
for(DeploymentUnit child : children)
dump(child, tmp, depth+1);
}
}
}