/*
* JBoss, Home of Professional Open Source
* Copyright 2012, Red Hat Inc., and individual contributors as indicated
* 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.as.jsf.deployment;
import org.jboss.as.ee.component.ComponentDescription;
import org.jboss.as.ee.component.EEApplicationClasses;
import org.jboss.as.ee.component.EEModuleDescription;
import org.jboss.as.ee.structure.DeploymentType;
import org.jboss.as.ee.structure.DeploymentTypeMarker;
import org.jboss.as.jsf.logging.JSFLogger;
import org.jboss.as.server.deployment.Attachments;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.server.deployment.DeploymentUtils;
import org.jboss.as.server.deployment.annotation.CompositeIndex;
import org.jboss.as.server.deployment.module.ResourceRoot;
import org.jboss.as.web.common.WarMetaData;
import org.jboss.as.web.common.WebComponentDescription;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.metadata.javaee.spec.ParamValueMetaData;
import org.jboss.metadata.parser.util.NoopXMLResolver;
import org.jboss.metadata.web.spec.WebMetaData;
import org.jboss.modules.Module;
import org.jboss.vfs.VirtualFile;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Sets up JSF managed beans as components using information in the annotations and
*
* @author Stuart Douglas
*/
public class JSFManagedBeanProcessor implements DeploymentUnitProcessor {
public static final DotName MANAGED_BEAN_ANNOTATION = DotName.createSimple("javax.faces.bean.ManagedBean");
private static final String WEB_INF_FACES_CONFIG = "WEB-INF/faces-config.xml";
private static final String MANAGED_BEAN = "managed-bean";
private static final String MANAGED_BEAN_CLASS = "managed-bean-class";
private static final String CONFIG_FILES = "javax.faces.CONFIG_FILES";
@Override
public void deploy(final DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
final CompositeIndex index = deploymentUnit.getAttachment(Attachments.COMPOSITE_ANNOTATION_INDEX);
final EEModuleDescription moduleDescription = deploymentUnit.getAttachment(org.jboss.as.ee.component.Attachments.EE_MODULE_DESCRIPTION);
final EEApplicationClasses applicationClassesDescription = deploymentUnit.getAttachment(org.jboss.as.ee.component.Attachments.EE_APPLICATION_CLASSES_DESCRIPTION);
final Module module = deploymentUnit.getAttachment(Attachments.MODULE);
if (index == null) {
return;
}
if (module == null) {
return;
}
if (!DeploymentTypeMarker.isType(DeploymentType.WAR, deploymentUnit)) {
return;
}
final Set<String> managedBeanClasses = new HashSet<String>();
handleAnnotations(index, managedBeanClasses);
processXmlManagedBeans(deploymentUnit, managedBeanClasses);
for (String managedBean : managedBeanClasses) {
//try and load the class, and skip the class if it cannot be loaded
//this is not ideal, but we are not allowed to let the deployment
//fail due to missing managed beans
try {
final Class<?> componentClass = module.getClassLoader().loadClass(managedBean);
componentClass.getConstructor();
} catch (ClassNotFoundException e) {
JSFLogger.ROOT_LOGGER.managedBeanLoadFail(managedBean);
continue;
} catch (NoSuchMethodException e) {
JSFLogger.ROOT_LOGGER.managedBeanNoDefaultConstructor(managedBean);
continue;
}
installManagedBeanComponent(managedBean, moduleDescription, deploymentUnit, applicationClassesDescription);
}
}
/**
* Parse the faces config files looking for managed bean classes. The parser is quite
* simplistic as the only information we need is the managed-bean-class element
*/
private void processXmlManagedBeans(final DeploymentUnit deploymentUnit, final Set<String> managedBeanClasses) {
for (final VirtualFile facesConfig : getConfigurationFiles(deploymentUnit)) {
InputStream is = null;
try {
is = facesConfig.openStream();
final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
inputFactory.setXMLResolver(NoopXMLResolver.create());
XMLStreamReader parser = inputFactory.createXMLStreamReader(is);
StringBuilder className = null;
int indent = 0;
boolean managedBean = false;
boolean managedBeanClass = false;
while (true) {
int event = parser.next();
if (event == XMLStreamConstants.END_DOCUMENT) {
parser.close();
break;
}
if (event == XMLStreamConstants.START_ELEMENT) {
indent++;
if (indent == 2) {
if (parser.getLocalName().equals(MANAGED_BEAN)) {
managedBean = true;
}
} else if (indent == 3 && managedBean) {
if (parser.getLocalName().equals(MANAGED_BEAN_CLASS)) {
managedBeanClass = true;
className = new StringBuilder();
}
}
} else if (event == XMLStreamConstants.END_ELEMENT) {
indent--;
managedBeanClass = false;
if (indent == 1) {
managedBean = false;
}
if (className != null) {
managedBeanClasses.add(className.toString().trim());
className = null;
}
} else if (managedBeanClass && event == XMLStreamConstants.CHARACTERS) {
className.append(parser.getText());
}
}
} catch (Exception e) {
JSFLogger.ROOT_LOGGER.managedBeansConfigParseFailed(facesConfig);
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
// Ignore
}
}
}
}
public Set<VirtualFile> getConfigurationFiles(DeploymentUnit deploymentUnit) {
final Set<VirtualFile> ret = new HashSet<VirtualFile>();
final List<ResourceRoot> resourceRoots = DeploymentUtils.allResourceRoots(deploymentUnit);
for (final ResourceRoot resourceRoot : resourceRoots) {
final VirtualFile webInfFacesConfig = resourceRoot.getRoot().getChild(WEB_INF_FACES_CONFIG);
if (webInfFacesConfig.exists()) {
ret.add(webInfFacesConfig);
}
//look for files that end in .faces-config.xml
final VirtualFile metaInf = resourceRoot.getRoot().getChild("META-INF");
if (metaInf.exists() && metaInf.isDirectory()) {
for (final VirtualFile file : metaInf.getChildren()) {
if (file.getName().equals("faces-config.xml") || file.getName().endsWith(".faces-config.xml")) {
ret.add(file);
}
}
}
}
String configFiles = null;
//now look for files in the javax.faces.CONFIG_FILES context param
final WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData != null) {
final WebMetaData webMetaData = warMetaData.getWebMetaData();
if (webMetaData != null) {
final List<ParamValueMetaData> contextParams = webMetaData.getContextParams();
if (contextParams != null) {
for (final ParamValueMetaData param : contextParams) {
if (param.getParamName().equals(CONFIG_FILES)) {
configFiles = param.getParamValue();
break;
}
}
}
}
}
if (configFiles != null) {
final String[] files = configFiles.split(",");
final ResourceRoot deploymentRoot = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_ROOT);
if (deploymentRoot != null) {
for (final String file : files) {
final VirtualFile configFile = deploymentRoot.getRoot().getChild(file);
if (configFile.exists()) {
ret.add(configFile);
}
}
}
}
return ret;
}
private void handleAnnotations(final CompositeIndex index, final Set<String> managedBeanClasses) throws DeploymentUnitProcessingException {
final List<AnnotationInstance> annotations = index.getAnnotations(MANAGED_BEAN_ANNOTATION);
if (annotations != null) {
for (final AnnotationInstance annotation : annotations) {
final AnnotationTarget target = annotation.target();
if (target instanceof ClassInfo) {
final String className = ((ClassInfo) target).name().toString();
managedBeanClasses.add(className);
} else {
throw new DeploymentUnitProcessingException(JSFLogger.ROOT_LOGGER.invalidManagedBeanAnnotation(target));
}
}
}
}
private void installManagedBeanComponent(String className, final EEModuleDescription moduleDescription, final DeploymentUnit deploymentUnit, final EEApplicationClasses applicationClassesDescription) {
final ComponentDescription componentDescription = new WebComponentDescription(MANAGED_BEAN.toString() + "." + className, className, moduleDescription, deploymentUnit.getServiceName(), applicationClassesDescription);
moduleDescription.addComponent(componentDescription);
deploymentUnit.addToAttachmentList(WebComponentDescription.WEB_COMPONENTS, componentDescription.getStartServiceName());
}
@Override
public void undeploy(final DeploymentUnit context) {
}
}