| /* |
| * 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.scrplugin; |
| |
| |
| import java.io.File; |
| import java.util.*; |
| |
| import org.apache.felix.scrplugin.helper.*; |
| import org.apache.felix.scrplugin.om.*; |
| import org.apache.felix.scrplugin.om.metatype.*; |
| import org.apache.felix.scrplugin.tags.*; |
| import org.apache.felix.scrplugin.tags.annotation.AnnotationJavaClassDescription; |
| import org.apache.felix.scrplugin.tags.qdox.QDoxJavaClassDescription; |
| import org.apache.felix.scrplugin.xml.ComponentDescriptorIO; |
| import org.apache.felix.scrplugin.xml.MetaTypeIO; |
| import org.osgi.service.metatype.MetaTypeService; |
| |
| |
| /** |
| * The <code>SCRDescriptorGenerator</code> class does the hard work of |
| * generating the SCR descriptors. This class is being instantiated and |
| * configured by clients and the {@link #execute()} method called to generate |
| * the descriptor files. |
| * <p> |
| * When using this class carefully consider calling <i>all</i> setter methods |
| * to properly configure the generator. All setter method document, which |
| * default value is assumed for the respective property if the setter is |
| * not called. |
| * <p> |
| * Instances of this class are not thread save and should not be reused. |
| */ |
| public class SCRDescriptorGenerator |
| { |
| |
| private final Log logger; |
| |
| private File outputDirectory = null; |
| |
| private JavaClassDescriptorManager descriptorManager; |
| |
| private String finalName = "serviceComponents.xml"; |
| |
| private String metaTypeName = "metatype.xml"; |
| |
| private boolean generateAccessors = true; |
| |
| protected boolean strictMode = false; |
| |
| private Map<String, String> properties = new HashMap<String, String>(); |
| |
| private String specVersion = null; |
| |
| |
| /** |
| * Create an instance of this generator using the given {@link Log} |
| * instance of logging. |
| */ |
| public SCRDescriptorGenerator( Log logger ) |
| { |
| this.logger = logger; |
| } |
| |
| |
| /** |
| * Sets the directory where the descriptor files will be created. |
| * <p> |
| * This field has no default value and this setter <b>must</b> called prior |
| * to calling {@link #execute()}. |
| */ |
| public void setOutputDirectory( File outputDirectory ) |
| { |
| this.outputDirectory = outputDirectory; |
| } |
| |
| |
| /** |
| * Sets the {@link JavaClassDescriptorManager} instance used to access |
| * existing descriptors and to parse JavaDoc tags as well as interpret |
| * the SCR annotations. |
| * <p> |
| * This field has no default value and this setter <b>must</b> called prior |
| * to calling {@link #execute()}. |
| */ |
| public void setDescriptorManager( JavaClassDescriptorManager descriptorManager ) |
| { |
| this.descriptorManager = descriptorManager; |
| } |
| |
| |
| /** |
| * Sets the name of the SCR declaration descriptor file. This file will be |
| * created in the <i>OSGI-INF</i> directory below the |
| * {@link #setOutputDirectory(File) output directory}. |
| * <p> |
| * This file will be overwritten if already existing. If no descriptors |
| * are created the file is actually removed. |
| * <p> |
| * The default value of this property is <code>serviceComponents.xml</code>. |
| */ |
| public void setFinalName( String finalName ) |
| { |
| this.finalName = finalName; |
| } |
| |
| |
| /** |
| * Sets the name of the file taking the Metatype Service descriptors. This |
| * file will be created in the <i>OSGI-INF/metatype</i> directory below the |
| * {@link #setOutputDirectory(File) output directory}. |
| * <p> |
| * This file will be overwritten if already existing. If no descriptors |
| * are created the file is actually removed. |
| * <p> |
| * The default value of this property is <code>metatype.xml</code>. |
| */ |
| public void setMetaTypeName( String metaTypeName ) |
| { |
| this.metaTypeName = metaTypeName; |
| } |
| |
| |
| /** |
| * Defines whether bind and unbind methods are automatically created by |
| * the SCR descriptor generator. |
| * <p> |
| * The generator uses the ASM library to create the method byte codes |
| * directly inside the class files. If bind and unbind methods are not |
| * to be created, the generator fails if such methods are missing. |
| * <p> |
| * The default value of this property is <code>true</code>. |
| */ |
| public void setGenerateAccessors( boolean generateAccessors ) |
| { |
| this.generateAccessors = generateAccessors; |
| } |
| |
| |
| /** |
| * Defines whether warnings should be considered as errors and thus cause |
| * the generation process to fail. |
| * <p> |
| * The default value of this property is <code>false</code>. |
| */ |
| public void setStrictMode( boolean strictMode ) |
| { |
| this.strictMode = strictMode; |
| } |
| |
| |
| /** |
| * Sets global properties to be set for each descriptor. If a descriptor |
| * provides properties of the same name, the descriptor properties are preferred |
| * over the properties provided here. |
| * <p> |
| * The are no default global properties. |
| */ |
| public void setProperties( Map<String, String> properties ) |
| { |
| this.properties = new HashMap<String, String>( properties ); |
| } |
| |
| |
| /** |
| * Sets the Declarative Services specification version number to be forced |
| * on the declarations. |
| * <p> |
| * Supported values for this property are <code>null</code> to autodetect |
| * the specification version, <code>1.0</code> to force 1.0 descriptors and |
| * <code>1.1</code> to force 1.1 descriptors. If 1.0 descriptors are forced |
| * the generation fails if a descriptor requires 1.1 functionality. |
| * <p> |
| * The default is to generate the descriptor version according to the |
| * capabilities used by the descriptors. If no 1.1 capabilities, such as |
| * <code>configuration-policy</code>, are used, version 1.0 is used, |
| * otherwise a 1.1 descriptor is generated. |
| */ |
| public void setSpecVersion( String specVersion ) |
| { |
| this.specVersion = specVersion; |
| } |
| |
| |
| /** |
| * Actually generates the Declarative Services and Metatype descriptors |
| * scanning the java sources provided by the |
| * {@link #setDescriptorManager(JavaClassDescriptorManager) descriptor manager}. |
| * |
| * @return <code>true</code> if descriptors have been generated. |
| * |
| * @throws SCRDescriptorException |
| * @throws SCRDescriptorFailureException |
| */ |
| public boolean execute() throws SCRDescriptorException, SCRDescriptorFailureException |
| { |
| this.logger.debug( "Starting SCRDescriptorMojo...." ); |
| this.logger.debug( "..generating accessors: " + this.generateAccessors ); |
| this.logger.debug( "..parsing javadocs: " + this.descriptorManager.isParseJavadocs() ); |
| this.logger.debug( "..processing annotations: " + this.descriptorManager.isProcessAnnotations() ); |
| |
| // check speck version configuration |
| int specVersion = toSpecVersionCode( this.specVersion, null ); |
| if ( this.specVersion == null ) |
| { |
| this.logger.debug( "..auto detecting spec version" ); |
| } |
| else |
| { |
| this.logger.debug( "..using spec version " + this.specVersion + " (" + specVersion + ")" ); |
| } |
| |
| final IssueLog iLog = new IssueLog( this.strictMode ); |
| |
| final MetaData metaData = new MetaData(); |
| metaData.setLocalization( MetaTypeService.METATYPE_DOCUMENTS_LOCATION + "/metatype" ); |
| |
| // iterate through all source classes and check for component tag |
| final JavaClassDescription[] javaSources = descriptorManager.getSourceDescriptions(); |
| Arrays.sort( javaSources, new JavaClassDescriptionInheritanceComparator() ); |
| |
| final List<Component> scannedComponents = new ArrayList<Component>(); |
| for ( int i = 0; i < javaSources.length; i++ ) |
| { |
| this.logger.debug( "Testing source " + javaSources[i].getName() ); |
| final JavaTag tag = javaSources[i].getTagByName( Constants.COMPONENT ); |
| if ( tag != null ) |
| { |
| // FELIX-2853 : Deprecate javadoc tags. |
| // This is not the most clever way of doing this, but it is the least intrusive... |
| if ( javaSources[i] instanceof QDoxJavaClassDescription |
| && !(javaSources[i] instanceof AnnotationJavaClassDescription)) { |
| iLog.addDeprecationWarning("Class " + javaSources[i].getName() + " is using deprecated javadoc tags ", |
| tag.getSourceLocation(), tag.getLineNumber()); |
| } |
| this.logger.debug( "Processing service class " + javaSources[i].getName() ); |
| // check if there is more than one component tag! |
| if ( javaSources[i].getTagsByName( Constants.COMPONENT, false ).length > 1 ) |
| { |
| iLog.addError( "Class " + javaSources[i].getName() + " has more than one " + Constants.COMPONENT |
| + " tag." + " Merge the tags to a single tag.", tag.getSourceLocation(), tag.getLineNumber() ); |
| } |
| else |
| { |
| try |
| { |
| final Component comp = this.createComponent( javaSources[i], tag, metaData, iLog ); |
| if ( comp.getSpecVersion() > specVersion ) |
| { |
| // if a spec version has been configured and a component requires a higher |
| // version, this is considered an error! |
| if ( this.specVersion != null ) |
| { |
| String v = Constants.COMPONENT_DS_SPEC_VERSION_10; |
| if ( comp.getSpecVersion() == Constants.VERSION_1_1 ) |
| { |
| v = Constants.COMPONENT_DS_SPEC_VERSION_11; |
| } |
| iLog.addError( "Component " + comp + " requires spec version " + v |
| + " but plugin is configured to use version " + this.specVersion, tag |
| .getSourceLocation(), tag.getLineNumber() ); |
| } |
| specVersion = comp.getSpecVersion(); |
| } |
| scannedComponents.add( comp ); |
| } |
| catch ( SCRDescriptorException sde ) |
| { |
| iLog.addError( sde.getMessage(), sde.getSourceLocation(), sde.getLineNumber() ); |
| } |
| } |
| } |
| } |
| this.logger.debug( "..generating descriptor for spec version: " + this.specVersion ); |
| |
| // now check for abstract components and fill components objects |
| final Components components = new Components(); |
| final Components abstractComponents = new Components(); |
| components.setSpecVersion( specVersion ); |
| abstractComponents.setSpecVersion( specVersion ); |
| |
| for ( final Component comp : scannedComponents ) |
| { |
| final int errorCount = iLog.getNumberOfErrors(); |
| // before we can validate we should check the references for bind/unbind method |
| // in order to create them if possible |
| |
| for ( final Reference ref : comp.getReferences() ) |
| { |
| // if this is a field with a single cardinality, |
| // we look for the bind/unbind methods |
| // and create them if they are not availabe |
| if ( this.generateAccessors && !ref.isLookupStrategy() ) |
| { |
| if ( ref.getJavaTag().getField() != null |
| && comp.getJavaClassDescription() instanceof ModifiableJavaClassDescription ) |
| { |
| if ( ref.getCardinality().equals( "0..1" ) || ref.getCardinality().equals( "1..1" ) ) |
| { |
| final String bindValue = ref.getBind(); |
| final String unbindValue = ref.getUnbind(); |
| final String name = ref.getName(); |
| final String type = ref.getInterfacename(); |
| |
| boolean createBind = false; |
| boolean createUnbind = false; |
| // Only create method if no bind name has been specified |
| if ( bindValue == null && ref.findMethod( specVersion, "bind" ) == null ) |
| { |
| // create bind method |
| createBind = true; |
| } |
| if ( unbindValue == null && ref.findMethod( specVersion, "unbind" ) == null ) |
| { |
| // create unbind method |
| createUnbind = true; |
| } |
| if ( createBind || createUnbind ) |
| { |
| ( ( ModifiableJavaClassDescription ) comp.getJavaClassDescription() ).addMethods( name, |
| type, createBind, createUnbind ); |
| } |
| } |
| } |
| } |
| } |
| comp.validate( specVersion, iLog ); |
| // ignore component if it has errors |
| if ( iLog.getNumberOfErrors() == errorCount ) |
| { |
| if ( !comp.isDs() ) |
| { |
| logger.debug( "Ignoring descriptor " + comp ); |
| } |
| else if ( comp.isAbstract() ) |
| { |
| this.logger.debug( "Adding abstract descriptor " + comp ); |
| abstractComponents.addComponent( comp ); |
| } |
| else |
| { |
| this.logger.debug( "Adding descriptor " + comp ); |
| components.addComponent( comp ); |
| abstractComponents.addComponent( comp ); |
| } |
| } |
| } |
| |
| // log issues |
| iLog.logMessages( logger ); |
| |
| // after checking all classes, throw if there were any failures |
| if ( iLog.hasErrors() ) |
| { |
| throw new SCRDescriptorFailureException( "SCR Descriptor parsing had failures (see log)" ); |
| } |
| |
| boolean addResources = false; |
| // write meta type info if there is a file name |
| if ( !StringUtils.isEmpty( this.metaTypeName ) ) |
| { |
| File mtFile = new File( this.outputDirectory, "OSGI-INF" + File.separator + "metatype" + File.separator |
| + this.metaTypeName ); |
| final int size = metaData.getOCDs().size() + metaData.getDesignates().size(); |
| if ( size > 0 ) |
| { |
| this.logger.info( "Generating " + size + " MetaType Descriptors to " + mtFile ); |
| mtFile.getParentFile().mkdirs(); |
| MetaTypeIO.write( metaData, mtFile ); |
| addResources = true; |
| } |
| else |
| { |
| if ( mtFile.exists() ) |
| { |
| mtFile.delete(); |
| } |
| } |
| |
| } |
| else |
| { |
| this.logger.info( "Meta type file name is not set: meta type info is not written." ); |
| } |
| |
| // if we have descriptors, write them in our scr private file (for component inheritance) |
| final File adFile = new File( this.outputDirectory, Constants.ABSTRACT_DESCRIPTOR_RELATIVE_PATH ); |
| if ( !abstractComponents.getComponents().isEmpty() ) |
| { |
| this.logger.info( "Writing abstract service descriptor " + adFile + " with " |
| + abstractComponents.getComponents().size() + " entries." ); |
| adFile.getParentFile().mkdirs(); |
| ComponentDescriptorIO.write( abstractComponents, adFile, true ); |
| addResources = true; |
| } |
| else |
| { |
| this.logger.debug( "No abstract SCR Descriptors found in project." ); |
| // remove file |
| if ( adFile.exists() ) |
| { |
| this.logger.debug( "Removing obsolete abstract service descriptor " + adFile ); |
| adFile.delete(); |
| } |
| } |
| |
| // check descriptor file |
| final File descriptorFile = StringUtils.isEmpty( this.finalName ) ? null : new File( new File( |
| this.outputDirectory, "OSGI-INF" ), this.finalName ); |
| |
| // terminate if there is nothing else to write |
| if ( components.getComponents().isEmpty() ) |
| { |
| this.logger.debug( "No SCR Descriptors found in project." ); |
| // remove file if it exists |
| if ( descriptorFile != null && descriptorFile.exists() ) |
| { |
| this.logger.debug( "Removing obsolete service descriptor " + descriptorFile ); |
| descriptorFile.delete(); |
| } |
| } |
| else |
| { |
| if ( descriptorFile == null ) |
| { |
| throw new SCRDescriptorFailureException( "Descriptor file name must not be empty." ); |
| } |
| |
| // finally the descriptors have to be written .... |
| descriptorFile.getParentFile().mkdirs(); // ensure parent dir |
| |
| this.logger.info( "Generating " + components.getComponents().size() + " Service Component Descriptors to " |
| + descriptorFile ); |
| |
| ComponentDescriptorIO.write( components, descriptorFile, false ); |
| addResources = true; |
| } |
| |
| return addResources; |
| } |
| |
| |
| /** |
| * Create a component for the java class description. |
| * @param description |
| * @return The generated component descriptor or null if any error occurs. |
| * @throws SCRDescriptorException |
| */ |
| protected Component createComponent( JavaClassDescription description, JavaTag componentTag, MetaData metaData, |
| final IssueLog iLog ) throws SCRDescriptorException |
| { |
| // create a new component |
| final Component component = new Component( componentTag ); |
| |
| // set implementation |
| component.setImplementation( new Implementation( description.getName() ) ); |
| |
| final boolean inherited = getBoolean( componentTag, Constants.COMPONENT_INHERIT, true ); |
| final OCD ocd = this.doComponent( componentTag, component, metaData,inherited, iLog ); |
| |
| this.doServices( description.getTagsByName( Constants.SERVICE, inherited ), component, description ); |
| |
| // collect references from class tags and fields |
| final Map<String, Object[]> references = new LinkedHashMap<String, Object[]>(); |
| // Utility handler for propertie |
| final PropertyHandler propertyHandler = new PropertyHandler( component, ocd ); |
| |
| JavaClassDescription currentDescription = description; |
| do |
| { |
| // properties |
| final JavaTag[] props = currentDescription.getTagsByName( Constants.PROPERTY, false ); |
| for ( int i = 0; i < props.length; i++ ) |
| { |
| propertyHandler.testProperty( props[i], null, description == currentDescription ); |
| } |
| |
| // references |
| final JavaTag[] refs = currentDescription.getTagsByName( Constants.REFERENCE, false ); |
| for ( int i = 0; i < refs.length; i++ ) |
| { |
| this.testReference( references, refs[i], null, description == currentDescription ); |
| } |
| |
| // fields |
| final JavaField[] fields = currentDescription.getFields(); |
| for ( int i = 0; i < fields.length; i++ ) |
| { |
| JavaTag tag = fields[i].getTagByName( Constants.REFERENCE ); |
| if ( tag != null ) |
| { |
| this.testReference( references, tag, fields[i].getName(), description == currentDescription ); |
| } |
| |
| propertyHandler.handleField( fields[i], description == currentDescription ); |
| } |
| |
| currentDescription = currentDescription.getSuperClass(); |
| } |
| while ( inherited && currentDescription != null ); |
| |
| // process properties |
| propertyHandler.processProperties( this.properties, iLog ); |
| |
| // process references |
| final Iterator<Map.Entry<String, Object[]>> refIter = references.entrySet().iterator(); |
| while ( refIter.hasNext() ) |
| { |
| final Map.Entry<String, Object[]> entry = refIter.next(); |
| final String refName = entry.getKey(); |
| final Object[] values = entry.getValue(); |
| final JavaTag tag = ( JavaTag ) values[0]; |
| this.doReference( tag, refName, component, values[1].toString(), ((Boolean)values[2]).booleanValue() ); |
| } |
| |
| // pid handling |
| final boolean createPid = getBoolean( componentTag, Constants.COMPONENT_CREATE_PID, true ); |
| if ( createPid ) |
| { |
| // check for an existing pid first |
| boolean found = false; |
| final Iterator<Property> iter = component.getProperties().iterator(); |
| while ( !found && iter.hasNext() ) |
| { |
| final Property prop = iter.next(); |
| found = org.osgi.framework.Constants.SERVICE_PID.equals( prop.getName() ); |
| } |
| if ( !found ) |
| { |
| final Property pid = new Property(); |
| component.addProperty( pid ); |
| pid.setName( org.osgi.framework.Constants.SERVICE_PID ); |
| pid.setValue( component.getName() ); |
| } |
| } |
| return component; |
| } |
| |
| |
| /** |
| * Fill the component object with the information from the tag. |
| * @param tag |
| * @param component |
| */ |
| protected OCD doComponent( final JavaTag tag, |
| final Component component, |
| final MetaData metaData, |
| final boolean inherit, |
| final IssueLog iLog ) |
| throws SCRDescriptorException |
| { |
| |
| // check if this is an abstract definition |
| final String abstractType = tag.getNamedParameter( Constants.COMPONENT_ABSTRACT ); |
| if ( abstractType != null ) |
| { |
| component.setAbstract( "yes".equalsIgnoreCase( abstractType ) || "true".equalsIgnoreCase( abstractType ) ); |
| } |
| else |
| { |
| // default true for abstract classes, false otherwise |
| component.setAbstract( tag.getJavaClassDescription().isAbstract() ); |
| } |
| |
| // check if this is a definition to ignore |
| final String ds = tag.getNamedParameter( Constants.COMPONENT_DS ); |
| component.setDs( ( ds == null ) ? true : ( "yes".equalsIgnoreCase( ds ) || "true".equalsIgnoreCase( ds ) ) ); |
| |
| String name = tag.getNamedParameter( Constants.COMPONENT_NAME ); |
| component.setName( StringUtils.isEmpty( name ) ? component.getImplementation().getClassame() : name ); |
| |
| component.setEnabled( Boolean.valueOf( getBoolean( tag, Constants.COMPONENT_ENABLED, true ) ) ); |
| component.setFactory( tag.getNamedParameter( Constants.COMPONENT_FACTORY ) ); |
| |
| // FELIX-1703: support explicit SCR version declaration |
| final String dsSpecVersion = tag.getNamedParameter( Constants.COMPONENT_DS_SPEC_VERSION ); |
| if ( dsSpecVersion != null ) |
| { |
| component.setSpecVersion( toSpecVersionCode( dsSpecVersion, tag ) ); |
| } |
| |
| // FELIX-593: immediate attribute does not default to true all the |
| // times hence we only set it if declared in the tag |
| if ( tag.getNamedParameter( Constants.COMPONENT_IMMEDIATE ) != null ) |
| { |
| component.setImmediate( Boolean.valueOf( getBoolean( tag, Constants.COMPONENT_IMMEDIATE, true ) ) ); |
| } |
| |
| // check for V1.1 attributes: configuration policy |
| if ( tag.getNamedParameter( Constants.COMPONENT_CONFIG_POLICY ) != null ) |
| { |
| component.setSpecVersion( Constants.VERSION_1_1 ); |
| component.setConfigurationPolicy( tag.getNamedParameter( Constants.COMPONENT_CONFIG_POLICY ) ); |
| } |
| // check for V1.1 attributes: activate, deactivate, modified |
| component.setActivate(this.checkLifecycleMethod(component, Constants.COMPONENT_ACTIVATE, tag, inherit)); |
| component.setDeactivate(this.checkLifecycleMethod(component, Constants.COMPONENT_DEACTIVATE, tag, inherit)); |
| component.setModified(this.checkLifecycleMethod(component, Constants.COMPONENT_MODIFIED, tag, inherit)); |
| |
| // whether metatype information is to generated for the component |
| final String metaType = tag.getNamedParameter( Constants.COMPONENT_METATYPE ); |
| final boolean hasMetaType = metaType == null || "yes".equalsIgnoreCase( metaType ) |
| || "true".equalsIgnoreCase( metaType ); |
| if ( !component.isAbstract() && hasMetaType ) |
| { |
| // ocd |
| final OCD ocd = new OCD(); |
| metaData.addOCD( ocd ); |
| ocd.setId( component.getName() ); |
| String ocdName = tag.getNamedParameter( Constants.COMPONENT_LABEL ); |
| if ( ocdName == null ) |
| { |
| ocdName = "%" + component.getName() + ".name"; |
| } |
| ocd.setName( ocdName ); |
| String ocdDescription = tag.getNamedParameter( Constants.COMPONENT_DESCRIPTION ); |
| if ( ocdDescription == null ) |
| { |
| ocdDescription = "%" + component.getName() + ".description"; |
| } |
| ocd.setDescription( ocdDescription ); |
| // designate |
| final Designate designate = new Designate(); |
| metaData.addDesignate( designate ); |
| designate.setPid( component.getName() ); |
| |
| // factory pid |
| final String setFactoryPidValue = tag.getNamedParameter( Constants.COMPONENT_SET_METATYPE_FACTORY_PID ); |
| final boolean setFactoryPid = setFactoryPidValue != null |
| && ( "yes".equalsIgnoreCase( setFactoryPidValue ) || "true".equalsIgnoreCase( setFactoryPidValue ) ); |
| if ( setFactoryPid ) |
| { |
| if ( component.getFactory() == null ) |
| { |
| designate.setFactoryPid( component.getName() ); |
| } |
| else |
| { |
| iLog.addWarning( "Component factory " + component.getName() |
| + " should not set metatype factory pid.", tag.getSourceLocation(), tag.getLineNumber() ); |
| } |
| } |
| // designate.object |
| final MTObject mtobject = new MTObject(); |
| designate.setObject( mtobject ); |
| mtobject.setOcdref( component.getName() ); |
| return ocd; |
| } |
| return null; |
| } |
| |
| private String checkLifecycleMethod(final Component component, |
| final String methodTagName, |
| final JavaTag tag, |
| final boolean inherit) |
| throws SCRDescriptorException |
| { |
| String method = null; |
| if ( tag.getNamedParameter( methodTagName ) != null ) |
| { |
| method = tag.getNamedParameter( methodTagName ); |
| } |
| else if ( inherit ) |
| { |
| // check if a super class has the activate method specified |
| JavaClassDescription desc = tag.getJavaClassDescription().getSuperClass(); |
| while ( desc != null && method == null ) |
| { |
| final JavaTag componentTag = desc.getTagByName( Constants.COMPONENT ); |
| if ( componentTag != null && componentTag.getNamedParameter( methodTagName ) != null ) |
| { |
| method = componentTag.getNamedParameter( methodTagName ); |
| } |
| desc = desc.getSuperClass(); |
| } |
| } |
| if ( method != null ) |
| { |
| component.setSpecVersion( Constants.VERSION_1_1 ); |
| } |
| return method; |
| } |
| |
| /** |
| * Process the service annotations |
| * @param services |
| * @param component |
| * @param description |
| * @throws SCRDescriptorException |
| */ |
| protected void doServices( JavaTag[] services, Component component, JavaClassDescription description ) |
| throws SCRDescriptorException |
| { |
| // no services, hence certainly no service factory |
| if ( services == null || services.length == 0 ) |
| { |
| return; |
| } |
| |
| final Service service = new Service(); |
| component.setService( service ); |
| boolean serviceFactory = false; |
| for ( int i = 0; i < services.length; i++ ) |
| { |
| final String name = services[i].getNamedParameter( Constants.SERVICE_INTERFACE ); |
| if ( StringUtils.isEmpty( name ) ) |
| { |
| |
| this.addInterfaces( service, services[i], description ); |
| } |
| else |
| { |
| String interfaceName = name; |
| // check if the value points to a class/interface |
| // and search through the imports |
| // but only for local services |
| if ( description instanceof QDoxJavaClassDescription ) |
| { |
| final JavaClassDescription serviceClass = description.getReferencedClass( name ); |
| if ( serviceClass == null ) |
| { |
| throw new SCRDescriptorException( "Interface '" + name + "' in class " + description.getName() |
| + " does not point to a valid class/interface.", services[i] ); |
| } |
| interfaceName = serviceClass.getName(); |
| } |
| final Interface interf = new Interface( services[i] ); |
| interf.setInterfacename( interfaceName ); |
| service.addInterface( interf ); |
| } |
| |
| serviceFactory |= getBoolean( services[i], Constants.SERVICE_FACTORY, false ); |
| } |
| |
| service.setServicefactory( serviceFactory ); |
| } |
| |
| |
| /** |
| * Recursively add interfaces to the service. |
| */ |
| protected void addInterfaces( final Service service, final JavaTag serviceTag, |
| final JavaClassDescription description ) throws SCRDescriptorException |
| { |
| if ( description != null ) |
| { |
| JavaClassDescription[] interfaces = description.getImplementedInterfaces(); |
| for ( int j = 0; j < interfaces.length; j++ ) |
| { |
| final Interface interf = new Interface( serviceTag ); |
| interf.setInterfacename( interfaces[j].getName() ); |
| service.addInterface( interf ); |
| // recursivly add interfaces implemented by this interface |
| this.addInterfaces( service, serviceTag, interfaces[j] ); |
| } |
| |
| // try super class |
| this.addInterfaces( service, serviceTag, description.getSuperClass() ); |
| } |
| } |
| |
| |
| /** |
| * Test a newly found reference |
| * @param references |
| * @param reference |
| * @param defaultName |
| * @param isInspectedClass |
| * @throws SCRDescriptorException |
| */ |
| protected void testReference( Map<String, Object[]> references, JavaTag reference, String defaultName, |
| boolean isInspectedClass ) throws SCRDescriptorException |
| { |
| String refName = this.getReferenceName( reference, defaultName ); |
| |
| boolean setName = refName != null; |
| if ( refName == null) |
| { |
| refName = this.getReferencedInterface(reference, isInspectedClass, null); |
| } |
| |
| if ( refName != null ) |
| { |
| if ( references.containsKey( refName ) ) |
| { |
| // if the current class is the class we are currently inspecting, we |
| // have found a duplicate definition |
| if ( isInspectedClass ) |
| { |
| throw new SCRDescriptorException( "Duplicate definition for reference " + refName + " in class " |
| + reference.getJavaClassDescription().getName(), reference ); |
| } |
| } |
| else |
| { |
| // ensure interface |
| final String type = this.getReferencedInterface(reference, isInspectedClass, refName); |
| references.put( refName, new Object[] |
| { reference, type, (setName ? Boolean.TRUE : Boolean.FALSE) } ); |
| } |
| } |
| else |
| { |
| throw new SCRDescriptorException( "No name detectable for reference in class " |
| + reference.getJavaClassDescription().getName(), reference ); |
| } |
| } |
| |
| protected String getReferencedInterface(final JavaTag reference, |
| final boolean isInspectedClass, |
| final String refName) throws SCRDescriptorException |
| { |
| String type = reference.getNamedParameter( Constants.REFERENCE_INTERFACE ); |
| if ( StringUtils.isEmpty( type ) ) |
| { |
| if ( reference.getField() != null ) |
| { |
| type = reference.getField().getType(); |
| } |
| else |
| { |
| throw new SCRDescriptorException( "Interface missing for reference " + (refName == null ? "" : refName) + " in class " |
| + reference.getJavaClassDescription().getName(), reference ); |
| } |
| } |
| else if ( isInspectedClass ) |
| { |
| // check if the value points to a class/interface |
| // and search through the imports |
| final JavaClassDescription serviceClass = reference.getJavaClassDescription().getReferencedClass( |
| type ); |
| if ( serviceClass == null ) |
| { |
| throw new SCRDescriptorException( "Interface '" + type + "' in class " |
| + reference.getJavaClassDescription().getName() |
| + " does not point to a valid class/interface.", reference ); |
| } |
| type = serviceClass.getName(); |
| } |
| return type; |
| } |
| |
| protected String getReferenceName( final JavaTag reference, |
| final String defaultName) throws SCRDescriptorException |
| { |
| String name = reference.getNamedParameter( Constants.REFERENCE_NAME ); |
| if ( !StringUtils.isEmpty( name ) ) |
| { |
| return name; |
| } |
| name = reference.getNamedParameter( Constants.REFERENCE_NAME_REF ); |
| if ( !StringUtils.isEmpty( name ) ) |
| { |
| final JavaField refField = this.getReferencedField( reference, name ); |
| final String[] values = refField.getInitializationExpression(); |
| if ( values == null || values.length == 0 ) |
| { |
| throw new SCRDescriptorException( "Referenced field for " + name |
| + " has no values for a reference name.", reference ); |
| } |
| if ( values.length > 1 ) |
| { |
| throw new SCRDescriptorException( "Referenced field " + name |
| + " has more than one value for a reference name.", reference ); |
| } |
| return values[0]; |
| } |
| |
| return defaultName; |
| } |
| |
| |
| protected JavaField getReferencedField( final JavaTag tag, String ref ) throws SCRDescriptorException |
| { |
| int classSep = ref.lastIndexOf( '.' ); |
| JavaField field = null; |
| if ( classSep == -1 ) |
| { |
| // local variable |
| field = tag.getJavaClassDescription().getFieldByName( ref ); |
| } |
| if ( field == null ) |
| { |
| field = tag.getJavaClassDescription().getExternalFieldByName( ref ); |
| } |
| if ( field == null ) |
| { |
| throw new SCRDescriptorException( "Reference references unknown field " + ref + " in class " |
| + tag.getJavaClassDescription().getName(), tag ); |
| } |
| return field; |
| } |
| |
| |
| /** |
| * Process a reference |
| * @param reference |
| * @param defaultName |
| * @param component |
| */ |
| protected void doReference( JavaTag reference, String name, Component component, String type, |
| final boolean setName) |
| throws SCRDescriptorException |
| { |
| final Reference ref = new Reference( reference, component.getJavaClassDescription() ); |
| if ( setName ) |
| { |
| ref.setName( name ); |
| } |
| ref.setInterfacename( type ); |
| ref.setCardinality( reference.getNamedParameter( Constants.REFERENCE_CARDINALITY ) ); |
| if ( ref.getCardinality() == null ) |
| { |
| ref.setCardinality( "1..1" ); |
| } |
| ref.setPolicy( reference.getNamedParameter( Constants.REFERENCE_POLICY ) ); |
| if ( ref.getPolicy() == null ) |
| { |
| ref.setPolicy( "static" ); |
| } |
| ref.setTarget( reference.getNamedParameter( Constants.REFERENCE_TARGET ) ); |
| final String bindValue = reference.getNamedParameter( Constants.REFERENCE_BIND ); |
| if ( bindValue != null ) |
| { |
| ref.setBind( bindValue ); |
| } |
| final String unbindValue = reference.getNamedParameter( Constants.REFERENCE_UNDBIND ); |
| if ( unbindValue != null ) |
| { |
| ref.setUnbind( unbindValue ); |
| } |
| final String updatedValue = reference.getNamedParameter( Constants.REFERENCE_UPDATED ); |
| if ( updatedValue != null ) |
| { |
| ref.setUpdated( updatedValue ); |
| } |
| final String isChecked = reference.getNamedParameter( Constants.REFERENCE_CHECKED ); |
| if ( isChecked != null ) |
| { |
| ref.setChecked( Boolean.valueOf( isChecked ).booleanValue() ); |
| } |
| final String strategy = reference.getNamedParameter( Constants.REFERENCE_STRATEGY ); |
| if ( strategy != null ) |
| { |
| ref.setStrategy( strategy ); |
| } |
| |
| component.addReference( ref ); |
| } |
| |
| |
| public static boolean getBoolean( JavaTag tag, String name, boolean defaultValue ) |
| { |
| String value = tag.getNamedParameter( name ); |
| return ( value == null ) ? defaultValue : Boolean.valueOf( value ).booleanValue(); |
| } |
| |
| |
| |
| /** |
| * Converts the specification version string to a specification version |
| * code. Currently the following conversions are supported: |
| * <table> |
| * <tr><td><code>null</code></td><td>0</td></tr> |
| * <tr><td>1.0</td><td>0</td></tr> |
| * <tr><td>1.1</td><td>1</td></tr> |
| * <tr><td>1.1-felix</td><td>2</td></tr> |
| * </table> |
| * |
| * @param specVersion The specification version to convert. This may be |
| * <code>null</code> to assume the default version. |
| * |
| * @return The specification version code. |
| * |
| * @throws SCRDescriptorException if the <code>specVersion</code> parameter |
| * is not a supported value. |
| */ |
| private int toSpecVersionCode( String specVersion, JavaTag tag ) throws SCRDescriptorException |
| { |
| if ( specVersion == null || specVersion.equals( Constants.COMPONENT_DS_SPEC_VERSION_10 ) ) |
| { |
| return Constants.VERSION_1_0; |
| } |
| else if ( specVersion.equals( Constants.COMPONENT_DS_SPEC_VERSION_11 ) ) |
| { |
| return Constants.VERSION_1_1; |
| } |
| else if ( specVersion.equals( Constants.COMPONENT_DS_SPEC_VERSION_11_FELIX ) ) |
| { |
| return Constants.VERSION_1_1_FELIX; |
| } |
| |
| // unknown specVersion string |
| throw new SCRDescriptorException( "Unsupported or unknown DS spec version: " + specVersion, tag ); |
| } |
| } |