| /* |
| * 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.jcrocm; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.model.Resource; |
| import org.apache.maven.plugin.AbstractMojo; |
| import org.apache.maven.plugin.MojoFailureException; |
| import org.apache.maven.plugins.annotations.LifecyclePhase; |
| import org.apache.maven.plugins.annotations.Mojo; |
| import org.apache.maven.plugins.annotations.Parameter; |
| import org.apache.maven.plugins.annotations.ResolutionScope; |
| import org.apache.maven.project.MavenProject; |
| import org.codehaus.plexus.util.IOUtil; |
| import org.codehaus.plexus.util.StringUtils; |
| |
| import com.thoughtworks.qdox.JavaDocBuilder; |
| import com.thoughtworks.qdox.model.DocletTag; |
| import com.thoughtworks.qdox.model.JavaClass; |
| import com.thoughtworks.qdox.model.JavaField; |
| import com.thoughtworks.qdox.model.JavaMethod; |
| import com.thoughtworks.qdox.model.JavaSource; |
| |
| /** |
| * The <code>JcrOcmMojo</code> implements the (default) ocm goal of the |
| * <em>maven-jcrocm-plugin</em>. It is by default run in the |
| * <code>generate-resources</code> phase and requires the compile scoped |
| * dependencies to be resolved. |
| */ |
| @Mojo(name = "ocm", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, requiresDependencyResolution = ResolutionScope.COMPILE) |
| public class JcrOcmMojo extends AbstractMojo { |
| |
| @Parameter ( defaultValue = "${project.build.directory}/sling-generated", readonly = true) |
| private File outputDirectory; |
| |
| @Parameter( defaultValue = "${project}", readonly = true ) |
| private MavenProject project; |
| |
| /** |
| * Name and path of the generated descriptor. |
| */ |
| @Parameter( property = "jcrocm.descriptor.name", defaultValue = "mappings.xml") |
| private String finalName; |
| |
| public void execute() throws MojoFailureException { |
| |
| boolean hasFailures = false; |
| |
| // prepare QDox and prime with the compile class path of the project |
| JavaDocBuilder builder = new JavaDocBuilder(); |
| try { |
| builder.getClassLibrary().addClassLoader(this.getCompileClassLoader()); |
| } catch (IOException ioe) { |
| throw new MojoFailureException("Cannot prepare QDox"); |
| } |
| |
| // add the sources from the project |
| for (Iterator i = this.project.getCompileSourceRoots().iterator(); i.hasNext();) { |
| try { |
| builder.addSourceTree(new File((String) i.next())); |
| } catch (OutOfMemoryError oome) { |
| // this may be the case for big sources and not enough VM mem |
| builder = null; // drop the builder to help GC now |
| |
| // fail with some explanation |
| throw new MojoFailureException( |
| "Failed analyzing source due to not enough memory, try setting Max Heap Size higher, e.g. using MAVEN_OPTS=-Xmx128m"); |
| } |
| } |
| |
| // parse the sources and get them |
| JavaSource[] javaSources = builder.getSources(); |
| List descriptors = new ArrayList(); |
| for (int i = 0; i < javaSources.length; i++) { |
| JavaClass[] javaClasses = javaSources[i].getClasses(); |
| for (int j = 0; javaClasses != null && j < javaClasses.length; j++) { |
| DocletTag tag = javaClasses[j].getTagByName(ClassDescriptor.TAG_CLASS_DESCRIPTOR); |
| if (tag != null) { |
| ClassDescriptor descriptor = this.createClassDescriptor(javaClasses[j]); |
| if (descriptor != null) { |
| descriptors.add(descriptor); |
| } else { |
| hasFailures = true; |
| } |
| } |
| } |
| } |
| |
| // after checking all classes, throw if there were any failures |
| if (hasFailures) { |
| throw new MojoFailureException("Jackrabbit OCM Descriptor parsing had failures (see log)"); |
| } |
| |
| // terminate if there is nothing to write |
| if (descriptors.isEmpty()) { |
| this.getLog().info("No Jackrabbit OCM Descriptors found in project"); |
| return; |
| } |
| |
| // finally the descriptors have to be written .... |
| if (StringUtils.isEmpty(this.finalName)) { |
| this.getLog().error("Descriptor file name must not be empty"); |
| return; |
| } |
| |
| // prepare the descriptor output file |
| File descriptorFile = new File(new File(this.outputDirectory, "SLING-INF"), this.finalName); |
| descriptorFile.getParentFile().mkdirs(); // ensure parent dir |
| |
| this.getLog().info("Generating " + descriptors.size() |
| + " OCM Mapping Descriptors to " + descriptorFile); |
| |
| // write out all the class descriptors in parse order |
| FileOutputStream descriptorStream = null; |
| XMLWriter xw = null; |
| try { |
| descriptorStream = new FileOutputStream(descriptorFile); |
| xw = new XMLWriter(descriptorStream, false); |
| xw.printElementStart("jackrabbit-ocm", false); |
| for (Iterator di=descriptors.iterator(); di.hasNext(); ) { |
| ClassDescriptor sd = (ClassDescriptor) di.next(); |
| sd.generate(xw); |
| } |
| xw.printElementEnd("jackrabbit-ocm"); |
| |
| } catch (IOException ioe) { |
| hasFailures = true; |
| this.getLog().error("Cannot write descriptor to " + descriptorFile, ioe); |
| throw new MojoFailureException("Failed to write descriptor to " + descriptorFile); |
| } finally { |
| IOUtil.close(xw); |
| IOUtil.close(descriptorStream); |
| |
| // remove the descriptor file in case of write failure |
| if (hasFailures) { |
| descriptorFile.delete(); |
| } |
| } |
| |
| // now add the descriptor file to the maven resources |
| final String ourRsrcPath = this.outputDirectory.getAbsolutePath(); |
| boolean found = false; |
| final Iterator rsrcIterator = this.project.getResources().iterator(); |
| while ( !found && rsrcIterator.hasNext() ) { |
| final Resource rsrc = (Resource)rsrcIterator.next(); |
| found = rsrc.getDirectory().equals(ourRsrcPath); |
| } |
| if ( !found ) { |
| final Resource resource = new Resource(); |
| resource.setDirectory(this.outputDirectory.getAbsolutePath()); |
| this.project.addResource(resource); |
| } |
| // and set include accordingly |
| this.project.getProperties().setProperty("Sling-Mappings", "SLING-INF/" + this.finalName); |
| } |
| |
| /** |
| * Creates an URL class loader containing all compile artifacts. |
| * |
| * @return The URLClassLoader for the compile artifacts. |
| * @throws IOException If any error occurrs creating URLs for the compile |
| * artifact files. |
| */ |
| private ClassLoader getCompileClassLoader() throws IOException { |
| List artifacts = this.project.getCompileArtifacts(); |
| URL[] path = new URL[artifacts.size()]; |
| int i = 0; |
| for (Iterator ai=artifacts.iterator(); ai.hasNext(); ) { |
| Artifact a = (Artifact) ai.next(); |
| path[i++] = a.getFile().toURI().toURL(); |
| } |
| return new URLClassLoader(path); |
| } |
| |
| private ClassDescriptor createClassDescriptor(JavaClass javaClass) { |
| |
| // create the class descriptor for the java class, return early if none |
| ClassDescriptor cd = ClassDescriptor.fromClass(this.getLog(), javaClass); |
| if (cd == null) { |
| return null; |
| } |
| |
| // analyze fields for mapping descriptors |
| JavaField[] fields = javaClass.getFields(); |
| for (int i=0; fields != null && i < fields.length; i++) { |
| cd.addChild(FieldDescriptor.fromField(this.getLog(), fields[i])); |
| cd.addChild(BeanDescriptor.fromField(this.getLog(), fields[i])); |
| cd.addChild(CollectionDescriptor.fromField(this.getLog(), fields[i])); |
| } |
| |
| // analyze methods for mapping descriptors |
| JavaMethod[] methods = javaClass.getMethods(); |
| for (int i=0; methods != null && i < methods.length; i++) { |
| cd.addChild(FieldDescriptor.fromMethod(this.getLog(), methods[i])); |
| cd.addChild(BeanDescriptor.fromMethod(this.getLog(), methods[i])); |
| cd.addChild(CollectionDescriptor.fromMethod(this.getLog(), methods[i])); |
| } |
| |
| // return nothing if validation fails |
| return cd.validate() ? cd : null; |
| } |
| } |