This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag maven-sling-plugin-2.1.0 in repository https://gitbox.apache.org/repos/asf/sling-maven-sling-plugin.git
commit c609e548beeb018dcde20dd8c3e4bf327ecd965a Author: Justin Edelson <[email protected]> AuthorDate: Fri Dec 2 02:51:45 2011 +0000 SLING-2313 - creating a new adapter annotations module and enhancing the sling plugin to generate the adapter JSON file git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/maven/maven-sling-plugin@1209336 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 15 ++ .../bundlesupport/GenerateAdapterMetadataMojo.java | 243 +++++++++++++++++++++ 2 files changed, 258 insertions(+) diff --git a/pom.xml b/pom.xml index 1499e96..291a0f9 100644 --- a/pom.xml +++ b/pom.xml @@ -108,5 +108,20 @@ <artifactId>maven-archiver</artifactId> <version>2.0</version> </dependency> + <dependency> + <groupId>net.sf.scannotation</groupId> + <artifactId>scannotation</artifactId> + <version>1.0.2</version> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>adapter-annotations</artifactId> + <version>0.0.1-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>asm</groupId> + <artifactId>asm-all</artifactId> + <version>3.3.1</version> + </dependency> </dependencies> </project> diff --git a/src/main/java/org/apache/sling/maven/bundlesupport/GenerateAdapterMetadataMojo.java b/src/main/java/org/apache/sling/maven/bundlesupport/GenerateAdapterMetadataMojo.java new file mode 100644 index 0000000..04639b6 --- /dev/null +++ b/src/main/java/org/apache/sling/maven/bundlesupport/GenerateAdapterMetadataMojo.java @@ -0,0 +1,243 @@ +/* + * 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.sling.maven.bundlesupport; + +import static org.objectweb.asm.ClassReader.*; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.maven.model.Resource; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; +import org.apache.sling.adapter.annotations.Adaptable; +import org.apache.sling.commons.json.JSONException; +import org.apache.sling.commons.json.JSONObject; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.StringUtils; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.scannotation.AnnotationDB; + +/** + * @goal generate-adapter-metadata + * @phase process-classes + * @threadSafe + * @description Build Adapter Metadata from Annotated Classes + * @requiresDependencyResolution compile + */ +public class GenerateAdapterMetadataMojo extends AbstractMojo { + + private static final int JSON_INDENTATION = 4; + + private static final String ADAPTABLE_DESC = "L" + Adaptable.class.getName().replace('.', '/') + ";"; + + private static final String DEFAULT_CONDITION = "If the adaptable is a %s."; + + private static String getSimpleName(final ClassNode clazz) { + final String internalName = clazz.name; + final int idx = internalName.lastIndexOf('/'); + if (idx == -1) { + return internalName; + } else { + return internalName.substring(idx + 1); + } + } + + /** + * @parameter expression="${project.build.outputDirectory}" + * @required + * @readonly + */ + private File buildOutputDirectory; + + /** + * Name of the generated descriptor file. + * + * @parameter expression="${adapter.descriptor.name}" + * default-value="SLING-INF/adapters.json" + */ + private String fileName; + + /** + * @parameter + * expression="${project.build.directory}/adapter-plugin-generated" + * @required + * @readonly + */ + private File outputDirectory; + + /** + * The Maven project. + * + * @parameter expression="${project}" + * @required + * @readonly + */ + private MavenProject project; + + public void execute() throws MojoExecutionException, MojoFailureException { + try { + final JSONObject descriptor = new JSONObject(); + + final AnnotationDB annotationDb = new AnnotationDB(); + annotationDb.scanArchives(buildOutputDirectory.toURI().toURL()); + + final Set<String> annotatedClassNames = annotationDb.getAnnotationIndex().get(Adaptable.class.getName()); + if (annotatedClassNames.isEmpty()) { + getLog().debug("No classes found with adaptable annotations."); + } + + for (final String annotatedClassName : annotatedClassNames) { + getLog().info(String.format("found adaptable annotation on %s", annotatedClassName)); + final String pathToClassFile = annotatedClassName.replace('.', '/') + ".class"; + final File classFile = new File(buildOutputDirectory, pathToClassFile); + final FileInputStream input = new FileInputStream(classFile); + final ClassReader classReader; + try { + classReader = new ClassReader(input); + } finally { + input.close(); + } + final ClassNode classNode = new ClassNode(); + classReader.accept(classNode, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES); + + @SuppressWarnings("unchecked") + final List<AnnotationNode> annotations = classNode.invisibleAnnotations; + for (final AnnotationNode annotation : annotations) { + if (ADAPTABLE_DESC.equals(annotation.desc)) { + parseAdaptableAnnotation(annotation, classNode, descriptor); + } + } + + } + + final File outputFile = new File(outputDirectory, fileName); + outputFile.getParentFile().mkdirs(); + final FileWriter writer = new FileWriter(outputFile); + try { + IOUtil.copy(descriptor.toString(JSON_INDENTATION), writer); + } finally { + IOUtil.close(writer); + } + addResource(); + + } catch (IOException e) { + throw new MojoExecutionException("Unable to generate metadata", e); + } catch (JSONException e) { + throw new MojoExecutionException("Unable to generate metadata", e); + } + + } + + private void addResource() { + final String ourRsrcPath = this.outputDirectory.getAbsolutePath(); + boolean found = false; + @SuppressWarnings("unchecked") + final Iterator<Resource> rsrcIterator = this.project.getResources().iterator(); + while (!found && rsrcIterator.hasNext()) { + final Resource rsrc = rsrcIterator.next(); + found = rsrc.getDirectory().equals(ourRsrcPath); + } + if (!found) { + final Resource resource = new Resource(); + resource.setDirectory(this.outputDirectory.getAbsolutePath()); + this.project.addResource(resource); + } + + } + + @SuppressWarnings("unchecked") + private void parseAdaptableAnnotation(final AnnotationNode annotation, final ClassNode annotatedClass, + final JSONObject descriptor) throws JSONException { + String adaptableClassName = null; + List<AnnotationNode> adapters = null; + + final List<?> values = annotation.values; + + final Iterator<?> it = values.iterator(); + while (it.hasNext()) { + Object name = it.next(); + Object value = it.next(); + + if ("adaptableClass".equals(name)) { + adaptableClassName = ((Type) value).getClassName(); + } else if ("adapters".equals(name)) { + adapters = (List<AnnotationNode>) value; + } + } + + if (adaptableClassName == null || adapters == null) { + throw new IllegalArgumentException( + "Adaptable annotation is malformed. Expecting a classname and a list of adapter annotation."); + } + + JSONObject adaptableDescription; + if (descriptor.has(adaptableClassName)) { + adaptableDescription = descriptor.getJSONObject(adaptableClassName); + } else { + adaptableDescription = new JSONObject(); + descriptor.put(adaptableClassName, adaptableDescription); + } + + for (final AnnotationNode adapter : adapters) { + parseAdapterAnnotation(adapter, annotatedClass, adaptableDescription); + } + } + + @SuppressWarnings("unchecked") + private void parseAdapterAnnotation(final AnnotationNode annotation, final ClassNode annotatedClass, + final JSONObject adaptableDescription) throws JSONException { + String condition = null; + List<Type> adapterClasses = null; + + final List<?> values = annotation.values; + + final Iterator<?> it = values.iterator(); + while (it.hasNext()) { + final Object name = it.next(); + final Object value = it.next(); + + if (StringUtils.isEmpty(condition)) { + condition = String.format(DEFAULT_CONDITION, getSimpleName(annotatedClass)); + } + + if ("condition".equals(name)) { + condition = (String) value; + } else if ("value".equals(name)) { + adapterClasses = (List<Type>) value; + } + } + + if (adapterClasses == null) { + throw new IllegalArgumentException("Adapter annotation is malformed. Expecting a list of adapter classes"); + } + + for (final Type adapterClass : adapterClasses) { + adaptableDescription.accumulate(condition, adapterClass.getClassName()); + } + } +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
