/** * 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); } }