All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.permazen.SchemaUtilityImpl Maven / Gradle / Ivy

The newest version!

/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package io.permazen;

import com.google.common.base.Preconditions;

import io.permazen.annotation.PermazenType;
import io.permazen.core.Database;
import io.permazen.core.InvalidSchemaException;
import io.permazen.core.TransactionConfig;
import io.permazen.encoding.EncodingRegistry;
import io.permazen.kv.simple.MemoryKVDatabase;
import io.permazen.maven.SchemaUtility;
import io.permazen.schema.SchemaId;
import io.permazen.schema.SchemaModel;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;

/**
 * Schema utility API.
 *
 * 

* This API is used by the Maven plugin. We do things this way to avoid plugin class loader issues * and to ensure that the schema logic applied at build time matches what will be applied at run time. */ public class SchemaUtilityImpl implements SchemaUtility { private static final String PERMAZEN_CLASS_SCANNER_CLASS_NAME = "io.permazen.spring.PermazenClassScanner"; private Log log; private Permazen pdb; private SchemaModel schema; @Override public String configure(Log log, String[] packageNames, String[] classNames, String encodingRegistryClass) throws MojoExecutionException, MojoFailureException { // Sanity check Preconditions.checkState(this.pdb == null, "already configured"); Preconditions.checkArgument(log != null, "null log"); Preconditions.checkArgument(packageNames != null || classNames != null, "no package or class names"); // Initialize this.log = log; // Gather model and encoding classes final HashSet> modelClasses = new HashSet<>(); // Do package scanning - requires permazen-spring.jar if (packageNames != null && packageNames.length > 0) { // Scan for @PermazenType classes using PermazenClassScanner - if available on the classpath this.log.info(String.format("Scanning for @%s annotations in package(s): %s", PermazenType.class.getSimpleName(), Stream.of(packageNames).collect(Collectors.joining(", ")))); final Class scannerClass = this.loadClass(PERMAZEN_CLASS_SCANNER_CLASS_NAME, name -> String.format( "Failed to load class \"%s\" required to support - is permazen-spring.jar a dependency?", name)); final List scannedClasses; try { scannedClasses = (List)scannerClass.getMethod("scanForClasses", String[].class) .invoke(scannerClass.getConstructor().newInstance(), (Object)packageNames); } catch (ReflectiveOperationException e) { throw new MojoExecutionException(String.format("Error scanning for classes using %s", scannerClass), e); } for (Object obj : scannedClasses) { final String className = (String)obj; this.log.info("Adding Permazen model class " + className); final Class cl = this.loadClass(className, name -> String.format("Failed to load model class \"%s\"", name)); modelClasses.add(cl); } } // Do specific class scanning if (classNames != null && classNames.length > 0) { for (String className : classNames) { // Load class final Class cl = this.loadClass(className, name -> String.format("Failed to load class \"%s\"", name)); // Add model classes if (cl.isAnnotationPresent(PermazenType.class)) { this.log.info("Adding Permazen model " + cl); modelClasses.add(cl); } } } // Instantiate the EncodingRegistry, if any EncodingRegistry encodingRegistry = null; if (encodingRegistryClass != null) { this.log.info("Loading custom encoding registry " + encodingRegistryClass); final Class cl = this.loadClass(encodingRegistryClass, EncodingRegistry.class, name -> String.format("Failed to load the configured \"%s\"", encodingRegistryClass)); try { encodingRegistry = cl.getConstructor().newInstance(); } catch (ReflectiveOperationException e) { throw new MojoExecutionException(String.format( "Failed to instantiate the configured \"%s\"", encodingRegistryClass), e); } } // Construct database and schema model this.log.info("Generating Permazen schema from schema classes"); try { final Database db = new Database(new MemoryKVDatabase()); if (encodingRegistry != null) db.setEncodingRegistry(encodingRegistry); this.pdb = PermazenConfig.builder() .database(db) .modelClasses(modelClasses) .build() .newPermazen(); this.pdb.initialize(); this.schema = this.pdb.getSchemaModel(false); this.schema.lockDown(true); } catch (Exception e) { throw new MojoFailureException(String.format("Schema generation failed: %s", e.getMessage()), e); } // Return schema ID final SchemaId schemaId = this.schema.getSchemaId(); this.log.info("Schema ID is \"" + schemaId + "\""); return schemaId.toString(); } @Override public void generateSchema(File file) throws MojoExecutionException, MojoFailureException { Preconditions.checkState(this.pdb != null, "not configured"); Preconditions.checkArgument(file != null, "null file"); // Create directory final File dir = file.getParentFile(); if (!dir.exists()) { if (this.log.isDebugEnabled()) this.log.debug("Creating directory " + dir); try { Files.createDirectories(dir.toPath()); } catch (IOException e) { throw new MojoExecutionException(String.format("Error creating directory \"%s\": %s", dir, e.getMessage()), e); } } // Write schema model to file this.log.info("Writing Permazen schema to " + file); try (BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file))) { this.schema.toXML(output, false, true); } catch (IOException e) { throw new MojoExecutionException(String.format("Error writing schema to \"%s\": %s", file, e.getMessage()), e); } } @Override public boolean verifySchema(File file) throws MojoExecutionException { Preconditions.checkState(this.pdb != null, "not configured"); Preconditions.checkArgument(file != null, "null file"); // Read file this.log.info("Verifying Permazen schema matches " + file); final SchemaModel verifyModel; try (BufferedInputStream input = new BufferedInputStream(new FileInputStream(file))) { verifyModel = SchemaModel.fromXML(input); } catch (IOException | InvalidSchemaException e) { throw new MojoExecutionException(String.format("Error reading schema from \"%s\": %s", file, e), e); } verifyModel.lockDown(true); // Compare final boolean matched = this.schema.getSchemaId().equals(verifyModel.getSchemaId()); if (!matched) this.log.error("Schema verification failed:\n" + this.schema.differencesFrom(verifyModel)); else this.log.info("Schema verification succeeded"); // Done return matched; } @Override public boolean verifySchemas(Iterator files) throws MojoExecutionException { Preconditions.checkState(this.pdb != null, "not configured"); Preconditions.checkArgument(files != null, "null files"); // Check fiels boolean success = true; while (files.hasNext()) { final File file = files.next(); // Read next file this.log.info("Checking schema for conflicts with " + file); final SchemaModel otherSchema; try (BufferedInputStream input = new BufferedInputStream(new FileInputStream(file))) { otherSchema = SchemaModel.fromXML(input); } catch (IOException | InvalidSchemaException e) { throw new MojoExecutionException(String.format("Error reading schema from \"%s\": %s", file, e), e); } // Check compatible try { TransactionConfig.builder() .schemaModel(otherSchema) .build() .newTransaction(this.pdb.getDatabase()) .rollback(); } catch (Exception e) { this.log.error("Schema conflict found with " + file + ": " + e); success = false; } } // Done return success; } private Class loadClass(String className, Class type, UnaryOperator errorGenerator) throws MojoExecutionException { Preconditions.checkArgument(type != null, "null type"); final Class cl = this.loadClass(className, errorGenerator); try { return cl.asSubclass(type); } catch (ClassCastException e) { throw new MojoExecutionException(String.format( "Error loading class \"%s\" - expected a subtype of \"%s\"", className, type.getName()), e); } } private Class loadClass(String className, UnaryOperator errorGenerator) throws MojoExecutionException { Preconditions.checkArgument(className != null, "null className"); Preconditions.checkArgument(errorGenerator != null, "null errorGenerator"); try { return Class.forName(className, false, Thread.currentThread().getContextClassLoader()); } catch (ClassNotFoundException e) { throw new MojoExecutionException(errorGenerator.apply(className), e); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy