/* * Copyright (C) 2007 the original author or authors. * * Licensed 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.codehaus.plexus.metadata.gleaner; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Modifier; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.List; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Configuration; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.component.repository.ComponentDescriptor; import org.codehaus.plexus.component.repository.ComponentRequirement; import org.codehaus.plexus.component.repository.ComponentRequirementList; import org.codehaus.plexus.configuration.PlexusConfiguration; import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; import org.codehaus.plexus.metadata.ann.AnnClass; import org.codehaus.plexus.metadata.ann.AnnField; import org.codehaus.plexus.metadata.ann.AnnReader; import org.codehaus.plexus.util.IOUtil; /** * A class component gleaner which inspects each type for <tt>org.codehaus.plexus.component.annotations.*</tt> annotations * and when found translates them into a {@link ComponentDescriptor}. * * @version $Id$ */ public class AnnotationComponentGleaner extends ComponentGleanerSupport implements ClassComponentGleaner { public ComponentDescriptor<?> glean(String className, ClassLoader cl) throws ComponentGleanerException { assert className != null; assert cl != null; AnnClass annClass = readClass(className.replace('.', '/'), cl); // Skip abstract classes if (Modifier.isAbstract(annClass.getAccess())) { return null; } Component anno = annClass.getAnnotation(Component.class); if (anno == null) { return null; } ComponentDescriptor<?> component = new ComponentDescriptor<Object>(); component.setRole(anno.role().getName()); component.setRoleHint(filterEmptyAsNull(anno.hint())); component.setImplementation(className); component.setVersion(filterEmptyAsNull(anno.version())); component.setComponentType(filterEmptyAsNull(anno.type())); component.setInstantiationStrategy(filterEmptyAsNull(anno.instantiationStrategy())); component.setLifecycleHandler(filterEmptyAsNull(anno.lifecycleHandler())); component.setComponentProfile(filterEmptyAsNull(anno.profile())); component.setComponentComposer(filterEmptyAsNull(anno.composer())); component.setComponentConfigurator(filterEmptyAsNull(anno.configurator())); component.setComponentFactory(filterEmptyAsNull(anno.factory())); component.setDescription(filterEmptyAsNull(anno.description())); component.setAlias(filterEmptyAsNull(anno.alias())); component.setIsolatedRealm(anno.isolatedRealm()); for (AnnClass c : getClasses(annClass, cl)) { for (AnnField field : c.getFields().values()) { ComponentRequirement requirement = findRequirement(field, c, cl); if (requirement != null) { component.addRequirement(requirement); } PlexusConfiguration config = findConfiguration(field, c, cl); if (config != null) { addChildConfiguration(component, config); } } // // TODO: Inspect methods? // } return component; } private AnnClass readClass(String className, ClassLoader cl) throws ComponentGleanerException { InputStream is = null; try { // only read annotation from project classes (not jars) Enumeration<URL> en = cl.getResources( className + ".class" ); while ( en.hasMoreElements() ) { URL url = en.nextElement(); if( url.toString().startsWith( "file:" ) ) { is = url.openStream(); return AnnReader.read( is, cl ); } } throw new ComponentGleanerException("Can't find class " + className); } catch (IOException ex) { throw new ComponentGleanerException("Can't read class " + className, ex); } finally { IOUtil.close(is); } } private AnnClass readClass2(String className, ClassLoader cl) throws ComponentGleanerException { InputStream is = null; try { is = cl.getResourceAsStream(className + ".class"); return AnnReader.read(is, cl); } catch (IOException ex) { throw new ComponentGleanerException("Can't read class " + className, ex); } finally { IOUtil.close(is); } } /** * Returns a list of all of the classes which the given type inherits from. */ private List<AnnClass> getClasses(AnnClass annClass, ClassLoader cl) throws ComponentGleanerException { assert annClass != null; List<AnnClass> classes = new ArrayList<AnnClass>(); while(annClass!=null) { classes.add(annClass); if(annClass.getSuperName()!=null) { annClass = readClass2(annClass.getSuperName(), cl); } else { break; } // // TODO: See if we need to include interfaces here too? // } return classes; } private ComponentRequirement findRequirement(final AnnField field, AnnClass annClass, ClassLoader cl) throws ComponentGleanerException { assert field != null; Requirement anno = field.getAnnotation(Requirement.class); if (anno == null) { return null; } String fieldType = field.getType(); // TODO implement type resolution without loading classes Class<?> type; try { type = Class.forName(fieldType, false, cl); } catch (ClassNotFoundException ex) { // TODO Auto-generated catch block throw new ComponentGleanerException("Can't load class " + fieldType); } ComponentRequirement requirement; if (isRequirementListType(type)) { requirement = new ComponentRequirementList(); String[] hints = anno.hints(); if (hints != null && hints.length > 0) { ((ComponentRequirementList)requirement).setRoleHints(Arrays.asList(hints)); } // // TODO: See if we can glean any type details out of any generic information from the map or collection // } else { requirement = new ComponentRequirement(); requirement.setRoleHint(filterEmptyAsNull(anno.hint())); } // TODO need to read default annotation values // if (anno.role()==null || anno.role().isAssignableFrom(Object.class)) { if (anno.role().isAssignableFrom(Object.class)) { requirement.setRole(type.getName()); } else { requirement.setRole(anno.role().getName()); } requirement.setFieldName(field.getName()); requirement.setFieldMappingType(type.getName()); return requirement; } private PlexusConfiguration findConfiguration(AnnField field, AnnClass c, ClassLoader cl) { assert field != null; Configuration anno = field.getAnnotation(Configuration.class); if (anno == null) { return null; } String name = filterEmptyAsNull(anno.name()); if (name == null) { name = field.getName(); } name = deHump(name); XmlPlexusConfiguration config = new XmlPlexusConfiguration(name); String value = filterEmptyAsNull(anno.value()); if (value != null) { config.setValue(value); } return config; } }