You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pd...@apache.org on 2016/09/18 22:04:20 UTC
svn commit: r1761367 - in /felix/trunk/dependencymanager:
org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/
org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/
or...
Author: pderop
Date: Sun Sep 18 22:04:20 2016
New Revision: 1761367
URL: http://svn.apache.org/viewvc?rev=1761367&view=rev
Log:
FELIX-4384: committed improvement for the support of annotation inheritance.
Added:
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/InheritedAnnotations.java
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/InheritedAnnotationsTest.java
Modified:
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java
Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java?rev=1761367&r1=1761366&r2=1761367&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java Sun Sep 18 22:04:20 2016
@@ -24,7 +24,9 @@ import java.util.Arrays;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
+import java.util.function.Supplier;
import org.apache.felix.dm.annotation.api.AdapterService;
import org.apache.felix.dm.annotation.api.AspectService;
@@ -91,14 +93,13 @@ public class AnnotationCollector extends
private final static String A_UNREGISTERED = Unregistered.class.getName();
private Logger m_logger;
- private String m_className;
private String[] m_interfaces;
private boolean m_isField;
private String m_field;
private String m_method;
private String m_descriptor;
- private Set<String> m_dependencyNames = new HashSet<String>();
- private List<EntryWriter> m_writers = new ArrayList<EntryWriter>();
+ private final Set<String> m_dependencyNames = new HashSet<String>();
+ private final List<EntryWriter> m_writers = new ArrayList<EntryWriter>();
private MetaType m_metaType;
private String m_startMethod;
private String m_stopMethod;
@@ -107,14 +108,34 @@ public class AnnotationCollector extends
private String m_compositionMethod;
private String m_starter;
private String m_stopper;
- private Set<String> m_importService = new HashSet<String>();
- private Set<String> m_exportService = new HashSet<String>();
+ private final Set<String> m_importService = new HashSet<String>();
+ private final Set<String> m_exportService = new HashSet<String>();
private String m_bundleContextField;
private String m_dependencyManagerField;
private String m_componentField;
private String m_registeredMethod;
private String m_unregisteredMethod;
+ private TypeRef m_superClass;
+ private boolean m_baseClass = true;
+
+ /**
+ * Name of the class annotated with @Component (or other kind of components, like aspect, adapters).
+ */
+ private String m_componentClassName;
+ /*
+ * Name of class currently being parsed (the component class name at first, then the inherited classes).
+ * See DescriptorGenerator class, which first calls parseClassFileWithCollector method with the component class, then it calls
+ * again the parseClassFileWithCollector method with all inherited component super classes.
+ */
+ private String m_currentClassName;
+
+ /**
+ * Contains all bind methods annotated with a dependency.
+ * Each entry has the format: "methodName/method signature".
+ */
+ private final Set<String> m_bindMethods = new HashSet<>();
+
/**
* When more than one @Property annotation are declared on a component type (outside of the @Component annotation), then a @Repeatable
* annotation is used as the container for the @Property annotations. When such annotation is found, it is stored in this attribute, which
@@ -143,6 +164,14 @@ public class AnnotationCollector extends
m_logger = reporter;
m_metaType = metaType;
}
+
+ /**
+ * Indicates that we are parsing a superclass of a given component class.
+ */
+ public void baseClass(boolean baseClass) {
+ m_logger.debug("baseClass:%b", baseClass);
+ m_baseClass = baseClass;
+ }
/**
* Parses the name of the class.
@@ -152,8 +181,12 @@ public class AnnotationCollector extends
@Override
public void classBegin(int access, TypeRef name)
{
- m_className = name.getFQN();
- m_logger.debug("class name: %s", m_className);
+ if (m_baseClass)
+ {
+ m_componentClassName = name.getFQN();
+ m_logger.debug("Parsing class: %s", m_componentClassName);
+ }
+ m_currentClassName = name.getFQN();
}
/**
@@ -162,17 +195,20 @@ public class AnnotationCollector extends
@Override
public void implementsInterfaces(TypeRef[] interfaces)
{
- List<String> result = new ArrayList<String>();
- for (int i = 0; i < interfaces.length; i++)
- {
- if (!interfaces[i].getBinary().equals("scala/ScalaObject"))
- {
- result.add(interfaces[i].getFQN());
- }
- }
+ if (m_baseClass)
+ {
+ List<String> result = new ArrayList<String>();
+ for (int i = 0; i < interfaces.length; i++)
+ {
+ if (!interfaces[i].getBinary().equals("scala/ScalaObject"))
+ {
+ result.add(interfaces[i].getFQN());
+ }
+ }
- m_interfaces = result.toArray(new String[result.size()]);
- m_logger.debug("implements: %s", Arrays.toString(m_interfaces));
+ m_interfaces = result.toArray(new String[result.size()]);
+ m_logger.debug("implements: %s", Arrays.toString(m_interfaces));
+ }
}
/**
@@ -199,6 +235,15 @@ public class AnnotationCollector extends
m_descriptor = field.getDescriptor().toString();
}
+ @Override
+ public void extendsClass(TypeRef name) {
+ m_superClass = name;
+ }
+
+ public TypeRef getSuperClass() {
+ return m_superClass;
+ }
+
/**
* An annotation has been parsed. Always invoked AFTER the "method"/"field"/"classBegin" callbacks.
*/
@@ -206,91 +251,110 @@ public class AnnotationCollector extends
public void annotation(Annotation annotation)
{
m_logger.debug("Parsing annotation: %s", annotation.getName());
-
- if (annotation.getName().getFQN().equals(A_COMPONENT))
+
+ // if we are parsing a superclass of a given component, then ignore any component annotations.
+ String name = annotation.getName().getFQN();
+ if (! m_baseClass) {
+ String simpleName = name.indexOf(".") != -1 ? name.substring(name.lastIndexOf(".")+1) : name;
+ Optional<EntryType> type = m_componentTypes.stream().filter(writer -> writer.name().equals(simpleName)).findFirst();
+ if (type.isPresent()) {
+ m_logger.debug("Ignoring annotation %s from super class %s of component class %s", name, m_currentClassName, m_componentClassName);
+ return;
+ }
+ }
+
+ if (name.equals(A_COMPONENT))
{
parseComponentAnnotation(annotation);
}
- else if (annotation.getName().getFQN().equals(A_ASPECT_SERVICE))
+ else if (name.equals(A_ASPECT_SERVICE))
{
parseAspectService(annotation);
}
- else if (annotation.getName().getFQN().equals(A_ADAPTER_SERVICE))
+ else if (name.equals(A_ADAPTER_SERVICE))
{
parseAdapterService(annotation);
}
- else if (annotation.getName().getFQN().equals(A_BUNDLE_ADAPTER_SERVICE))
+ else if (name.equals(A_BUNDLE_ADAPTER_SERVICE))
{
parseBundleAdapterService(annotation);
}
- else if (annotation.getName().getFQN().equals(A_RESOURCE_ADAPTER_SERVICE))
+ else if (name.equals(A_RESOURCE_ADAPTER_SERVICE))
{
parseResourceAdapterService(annotation);
}
- else if (annotation.getName().getFQN().equals(A_FACTORYCONFIG_ADAPTER_SERVICE))
+ else if (name.equals(A_FACTORYCONFIG_ADAPTER_SERVICE))
{
parseFactoryConfigurationAdapterService(annotation);
}
- else if (annotation.getName().getFQN().equals(A_INIT))
+ else if (name.equals(A_INIT))
{
+ checkAlreadyDeclaredSingleAnnot(() -> m_initMethod, "@Init");
m_initMethod = m_method;
}
- else if (annotation.getName().getFQN().equals(A_START))
+ else if (name.equals(A_START))
{
+ checkAlreadyDeclaredSingleAnnot(() -> m_startMethod, "@Start");
m_startMethod = m_method;
}
- else if (annotation.getName().getFQN().equals(A_REGISTERED))
+ else if (name.equals(A_REGISTERED))
{
+ checkAlreadyDeclaredSingleAnnot(() -> m_registeredMethod, "@Registered");
m_registeredMethod = m_method;
}
- else if (annotation.getName().getFQN().equals(A_STOP))
+ else if (name.equals(A_STOP))
{
+ checkAlreadyDeclaredSingleAnnot(() -> m_stopMethod, "@Stop");
m_stopMethod = m_method;
}
- else if (annotation.getName().getFQN().equals(A_UNREGISTERED))
+ else if (name.equals(A_UNREGISTERED))
{
+ checkAlreadyDeclaredSingleAnnot(() -> m_unregisteredMethod, "@Unregistered");
m_unregisteredMethod = m_method;
}
- else if (annotation.getName().getFQN().equals(A_DESTROY))
+ else if (name.equals(A_DESTROY))
{
+ checkAlreadyDeclaredSingleAnnot(() -> m_destroyMethod, "@Destroy");
m_destroyMethod = m_method;
}
- else if (annotation.getName().getFQN().equals(A_COMPOSITION))
+ else if (name.equals(A_COMPOSITION))
{
+ checkAlreadyDeclaredSingleAnnot(() -> m_compositionMethod, "@Composition");
Patterns.parseMethod(m_method, m_descriptor, Patterns.COMPOSITION);
m_compositionMethod = m_method;
- } else if (annotation.getName().getFQN().equals(A_LIFCLE_CTRL))
+ }
+ else if (name.equals(A_LIFCLE_CTRL))
{
parseLifecycleAnnotation(annotation);
}
- else if (annotation.getName().getFQN().equals(A_SERVICE_DEP))
+ else if (name.equals(A_SERVICE_DEP))
{
parseServiceDependencyAnnotation(annotation);
}
- else if (annotation.getName().getFQN().equals(A_CONFIGURATION_DEPENDENCY))
+ else if (name.equals(A_CONFIGURATION_DEPENDENCY))
{
parseConfigurationDependencyAnnotation(annotation);
}
- else if (annotation.getName().getFQN().equals(A_BUNDLE_DEPENDENCY))
+ else if (name.equals(A_BUNDLE_DEPENDENCY))
{
parseBundleDependencyAnnotation(annotation);
}
- else if (annotation.getName().getFQN().equals(A_RESOURCE_DEPENDENCY))
+ else if (name.equals(A_RESOURCE_DEPENDENCY))
{
parseRersourceDependencyAnnotation(annotation);
}
- else if (annotation.getName().getFQN().equals(A_INJECT))
+ else if (name.equals(A_INJECT))
{
parseInject(annotation);
}
- else if (annotation.getName().getFQN().equals(A_REPEATABLE_PROPERTY))
+ else if (name.equals(A_REPEATABLE_PROPERTY))
{
parseRepeatableProperties(annotation);
}
else if (annotation.getName().getFQN().equals(A_PROPERTY))
{
m_singleProperty = annotation;
- }
+ }
}
/**
@@ -299,21 +363,37 @@ public class AnnotationCollector extends
*/
public boolean finish()
{
- if (m_writers.size() == 0)
- {
- m_logger.info("No components found for class " + m_className);
- return false;
- }
+ m_logger.info("finish %s", m_componentClassName);
- // We must have at least a valid component annotation type (component, aspect, or adapters)
-
- EntryWriter componentWriter = m_writers.stream()
+ // check if we have a component (or adapter) annotation.
+ Optional<EntryWriter> componentWriter = m_writers.stream()
.filter(writer -> m_componentTypes.indexOf(writer.getEntryType()) != -1)
- .findFirst()
- .orElseThrow(() -> new IllegalStateException(": the class " + m_className + " must be annotated with either one of the following types: " + m_componentTypes));
+ .findFirst();
- // Add any repeated @Property annotations to the component (or to the aspect, or adapter).
-
+ if (! componentWriter.isPresent() || m_writers.size() == 0) {
+ m_logger.info("No components found for class " + m_componentClassName);
+ return false;
+ }
+
+ finishComponentAnnotation(componentWriter.get());
+
+ // log all meta data for component annotations, dependencies, etc ...
+ StringBuilder sb = new StringBuilder();
+ sb.append("Parsed annotation for class ");
+ sb.append(m_componentClassName);
+ for (int i = m_writers.size() - 1; i >= 0; i--)
+ {
+ sb.append("\n\t").append(m_writers.get(i).toString());
+ }
+ m_logger.info(sb.toString());
+ return true;
+ }
+
+ private void finishComponentAnnotation(EntryWriter componentWriter) {
+ // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
+ addCommonServiceParams(componentWriter);
+
+ // Add any repeated @Property annotations to the component (or to the aspect, or adapter).
if (m_repeatableProperty != null)
{
Object[] properties = m_repeatableProperty.get("value");
@@ -329,30 +409,26 @@ public class AnnotationCollector extends
if (m_singleProperty != null) {
parseProperty(m_singleProperty, componentWriter);
}
-
- StringBuilder sb = new StringBuilder();
- sb.append("Parsed annotation for class ");
- sb.append(m_className);
- for (int i = m_writers.size() - 1; i >= 0; i--)
- {
- sb.append("\n\t").append(m_writers.get(i).toString());
- }
- m_logger.info(sb.toString());
- return true;
}
/**
* Writes the generated component descriptor in the given print writer.
- * The first line must be the service (@Service or AspectService).
+ * The first line must be the component descriptor (@Component or AspectService, etc ..).
* @param pw the writer where the component descriptor will be written.
*/
public void writeTo(PrintWriter pw)
{
- // The last element our our m_writers list contains either the Service, or the AspectService descriptor.
- for (int i = m_writers.size() - 1; i >= 0; i--)
- {
- pw.println(m_writers.get(i).toString());
- }
+ // write first the component descriptor (@Component, @AspectService, ...)
+ EntryWriter componentWriter = m_writers.stream()
+ .filter(writer -> m_componentTypes.indexOf(writer.getEntryType()) != -1)
+ .findFirst()
+ .orElseThrow(() -> new IllegalStateException("Component type not found while scanning class " + m_componentClassName));
+ pw.println(componentWriter);
+
+ // and write other component descriptors (dependencies, and other annotations)
+ m_writers.stream()
+ .filter(writer -> m_componentTypes.indexOf(writer.getEntryType()) == -1)
+ .forEach(dependency -> pw.println(dependency.toString()));
}
/**
@@ -384,16 +460,25 @@ public class AnnotationCollector extends
m_repeatableProperty = repeatedProperties;
}
+ /**
+ * Checks double declaration of an annotation, which normally must be declared only one time.
+ * @param field the field supplier (if not null, means the annotation is already declared)
+ * @param annot the annotation name.
+ * @throws IllegalStateException if annotation is already declared.
+ */
+ private void checkAlreadyDeclaredSingleAnnot(Supplier<Object> field, String annot) {
+ if (field.get() != null) {
+ throw new IllegalStateException("detected multiple " + annot + " annotation from class " + m_currentClassName + " (on from child classes)");
+ }
+ }
+
private void parseComponentAnnotation(Annotation annotation)
{
EntryWriter writer = new EntryWriter(EntryType.Component);
m_writers.add(writer);
- // Register previously parsed annotations common to services (Init/Start/...)
- addCommonServiceParams(writer);
-
// impl attribute
- writer.put(EntryParam.impl, m_className);
+ writer.put(EntryParam.impl, m_componentClassName);
// properties attribute
parseProperties(annotation, writer);
@@ -484,7 +569,7 @@ public class AnnotationCollector extends
if (m_starter == null)
{
throw new IllegalArgumentException("Can't use a @LifecycleController annotation for stopping a service without declaring a " +
- "@LifecycleController that starts the component in class " + m_className);
+ "@LifecycleController that starts the component in class " + m_currentClassName);
}
}
@@ -505,6 +590,17 @@ public class AnnotationCollector extends
}
/**
+ * Check if a dependency is already declared in another same bindMethod (or class field) on another child class.
+ */
+ private void checkDependencyAlreadyDeclaredInChild(Annotation annotation, String methodOrField, boolean method) {
+ if (! m_baseClass && m_bindMethods.contains(methodOrField + "/" + m_descriptor)) {
+ throw new IllegalStateException("Annotation " + annotation.getName().getShortName()
+ + " declared on " + m_currentClassName + "." + methodOrField + (method ? " method" : " field") + " is already declared in child classe(s)");
+ }
+ m_bindMethods.add(methodOrField + "/" + m_descriptor);
+ }
+
+ /**
* Parses a ServiceDependency Annotation.
* @param annotation the ServiceDependency Annotation.
*/
@@ -519,10 +615,13 @@ public class AnnotationCollector extends
{
if (m_isField)
{
+ checkDependencyAlreadyDeclaredInChild(annotation, m_field, false);
service = Patterns.parseClass(m_descriptor, Patterns.CLASS, 1);
}
else
{
+ // if we are parsing some inherited classes, detect if the bind method is already declared in child classes
+ checkDependencyAlreadyDeclaredInChild(annotation, m_method, true);
// parse "bind(Component, ServiceReference, Service)" signature
service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS1, 3, false);
@@ -597,7 +696,7 @@ public class AnnotationCollector extends
Long t = (Long) annotation.get(EntryParam.timeout.toString());
if (t != null && t.longValue() < -1)
{
- throw new IllegalArgumentException("Invalid timeout value " + t + " in ServiceDependency annotation from class " + m_className);
+ throw new IllegalArgumentException("Invalid timeout value " + t + " in ServiceDependency annotation from class " + m_currentClassName);
}
// required attribute (not valid if parsing a temporal service dependency)
@@ -648,6 +747,8 @@ public class AnnotationCollector extends
*/
private void parseConfigurationDependencyAnnotation(Annotation annotation)
{
+ checkDependencyAlreadyDeclaredInChild(annotation, m_method, true);
+
EntryWriter writer = new EntryWriter(EntryType.ConfigurationDependency);
m_writers.add(writer);
@@ -699,7 +800,7 @@ public class AnnotationCollector extends
}
else
{
- pid = m_className;
+ pid = m_componentClassName;
}
}
@@ -730,9 +831,6 @@ public class AnnotationCollector extends
EntryWriter writer = new EntryWriter(EntryType.AspectService);
m_writers.add(writer);
- // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
- addCommonServiceParams(writer);
-
// Parse service filter
String filter = annotation.get(EntryParam.filter.toString());
if (filter != null)
@@ -746,7 +844,7 @@ public class AnnotationCollector extends
writer.put(EntryParam.ranking, ranking.toString());
// Generate Aspect Implementation
- writer.put(EntryParam.impl, m_className);
+ writer.put(EntryParam.impl, m_componentClassName);
// Parse Aspect properties.
parseProperties(annotation, writer);
@@ -761,13 +859,13 @@ public class AnnotationCollector extends
if (m_interfaces == null)
{
throw new IllegalStateException("Invalid AspectService annotation: " +
- "the service attribute has not been set and the class " + m_className
+ "the service attribute has not been set and the class " + m_componentClassName
+ " does not implement any interfaces");
}
if (m_interfaces.length != 1)
{
throw new IllegalStateException("Invalid AspectService annotation: " +
- "the service attribute has not been set and the class " + m_className
+ "the service attribute has not been set and the class " + m_componentClassName
+ " implements more than one interface");
}
@@ -793,7 +891,7 @@ public class AnnotationCollector extends
// "field" and "added/changed/removed/swap" attributes can't be mixed
if (field != null && (added != null || changed != null || removed != null || swap != null))
{
- throw new IllegalStateException("Annotation " + annotation + "can't applied on " + m_className
+ throw new IllegalStateException("Annotation " + annotation + "can't applied on " + m_componentClassName
+ " can't mix \"field\" attribute with \"added/changed/removed\" attributes");
}
@@ -816,11 +914,8 @@ public class AnnotationCollector extends
EntryWriter writer = new EntryWriter(EntryType.AdapterService);
m_writers.add(writer);
- // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
- addCommonServiceParams(writer);
-
// Generate Adapter Implementation
- writer.put(EntryParam.impl, m_className);
+ writer.put(EntryParam.impl, m_componentClassName);
// Parse adaptee filter
String adapteeFilter = annotation.get(EntryParam.adapteeFilter.toString());
@@ -862,11 +957,8 @@ public class AnnotationCollector extends
EntryWriter writer = new EntryWriter(EntryType.BundleAdapterService);
m_writers.add(writer);
- // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
- addCommonServiceParams(writer);
-
// Generate Adapter Implementation
- writer.put(EntryParam.impl, m_className);
+ writer.put(EntryParam.impl, m_componentClassName);
// Parse bundle filter
String filter = annotation.get(EntryParam.filter.toString());
@@ -905,11 +997,8 @@ public class AnnotationCollector extends
EntryWriter writer = new EntryWriter(EntryType.ResourceAdapterService);
m_writers.add(writer);
- // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
- addCommonServiceParams(writer);
-
// Generate Adapter Implementation
- writer.put(EntryParam.impl, m_className);
+ writer.put(EntryParam.impl, m_componentClassName);
// Parse resource filter
String filter = annotation.get(EntryParam.filter.toString());
@@ -944,11 +1033,8 @@ public class AnnotationCollector extends
EntryWriter writer = new EntryWriter(EntryType.FactoryConfigurationAdapterService);
m_writers.add(writer);
- // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
- addCommonServiceParams(writer);
-
// Generate Adapter Implementation
- writer.put(EntryParam.impl, m_className);
+ writer.put(EntryParam.impl, m_componentClassName);
// factory pid attribute (can be specified using the factoryPid attribute, or using the factoryPidClass attribute)
String factoryPidClass = parseClassAttrValue(annotation.get(EntryParam.factoryPidClass.toString()));
@@ -964,7 +1050,7 @@ public class AnnotationCollector extends
factoryPid = get(annotation, EntryParam.factoryPid.toString(), factoryPidClass);
if (factoryPid == null) {
- factoryPid = configType != null ? configType : m_className;
+ factoryPid = configType != null ? configType : m_componentClassName;
}
writer.put(EntryParam.factoryPid, factoryPid);
@@ -993,6 +1079,8 @@ public class AnnotationCollector extends
private void parseBundleDependencyAnnotation(Annotation annotation)
{
+ checkDependencyAlreadyDeclaredInChild(annotation, m_method, true);
+
EntryWriter writer = new EntryWriter(EntryType.BundleDependency);
m_writers.add(writer);
@@ -1014,6 +1102,8 @@ public class AnnotationCollector extends
private void parseRersourceDependencyAnnotation(Annotation annotation)
{
+ checkDependencyAlreadyDeclaredInChild(annotation, ! m_isField ? m_method : m_field, ! m_isField);
+
EntryWriter writer = new EntryWriter(EntryType.ResourceDependency);
m_writers.add(writer);
@@ -1050,7 +1140,7 @@ public class AnnotationCollector extends
{
if(! m_dependencyNames.add(name))
{
- throw new IllegalArgumentException("Duplicate dependency name " + name + " in Dependency " + annotation + " from class " + m_className);
+ throw new IllegalArgumentException("Duplicate dependency name " + name + " in Dependency " + annotation + " from class " + m_currentClassName);
}
writer.put(EntryParam.name, name);
}
@@ -1063,13 +1153,13 @@ public class AnnotationCollector extends
{
if (m_starter != null) {
throw new IllegalStateException("Lifecycle annotation already defined on field " +
- m_starter + " in class " + m_className);
+ m_starter + " in class (or super class of) " + m_componentClassName);
}
m_starter = m_field;
} else {
if (m_stopper != null) {
throw new IllegalStateException("Lifecycle annotation already defined on field " +
- m_stopper + " in class " + m_className);
+ m_stopper + " in class (or super class of) " + m_componentClassName);
}
m_stopper = m_field;
}
@@ -1114,7 +1204,7 @@ public class AnnotationCollector extends
{
throw new IllegalArgumentException("invalid option labels/values specified for property "
+ id +
- " in PropertyMetadata annotation from class " + m_className);
+ " in PropertyMetadata annotation from class " + m_currentClassName);
}
if (optionValues != null)
@@ -1131,7 +1221,7 @@ public class AnnotationCollector extends
m_metaType.add(ocd);
MetaType.Designate designate = new MetaType.Designate(pid, factory);
m_metaType.add(designate);
- m_logger.info("Parsed MetaType Properties from class " + m_className);
+ m_logger.info("Parsed MetaType Properties from class " + m_componentClassName);
}
}
@@ -1198,7 +1288,7 @@ public class AnnotationCollector extends
{
// Theorically impossible
throw new IllegalArgumentException("Invalid Property type " + type
- + " from annotation " + property + " in class " + m_className);
+ + " from annotation " + property + " in class " + m_componentClassName);
}
Object[] values;
@@ -1248,7 +1338,7 @@ public class AnnotationCollector extends
else
{
throw new IllegalArgumentException(
- "Missing Property value from annotation " + property + " in class " + m_className);
+ "Missing Property value from annotation " + property + " in class " + m_componentClassName);
}
if (properties.length() > 0) {
@@ -1257,7 +1347,7 @@ public class AnnotationCollector extends
}
catch (JSONException e)
{
- throw new IllegalArgumentException("Unexpected exception while parsing Property from class " + m_className, e);
+ throw new IllegalArgumentException("Unexpected exception while parsing Property from class " + m_componentClassName, e);
}
}
@@ -1279,7 +1369,7 @@ public class AnnotationCollector extends
try {
Long.valueOf(value.toString());
} catch (NumberFormatException e) {
- throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_componentClassName
+ " does not contain a valid Long value (" + value.toString() + ")");
}
}
@@ -1288,7 +1378,7 @@ public class AnnotationCollector extends
try {
Double.valueOf(value.toString());
} catch (NumberFormatException e) {
- throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_componentClassName
+ " does not contain a valid Double value (" + value.toString() + ")");
}
}
@@ -1297,7 +1387,7 @@ public class AnnotationCollector extends
try {
Float.valueOf(value.toString());
} catch (NumberFormatException e) {
- throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_componentClassName
+ " does not contain a valid Float value (" + value.toString() + ")");
}
}
@@ -1306,7 +1396,7 @@ public class AnnotationCollector extends
try {
Integer.valueOf(value.toString());
} catch (NumberFormatException e) {
- throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_componentClassName
+ " does not contain a valid Integer value (" + value.toString() + ")");
}
}
@@ -1315,7 +1405,7 @@ public class AnnotationCollector extends
try {
Byte.valueOf(value.toString());
} catch (NumberFormatException e) {
- throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_componentClassName
+ " does not contain a valid Byte value (" + value.toString() + ")");
}
}
@@ -1333,7 +1423,7 @@ public class AnnotationCollector extends
if (values[i].toString().length() != 1)
{
throw new IllegalArgumentException("Property \"" + name + "\" in class "
- + m_className + " does not contain a valid Character value (" + values[i] + ")");
+ + m_componentClassName + " does not contain a valid Character value (" + values[i] + ")");
}
try
{
@@ -1342,7 +1432,7 @@ public class AnnotationCollector extends
catch (NumberFormatException e2)
{
throw new IllegalArgumentException("Property \"" + name + "\" in class "
- + m_className + " does not contain a valid Character value (" + values[i].toString()
+ + m_componentClassName + " does not contain a valid Character value (" + values[i].toString()
+ ")");
}
}
@@ -1350,7 +1440,7 @@ public class AnnotationCollector extends
} else if (type.equals(Boolean.class)) {
for (Object value : values) {
if (! value.toString().equalsIgnoreCase("false") && ! value.toString().equalsIgnoreCase("true")) {
- throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_componentClassName
+ " does not contain a valid Boolean value (" + value.toString() + ")");
}
}
@@ -1359,7 +1449,7 @@ public class AnnotationCollector extends
try {
Short.valueOf(value.toString());
} catch (NumberFormatException e) {
- throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
+ throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_componentClassName
+ " does not contain a valid Short value (" + value.toString() + ")");
}
}
@@ -1408,20 +1498,29 @@ public class AnnotationCollector extends
{
if (Patterns.BUNDLE_CONTEXT.matcher(m_descriptor).matches())
{
+ if (m_bundleContextField != null) {
+ throw new IllegalStateException("detected multiple @Inject annotation from class " + m_currentClassName + " (on from child classes)");
+ }
m_bundleContextField = m_field;
}
else if (Patterns.DEPENDENCY_MANAGER.matcher(m_descriptor).matches())
{
+ if (m_dependencyManagerField != null) {
+ throw new IllegalStateException("detected multiple @Inject annotation from class " + m_currentClassName + " (on from child classes)");
+ }
m_dependencyManagerField = m_field;
}
else if (Patterns.COMPONENT.matcher(m_descriptor).matches())
{
+ if (m_componentField != null) {
+ throw new IllegalStateException("detected multiple @Inject annotation from class " + m_currentClassName + " (on from child classes)");
+ }
m_componentField = m_field;
}
else
{
throw new IllegalArgumentException("@Inject annotation can't be applied on the field \"" + m_field
- + "\" in class " + m_className);
+ + "\" in class " + m_currentClassName);
}
}
@@ -1434,14 +1533,14 @@ public class AnnotationCollector extends
if (m_registeredMethod != null)
{
throw new IllegalStateException("@Registered annotation can't be used on a Component " +
- " which does not provide a service (class=" + m_className + ")");
+ " which does not provide a service (class=" + m_currentClassName + ")");
}
if (m_unregisteredMethod != null)
{
throw new IllegalStateException("@Unregistered annotation can't be used on a Component " +
- " which does not provide a service (class=" + m_className + ")");
+ " which does not provide a service (class=" + m_currentClassName + ")");
}
}
Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java?rev=1761367&r1=1761366&r2=1761367&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/AnnotationPlugin.java Sun Sep 18 22:04:20 2016
@@ -18,8 +18,6 @@
*/
package org.apache.felix.dm.annotation.plugin.bnd;
-import java.io.PrintWriter;
-import java.io.StringWriter;
import java.util.Map;
import java.util.Set;
@@ -115,7 +113,7 @@ public class AnnotationPlugin implements
}
catch (Throwable t) {
- m_logger.error(parse(t));
+ m_logger.error("error: " + t.toString(), t);
}
finally {
@@ -165,16 +163,4 @@ public class AnnotationPlugin implements
analyzer.setProperty(REQUIRE_CAPABILITY, sb.toString());
}
}
-
- /**
- * Parse an exception into a string.
- * @param e The exception to parse
- * @return the parsed exception
- */
- private static String parse(Throwable e) {
- StringWriter buffer = new StringWriter();
- PrintWriter pw = new PrintWriter(buffer);
- e.printStackTrace(pw);
- return (buffer.toString());
- }
}
Modified: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java?rev=1761367&r1=1761366&r2=1761367&view=diff
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java (original)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/plugin/bnd/DescriptorGenerator.java Sun Sep 18 22:04:20 2016
@@ -33,6 +33,7 @@ import aQute.bnd.osgi.Clazz;
import aQute.bnd.osgi.EmbeddedResource;
import aQute.bnd.osgi.Resource;
import aQute.bnd.osgi.Clazz.QUERY;
+import aQute.bnd.osgi.Descriptors.TypeRef;
/**
* This helper parses all classes which contain DM annotations, and generates the corresponding component descriptors.
@@ -103,9 +104,28 @@ public class DescriptorGenerator
for (Clazz c : expanded)
{
- // Let's parse all annotations from that class !
AnnotationCollector reader = new AnnotationCollector(m_logger, metaType);
+ reader.baseClass(true);
+ m_logger.debug("scanning class %s", c.getClassName());
c.parseClassFileWithCollector(reader);
+
+ // parse inherited annotations.
+ while (reader.getSuperClass() != null) {
+ Clazz superClazz = m_analyzer.findClass(reader.getSuperClass());
+ if (superClazz == null) {
+ m_logger.error("Can't find super class %s from %s", reader.getSuperClass(), c.getClassName());
+ break;
+ }
+ if (isObject(reader.getSuperClass()) || isScalaObject(reader.getSuperClass())) {
+ /* don't scan java.lang.Object or scala object ! */
+ break;
+ }
+
+ m_logger.debug("scanning super class %s for class %s", reader.getSuperClass(), c.getClassName());
+ reader.baseClass(false);
+ superClazz.parseClassFileWithCollector(reader);
+ }
+
if (reader.finish())
{
// And store the generated component descriptors in our resource list.
@@ -127,7 +147,7 @@ public class DescriptorGenerator
return annotationsFound;
}
- /**
+ /**
* Returns the path of the descriptor.
* @return the path of the generated descriptors.
*/
@@ -179,6 +199,20 @@ public class DescriptorGenerator
{
return m_exportService;
}
+
+ /**
+ * Tests if a given type is java.lang.Object
+ */
+ private boolean isObject(TypeRef typeRef) {
+ return (typeRef.isJava());
+ }
+
+ /**
+ * Tests if a given type is scala object.
+ */
+ private boolean isScalaObject(TypeRef typeRef) {
+ return (typeRef.getBinary().equals("scala/ScalaObject"));
+ }
/**
* Creates a bnd resource that contains the generated dm descriptor.
Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/InheritedAnnotations.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/InheritedAnnotations.java?rev=1761367&view=auto
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/InheritedAnnotations.java (added)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/components/InheritedAnnotations.java Sun Sep 18 22:04:20 2016
@@ -0,0 +1,82 @@
+/*
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.felix.dm.runtime.itest.components;
+
+import org.apache.felix.dm.annotation.api.Component;
+import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.ServiceDependency;
+import org.apache.felix.dm.annotation.api.Start;
+import org.apache.felix.dm.itest.util.Ensure;
+import org.junit.Assert;
+
+public class InheritedAnnotations
+{
+ public final static String ENSURE = "InheritedAnnotations";
+
+ @Component(provides=Service1.class)
+ public static class Service1 {
+ @ServiceDependency(filter="(name=" + ENSURE + ")")
+ Ensure m_ensure;
+
+ }
+
+ @Component(provides=Service2.class)
+ public static class Service2 {
+ @ServiceDependency(filter="(name=" + ENSURE + ")")
+ Ensure m_ensure;
+ }
+
+ public static class Base {
+ @ServiceDependency(filter="(name=" + ENSURE + ")")
+ protected Ensure m_ensure;
+
+ protected Service1 m_s1;
+
+ @ServiceDependency
+ void bind(Service1 s1) {
+ m_s1 = s1;
+ }
+
+ @Init
+ void init() {
+ Assert.assertNotNull(m_ensure);
+ Assert.assertNotNull(m_s1);
+ m_ensure.step(1);
+ }
+ }
+
+ @Component
+ public static class Child extends Base {
+ private Service2 m_s2;
+
+ @ServiceDependency
+ void bind(Service2 s2) {
+ m_s2 = s2;
+ }
+
+ @Start
+ void start() {
+ Assert.assertNotNull(m_ensure);
+ Assert.assertNotNull(m_s1);
+ Assert.assertNotNull(m_s2);
+ m_ensure.step(2);
+ }
+ }
+
+}
Added: felix/trunk/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/InheritedAnnotationsTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/InheritedAnnotationsTest.java?rev=1761367&view=auto
==============================================================================
--- felix/trunk/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/InheritedAnnotationsTest.java (added)
+++ felix/trunk/dependencymanager/org.apache.felix.dependencymanager.runtime.itest/src/org/apache/felix/dm/runtime/itest/tests/InheritedAnnotationsTest.java Sun Sep 18 22:04:20 2016
@@ -0,0 +1,41 @@
+/*
+* 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
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* 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.felix.dm.runtime.itest.tests;
+
+import org.apache.felix.dm.itest.util.Ensure;
+import org.apache.felix.dm.itest.util.TestBase;
+import org.apache.felix.dm.runtime.itest.components.InheritedAnnotations;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Use case: Verify if a component may inherit from annotations defined in inherited classes.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class InheritedAnnotationsTest extends TestBase {
+
+ public void testInheritedAnnotation() throws Throwable {
+ Ensure e = new Ensure();
+ ServiceRegistration sr = register(e, InheritedAnnotations.ENSURE);
+ e.ensure();
+ e.waitForStep(2, 5000);
+ sr.unregister();
+ }
+
+}