blob: 403a58ee8367b5d2c0ed909a7c5642cd03460d5f [file] [log] [blame]
Pierre De Rop3a00a212015-03-01 09:27:46 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.felix.dm.annotation.plugin.bnd;
20
21import java.io.PrintWriter;
22import java.util.ArrayList;
23import java.util.Arrays;
24import java.util.HashSet;
25import java.util.List;
26import java.util.Set;
27
28import org.apache.felix.dm.annotation.api.AdapterService;
29import org.apache.felix.dm.annotation.api.AspectService;
30import org.apache.felix.dm.annotation.api.BundleAdapterService;
31import org.apache.felix.dm.annotation.api.BundleDependency;
32import org.apache.felix.dm.annotation.api.Component;
33import org.apache.felix.dm.annotation.api.Composition;
34import org.apache.felix.dm.annotation.api.ConfigurationDependency;
35import org.apache.felix.dm.annotation.api.Destroy;
36import org.apache.felix.dm.annotation.api.FactoryConfigurationAdapterService;
37import org.apache.felix.dm.annotation.api.Init;
38import org.apache.felix.dm.annotation.api.Inject;
39import org.apache.felix.dm.annotation.api.LifecycleController;
40import org.apache.felix.dm.annotation.api.Registered;
41import org.apache.felix.dm.annotation.api.ResourceAdapterService;
42import org.apache.felix.dm.annotation.api.ResourceDependency;
43import org.apache.felix.dm.annotation.api.ServiceDependency;
44import org.apache.felix.dm.annotation.api.Start;
45import org.apache.felix.dm.annotation.api.Stop;
46import org.apache.felix.dm.annotation.api.Unregistered;
47import org.json.JSONArray;
48import org.json.JSONException;
49import org.json.JSONObject;
50import org.osgi.framework.Bundle;
51
52import aQute.bnd.osgi.Annotation;
53import aQute.bnd.osgi.ClassDataCollector;
54import aQute.bnd.osgi.Clazz;
55import aQute.bnd.osgi.Descriptors.TypeRef;
56import aQute.bnd.osgi.Verifier;
57
58/**
59 * This is the scanner which does all the annotation parsing on a given class.
60 * To start the parsing, just invoke the parseClassFileWithCollector and finish methods.
61 * Once parsed, the corresponding component descriptors can be built using the "writeTo" method.
62 *
63 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
64 */
65public class AnnotationCollector extends ClassDataCollector
66{
67 private final static String A_INIT = Init.class.getName();
68 private final static String A_START = Start.class.getName();
69 private final static String A_STOP = Stop.class.getName();
70 private final static String A_DESTROY = Destroy.class.getName();
71 private final static String A_COMPOSITION = Composition.class.getName();
72 private final static String A_LIFCLE_CTRL = LifecycleController.class.getName();
73
74 private final static String A_COMPONENT = Component.class.getName();
75 private final static String A_SERVICE_DEP = ServiceDependency.class.getName();
76 private final static String A_CONFIGURATION_DEPENDENCY = ConfigurationDependency.class.getName();
77 private final static String A_BUNDLE_DEPENDENCY = BundleDependency.class.getName();
78 private final static String A_RESOURCE_DEPENDENCY = ResourceDependency.class.getName();
79 private final static String A_ASPECT_SERVICE = AspectService.class.getName();
80 private final static String A_ADAPTER_SERVICE = AdapterService.class.getName();
81 private final static String A_BUNDLE_ADAPTER_SERVICE = BundleAdapterService.class.getName();
82 private final static String A_RESOURCE_ADAPTER_SERVICE = ResourceAdapterService.class.getName();
83 private final static String A_FACTORYCONFIG_ADAPTER_SERVICE = FactoryConfigurationAdapterService.class.getName();
84 private final static String A_INJECT = Inject.class.getName();
85 private final static String A_REGISTERED = Registered.class.getName();
86 private final static String A_UNREGISTERED = Unregistered.class.getName();
87
88 private Logger m_logger;
89 private String m_className;
90 private String[] m_interfaces;
91 private boolean m_isField;
92 private String m_field;
93 private String m_method;
94 private String m_descriptor;
95 private Set<String> m_dependencyNames = new HashSet<String>();
96 private List<EntryWriter> m_writers = new ArrayList<EntryWriter>(); // Last elem is either Service or AspectService
97 private MetaType m_metaType;
98 private String m_startMethod;
99 private String m_stopMethod;
100 private String m_initMethod;
101 private String m_destroyMethod;
102 private String m_compositionMethod;
103 private String m_starter;
104 private String m_stopper;
105 private Set<String> m_importService = new HashSet<String>();
106 private Set<String> m_exportService = new HashSet<String>();
107 private String m_bundleContextField;
108 private String m_dependencyManagerField;
109 private String m_componentField;
110 private String m_registeredMethod;
111 private String m_unregisteredMethod;
112
113 /**
114 * This class represents a DependencyManager component descriptor entry.
115 * (Service, a ServiceDependency ... see EntryType enum).
116 */
117
118 /**
119 * Makes a new Collector for parsing a given class.
120 * @param reporter the object used to report logs.
121 */
122 public AnnotationCollector(Logger reporter, MetaType metaType)
123 {
124 m_logger = reporter;
125 m_metaType = metaType;
126 }
127
128 /**
129 * Parses the name of the class.
130 * @param access the class access
131 * @param name the class name (package are "/" separated).
132 */
133 @Override
134 public void classBegin(int access, TypeRef name)
135 {
136 m_className = name.getFQN();
137 m_logger.debug("class name: %s", m_className);
138 }
139
140 /**
141 * Parses the implemented interfaces ("/" separated).
142 */
143 @Override
144 public void implementsInterfaces(TypeRef[] interfaces)
145 {
146 List<String> result = new ArrayList<String>();
147 for (int i = 0; i < interfaces.length; i++)
148 {
149 if (!interfaces[i].getBinary().equals("scala/ScalaObject"))
150 {
151 result.add(interfaces[i].getFQN());
152 }
153 }
154
155 m_interfaces = result.toArray(new String[result.size()]);
156 m_logger.debug("implements: %s", Arrays.toString(m_interfaces));
157 }
158
159 /**
160 * Parses a method. Always invoked BEFORE eventual method annotation.
161 */
162 @Override
163 public void method(Clazz.MethodDef method)
164 {
165 m_logger.debug("Parsed method %s, descriptor=%s", method.getName(), method.getDescriptor());
166 m_isField = false;
167 m_method = method.getName();
168 m_descriptor = method.getDescriptor().toString();
169 }
170
171 /**
172 * Parses a field. Always invoked BEFORE eventual field annotation
173 */
174 @Override
175 public void field(Clazz.FieldDef field)
176 {
177 m_logger.debug("Parsed field %s, descriptor=%s", field.getName(), field.getDescriptor());
178 m_isField = true;
179 m_field = field.getName();
180 m_descriptor = field.getDescriptor().toString();
181 }
182
183 /**
184 * An annotation has been parsed. Always invoked AFTER the "method"/"field"/"classBegin" callbacks.
185 */
186 @Override
187 public void annotation(Annotation annotation)
188 {
189 m_logger.debug("Parsing annotation: %s", annotation.getName());
190
191 if (annotation.getName().getFQN().equals(A_COMPONENT))
192 {
193 parseComponentAnnotation(annotation);
194 }
195 else if (annotation.getName().getFQN().equals(A_ASPECT_SERVICE))
196 {
197 parseAspectService(annotation);
198 }
199 else if (annotation.getName().getFQN().equals(A_ADAPTER_SERVICE))
200 {
201 parseAdapterService(annotation);
202 }
203 else if (annotation.getName().getFQN().equals(A_BUNDLE_ADAPTER_SERVICE))
204 {
205 parseBundleAdapterService(annotation);
206 }
207 else if (annotation.getName().getFQN().equals(A_RESOURCE_ADAPTER_SERVICE))
208 {
209 parseResourceAdapterService(annotation);
210 }
211 else if (annotation.getName().getFQN().equals(A_FACTORYCONFIG_ADAPTER_SERVICE))
212 {
213 parseFactoryConfigurationAdapterService(annotation);
214 }
215 else if (annotation.getName().getFQN().equals(A_INIT))
216 {
217 m_initMethod = m_method;
218 }
219 else if (annotation.getName().getFQN().equals(A_START))
220 {
221 m_startMethod = m_method;
222 }
223 else if (annotation.getName().getFQN().equals(A_REGISTERED))
224 {
225 m_registeredMethod = m_method;
226 }
227 else if (annotation.getName().getFQN().equals(A_STOP))
228 {
229 m_stopMethod = m_method;
230 }
231 else if (annotation.getName().getFQN().equals(A_UNREGISTERED))
232 {
233 m_unregisteredMethod = m_method;
234 }
235 else if (annotation.getName().getFQN().equals(A_DESTROY))
236 {
237 m_destroyMethod = m_method;
238 }
239 else if (annotation.getName().getFQN().equals(A_COMPOSITION))
240 {
241 Patterns.parseMethod(m_method, m_descriptor, Patterns.COMPOSITION);
242 m_compositionMethod = m_method;
243 } else if (annotation.getName().getFQN().equals(A_LIFCLE_CTRL))
244 {
245 parseLifecycleAnnotation(annotation);
246 }
247 else if (annotation.getName().getFQN().equals(A_SERVICE_DEP))
248 {
249 parseServiceDependencyAnnotation(annotation);
250 }
251 else if (annotation.getName().getFQN().equals(A_CONFIGURATION_DEPENDENCY))
252 {
253 parseConfigurationDependencyAnnotation(annotation);
254 }
255 else if (annotation.getName().getFQN().equals(A_BUNDLE_DEPENDENCY))
256 {
257 parseBundleDependencyAnnotation(annotation);
258 }
259 else if (annotation.getName().getFQN().equals(A_RESOURCE_DEPENDENCY))
260 {
261 parseRersourceDependencyAnnotation(annotation);
262 }
263 else if (annotation.getName().getFQN().equals(A_INJECT))
264 {
265 parseInject(annotation);
266 }
267 }
268
269 /**
270 * Finishes up the class parsing. This method must be called once the parseClassFileWithCollector method has returned.
271 * @return true if some annotations have been parsed, false if not.
272 */
273 public boolean finish()
274 {
275 if (m_writers.size() == 0)
276 {
277 m_logger.info("No components found for class " + m_className);
278 return false;
279 }
280
281 // We must have at least a Service annotation.
282 checkServiceDeclared(EntryType.Component, EntryType.AspectService, EntryType.AdapterService,
283 EntryType.BundleAdapterService,
284 EntryType.ResourceAdapterService, EntryType.FactoryConfigurationAdapterService);
285
286 StringBuilder sb = new StringBuilder();
287 sb.append("Parsed annotation for class ");
288 sb.append(m_className);
289 for (int i = m_writers.size() - 1; i >= 0; i--)
290 {
291 sb.append("\n\t").append(m_writers.get(i).toString());
292 }
293 m_logger.info(sb.toString());
294 return true;
295 }
296
297 /**
298 * Writes the generated component descriptor in the given print writer.
299 * The first line must be the service (@Service or AspectService).
300 * @param pw the writer where the component descriptor will be written.
301 */
302 public void writeTo(PrintWriter pw)
303 {
304 // The last element our our m_writers list contains either the Service, or the AspectService descriptor.
305 for (int i = m_writers.size() - 1; i >= 0; i--)
306 {
307 pw.println(m_writers.get(i).toString());
308 }
309 }
310
311 /**
312 * Returns list of all imported services. Imported services are deduced from every
313 * @ServiceDependency annotations.
314 * @return the list of imported services, or null
315 */
316 public Set<String> getImportService()
317 {
318 return m_importService;
319 }
320
321 /**
322 * Returns list of all exported services. Imported services are deduced from every
323 * annotations which provides a service (@Component, etc ...)
324 * @return the list of exported services, or null
325 */
326 public Set<String> getExportService()
327 {
328 return m_exportService;
329 }
330
331 private void parseComponentAnnotation(Annotation annotation)
332 {
333 EntryWriter writer = new EntryWriter(EntryType.Component);
334 m_writers.add(writer);
335
336 // Register previously parsed annotations common to services (Init/Start/...)
337 addCommonServiceParams(writer);
338
339 // impl attribute
340 writer.put(EntryParam.impl, m_className);
341
342 // properties attribute
343 parseProperties(annotation, EntryParam.properties, writer);
344
345 // provides attribute.
346 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
347 {
348 // no service provided: check if @Registered/@Unregistered annotation are used
349 // and raise an error if true.
350 checkRegisteredUnregisteredNotPresent();
351 }
352
353 // factorySet attribute (deprecated, replaced by factoryName)
354 String factorySetName = writer.putString(annotation, EntryParam.factorySet, null);
355 if (factorySetName != null)
356 {
357 // When a component defines a factorySet, it means that a java.util.Set will
358 // be provided into the OSGi registry, in order to let anoter component add
359 // some component instance configurations into it.
360 // So, we have to indicate that the Set is provided as a service, in the Export-Serviec
361 // header.
362 m_exportService.add("java.util.Set");
363 }
364
365 // factoryName attribute
366 String factoryName = writer.putString(annotation, EntryParam.factoryName, null);
367 if (factoryName != null)
368 {
369 // When a component defines a factoryName, it means that a ComponentFactory will
370 // be provided into the OSGi registry, in order to let another component create some component instances.
371 // So, we have to indicate that the ComponentFactory is provided as a service, in the Export-Serviec
372 // header.
373 m_exportService.add("org.apache.felix.dependencymanager.runtime.api.ComponentFactory");
374 }
375
376 // factoryConfigure attribute
377 writer.putString(annotation, EntryParam.factoryConfigure, null);
378
379 // factoryMethod attribute
380 writer.putString(annotation, EntryParam.factoryMethod, null);
381 }
382
383 private void addCommonServiceParams(EntryWriter writer)
384 {
385 if (m_initMethod != null)
386 {
387 writer.put(EntryParam.init, m_initMethod);
388 }
389
390 if (m_startMethod != null)
391 {
392 writer.put(EntryParam.start, m_startMethod);
393 }
394
395 if (m_registeredMethod != null)
396 {
397 writer.put(EntryParam.registered, m_registeredMethod);
398 }
399
400 if (m_stopMethod != null)
401 {
402 writer.put(EntryParam.stop, m_stopMethod);
403 }
404
405 if (m_unregisteredMethod != null)
406 {
407 writer.put(EntryParam.unregistered, m_unregisteredMethod);
408 }
409
410 if (m_destroyMethod != null)
411 {
412 writer.put(EntryParam.destroy, m_destroyMethod);
413 }
414
415 if (m_compositionMethod != null)
416 {
417 writer.put(EntryParam.composition, m_compositionMethod);
418 }
419
420 if (m_starter != null)
421 {
422 writer.put(EntryParam.starter, m_starter);
423 }
424
425 if (m_stopper != null)
426 {
427 writer.put(EntryParam.stopper, m_stopper);
428 if (m_starter == null)
429 {
430 throw new IllegalArgumentException("Can't use a @LifecycleController annotation for stopping a service without declaring a " +
431 "@LifecycleController that starts the component in class " + m_className);
432 }
433 }
434
435 if (m_bundleContextField != null)
436 {
437 writer.put(EntryParam.bundleContextField, m_bundleContextField);
438 }
439
440 if (m_dependencyManagerField != null)
441 {
442 writer.put(EntryParam.dependencyManagerField, m_dependencyManagerField);
443 }
444
445 if (m_componentField != null)
446 {
447 writer.put(EntryParam.componentField, m_componentField);
448 }
449 }
450
451 /**
452 * Parses a ServiceDependency Annotation.
453 * @param annotation the ServiceDependency Annotation.
454 */
455 private void parseServiceDependencyAnnotation(Annotation annotation)
456 {
457 EntryWriter writer = new EntryWriter(EntryType.ServiceDependency);
458 m_writers.add(writer);
459
460 // service attribute
461 String service = annotation.get(EntryParam.service.toString());
462 if (service != null)
463 {
464 service = Patterns.parseClass(service, Patterns.CLASS, 1);
465 }
466 else
467 {
468 if (m_isField)
469 {
470 service = Patterns.parseClass(m_descriptor, Patterns.CLASS, 1);
471 }
472 else
473 {
474 service = Patterns.parseClass(m_descriptor, Patterns.BIND_CLASS, 2);
475 }
476 }
477 writer.put(EntryParam.service, service);
478
479 // Store this service in list of imported services.
480 m_importService.add(service);
481
482 // autoConfig attribute
483 if (m_isField)
484 {
485 writer.put(EntryParam.autoConfig, m_field);
486 }
487
488 // filter attribute
489 String filter = annotation.get(EntryParam.filter.toString());
490 if (filter != null)
491 {
492 Verifier.verifyFilter(filter, 0);
493 writer.put(EntryParam.filter, filter);
494 }
495
496 // defaultImpl attribute
497 writer.putClass(annotation, EntryParam.defaultImpl, null);
498
499 // added callback
500 writer.putString(annotation, EntryParam.added, (!m_isField) ? m_method : null);
501
502 // timeout parameter
503 writer.putString(annotation, EntryParam.timeout, null);
504 Long t = (Long) annotation.get(EntryParam.timeout.toString());
505 if (t != null && t.longValue() < -1)
506 {
507 throw new IllegalArgumentException("Invalid timeout value " + t + " in ServiceDependency annotation from class " + m_className);
508 }
509
510 // required attribute (not valid if parsing a temporal service dependency)
511 writer.putString(annotation, EntryParam.required, null);
512
513 // changed callback
514 writer.putString(annotation, EntryParam.changed, null);
515
516 // removed callback
517 writer.putString(annotation, EntryParam.removed, null);
518
519 // name attribute
520 parseDependencyName(writer, annotation);
521
522 // propagate attribute
523 writer.putString(annotation, EntryParam.propagate, null);
524 }
525
526 /**
527 * Parses a ConfigurationDependency annotation.
528 * @param annotation the ConfigurationDependency annotation.
529 */
530 private void parseConfigurationDependencyAnnotation(Annotation annotation)
531 {
532 EntryWriter writer = new EntryWriter(EntryType.ConfigurationDependency);
533 m_writers.add(writer);
534
535 // pid attribute (can be specified using the pid attribute, or using the classPid attribute)
536 String pid = annotation.get(EntryParam.pidClass.toString());
537 if (pid != null)
538 {
539 pid = Patterns.parseClass(pid, Patterns.CLASS, 1);
540 } else {
541 pid = get(annotation, EntryParam.pid.toString(), m_className);
542 }
543 writer.put(EntryParam.pid, pid);
544
545 // the method on which the annotation is applied
546 writer.put(EntryParam.updated, m_method);
547
548 // propagate attribute
549 writer.putString(annotation, EntryParam.propagate, null);
550
551 // Property Meta Types
552 parseMetaTypes(annotation, pid, false);
553
554 // name attribute
555 parseDependencyName(writer, annotation);
556 }
557
558 /**
559 * Parses an AspectService annotation.
560 * @param annotation
561 */
562 private void parseAspectService(Annotation annotation)
563 {
564 EntryWriter writer = new EntryWriter(EntryType.AspectService);
565 m_writers.add(writer);
566
567 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
568 addCommonServiceParams(writer);
569
570 // Parse service filter
571 String filter = annotation.get(EntryParam.filter.toString());
572 if (filter != null)
573 {
574 Verifier.verifyFilter(filter, 0);
575 writer.put(EntryParam.filter, filter);
576 }
577
578 // Parse service aspect ranking
579 Integer ranking = annotation.get(EntryParam.ranking.toString());
580 writer.put(EntryParam.ranking, ranking.toString());
581
582 // Generate Aspect Implementation
583 writer.put(EntryParam.impl, m_className);
584
585 // Parse Aspect properties.
586 parseProperties(annotation, EntryParam.properties, writer);
587
588 // Parse field/added/changed/removed attributes
589 parseAspectOrAdapterCallbackMethods(annotation, writer);
590
591 // Parse service interface this aspect is applying to
592 Object service = annotation.get(EntryParam.service.toString());
593 if (service == null)
594 {
595 if (m_interfaces == null)
596 {
597 throw new IllegalStateException("Invalid AspectService annotation: " +
598 "the service attribute has not been set and the class " + m_className
599 + " does not implement any interfaces");
600 }
601 if (m_interfaces.length != 1)
602 {
603 throw new IllegalStateException("Invalid AspectService annotation: " +
604 "the service attribute has not been set and the class " + m_className
605 + " implements more than one interface");
606 }
607
608 writer.put(EntryParam.service, m_interfaces[0]);
609 }
610 else
611 {
612 writer.putClass(annotation, EntryParam.service, null);
613 }
614
615 // Parse factoryMethod attribute
616 writer.putString(annotation, EntryParam.factoryMethod, null);
617 }
618
619 private void parseAspectOrAdapterCallbackMethods(Annotation annotation, EntryWriter writer)
620 {
621 String field = annotation.get(EntryParam.field.toString());
622 String added = annotation.get(EntryParam.added.toString());
623 String changed = annotation.get(EntryParam.changed.toString());
624 String removed = annotation.get(EntryParam.removed.toString());
625 String swap = annotation.get(EntryParam.swap.toString());
626
627 // "field" and "added/changed/removed/swap" attributes can't be mixed
628 if (field != null && (added != null || changed != null || removed != null || swap != null))
629 {
630 throw new IllegalStateException("Annotation " + annotation + "can't applied on " + m_className
631 + " can't mix \"field\" attribute with \"added/changed/removed\" attributes");
632 }
633
634 // Parse aspect impl field where to inject the original service.
635 writer.putString(annotation, EntryParam.field, null);
636
637 // Parse aspect impl callback methods.
638 writer.putString(annotation, EntryParam.added, null);
639 writer.putString(annotation, EntryParam.changed, null);
640 writer.putString(annotation, EntryParam.removed, null);
641 writer.putString(annotation, EntryParam.swap, null);
642 }
643
644 /**
645 * Parses an AspectService annotation.
646 * @param annotation
647 */
648 private void parseAdapterService(Annotation annotation)
649 {
650 EntryWriter writer = new EntryWriter(EntryType.AdapterService);
651 m_writers.add(writer);
652
653 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
654 addCommonServiceParams(writer);
655
656 // Generate Adapter Implementation
657 writer.put(EntryParam.impl, m_className);
658
659 // Parse adaptee filter
660 String adapteeFilter = annotation.get(EntryParam.adapteeFilter.toString());
661 if (adapteeFilter != null)
662 {
663 Verifier.verifyFilter(adapteeFilter, 0);
664 writer.put(EntryParam.adapteeFilter, adapteeFilter);
665 }
666
667 // Parse the mandatory adapted service interface.
668 writer.putClass(annotation, EntryParam.adapteeService, null);
669
670 // Parse Adapter properties.
671 parseProperties(annotation, EntryParam.properties, writer);
672
673 // Parse the provided adapter service (use directly implemented interface by default).
674 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
675 {
676 checkRegisteredUnregisteredNotPresent();
677 }
678
679 // Parse factoryMethod attribute
680 writer.putString(annotation, EntryParam.factoryMethod, null);
681
682 // Parse propagate flag.
683 // Parse factoryMethod attribute
684 writer.putString(annotation, EntryParam.propagate, null);
685
686 // Parse field/added/changed/removed attributes
687 parseAspectOrAdapterCallbackMethods(annotation, writer);
688 }
689
690 /**
691 * Parses a BundleAdapterService annotation.
692 * @param annotation
693 */
694 private void parseBundleAdapterService(Annotation annotation)
695 {
696 EntryWriter writer = new EntryWriter(EntryType.BundleAdapterService);
697 m_writers.add(writer);
698
699 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
700 addCommonServiceParams(writer);
701
702 // Generate Adapter Implementation
703 writer.put(EntryParam.impl, m_className);
704
705 // Parse bundle filter
706 String filter = annotation.get(EntryParam.filter.toString());
707 if (filter != null)
708 {
709 Verifier.verifyFilter(filter, 0);
710 writer.put(EntryParam.filter, filter);
711 }
712
713 // Parse stateMask attribute
714 writer.putString(annotation, EntryParam.stateMask, Integer.valueOf(
715 Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE).toString());
716
717 // Parse Adapter properties.
718 parseProperties(annotation, EntryParam.properties, writer);
719
720 // Parse the optional adapter service (use directly implemented interface by default).
721 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
722 {
723 checkRegisteredUnregisteredNotPresent();
724 }
725
726 // Parse propagate attribute
727 writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
728
729 // Parse factoryMethod attribute
730 writer.putString(annotation, EntryParam.factoryMethod, null);
731 }
732
733 /**
734 * Parses a BundleAdapterService annotation.
735 * @param annotation
736 */
737 private void parseResourceAdapterService(Annotation annotation)
738 {
739 EntryWriter writer = new EntryWriter(EntryType.ResourceAdapterService);
740 m_writers.add(writer);
741
742 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
743 addCommonServiceParams(writer);
744
745 // Generate Adapter Implementation
746 writer.put(EntryParam.impl, m_className);
747
748 // Parse resource filter
749 String filter = annotation.get(EntryParam.filter.toString());
750 if (filter != null)
751 {
752 Verifier.verifyFilter(filter, 0);
753 writer.put(EntryParam.filter, filter);
754 }
755
756 // Parse Adapter properties.
757 parseProperties(annotation, EntryParam.properties, writer);
758
759 // Parse the provided adapter service (use directly implemented interface by default).
760 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
761 {
762 checkRegisteredUnregisteredNotPresent();
763 }
764
765 // Parse propagate attribute
766 writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
767
768 // Parse changed attribute
769 writer.putString(annotation, EntryParam.changed, null);
770 }
771
772 /**
773 * Parses a Factory Configuration Adapter annotation.
774 * @param annotation
775 */
776 private void parseFactoryConfigurationAdapterService(Annotation annotation)
777 {
778 EntryWriter writer = new EntryWriter(EntryType.FactoryConfigurationAdapterService);
779 m_writers.add(writer);
780
781 // Register previously parsed Init/Start/Stop/Destroy/Composition annotations
782 addCommonServiceParams(writer);
783
784 // Generate Adapter Implementation
785 writer.put(EntryParam.impl, m_className);
786
787 // factory pid attribute (can be specified using the factoryPid attribute, or using the factoryPidClass attribute)
788 String factoryPid = annotation.get(EntryParam.factoryPidClass.toString());
789 if (factoryPid != null)
790 {
791 factoryPid = Patterns.parseClass(factoryPid, Patterns.CLASS, 1);
792 } else {
793 factoryPid = get(annotation, EntryParam.factoryPid.toString(), m_className);
794 }
795 writer.put(EntryParam.factoryPid, factoryPid);
796
797 // Parse updated callback
798 writer.putString(annotation, EntryParam.updated, "updated");
799
800 // propagate attribute
801 writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
802
803 // Parse the provided adapter service (use directly implemented interface by default).
804 if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
805 {
806 checkRegisteredUnregisteredNotPresent();
807 }
808
809 // Parse Adapter properties.
810 parseProperties(annotation, EntryParam.properties, writer);
811
812 // Parse optional meta types for configuration description.
813 parseMetaTypes(annotation, factoryPid, true);
814
815 // Parse factoryMethod attribute
816 writer.putString(annotation, EntryParam.factoryMethod, null);
817 }
818
819 private void parseBundleDependencyAnnotation(Annotation annotation)
820 {
821 EntryWriter writer = new EntryWriter(EntryType.BundleDependency);
822 m_writers.add(writer);
823
824 String filter = annotation.get(EntryParam.filter.toString());
825 if (filter != null)
826 {
827 Verifier.verifyFilter(filter, 0);
828 writer.put(EntryParam.filter, filter);
829 }
830
831 writer.putString(annotation, EntryParam.added, m_method);
832 writer.putString(annotation, EntryParam.changed, null);
833 writer.putString(annotation, EntryParam.removed, null);
834 writer.putString(annotation, EntryParam.required, null);
835 writer.putString(annotation, EntryParam.stateMask, null);
836 writer.putString(annotation, EntryParam.propagate, null);
837 parseDependencyName(writer, annotation);
838 }
839
840 private void parseRersourceDependencyAnnotation(Annotation annotation)
841 {
842 EntryWriter writer = new EntryWriter(EntryType.ResourceDependency);
843 m_writers.add(writer);
844
845 String filter = annotation.get(EntryParam.filter.toString());
846 if (filter != null)
847 {
848 Verifier.verifyFilter(filter, 0);
849 writer.put(EntryParam.filter, filter);
850 }
851
852 if (m_isField)
853 {
854 writer.put(EntryParam.autoConfig, m_field);
855 }
856
857 writer.putString(annotation, EntryParam.added, (!m_isField) ? m_method : null);
858 writer.putString(annotation, EntryParam.changed, null);
859 writer.putString(annotation, EntryParam.removed, null);
860 writer.putString(annotation, EntryParam.required, null);
861 writer.putString(annotation, EntryParam.propagate, null);
862 writer.putString(annotation, EntryParam.factoryMethod, null);
863 parseDependencyName(writer, annotation);
864 }
865
866 /**
867 * Parse the name of a given dependency.
868 * @param writer The writer where to write the dependency name
869 * @param annotation the dependency to be parsed
870 */
871 private void parseDependencyName(EntryWriter writer, Annotation annotation)
872 {
873 String name = annotation.get(EntryParam.name.toString());
874 if (name != null)
875 {
876 if(! m_dependencyNames.add(name))
877 {
878 throw new IllegalArgumentException("Duplicate dependency name " + name + " in Dependency " + annotation + " from class " + m_className);
879 }
880 writer.put(EntryParam.name, name);
881 }
882 }
883
884 private void parseLifecycleAnnotation(Annotation annotation)
885 {
886 Patterns.parseField(m_field, m_descriptor, Patterns.RUNNABLE);
887 if ("true".equals(get(annotation,EntryParam.start.name(), "true")))
888 {
889 if (m_starter != null) {
890 throw new IllegalStateException("Lifecycle annotation already defined on field " +
891 m_starter + " in class " + m_className);
892 }
893 m_starter = m_field;
894 } else {
895 if (m_stopper != null) {
896 throw new IllegalStateException("Lifecycle annotation already defined on field " +
897 m_stopper + " in class " + m_className);
898 }
899 m_stopper = m_field;
900 }
901 }
902
903 /**
904 * Parse optional meta types annotation attributes
905 * @param annotation
906 */
907 private void parseMetaTypes(Annotation annotation, String pid, boolean factory)
908 {
909 if (annotation.get("metadata") != null)
910 {
911 String propertiesHeading = annotation.get("heading");
912 String propertiesDesc = annotation.get("description");
913
914 MetaType.OCD ocd = new MetaType.OCD(pid, propertiesHeading, propertiesDesc);
915 for (Object p: (Object[]) annotation.get("metadata"))
916 {
917 Annotation property = (Annotation) p;
918 String heading = property.get("heading");
919 String id = property.get("id");
920 String type = (String) property.get("type");
921 type = (type != null) ? Patterns.parseClass(type, Patterns.CLASS, 1) : null;
922 Object[] defaults = (Object[]) property.get("defaults");
923 String description = property.get("description");
924 Integer cardinality = property.get("cardinality");
925 Boolean required = property.get("required");
926
927 MetaType.AD ad = new MetaType.AD(id, type, defaults, heading, description,
928 cardinality, required);
929
930 Object[] optionLabels = property.get("optionLabels");
931 Object[] optionValues = property.get("optionValues");
932
933 if (optionLabels == null
934 && optionValues != null
935 ||
936 optionLabels != null
937 && optionValues == null
938 ||
939 (optionLabels != null && optionValues != null && optionLabels.length != optionValues.length))
940 {
941 throw new IllegalArgumentException("invalid option labels/values specified for property "
942 + id +
943 " in PropertyMetadata annotation from class " + m_className);
944 }
945
946 if (optionValues != null)
947 {
948 for (int i = 0; i < optionValues.length; i++)
949 {
950 ad.add(new MetaType.Option(optionValues[i].toString(), optionLabels[i].toString()));
951 }
952 }
953
954 ocd.add(ad);
955 }
956
957 m_metaType.add(ocd);
958 MetaType.Designate designate = new MetaType.Designate(pid, factory);
959 m_metaType.add(designate);
960 m_logger.info("Parsed MetaType Properties from class " + m_className);
961 }
962 }
963
964 /**
965 * Parses a Property annotation (which represents a list of key-value pair).
966 * The properties are encoded using the following json form:
967 *
968 * properties ::= key-value-pair*
969 * key-value-pair ::= key value
970 * value ::= String | String[] | value-type
971 * value-type ::= jsonObject with value-type-info
972 * value-type-info ::= "type"=primitive java type
973 * "value"=String|String[]
974 *
975 * Exemple:
976 *
977 * "properties" : {
978 * "string-param" : "string-value",
979 * "string-array-param" : ["str1", "str2],
980 * "long-param" : {"type":"java.lang.Long", "value":"1"}}
981 * "long-array-param" : {"type":"java.lang.Long", "value":["1"]}}
982 * }
983 * }
984 *
985 * @param annotation the annotation where the Param annotation is defined
986 * @param attribute the attribute name which is of Param type
987 * @param writer the object where the parsed attributes are written
988 */
989 private void parseProperties(Annotation annotation, EntryParam attribute, EntryWriter writer)
990 {
991 try
992 {
993 Object[] parameters = annotation.get(attribute.toString());
994 if (parameters != null)
995 {
996 JSONObject properties = new JSONObject();
997 for (Object p : parameters)
998 {
999 Annotation a = (Annotation) p;
1000 String name = (String) a.get("name");
1001
1002 String type = a.get("type");
1003 Class<?> classType;
1004 try
1005 {
1006 classType = (type == null) ? String.class : Class.forName(Patterns.parseClass(type,
1007 Patterns.CLASS, 1));
1008 }
1009 catch (ClassNotFoundException e)
1010 {
1011 // Theorically impossible
1012 throw new IllegalArgumentException("Invalid Property type " + type
1013 + " from annotation " + annotation + " in class " + m_className);
1014 }
1015
1016 Object[] values;
1017
1018 if ((values = a.get("value")) != null)
1019 {
1020 values = checkPropertyType(name, classType, values);
1021 addProperty(properties, name, values, classType);
1022 }
1023 else if ((values = a.get("values")) != null)
1024 { // deprecated
1025 values = checkPropertyType(name, classType, values);
1026 addProperty(properties, name, values, classType);
1027 }
1028 else if ((values = a.get("longValue")) != null)
1029 {
1030 addProperty(properties, name, values, Long.class);
1031 }
1032 else if ((values = a.get("doubleValue")) != null)
1033 {
1034 addProperty(properties, name, values, Double.class);
1035 }
1036 else if ((values = a.get("floatValue")) != null)
1037 {
1038 addProperty(properties, name, values, Float.class);
1039 }
1040 else if ((values = a.get("intValue")) != null)
1041 {
1042 addProperty(properties, name, values, Integer.class);
1043 }
1044 else if ((values = a.get("byteValue")) != null)
1045 {
1046 addProperty(properties, name, values, Byte.class);
1047 }
1048 else if ((values = a.get("charValue")) != null)
1049 {
1050 addProperty(properties, name, values, Character.class);
1051 }
1052 else if ((values = a.get("booleanValue")) != null)
1053 {
1054 addProperty(properties, name, values, Boolean.class);
1055 }
1056 else if ((values = a.get("shortValue")) != null)
1057 {
1058 addProperty(properties, name, values, Short.class);
1059 }
1060 else
1061 {
1062 throw new IllegalArgumentException("Missing Property value from annotation "
1063 + annotation + " in class " + m_className);
1064 }
1065 }
1066 writer.putJsonObject(attribute, properties);
1067 }
1068 }
1069 catch (JSONException e)
1070 {
1071 throw new IllegalArgumentException("UNexpected exception while parsing Property from annotation "
1072 + annotation + " in class " + m_className, e);
1073 }
1074 }
1075
1076 /**
1077 * Checks if a property contains values that are compatible with a give primitive type.
1078 *
1079 * @param name the property name
1080 * @param values the values for the property name
1081 * @param type the primitive type.
1082 * @return the same property values, possibly modified if the type is 'Character' (the strings are converted to their character integer values).
1083 */
1084 private Object[] checkPropertyType(String name, Class<?> type, Object ... values)
1085 {
1086 if (type.equals(String.class))
1087 {
1088 return values;
1089 } else if (type.equals(Long.class)) {
1090 for (Object value : values) {
1091 try {
1092 Long.valueOf(value.toString());
1093 } catch (NumberFormatException e) {
1094 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1095 + " does not contain a valid Long value (" + value.toString() + ")");
1096 }
1097 }
1098 } else if (type.equals(Double.class)) {
1099 for (Object value : values) {
1100 try {
1101 Double.valueOf(value.toString());
1102 } catch (NumberFormatException e) {
1103 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1104 + " does not contain a valid Double value (" + value.toString() + ")");
1105 }
1106 }
1107 } else if (type.equals(Float.class)) {
1108 for (Object value : values) {
1109 try {
1110 Float.valueOf(value.toString());
1111 } catch (NumberFormatException e) {
1112 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1113 + " does not contain a valid Float value (" + value.toString() + ")");
1114 }
1115 }
1116 } else if (type.equals(Integer.class)) {
1117 for (Object value : values) {
1118 try {
1119 Integer.valueOf(value.toString());
1120 } catch (NumberFormatException e) {
1121 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1122 + " does not contain a valid Integer value (" + value.toString() + ")");
1123 }
1124 }
1125 } else if (type.equals(Byte.class)) {
1126 for (Object value : values) {
1127 try {
1128 Byte.valueOf(value.toString());
1129 } catch (NumberFormatException e) {
1130 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1131 + " does not contain a valid Byte value (" + value.toString() + ")");
1132 }
1133 }
1134 } else if (type.equals(Character.class)) {
1135 for (int i = 0; i < values.length; i++)
1136 {
1137 try
1138 {
1139 // If the string is already an integer, don't modify it
1140 Integer.valueOf(values[i].toString());
1141 }
1142 catch (NumberFormatException e)
1143 {
1144 // Converter the character string to its corresponding integer code.
1145 if (values[i].toString().length() != 1)
1146 {
1147 throw new IllegalArgumentException("Property \"" + name + "\" in class "
1148 + m_className + " does not contain a valid Character value (" + values[i] + ")");
1149 }
1150 try
1151 {
1152 values[i] = Integer.valueOf(values[i].toString().charAt(0));
1153 }
1154 catch (NumberFormatException e2)
1155 {
1156 throw new IllegalArgumentException("Property \"" + name + "\" in class "
1157 + m_className + " does not contain a valid Character value (" + values[i].toString()
1158 + ")");
1159 }
1160 }
1161 }
1162 } else if (type.equals(Boolean.class)) {
1163 for (Object value : values) {
1164 if (! value.toString().equalsIgnoreCase("false") && ! value.toString().equalsIgnoreCase("true")) {
1165 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1166 + " does not contain a valid Boolean value (" + value.toString() + ")");
1167 }
1168 }
1169 } else if (type.equals(Short.class)) {
1170 for (Object value : values) {
1171 try {
1172 Short.valueOf(value.toString());
1173 } catch (NumberFormatException e) {
1174 throw new IllegalArgumentException("Property \"" + name + "\" in class " + m_className
1175 + " does not contain a valid Short value (" + value.toString() + ")");
1176 }
1177 }
1178 }
1179
1180 return values;
1181 }
1182
1183 /**
1184 * Adds a key/value(s) pair in a properties map
1185 * @param properties the target properties map
1186 * @param name the property name
1187 * @param value the property value(s)
1188 * @param type the property type
1189 * @throws JSONException
1190 */
1191 private void addProperty(JSONObject props, String name, Object value, Class<?> type) throws JSONException {
1192 if (value.getClass().isArray())
1193 {
1194 Object[] array = (Object[]) value;
1195 if (array.length == 1)
1196 {
1197 value = array[0];
1198 }
1199 }
1200
1201 if (type.equals(String.class))
1202 {
1203 props.put(name, value.getClass().isArray() ? new JSONArray(value) : value);
1204 }
1205 else
1206 {
1207 JSONObject jsonValue = new JSONObject();
1208 jsonValue.put("type", type.getName());
1209 jsonValue.put("value", value.getClass().isArray() ? new JSONArray(value) : value);
1210 props.put(name, jsonValue);
1211 }
1212 }
1213
1214 /**
1215 * Parse Inject annotation, used to inject some special classes in some fields
1216 * (BundleContext/DependencyManager etc ...)
1217 * @param annotation the Inject annotation
1218 */
1219 private void parseInject(Annotation annotation)
1220 {
1221 if (Patterns.BUNDLE_CONTEXT.matcher(m_descriptor).matches())
1222 {
1223 m_bundleContextField = m_field;
1224 }
1225 else if (Patterns.DEPENDENCY_MANAGER.matcher(m_descriptor).matches())
1226 {
1227 m_dependencyManagerField = m_field;
1228 }
1229 else if (Patterns.COMPONENT.matcher(m_descriptor).matches())
1230 {
1231 m_componentField = m_field;
1232 }
1233 else
1234 {
1235 throw new IllegalArgumentException("@Inject annotation can't be applied on the field \"" + m_field
1236 + "\" in class " + m_className);
1237 }
1238 }
1239
1240 /**
1241 * Checks if the class is annotated with some given annotations. Notice that the Service
1242 * is always parsed at end of parsing, so, we have to check the last element of our m_writers
1243 * List.
1244 * @return true if one of the provided annotations has been found from the parsed class.
1245 */
1246 private void checkServiceDeclared(EntryType... types)
1247 {
1248 boolean ok = false;
1249 if (m_writers.size() > 0)
1250 {
1251 for (EntryType type: types)
1252 {
1253 if (m_writers.get(m_writers.size() - 1).getEntryType() == type)
1254 {
1255 ok = true;
1256 break;
1257 }
1258 }
1259 }
1260
1261 if (!ok)
1262 {
1263 throw new IllegalStateException(
1264 ": the class must be annotated with either one of the following types: "
1265 + Arrays.toString(types));
1266 }
1267 }
1268
1269 /**
1270 * This method checks if the @Registered and/or @Unregistered annotations have been defined
1271 * while they should not, because the component does not provide a service.
1272 */
1273 private void checkRegisteredUnregisteredNotPresent()
1274 {
1275 if (m_registeredMethod != null)
1276 {
1277 throw new IllegalStateException("@Registered annotation can't be used on a Component " +
1278 " which does not provide a service (class=" + m_className + ")");
1279
1280 }
1281
1282 if (m_unregisteredMethod != null)
1283 {
1284 throw new IllegalStateException("@Unregistered annotation can't be used on a Component " +
1285 " which does not provide a service (class=" + m_className + ")");
1286
1287 }
1288 }
1289
1290 /**
1291 * Get an annotation attribute, and return a default value if its not present.
1292 * @param <T> the type of the variable which is assigned to the return value of this method.
1293 * @param annotation The annotation we are parsing
1294 * @param name the attribute name to get from the annotation
1295 * @param defaultValue the default value to return if the attribute is not found in the annotation
1296 * @return the annotation attribute value, or the defaultValue if not found
1297 */
1298 @SuppressWarnings("unchecked")
1299 private <T> T get(Annotation annotation, String name, T defaultValue)
1300 {
1301 T value = (T) annotation.get(name);
1302 return value != null ? value : defaultValue;
1303 }
1304}