blob: bbb02d9af0cf8082b4447d4f0e28f0f6bf638035 [file] [log] [blame]
Carsten Ziegeler98e992a2012-06-13 15:50:00 +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.scrplugin.ds;
20
21import java.lang.reflect.Method;
22import java.lang.reflect.Modifier;
23import java.util.ArrayList;
24import java.util.List;
25
26import org.apache.felix.scrplugin.SCRDescriptorException;
27import org.apache.felix.scrplugin.SCRDescriptorFailureException;
Carsten Ziegeler44ee8942012-06-14 15:03:37 +000028import org.apache.felix.scrplugin.SpecVersion;
Carsten Ziegeler98e992a2012-06-13 15:50:00 +000029import org.apache.felix.scrplugin.annotations.AnnotationProcessor;
30import org.apache.felix.scrplugin.annotations.ClassAnnotation;
31import org.apache.felix.scrplugin.annotations.MethodAnnotation;
32import org.apache.felix.scrplugin.annotations.ScannedClass;
33import org.apache.felix.scrplugin.description.ClassDescription;
34import org.apache.felix.scrplugin.description.ComponentConfigurationPolicy;
35import org.apache.felix.scrplugin.description.ComponentDescription;
Carsten Ziegelereeae04e2012-06-13 16:01:20 +000036import org.apache.felix.scrplugin.description.PropertyDescription;
37import org.apache.felix.scrplugin.description.PropertyType;
38import org.apache.felix.scrplugin.description.PropertyUnbounded;
Carsten Ziegeler98e992a2012-06-13 15:50:00 +000039import org.apache.felix.scrplugin.description.ReferenceCardinality;
40import org.apache.felix.scrplugin.description.ReferenceDescription;
41import org.apache.felix.scrplugin.description.ReferencePolicy;
Carsten Ziegelerd156e362012-06-14 15:34:11 +000042import org.apache.felix.scrplugin.description.ReferencePolicyOption;
Carsten Ziegeler98e992a2012-06-13 15:50:00 +000043import org.apache.felix.scrplugin.description.ReferenceStrategy;
44import org.apache.felix.scrplugin.description.ServiceDescription;
Carsten Ziegeler98e992a2012-06-13 15:50:00 +000045import org.osgi.service.component.annotations.Activate;
46import org.osgi.service.component.annotations.Component;
47import org.osgi.service.component.annotations.Deactivate;
48import org.osgi.service.component.annotations.Modified;
49import org.osgi.service.component.annotations.Reference;
50
51/**
52 * This is the processor for the DS annotations.
53 */
54public class DSAnnotationProcessor implements AnnotationProcessor {
55
56 /**
Carsten Ziegeler15e73682012-06-17 13:17:53 +000057 * @see org.apache.felix.scrplugin.annotations.AnnotationProcessor#getName()
58 */
59 public String getName() {
60 return "DS Annotation Processor";
61 }
62
63 /**
Carsten Ziegeler98e992a2012-06-13 15:50:00 +000064 * @see org.apache.felix.scrplugin.annotations.AnnotationProcessor#process(org.apache.felix.scrplugin.annotations.ScannedClass, org.apache.felix.scrplugin.description.ClassDescription)
65 */
66 public void process(final ScannedClass scannedClass,
67 final ClassDescription describedClass)
68 throws SCRDescriptorFailureException, SCRDescriptorException {
69 final List<ClassAnnotation> componentTags = scannedClass.getClassAnnotations(Component.class.getName());
70 scannedClass.processed(componentTags);
71
72 for (final ClassAnnotation cad : componentTags) {
73 this.createComponent(cad, describedClass, scannedClass);
74 }
75
76 // search for the component descriptions and use the first one
77 final List<ComponentDescription> componentDescs = describedClass.getDescriptions(ComponentDescription.class);
78 ComponentDescription found = null;
79 if (!componentDescs.isEmpty()) {
80 found = componentDescs.get(0);
81 }
82
83 if (found != null) {
84 final ComponentDescription cd = found;
85
86 // search for methods
87 final List<MethodAnnotation> methodTags = scannedClass.getMethodAnnotations(null);
88 for (final MethodAnnotation m : methodTags) {
89 if (m.getName().equals(Activate.class.getName())) {
Carsten Ziegeler9a496fe2012-06-27 06:20:28 +000090 cd.setActivate(m.getAnnotatedMethod().getName());
Carsten Ziegeler98e992a2012-06-13 15:50:00 +000091 scannedClass.processed(m);
92 } else if (m.getName().equals(Deactivate.class.getName())) {
Carsten Ziegeler9a496fe2012-06-27 06:20:28 +000093 cd.setDeactivate(m.getAnnotatedMethod().getName());
Carsten Ziegeler98e992a2012-06-13 15:50:00 +000094 scannedClass.processed(m);
95 } else if (m.getName().equals(Modified.class.getName())) {
Carsten Ziegeler9a496fe2012-06-27 06:20:28 +000096 cd.setModified(m.getAnnotatedMethod().getName());
Carsten Ziegeler98e992a2012-06-13 15:50:00 +000097 scannedClass.processed(m);
98 } else if (m.getName().equals(Reference.class.getName()) ) {
99 this.processReference(describedClass, m);
100 scannedClass.processed(m);
101 }
102 }
103
104 }
105 }
106
107 /**
108 * @see org.apache.felix.scrplugin.annotations.AnnotationProcessor#getRanking()
109 */
110 public int getRanking() {
111 return 300;
112 }
113
114 /**
115 * Create a component description.
116 *
117 * @param cad The component annotation for the class.
118 * @param scannedClass The scanned class.
119 */
120 private ComponentDescription createComponent(final ClassAnnotation cad,
121 final ClassDescription describedClass,
122 final ScannedClass scannedClass)
123 throws SCRDescriptorException {
124 final ComponentDescription component = new ComponentDescription(cad);
125 describedClass.add(component);
126
127 // Although not defined in the spec, we support abstract classes.
128 final boolean classIsAbstract = Modifier.isAbstract(scannedClass.getClass().getModifiers());
129 component.setAbstract(classIsAbstract);
130
131 // name
132 component.setName(cad.getStringValue("name", scannedClass.getScannedClass().getName()));
133
134 // services
135 final List<String> listedInterfaces = new ArrayList<String>();
Carsten Ziegelerf38b5db2014-07-24 13:18:39 +0000136 if (cad.hasValue("service") ) {
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000137 final String[] interfaces = (String[]) cad.getValue("service");
Carsten Ziegelerf38b5db2014-07-24 13:18:39 +0000138 if ( interfaces != null ) {
139 for (final String t : interfaces) {
140 listedInterfaces.add(t);
141 }
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000142 }
143 } else {
144 // scan directly implemented interfaces
145 this.searchInterfaces(listedInterfaces, scannedClass.getScannedClass());
146 }
147 if ( listedInterfaces.size() > 0 ) {
148 final ServiceDescription serviceDesc = new ServiceDescription(cad);
149 describedClass.add(serviceDesc);
150
151 for(final String name : listedInterfaces) {
152 serviceDesc.addInterface(name);
153 }
154 serviceDesc.setServiceFactory(cad.getBooleanValue("servicefactory", false));
155 }
156
157 // factory
158 component.setFactory(cad.getStringValue("factory", null));
159
160 // enabled
161 if (cad.getValue("enabled") != null) {
162 component.setEnabled(cad.getBooleanValue("enabled", true));
163 }
164
165 // immediate
166 if (cad.getValue("immediate") != null) {
Carsten Ziegeler9defc092012-07-01 13:31:00 +0000167 component.setImmediate(cad.getBooleanValue("immediate", false));
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000168 }
169
Carsten Ziegelereeae04e2012-06-13 16:01:20 +0000170 // property
171 final String[] property = (String[])cad.getValue("property");
172 if ( property != null ) {
173 // TODO - what do we do if the value is invalid?
174 for(final String propDef : property) {
175 final int pos = propDef.indexOf('=');
176 if ( pos != -1 ) {
177 final String prefix = propDef.substring(0, pos);
178 final String value = propDef.substring(pos + 1);
179 final int typeSep = prefix.indexOf(':');
180 final String key = (typeSep == -1 ? prefix : prefix.substring(0, typeSep));
181 final String type = (typeSep == -1 ? PropertyType.String.name() : prefix.substring(typeSep + 1));
182
183 final PropertyType propType = PropertyType.valueOf(type);
Carsten Ziegeleradae60d2013-07-03 16:45:23 +0000184 // FELIX-4159 : check if this is a multi value prop
185 final List<PropertyDescription> existingProps = describedClass.getDescriptions(PropertyDescription.class);
186 PropertyDescription found = null;
187 for(final PropertyDescription current : existingProps) {
188 if ( current.getName().equals(key) ) {
189 found = current;
190 break;
191 }
192 }
193 if ( found == null ) {
194 final PropertyDescription pd = new PropertyDescription(cad);
195 describedClass.add(pd);
196 pd.setName(key);
197 pd.setValue(value);
198 pd.setType(propType);
199 pd.setUnbounded(PropertyUnbounded.DEFAULT);
200 } else {
201 if ( propType != found.getType() ) {
202 throw new SCRDescriptorException("Multi value property '" + key + "' has different types: "
203 + found.getType() + " & " + propType,
204 describedClass.getSource());
205 }
206 if ( found.getValue() != null ) {
207 final String[] values = new String[2];
208 values[0] = found.getValue();
209 values[1] = value;
210 found.setMultiValue(values);
211 } else {
212 final String[] oldValues = found.getMultiValue();
213 final String[] newValues = new String[oldValues.length + 1];
214 System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
215 newValues[oldValues.length] = value;
216 found.setMultiValue(newValues);
217 }
218 }
Carsten Ziegelereeae04e2012-06-13 16:01:20 +0000219 }
220 }
221 }
222 // TODO: properties
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000223
224 // xmlns
225 if (cad.getValue("xmlns") != null) {
226 final SpecVersion spec = SpecVersion.fromNamespaceUrl(cad.getValue("xmlns").toString());
227 if ( spec == null ) {
228 throw new SCRDescriptorException("Unknown xmlns attribute value: " + cad.getValue("xmlns"),
Carsten Ziegeler9a496fe2012-06-27 06:20:28 +0000229 describedClass.getSource());
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000230 }
231 component.setSpecVersion(spec);
232 }
233
234 // configuration policy
Carsten Ziegelerdd7245d2012-12-20 12:24:32 +0000235 component.setConfigurationPolicy(ComponentConfigurationPolicy.valueOf(cad.getEnumValue("configurationPolicy",
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000236 ComponentConfigurationPolicy.OPTIONAL.name())));
237
Carsten Ziegeler66005132012-06-14 15:48:32 +0000238 // configuration pid
239 component.setConfigurationPid(cad.getStringValue("configurationPid", null));
Carsten Ziegeler283ace92012-06-27 09:51:56 +0000240 component.setCreatePid(false);
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000241
242 // no inheritance
243 component.setInherit(false);
244
245 return component;
246 }
247
248 /**
249 * Get all directly implemented interfaces
250 */
251 private void searchInterfaces(final List<String> interfaceList, final Class<?> javaClass) {
252 final Class<?>[] interfaces = javaClass.getInterfaces();
253 for (final Class<?> i : interfaces) {
254 interfaceList.add(i.getName());
255 }
256 }
257
258 /**
259 * Process a reference
260 */
261 private void processReference(final ClassDescription describedClass, final MethodAnnotation ad) {
262 final ReferenceDescription ref = new ReferenceDescription(ad);
263 describedClass.add(ref);
264
265 ref.setStrategy(ReferenceStrategy.EVENT);
266
267 final String methodName = ad.getAnnotatedMethod().getName();
268 final String defaultUnbindMethodName;
269 final String refNameByMethod;
270 if ( methodName.startsWith("add") ) {
271 refNameByMethod = methodName.substring(3);
272 defaultUnbindMethodName = "remove" + refNameByMethod;
273 } else if ( methodName.startsWith("set") ) {
274 refNameByMethod = methodName.substring(3);
Carsten Ziegelerde638702012-08-13 04:22:00 +0000275 defaultUnbindMethodName = "unset" + refNameByMethod;
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000276 } else if ( methodName.startsWith("bind") ) {
277 refNameByMethod = methodName.substring(4);
Carsten Ziegelerde638702012-08-13 04:22:00 +0000278 defaultUnbindMethodName = "unbind" + refNameByMethod;
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000279 } else {
280 refNameByMethod = methodName;
281 defaultUnbindMethodName = "un" + refNameByMethod;
282 }
283 final String defaultUpdateMethodName = "updated" + refNameByMethod;
284
285 // bind method
Carsten Ziegeler9a496fe2012-06-27 06:20:28 +0000286 ref.setBind(ad.getAnnotatedMethod().getName());
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000287 // unbind method
288 final String unbind = ad.getStringValue("unbind",
289 this.hasMethod(describedClass, defaultUnbindMethodName) ? defaultUnbindMethodName : "-");
290 if ( !unbind.equals("-") ) {
Carsten Ziegeler9a496fe2012-06-27 06:20:28 +0000291 ref.setUnbind(unbind);
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000292 }
293 // update method
Carsten Ziegeler9a496fe2012-06-27 06:20:28 +0000294 final String updated = ad.getStringValue("updated",
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000295 this.hasMethod(describedClass, defaultUpdateMethodName) ? defaultUpdateMethodName : "-");
Carsten Ziegeler9a496fe2012-06-27 06:20:28 +0000296 if ( !updated.equals("-") ) {
297 ref.setUpdated(updated);
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000298 }
299
300 // name
301 ref.setName(ad.getStringValue("name", refNameByMethod));
302 // service
303 final String serviceName = ad.getStringValue("service", null);
304 if ( serviceName != null ) {
305 ref.setInterfaceName(serviceName);
306 } else {
307 final Class<?>[] params = ad.getAnnotatedMethod().getParameterTypes();
308 if ( params != null && params.length > 0 ) {
309 ref.setInterfaceName(params[0].getName());
310 }
311 }
312 // cardinality
313 final String cardinality = ad.getEnumValue("cardinality", "MANDATORY");
314 if ( cardinality.equals("OPTIONAL") ) {
315 ref.setCardinality(ReferenceCardinality.OPTIONAL_UNARY);
316 } else if ( cardinality.equals("MULTIPLE") ) {
317 ref.setCardinality(ReferenceCardinality.OPTIONAL_MULTIPLE);
318 } else if ( cardinality.equals("AT_LEAST_ONE") ) {
319 ref.setCardinality(ReferenceCardinality.MANDATORY_MULTIPLE);
320 } else {
321 ref.setCardinality(ReferenceCardinality.MANDATORY_UNARY);
322 }
323
324 // policy
325 ref.setPolicy(ReferencePolicy.valueOf(ad.getEnumValue("policy", ReferencePolicy.STATIC.name())));
Carsten Ziegelerd156e362012-06-14 15:34:11 +0000326 // policy option
327 ref.setPolicyOption(ReferencePolicyOption.valueOf(ad.getEnumValue("policyOption", ReferencePolicyOption.RELUCTANT.name())));
Carsten Ziegeler98e992a2012-06-13 15:50:00 +0000328 // target
329 ref.setTarget(ad.getStringValue("target", null));
330 }
331
332 private boolean hasMethod(final ClassDescription classDescription, final String name) {
333 final Method[] allMethods = classDescription.getDescribedClass().getDeclaredMethods();
334 for(final Method m : allMethods) {
335 if ( m.getName().equals(name) ) {
336 return true;
337 }
338 }
339 return false;
340 }
341}