blob: 621942471a1166680174ee95c866e64a87a91d1e [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.aries.blueprint.plugin.model;
import org.apache.aries.blueprint.plugin.handlers.Handlers;
import org.apache.aries.blueprint.plugin.spi.BeanAnnotationHandler;
import org.apache.aries.blueprint.plugin.spi.BeanEnricher;
import org.apache.aries.blueprint.plugin.spi.ContextEnricher;
import org.apache.aries.blueprint.plugin.spi.FieldAnnotationHandler;
import org.apache.aries.blueprint.plugin.spi.InjectLikeHandler;
import org.apache.aries.blueprint.plugin.spi.MethodAnnotationHandler;
import org.apache.aries.blueprint.plugin.spi.XmlWriter;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import static org.apache.aries.blueprint.plugin.model.AnnotationHelper.findSingleton;
import static org.apache.aries.blueprint.plugin.model.NamingHelper.getBeanName;
class Bean implements BeanEnricher, XmlWriter, Comparable<Bean>{
private static final String NS_EXT = "http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0";
String id;
final Class<?> clazz;
SortedSet<Property> properties = new TreeSet<>();
List<Argument> constructorArguments = new ArrayList<>();
final Map<String, String> attributes = new HashMap<>();
final Map<String, XmlWriter> beanContentWriters = new HashMap<>();
protected final ContextEnricher contextEnricher;
private final Introspector introspector;
Bean(Class<?> clazz, ContextEnricher contextEnricher) {
this.clazz = clazz;
this.id = getBeanName(clazz);
this.contextEnricher = contextEnricher;
introspector = new Introspector(clazz);
setScope(clazz);
handleCustomBeanAnnotations();
handleFieldsAnnotation(introspector);
handleMethodsAnnotation(introspector);
}
private void setScope(Class<?> clazz) {
attributes.put("scope", findSingleton(clazz) ? "singleton" : "prototype");
}
void resolveDependency(BlueprintRegistry blueprintRegistry) {
resolveArguments(blueprintRegistry);
resolveFields(blueprintRegistry);
resolveMethods(blueprintRegistry);
}
private void handleMethodsAnnotation(Introspector introspector) {
for (MethodAnnotationHandler methodAnnotationHandler : Handlers.METHOD_ANNOTATION_HANDLERS) {
List<Method> methods = introspector.methodsWith(methodAnnotationHandler.getAnnotation());
if (methods.size() > 0) {
methodAnnotationHandler.handleMethodAnnotation(clazz, methods, contextEnricher, this);
}
}
}
private void handleFieldsAnnotation(Introspector introspector) {
for (FieldAnnotationHandler fieldAnnotationHandler : Handlers.FIELD_ANNOTATION_HANDLERS) {
List<Field> fields = introspector.fieldsWith(fieldAnnotationHandler.getAnnotation());
if (fields.size() > 0) {
fieldAnnotationHandler.handleFieldAnnotation(clazz, fields, contextEnricher, this);
}
}
}
private void handleCustomBeanAnnotations() {
for (BeanAnnotationHandler beanAnnotationHandler : Handlers.BEAN_ANNOTATION_HANDLERS) {
Object annotation = AnnotationHelper.findAnnotation(clazz.getAnnotations(), beanAnnotationHandler.getAnnotation());
if (annotation != null) {
beanAnnotationHandler.handleBeanAnnotation(clazz, id, contextEnricher, this);
}
}
}
private void resolveMethods(BlueprintRegistry blueprintRegistry) {
for (Method method : introspector.methodsWith(AnnotationHelper.injectDependencyAnnotations)) {
Property prop = Property.create(blueprintRegistry, method);
if (prop != null) {
properties.add(prop);
}
}
}
private void resolveFields(BlueprintRegistry matcher) {
for (Field field : introspector.fieldsWith(AnnotationHelper.injectDependencyAnnotations)) {
Property prop = Property.create(matcher, field);
if (prop != null) {
properties.add(prop);
}
}
}
protected void resolveArguments(BlueprintRegistry matcher) {
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor constructor : declaredConstructors) {
if (declaredConstructors.length == 1 || shouldInject(constructor)) {
resolveArguments(matcher, constructor.getParameterTypes(), constructor.getParameterAnnotations());
break;
}
}
}
private boolean shouldInject(AnnotatedElement annotatedElement) {
for (InjectLikeHandler injectLikeHandler : Handlers.BEAN_INJECT_LIKE_HANDLERS) {
if (annotatedElement.getAnnotation(injectLikeHandler.getAnnotation()) != null) {
return true;
}
}
return false;
}
void resolveArguments(BlueprintRegistry blueprintRegistry, Class[] parameterTypes, Annotation[][] parameterAnnotations) {
for (int i = 0; i < parameterTypes.length; ++i) {
constructorArguments.add(new Argument(blueprintRegistry, parameterTypes[i], parameterAnnotations[i]));
}
}
private void writeProperties(XMLStreamWriter writer) throws XMLStreamException {
for (Property property : properties) {
property.write(writer);
}
}
private void writeArguments(XMLStreamWriter writer) throws XMLStreamException {
for (Argument argument : constructorArguments) {
argument.write(writer);
}
}
private boolean needFieldInjection() {
for (Property property : properties) {
if (property.isField) {
return true;
}
}
return false;
}
@Override
public void addAttribute(String key, String value) {
attributes.put(key, value);
}
@Override
public void addBeanContentWriter(String id, XmlWriter blueprintWriter) {
beanContentWriters.put(id, blueprintWriter);
}
private void writeCustomContent(XMLStreamWriter writer) throws XMLStreamException {
List<String> customWriterKeys = new ArrayList<>(beanContentWriters.keySet());
Collections.sort(customWriterKeys);
for (String customWriterKey : customWriterKeys) {
beanContentWriters.get(customWriterKey).write(writer);
}
}
private void writeAttributes(XMLStreamWriter writer) throws XMLStreamException {
for (Map.Entry<String, String> entry : attributes.entrySet()) {
if ("scope".equals(entry.getKey()) && "singleton".equals(entry.getValue())) {
continue;
}
writer.writeAttribute(entry.getKey(), entry.getValue());
}
}
@Override
public void write(XMLStreamWriter writer) throws XMLStreamException {
writeBeanStart(writer);
writeCustomContent(writer);
writeArguments(writer);
writeProperties(writer);
writer.writeEndElement();
}
private void writeBeanStart(XMLStreamWriter writer) throws XMLStreamException {
writer.writeStartElement("bean");
writer.writeAttribute("id", id);
writer.writeAttribute("class", clazz.getName());
if (needFieldInjection()) {
writer.writeNamespace("ext", NS_EXT);
writer.writeAttribute("ext", NS_EXT, "field-injection", "true");
}
writeAttributes(writer);
}
BeanRef toBeanRef() {
return new BeanRef(clazz, id, clazz.getAnnotations());
}
@Override
public int compareTo(Bean o) {
return id.compareTo(o.id);
}
}