/*
* Copyright (c) 2013 Evolveum
*
* 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 com.evolveum.midpoint.prism.maven;
import com.evolveum.midpoint.prism.ComplexTypeDefinition;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismContextImpl;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.schema.PrismSchema;
import com.evolveum.midpoint.prism.schema.SchemaDefinitionFactory;
import com.evolveum.midpoint.prism.schema.SchemaRegistry;
import com.evolveum.midpoint.prism.schema.SchemaRegistryImpl;
import com.evolveum.midpoint.prism.xml.GlobalDynamicNamespacePrefixMapper;
import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import org.apache.maven.archiver.MavenArchiveConfiguration;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.zip.ZipArchiver;
import org.jetbrains.annotations.NotNull;
import org.xml.sax.SAXException;
import java.io.*;
/**
* @goal schemadoc
* @requiresDependencyResolution compile
* @phase package
*/
//@Mojo(name="schemadoc")
public class SchemaDocMojo extends AbstractMojo {
private static final String VELOCITY_CONTEXT_VAR_PRISM_CONTEXT = "prismContext";
private static final String VELOCITY_CONTEXT_VAR_SCHEMA = "schema";
private static final String VELOCITY_CONTEXT_VAR_SCHEMA_REGISTRY = "schemaRegistry";
private static final String VELOCITY_CONTEXT_VAR_PATH = "path";
private static final String VELOCITY_CONTEXT_VAR_DEFINITION = "definition";
private static final String VELOCITY_CONTEXT_VAR_PREFIX_TO_BASE = "prefixToBase";
private static final String TEMPLATE_SCHEMA_INDEX_NAME = "schema-index.vm";
private static final String TEMPLATE_SCHEMA_NAME = "schema.vm";
private static final String TEMPLATE_OBJECT_DEFINITION_NAME = "object-definition.vm";
private static final String TEMPLATE_COMPLEX_TYPE_DEFINITION_NAME = "complex-type-definition.vm";
/**
* @parameter
*/
private File[] schemaFiles;
/**
* @parameter
*/
private File[] catalogFiles;
/**
* @parameter default-value="${project.build.directory}" required=true
*/
private File buildDir;
/**
* @parameter default-value="${project.build.directory}/schemadoc" required=true
*/
private File destDir;
/**
* @parameter default-value="src/main/schemadoc/templates" required=true
*/
private File templateDir;
/**
* @parameter default-value="src/main/schemadoc/resources"
*/
private File resourcesDir;
/** @parameter default-value="${project}" */
private org.apache.maven.project.MavenProject project;
/** @parameter */
private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
/** @parameter default-value="${project.build.finalName}" */
private String finalName;
/**
* @component
*/
private MavenProjectHelper projectHelper;
/** @component role="org.codehaus.plexus.archiver.Archiver" roleHint="zip" */
private ZipArchiver zipArchiver;
private String getTemplateDirName() {
return templateDir.getAbsolutePath();
}
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info( "SchemaDoc plugin started" );
PrismContext prismContext = createInitializedPrismContext();
File outDir = initializeOutDir();
PathGenerator pathGenerator = new PathGenerator(outDir);
VelocityEngine velocityEngine = createVelocityEngine();
SchemaRegistry schemaRegistry = prismContext.getSchemaRegistry();
try {
renderSchemaIndex(schemaRegistry, prismContext, velocityEngine, pathGenerator);
} catch (IOException e) {
throw new MojoExecutionException(e.getMessage(),e);
}
for (PrismSchema schema: schemaRegistry.getSchemas()) {
try {
renderSchema(schema, prismContext, velocityEngine, pathGenerator);
} catch (IOException e) {
throw new MojoExecutionException(e.getMessage(),e);
}
}
try {
copyResources(outDir);
} catch (IOException e) {
throw new MojoExecutionException(e.getMessage(),e);
}
File archiveFile = null;
try {
archiveFile = generateArchive(outDir, finalName + "-schemadoc.zip");
} catch (IOException e) {
throw new MojoExecutionException(e.getMessage(),e);
} catch (ArchiverException e) {
throw new MojoExecutionException(e.getMessage(),e);
}
projectHelper.attachArtifact(project, "zip", "schemadoc", archiveFile);
getLog().info( "SchemaDoc plugin finished" );
}
private void renderSchemaIndex(SchemaRegistry schemaRegistry, PrismContext prismContext, VelocityEngine velocityEngine, PathGenerator pathGenerator) throws IOException {
getLog().info("Rendering schema index");
VelocityContext velocityContext = new VelocityContext();
populateVelocityContextBase(velocityContext, prismContext, pathGenerator, null, ".");
velocityContext.put(VELOCITY_CONTEXT_VAR_SCHEMA_REGISTRY, schemaRegistry);
Template template = velocityEngine.getTemplate(TEMPLATE_SCHEMA_INDEX_NAME);
Writer writer = new FileWriter(pathGenerator.prepareSchemaIndexOutputFile());
template.merge(velocityContext, writer);
writer.close();
}
private void renderSchema(PrismSchema schema, PrismContext prismContext, VelocityEngine velocityEngine, PathGenerator pathGenerator) throws IOException {
getLog().info("Processing schema: "+schema);
VelocityContext velocityContext = new VelocityContext();
populateVelocityContextBase(velocityContext, prismContext, pathGenerator, schema, "..");
Template template = velocityEngine.getTemplate(TEMPLATE_SCHEMA_NAME);
Writer writer = new FileWriter(pathGenerator.prepareSchemaOutputFile(schema));
template.merge(velocityContext, writer);
writer.close();
// Object Definitions
for (PrismObjectDefinition objectDefinition: schema.getObjectDefinitions()) {
renderObjectDefinition(objectDefinition, schema, prismContext, velocityEngine, pathGenerator);
}
// Types
for (ComplexTypeDefinition typeDefinition : schema.getComplexTypeDefinitions()) {
renderComplexTypeDefinition(typeDefinition, schema, prismContext, velocityEngine, pathGenerator);
}
}
private void renderObjectDefinition(PrismObjectDefinition objectDefinition, PrismSchema schema, PrismContext prismContext, VelocityEngine velocityEngine, PathGenerator pathGenerator) throws IOException {
getLog().info(" Processing object definition: "+objectDefinition);
VelocityContext velocityContext = new VelocityContext();
populateVelocityContextBase(velocityContext, prismContext, pathGenerator, schema, "../..");
velocityContext.put(VELOCITY_CONTEXT_VAR_DEFINITION, objectDefinition);
Template template = velocityEngine.getTemplate(TEMPLATE_OBJECT_DEFINITION_NAME);
Writer writer = new FileWriter(pathGenerator.prepareObjectDefinitionOutputFile(schema, objectDefinition));
template.merge(velocityContext, writer);
writer.close();
}
private void renderComplexTypeDefinition(ComplexTypeDefinition typeDefinition, PrismSchema schema, PrismContext prismContext, VelocityEngine velocityEngine, PathGenerator pathGenerator) throws IOException {
getLog().info(" Processing complex type definition: "+typeDefinition);
VelocityContext velocityContext = new VelocityContext();
populateVelocityContextBase(velocityContext, prismContext, pathGenerator, schema, "../..");
velocityContext.put(VELOCITY_CONTEXT_VAR_DEFINITION, typeDefinition);
Template template = velocityEngine.getTemplate(TEMPLATE_COMPLEX_TYPE_DEFINITION_NAME);
Writer writer = new FileWriter(pathGenerator.prepareTypeDefinitionOutputFile(schema, typeDefinition));
template.merge(velocityContext, writer);
writer.close();
}
private void populateVelocityContextBase(VelocityContext velocityContext, PrismContext prismContext, PathGenerator pathGenerator,
PrismSchema schema, String prefixToBase) {
if (schema != null) {
velocityContext.put(VELOCITY_CONTEXT_VAR_SCHEMA, schema);
}
velocityContext.put(VELOCITY_CONTEXT_VAR_PRISM_CONTEXT, prismContext);
velocityContext.put(VELOCITY_CONTEXT_VAR_PATH, pathGenerator);
velocityContext.put(VELOCITY_CONTEXT_VAR_PREFIX_TO_BASE, prefixToBase);
}
private File initializeOutDir() throws MojoFailureException {
getLog().info("Output dir: "+destDir);
if ( destDir.exists() && !destDir.isDirectory() ) {
throw new MojoFailureException("Destination directory is not a directory: "+destDir);
}
if (destDir.exists() && !destDir.canWrite()) {
throw new MojoFailureException("Destination directory is not writable: "+destDir);
}
destDir.mkdirs();
return destDir;
}
private PrismContext createInitializedPrismContext() throws MojoFailureException {
try {
SchemaRegistryImpl schemaRegistry = createSchemaRegistry();
for (File schemaFile: schemaFiles) {
getLog().info("SchemaDoc: registering schema file: "+schemaFile);
if (!schemaFile.exists()) {
throw new MojoFailureException("Schema file "+schemaFile+" does not exist");
}
schemaRegistry.registerPrismSchemaFile(schemaFile);
}
if (catalogFiles != null && catalogFiles.length > 0) {
for (File catalogFile : catalogFiles) {
getLog().info("SchemaDoc: using catalog file: " + catalogFile);
if (!catalogFile.exists()) {
throw new IOException("Catalog file '" + catalogFile + "' does not exist.");
}
}
schemaRegistry.setCatalogFiles(catalogFiles);
}
PrismContextImpl context = PrismContextImpl.create(schemaRegistry);
context.setDefinitionFactory(new SchemaDefinitionFactory());
context.initialize();
return context;
} catch (SchemaException e) {
handleFailure(e);
// never reached
return null;
} catch (FileNotFoundException e) {
handleFailure(e);
// never reached
return null;
} catch (SAXException e) {
handleFailure(e);
// never reached
return null;
} catch (IOException e) {
handleFailure(e);
// never reached
return null;
}
}
private void handleFailure(Exception e) throws MojoFailureException {
e.printStackTrace();
throw new MojoFailureException(e.getMessage());
}
@NotNull
private SchemaRegistryImpl createSchemaRegistry() throws SchemaException {
SchemaRegistryImpl schemaRegistry = new SchemaRegistryImpl();
schemaRegistry.setNamespacePrefixMapper(new GlobalDynamicNamespacePrefixMapper());
return schemaRegistry;
}
private VelocityEngine createVelocityEngine() {
VelocityEngine ve = new VelocityEngine();
ve.setProperty("resource.loader","file");
ve.setProperty("file.resource.loader.class","org.apache.velocity.runtime.resource.loader.FileResourceLoader");
ve.setProperty("file.resource.loader.path", getTemplateDirName());
ve.setProperty("file.resource.loader.cache","true");
ve.setProperty("directive.set.null.allowed","true");
ve.init();
return ve;
}
private void copyResources(File outDir) throws IOException {
if (resourcesDir.exists()) {
MiscUtil.copyDirectory(resourcesDir, outDir);
}
}
private File generateArchive(File outDir, String archiveFilename) throws IOException, ArchiverException {
File zipFile = new File(buildDir, archiveFilename);
if (zipFile.exists()) {
zipFile.delete();
}
zipArchiver.addDirectory(outDir);
zipArchiver.setDestFile(zipFile);
zipArchiver.createArchive();
return zipFile;
}
}