/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss 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.beans.metadata.plugins;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import org.jboss.beans.metadata.api.model.AutowireType;
import org.jboss.beans.metadata.api.model.FromContext;
import org.jboss.beans.metadata.api.model.InjectOption;
import org.jboss.beans.metadata.spi.MetaDataVisitor;
import org.jboss.beans.metadata.spi.MetaDataVisitorNode;
import org.jboss.dependency.plugins.AttributeCallbackItem;
import org.jboss.dependency.spi.ControllerContext;
import org.jboss.dependency.spi.ControllerState;
import org.jboss.dependency.spi.DependencyItem;
import org.jboss.kernel.plugins.dependency.SearchClassContextDependencyItem;
import org.jboss.reflect.spi.TypeInfo;
import org.jboss.util.JBossStringBuilder;
/**
* Injection value.
*
* @author <a href="ales.justin@gmail.com">Ales Justin</a>
*/
@XmlType
public class AbstractInjectionValueMetaData extends AbstractDependencyValueMetaData
{
private static final long serialVersionUID = 3L;
protected AutowireType injectionType = AutowireType.BY_CLASS;
protected InjectOption injectionOption = InjectOption.STRICT;
protected FromContext fromContext;
/**
* Simplyifies things with AutowireType.BY_NAME
*/
protected AbstractPropertyMetaData propertyMetaData;
/**
* Create a new injection value
*/
public AbstractInjectionValueMetaData()
{
}
/**
* Create a new injection value
*
* @param value the value
*/
public AbstractInjectionValueMetaData(Object value)
{
super(value);
}
/**
* Create a new injection value
*
* @param value the value
* @param property the property
*/
public AbstractInjectionValueMetaData(Object value, String property)
{
super(value, property);
}
public AutowireType getInjectionType()
{
return injectionType;
}
@XmlAttribute(name="type")
public void setInjectionType(AutowireType injectionType)
{
this.injectionType = injectionType;
}
public InjectOption getInjectionOption()
{
return injectionOption;
}
@XmlAttribute(name="option")
public void setInjectionOption(InjectOption injectionOption)
{
this.injectionOption = injectionOption;
}
public FromContext getFromContext()
{
return fromContext;
}
@XmlAttribute(name="fromContext")
public void setFromContext(FromContext fromContext)
{
this.fromContext = fromContext;
}
public AbstractPropertyMetaData getPropertyMetaData()
{
return propertyMetaData;
}
@XmlTransient
public void setPropertyMetaData(AbstractPropertyMetaData propertyMetaData)
{
this.propertyMetaData = propertyMetaData;
}
/**
* Add install/callback item.
*
* @param name the callback name
*/
protected void addInstallItem(Object name)
{
if (propertyMetaData == null)
throw new IllegalArgumentException("Illegal usage of option Callback - injection not used with property = " + this);
context.getDependencyInfo().addInstallItem(new AttributeCallbackItem<Object>(name, whenRequiredState, dependentState, context, propertyMetaData.getName()));
}
protected boolean isLookupValid(ControllerContext lookup)
{
boolean lookupExists = super.isLookupValid(lookup);
boolean isCallback = InjectOption.CALLBACK.equals(injectionOption);
boolean isOptional = InjectOption.OPTIONAL.equals(injectionOption);
if (lookupExists == false && isCallback)
{
addInstallItem(getUnderlyingValue());
}
return lookupExists || isCallback || isOptional;
}
protected boolean isOptional()
{
return InjectOption.OPTIONAL.equals(injectionOption);
}
@SuppressWarnings({"unchecked", "deprecation"})
public Object getValue(TypeInfo info, ClassLoader cl) throws Throwable
{
// controller context property injection
if (fromContext != null)
{
ControllerState state = dependentState;
if (state == null)
state = ControllerState.INSTANTIATED;
ControllerContext lookup = getControllerContext(getUnderlyingValue(), state);
if (lookup == null)
throw new Error("Should not be here - dependency failed - " + this);
return fromContext.executeLookup(lookup);
}
// by class type
if (getUnderlyingValue() == null)
{
ControllerContext lookup = getControllerContext(info.getType(), ControllerState.INSTALLED);
if (lookup == null)
{
if (InjectOption.STRICT.equals(injectionOption))
{
throw new IllegalArgumentException("Possible multiple matching beans, see log for info.");
}
else
{
if (InjectOption.CALLBACK.equals(injectionOption))
addInstallItem(info.getType());
return null;
}
}
// TODO - add progression here, then fix BeanMetaData as well
return lookup.getTarget();
}
return super.getValue(info, cl);
}
protected boolean addDependencyItem()
{
return InjectOption.STRICT.equals(injectionOption) || fromContext != null;
}
@XmlTransient
public Object getUnderlyingValue()
{
Object original = super.getUnderlyingValue();
// might be used for internal compare, in that case context will still be null
return (fromContext != null && original == null) ? (context != null ? context.getName() : null) : original;
}
public void initialVisit(MetaDataVisitor visitor)
{
// controller context property injection
if (fromContext != null)
{
// check if dependent is not set when used on itself
if (super.getUnderlyingValue() == null && dependentState == null)
{
dependentState = fromContext.getWhenValid();
}
super.initialVisit(visitor);
return;
}
// no bean specified
if (getUnderlyingValue() == null)
{
// check for property
if (property != null)
{
property = null;
log.warn("Ignoring property - contextual injection: " + this);
}
if (AutowireType.BY_NAME.equals(injectionType))
{
if (propertyMetaData == null)
throw new IllegalArgumentException("Illegal usage of type ByName - injection not used with property = " + this);
setValue(propertyMetaData.getName());
}
else
{
visitor.initialVisit(this);
}
}
// check if was maybe set with by_name
if (getUnderlyingValue() != null)
{
super.initialVisit(visitor);
}
}
@SuppressWarnings("deprecation")
public void describeVisit(MetaDataVisitor visitor)
{
// no bean and not by_name
if (getUnderlyingValue() == null)
{
if (AutowireType.BY_CLASS.equals(injectionType))
{
context = visitor.getControllerContext();
// dependency item or install item
if (InjectOption.STRICT.equals(injectionOption))
{
// add dependency item only for strict inject behaviour
// we pop it so that parent node has the same semantics as this one
// meaning that his current peek is also his parent
// and all other nodes that cannot determine type follow the same
// contract - popping and pushing
// maybe the whole thing can be rewritten to LinkedList
// or simply using the fact that Stack is also a Vector?
MetaDataVisitorNode node = visitor.visitorNodeStack().pop();
try
{
if (node instanceof TypeProvider)
{
TypeProvider typeProvider = (TypeProvider)node;
Class<?> injectionClass = typeProvider.getType(visitor, this).getType();
log.debug(context.getName() + ": Contextual injection usage (class -> classloader): " + injectionClass + " -> " + SecurityActions.getClassLoader(injectionClass) + " defined by " + node);
// set when required
ControllerState whenRequired = whenRequiredState;
if (whenRequired == null)
{
whenRequired = visitor.getContextState();
}
DependencyItem item = new SearchClassContextDependencyItem(
context.getName(),
injectionClass,
whenRequired,
dependentState,
search);
visitor.addDependency(item);
}
else
{
throw new Error(TypeProvider.ERROR_MSG);
}
}
catch (Error error)
{
throw error;
}
catch (Throwable throwable)
{
throw new Error(throwable);
}
finally
{
visitor.visitorNodeStack().push(node);
}
}
}
else
{
throw new IllegalArgumentException("Unknown injection type=" + injectionType);
}
}
super.describeVisit(visitor);
}
public void toString(JBossStringBuilder buffer)
{
super.toString(buffer);
if (injectionType != null)
buffer.append(" injectionType=").append(injectionType);
if (propertyMetaData != null)
buffer.append(" propertyMetaData=").append(propertyMetaData.getName()); //else overflow - indefinite recursion
if (fromContext != null)
buffer.append(" fromContext=").append(fromContext);
}
public AbstractInjectionValueMetaData clone()
{
return (AbstractInjectionValueMetaData)super.clone();
}
}