/*
* JBoss, Home of Professional Open Source
* Copyright 2010, 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.ee.component.deployers;
import org.jboss.as.ee.logging.EeLogger;
import org.jboss.as.ee.component.Attachments;
import org.jboss.as.ee.component.BindingConfiguration;
import org.jboss.as.ee.component.ComponentDescription;
import org.jboss.as.ee.component.DeploymentDescriptorEnvironment;
import org.jboss.as.ee.component.EEApplicationClasses;
import org.jboss.as.ee.component.EnvEntryInjectionSource;
import org.jboss.as.ee.component.FixedInjectionSource;
import org.jboss.as.ee.component.InjectionSource;
import org.jboss.as.ee.component.LookupInjectionSource;
import org.jboss.as.ee.component.ResourceInjectionTarget;
import org.jboss.as.naming.ImmediateManagedReference;
import org.jboss.as.naming.ManagedReference;
import org.jboss.as.naming.ManagedReferenceFactory;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.reflect.DeploymentReflectionIndex;
import org.jboss.metadata.javaee.spec.EnvironmentEntriesMetaData;
import org.jboss.metadata.javaee.spec.EnvironmentEntryMetaData;
import org.jboss.metadata.javaee.spec.MessageDestinationReferenceMetaData;
import org.jboss.metadata.javaee.spec.MessageDestinationReferencesMetaData;
import org.jboss.metadata.javaee.spec.ResourceEnvironmentReferenceMetaData;
import org.jboss.metadata.javaee.spec.ResourceEnvironmentReferencesMetaData;
import org.jboss.metadata.javaee.spec.ResourceReferenceMetaData;
import org.jboss.metadata.javaee.spec.ResourceReferencesMetaData;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import static org.jboss.as.ee.logging.EeLogger.ROOT_LOGGER;
/**
* Deployment processor that sets up env-entry, resource-ref and resource-env-ref bindings
*
* @author Stuart Douglas
* @author Eduardo Martins
*/
public class ResourceReferenceProcessor extends AbstractDeploymentDescriptorBindingsProcessor {
private static final String JAVAX_NAMING_CONTEXT = "javax.naming.Context";
@Override
protected List<BindingConfiguration> processDescriptorEntries(DeploymentUnit deploymentUnit, DeploymentDescriptorEnvironment environment, ResourceInjectionTarget resourceInjectionTarget, final ComponentDescription componentDescription, ClassLoader classLoader, DeploymentReflectionIndex deploymentReflectionIndex, final EEApplicationClasses applicationClasses) throws DeploymentUnitProcessingException {
List<BindingConfiguration> bindings = new ArrayList<BindingConfiguration>();
bindings.addAll(getEnvironmentEntries(environment, classLoader, deploymentReflectionIndex, resourceInjectionTarget));
bindings.addAll(getResourceEnvRefEntries(deploymentUnit, environment, classLoader, deploymentReflectionIndex, resourceInjectionTarget));
bindings.addAll(getResourceRefEntries(deploymentUnit, environment, classLoader, deploymentReflectionIndex, resourceInjectionTarget));
bindings.addAll(getMessageDestinationRefs(environment, classLoader, deploymentReflectionIndex, resourceInjectionTarget, deploymentUnit));
return bindings;
}
private List<BindingConfiguration> getResourceEnvRefEntries(final DeploymentUnit deploymentUnit, final DeploymentDescriptorEnvironment environment, ClassLoader classLoader, DeploymentReflectionIndex deploymentReflectionIndex, ResourceInjectionTarget resourceInjectionTarget) throws DeploymentUnitProcessingException {
List<BindingConfiguration> bindings = new ArrayList<BindingConfiguration>();
final ResourceEnvironmentReferencesMetaData resourceEnvRefs = environment.getEnvironment().getResourceEnvironmentReferences();
final EEResourceReferenceProcessorRegistry registry = deploymentUnit.getAttachment(Attachments.RESOURCE_REFERENCE_PROCESSOR_REGISTRY);
if (resourceEnvRefs == null) {
return bindings;
}
for (ResourceEnvironmentReferenceMetaData resourceEnvRef : resourceEnvRefs) {
final String name;
if (resourceEnvRef.getName().startsWith("java:")) {
name = resourceEnvRef.getName();
} else {
name = environment.getDefaultContext() + resourceEnvRef.getName();
}
Class<?> classType = null;
if (resourceEnvRef.getType() != null) {
try {
classType = classLoader.loadClass(resourceEnvRef.getType());
} catch (ClassNotFoundException e) {
throw EeLogger.ROOT_LOGGER.cannotLoad(e, resourceEnvRef.getType());
}
}
// our injection (source) comes from the local (ENC) lookup, no matter what.
InjectionSource injectionSource = new LookupInjectionSource(name);
classType = processInjectionTargets(resourceInjectionTarget, injectionSource, classLoader, deploymentReflectionIndex, resourceEnvRef, classType);
if (!isEmpty(resourceEnvRef.getLookupName())) {
injectionSource = new LookupInjectionSource(resourceEnvRef.getLookupName(), classType != null && JAVAX_NAMING_CONTEXT.equals(classType.getName()));
} else {
if (classType == null) {
throw EeLogger.ROOT_LOGGER.cannotDetermineType(name);
}
//check if it is a well known type
final String lookup = ResourceInjectionAnnotationParsingProcessor.FIXED_LOCATIONS.get(classType.getName());
if (lookup != null) {
injectionSource = new LookupInjectionSource(lookup);
} else {
final EEResourceReferenceProcessor resourceReferenceProcessor = registry.getResourceReferenceProcessor(classType.getName());
if (resourceReferenceProcessor != null) {
injectionSource = resourceReferenceProcessor.getResourceReferenceBindingSource();
} else {
//TODO: how are we going to handle these? Previously they would have been handled by jboss-*.xml
if (resourceEnvRef.getResourceEnvRefName().startsWith("java:")) {
ROOT_LOGGER.cannotResolve("resource-env-ref", name);
continue;
} else {
injectionSource = new LookupInjectionSource("java:jboss/resources/" + resourceEnvRef.getResourceEnvRefName());
}
}
}
}
bindings.add(new BindingConfiguration(name, injectionSource));
}
return bindings;
}
private List<BindingConfiguration> getResourceRefEntries(final DeploymentUnit deploymentUnit, DeploymentDescriptorEnvironment environment, ClassLoader classLoader, DeploymentReflectionIndex deploymentReflectionIndex, ResourceInjectionTarget resourceInjectionTarget) throws DeploymentUnitProcessingException {
List<BindingConfiguration> bindings = new ArrayList<BindingConfiguration>();
final EEResourceReferenceProcessorRegistry registry = deploymentUnit.getAttachment(Attachments.RESOURCE_REFERENCE_PROCESSOR_REGISTRY);
final ResourceReferencesMetaData resourceRefs = environment.getEnvironment().getResourceReferences();
if (resourceRefs == null) {
return bindings;
}
for (final ResourceReferenceMetaData resourceRef : resourceRefs) {
final String name;
if (resourceRef.getName().startsWith("java:")) {
name = resourceRef.getName();
} else {
name = environment.getDefaultContext() + resourceRef.getName();
}
Class<?> classType = null;
if (resourceRef.getType() != null) {
try {
classType = classLoader.loadClass(resourceRef.getType());
} catch (ClassNotFoundException e) {
throw EeLogger.ROOT_LOGGER.cannotLoad(e, resourceRef.getType());
}
}
// our injection (source) comes from the local (ENC) lookup, no matter what.
InjectionSource injectionSource = new LookupInjectionSource(name);
classType = processInjectionTargets(resourceInjectionTarget, injectionSource, classLoader, deploymentReflectionIndex, resourceRef, classType);
if (!isEmpty(resourceRef.getLookupName())) {
injectionSource = new LookupInjectionSource(resourceRef.getLookupName(), classType != null && JAVAX_NAMING_CONTEXT.equals(classType.getName()));
} else if (!isEmpty(resourceRef.getResUrl())) {
final String url = resourceRef.getResUrl();
if (classType != null && classType.equals(URI.class)) {
try {
//we need a newURI every time
injectionSource = new FixedInjectionSource(new ManagedReferenceFactory() {
@Override
public ManagedReference getReference() {
try {
return new ImmediateManagedReference(new URI(url));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}, new URI(url));
} catch (URISyntaxException e) {
throw EeLogger.ROOT_LOGGER.cannotParseResourceRefUri(e, resourceRef.getResUrl());
}
} else {
try {
injectionSource = new FixedInjectionSource(new ManagedReferenceFactory() {
@Override
public ManagedReference getReference() {
try {
return new ImmediateManagedReference(new URL(url));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
}, new URL(url));
} catch (MalformedURLException e) {
throw EeLogger.ROOT_LOGGER.cannotParseResourceRefUri(e, resourceRef.getResUrl());
}
}
} else {
if (classType == null) {
throw EeLogger.ROOT_LOGGER.cannotDetermineType(name);
}
//check if it is a well known type
final String lookup = ResourceInjectionAnnotationParsingProcessor.FIXED_LOCATIONS.get(classType.getName());
if (lookup != null) {
injectionSource = new LookupInjectionSource(lookup);
} else {
final EEResourceReferenceProcessor resourceReferenceProcessor = registry.getResourceReferenceProcessor(classType.getName());
if (resourceReferenceProcessor != null) {
injectionSource = resourceReferenceProcessor.getResourceReferenceBindingSource();
} else if (!resourceRef.getResourceRefName().startsWith("java:")) {
injectionSource = new LookupInjectionSource("java:jboss/resources/" + resourceRef.getResourceRefName());
} else {
//if we cannot resolve it just log
ROOT_LOGGER.cannotResolve("resource-env-ref", name);
continue;
}
}
}
bindings.add(new BindingConfiguration(name, injectionSource));
}
return bindings;
}
private List<BindingConfiguration> getEnvironmentEntries(final DeploymentDescriptorEnvironment environment, final ClassLoader classLoader, DeploymentReflectionIndex deploymentReflectionIndex, ResourceInjectionTarget resourceInjectionTarget) throws DeploymentUnitProcessingException {
final List<BindingConfiguration> bindings = new ArrayList<BindingConfiguration>();
final EnvironmentEntriesMetaData envEntries = environment.getEnvironment().getEnvironmentEntries();
if (envEntries == null) {
return bindings;
}
for (final EnvironmentEntryMetaData envEntry : envEntries) {
final String name;
if (envEntry.getName().startsWith("java:")) {
name = envEntry.getName();
} else {
name = environment.getDefaultContext() + envEntry.getEnvEntryName();
}
Class<?> classType = null;
if (envEntry.getType() != null) {
try {
classType = this.loadClass(envEntry.getType(), classLoader);
} catch (ClassNotFoundException e) {
throw EeLogger.ROOT_LOGGER.cannotLoad(e, envEntry.getType());
}
}
final String value = envEntry.getValue();
final String lookup = envEntry.getLookupName();
if (!isEmpty(value) && !isEmpty(lookup)) {
throw EeLogger.ROOT_LOGGER.cannotSpecifyBoth("<env-entry-value>", "<lookup-name>");
} else if (isEmpty(lookup) && isEmpty(value)) {
//if no value is provided then it is not an error
//this reference should simply be ignored
// (Java ee platform spec 6.0 fr pg 80)
continue;
}
// our injection (source) comes from the local (ENC) lookup, no matter what.
LookupInjectionSource injectionSource = new LookupInjectionSource(name);
classType = processInjectionTargets(resourceInjectionTarget, injectionSource, classLoader, deploymentReflectionIndex, envEntry, classType);
if (classType == null) {
throw EeLogger.ROOT_LOGGER.cannotDetermineType("<env-entry>", name, "<env-entry-type>");
}
final String type = classType.getName();
final BindingConfiguration bindingConfiguration;
if (!isEmpty(lookup)) {
bindingConfiguration = new BindingConfiguration(name, new LookupInjectionSource(lookup));
} else if (type.equals(String.class.getName())) {
bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(value));
} else if (type.equals(Integer.class.getName()) || type.equals("int")) {
bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Integer.valueOf(value)));
} else if (type.equals(Short.class.getName()) || type.equals("short")) {
bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Short.valueOf(value)));
} else if (type.equals(Long.class.getName()) || type.equals("long")) {
bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Long.valueOf(value)));
} else if (type.equals(Byte.class.getName()) || type.equals("byte")) {
bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Byte.valueOf(value)));
} else if (type.equals(Double.class.getName()) || type.equals("double")) {
bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Double.valueOf(value)));
} else if (type.equals(Float.class.getName()) || type.equals("float")) {
bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Float.valueOf(value)));
} else if (type.equals(Boolean.class.getName()) || type.equals("boolean")) {
bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Boolean.valueOf(value)));
} else if (type.equals(Character.class.getName()) || type.equals("char")) {
if (value.length() != 1) {
throw EeLogger.ROOT_LOGGER.invalidCharacterLength("env-entry", value);
}
bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(value.charAt(0)));
} else if (type.equals(Class.class.getName())) {
try {
bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(classLoader.loadClass(value)));
} catch (ClassNotFoundException e) {
throw EeLogger.ROOT_LOGGER.cannotLoad(value);
}
} else if (classType.isEnum() || (classType.getEnclosingClass() != null && classType.getEnclosingClass().isEnum())) {
bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Enum.valueOf((Class) classType, value)));
} else {
throw EeLogger.ROOT_LOGGER.unknownElementType("env-entry", type);
}
bindings.add(bindingConfiguration);
}
return bindings;
}
/**
* TODO: should this be part of the messaging subsystem
*/
private List<BindingConfiguration> getMessageDestinationRefs(final DeploymentDescriptorEnvironment environment, final ClassLoader classLoader, final DeploymentReflectionIndex deploymentReflectionIndex, final ResourceInjectionTarget resourceInjectionTarget, final DeploymentUnit deploymentUnit) throws DeploymentUnitProcessingException {
final List<BindingConfiguration> bindings = new ArrayList<BindingConfiguration>();
final MessageDestinationReferencesMetaData messageDestinationReferences = environment.getEnvironment().getMessageDestinationReferences();
if (messageDestinationReferences == null) {
return bindings;
}
for (final MessageDestinationReferenceMetaData messageRef : messageDestinationReferences) {
final String name;
if (messageRef.getName().startsWith("java:")) {
name = messageRef.getName();
} else {
name = environment.getDefaultContext() + messageRef.getName();
}
Class<?> classType = null;
if (messageRef.getType() != null) {
try {
classType = classLoader.loadClass(messageRef.getType());
} catch (ClassNotFoundException e) {
throw EeLogger.ROOT_LOGGER.cannotLoad(e, messageRef.getType());
}
}
// our injection (source) comes from the local (ENC) lookup, no matter what.
final LookupInjectionSource injectionSource = new LookupInjectionSource(name);
classType = processInjectionTargets(resourceInjectionTarget, injectionSource, classLoader, deploymentReflectionIndex, messageRef, classType);
final BindingConfiguration bindingConfiguration;
if (!isEmpty(messageRef.getLookupName())) {
bindingConfiguration = new BindingConfiguration(name, new LookupInjectionSource(messageRef.getLookupName()));
bindings.add(bindingConfiguration);
} else if (!isEmpty(messageRef.getMappedName())) {
bindingConfiguration = new BindingConfiguration(name, new LookupInjectionSource(messageRef.getMappedName()));
bindings.add(bindingConfiguration);
} else if (!isEmpty(messageRef.getLink())) {
final MessageDestinationInjectionSource messageDestinationInjectionSource = new MessageDestinationInjectionSource(messageRef.getLink(), name);
bindingConfiguration = new BindingConfiguration(name, messageDestinationInjectionSource);
deploymentUnit.addToAttachmentList(Attachments.MESSAGE_DESTINATIONS, messageDestinationInjectionSource);
bindings.add(bindingConfiguration);
} else {
ROOT_LOGGER.cannotResolve("message-destination-ref", name);
}
}
return bindings;
}
private boolean isEmpty(final String string) {
return string == null || string.isEmpty();
}
@Override
public void undeploy(DeploymentUnit context) {
}
private Class<?> loadClass(String className, ClassLoader cl) throws ClassNotFoundException {
if (className == null || className.trim().isEmpty()) {
throw EeLogger.ROOT_LOGGER.cannotBeNullOrEmpty("Classname", className);
}
if (className.equals(void.class.getName())) {
return void.class;
}
if (className.equals(byte.class.getName())) {
return byte.class;
}
if (className.equals(short.class.getName())) {
return short.class;
}
if (className.equals(int.class.getName())) {
return int.class;
}
if (className.equals(long.class.getName())) {
return long.class;
}
if (className.equals(char.class.getName())) {
return char.class;
}
if (className.equals(boolean.class.getName())) {
return boolean.class;
}
if (className.equals(float.class.getName())) {
return float.class;
}
if (className.equals(double.class.getName())) {
return double.class;
}
// Now that we know its not a primitive, lets just allow
// the passed classloader to handle the request.
return Class.forName(className, false, cl);
}
}