Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 1 | /* |
| 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 | */ |
| 19 | package org.apache.felix.scrplugin.ds; |
| 20 | |
| 21 | import java.lang.reflect.Method; |
| 22 | import java.lang.reflect.Modifier; |
| 23 | import java.util.ArrayList; |
| 24 | import java.util.List; |
| 25 | |
| 26 | import org.apache.felix.scrplugin.SCRDescriptorException; |
| 27 | import org.apache.felix.scrplugin.SCRDescriptorFailureException; |
Carsten Ziegeler | 44ee894 | 2012-06-14 15:03:37 +0000 | [diff] [blame] | 28 | import org.apache.felix.scrplugin.SpecVersion; |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 29 | import org.apache.felix.scrplugin.annotations.AnnotationProcessor; |
| 30 | import org.apache.felix.scrplugin.annotations.ClassAnnotation; |
| 31 | import org.apache.felix.scrplugin.annotations.MethodAnnotation; |
| 32 | import org.apache.felix.scrplugin.annotations.ScannedClass; |
| 33 | import org.apache.felix.scrplugin.description.ClassDescription; |
| 34 | import org.apache.felix.scrplugin.description.ComponentConfigurationPolicy; |
| 35 | import org.apache.felix.scrplugin.description.ComponentDescription; |
Carsten Ziegeler | eeae04e | 2012-06-13 16:01:20 +0000 | [diff] [blame] | 36 | import org.apache.felix.scrplugin.description.PropertyDescription; |
| 37 | import org.apache.felix.scrplugin.description.PropertyType; |
| 38 | import org.apache.felix.scrplugin.description.PropertyUnbounded; |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 39 | import org.apache.felix.scrplugin.description.ReferenceCardinality; |
| 40 | import org.apache.felix.scrplugin.description.ReferenceDescription; |
| 41 | import org.apache.felix.scrplugin.description.ReferencePolicy; |
Carsten Ziegeler | d156e36 | 2012-06-14 15:34:11 +0000 | [diff] [blame] | 42 | import org.apache.felix.scrplugin.description.ReferencePolicyOption; |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 43 | import org.apache.felix.scrplugin.description.ReferenceStrategy; |
| 44 | import org.apache.felix.scrplugin.description.ServiceDescription; |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 45 | import org.osgi.service.component.annotations.Activate; |
| 46 | import org.osgi.service.component.annotations.Component; |
| 47 | import org.osgi.service.component.annotations.Deactivate; |
| 48 | import org.osgi.service.component.annotations.Modified; |
| 49 | import org.osgi.service.component.annotations.Reference; |
| 50 | |
| 51 | /** |
| 52 | * This is the processor for the DS annotations. |
| 53 | */ |
| 54 | public class DSAnnotationProcessor implements AnnotationProcessor { |
| 55 | |
| 56 | /** |
Carsten Ziegeler | 15e7368 | 2012-06-17 13:17:53 +0000 | [diff] [blame] | 57 | * @see org.apache.felix.scrplugin.annotations.AnnotationProcessor#getName() |
| 58 | */ |
| 59 | public String getName() { |
| 60 | return "DS Annotation Processor"; |
| 61 | } |
| 62 | |
| 63 | /** |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 64 | * @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 Ziegeler | 9a496fe | 2012-06-27 06:20:28 +0000 | [diff] [blame] | 90 | cd.setActivate(m.getAnnotatedMethod().getName()); |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 91 | scannedClass.processed(m); |
| 92 | } else if (m.getName().equals(Deactivate.class.getName())) { |
Carsten Ziegeler | 9a496fe | 2012-06-27 06:20:28 +0000 | [diff] [blame] | 93 | cd.setDeactivate(m.getAnnotatedMethod().getName()); |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 94 | scannedClass.processed(m); |
| 95 | } else if (m.getName().equals(Modified.class.getName())) { |
Carsten Ziegeler | 9a496fe | 2012-06-27 06:20:28 +0000 | [diff] [blame] | 96 | cd.setModified(m.getAnnotatedMethod().getName()); |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 97 | 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 Ziegeler | f38b5db | 2014-07-24 13:18:39 +0000 | [diff] [blame] | 136 | if (cad.hasValue("service") ) { |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 137 | final String[] interfaces = (String[]) cad.getValue("service"); |
Carsten Ziegeler | f38b5db | 2014-07-24 13:18:39 +0000 | [diff] [blame] | 138 | if ( interfaces != null ) { |
| 139 | for (final String t : interfaces) { |
| 140 | listedInterfaces.add(t); |
| 141 | } |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 142 | } |
| 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 Ziegeler | 9defc09 | 2012-07-01 13:31:00 +0000 | [diff] [blame] | 167 | component.setImmediate(cad.getBooleanValue("immediate", false)); |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 168 | } |
| 169 | |
Carsten Ziegeler | eeae04e | 2012-06-13 16:01:20 +0000 | [diff] [blame] | 170 | // 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 Ziegeler | adae60d | 2013-07-03 16:45:23 +0000 | [diff] [blame] | 184 | // 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 Ziegeler | eeae04e | 2012-06-13 16:01:20 +0000 | [diff] [blame] | 219 | } |
| 220 | } |
| 221 | } |
| 222 | // TODO: properties |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 223 | |
| 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 Ziegeler | 9a496fe | 2012-06-27 06:20:28 +0000 | [diff] [blame] | 229 | describedClass.getSource()); |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 230 | } |
| 231 | component.setSpecVersion(spec); |
| 232 | } |
| 233 | |
| 234 | // configuration policy |
Carsten Ziegeler | dd7245d | 2012-12-20 12:24:32 +0000 | [diff] [blame] | 235 | component.setConfigurationPolicy(ComponentConfigurationPolicy.valueOf(cad.getEnumValue("configurationPolicy", |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 236 | ComponentConfigurationPolicy.OPTIONAL.name()))); |
| 237 | |
Carsten Ziegeler | 6600513 | 2012-06-14 15:48:32 +0000 | [diff] [blame] | 238 | // configuration pid |
| 239 | component.setConfigurationPid(cad.getStringValue("configurationPid", null)); |
Carsten Ziegeler | 283ace9 | 2012-06-27 09:51:56 +0000 | [diff] [blame] | 240 | component.setCreatePid(false); |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 241 | |
| 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 Ziegeler | de63870 | 2012-08-13 04:22:00 +0000 | [diff] [blame] | 275 | defaultUnbindMethodName = "unset" + refNameByMethod; |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 276 | } else if ( methodName.startsWith("bind") ) { |
| 277 | refNameByMethod = methodName.substring(4); |
Carsten Ziegeler | de63870 | 2012-08-13 04:22:00 +0000 | [diff] [blame] | 278 | defaultUnbindMethodName = "unbind" + refNameByMethod; |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 279 | } else { |
| 280 | refNameByMethod = methodName; |
| 281 | defaultUnbindMethodName = "un" + refNameByMethod; |
| 282 | } |
| 283 | final String defaultUpdateMethodName = "updated" + refNameByMethod; |
| 284 | |
| 285 | // bind method |
Carsten Ziegeler | 9a496fe | 2012-06-27 06:20:28 +0000 | [diff] [blame] | 286 | ref.setBind(ad.getAnnotatedMethod().getName()); |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 287 | // unbind method |
| 288 | final String unbind = ad.getStringValue("unbind", |
| 289 | this.hasMethod(describedClass, defaultUnbindMethodName) ? defaultUnbindMethodName : "-"); |
| 290 | if ( !unbind.equals("-") ) { |
Carsten Ziegeler | 9a496fe | 2012-06-27 06:20:28 +0000 | [diff] [blame] | 291 | ref.setUnbind(unbind); |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 292 | } |
| 293 | // update method |
Carsten Ziegeler | 9a496fe | 2012-06-27 06:20:28 +0000 | [diff] [blame] | 294 | final String updated = ad.getStringValue("updated", |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 295 | this.hasMethod(describedClass, defaultUpdateMethodName) ? defaultUpdateMethodName : "-"); |
Carsten Ziegeler | 9a496fe | 2012-06-27 06:20:28 +0000 | [diff] [blame] | 296 | if ( !updated.equals("-") ) { |
| 297 | ref.setUpdated(updated); |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 298 | } |
| 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 Ziegeler | d156e36 | 2012-06-14 15:34:11 +0000 | [diff] [blame] | 326 | // policy option |
| 327 | ref.setPolicyOption(ReferencePolicyOption.valueOf(ad.getEnumValue("policyOption", ReferencePolicyOption.RELUCTANT.name()))); |
Carsten Ziegeler | 98e992a | 2012-06-13 15:50:00 +0000 | [diff] [blame] | 328 | // 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 | } |