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

org.bndly.schema.beans.SchemaServiceInterfaceGeneratorMojo Maven / Gradle / Ivy

The newest version!
package org.bndly.schema.beans;

/*-
 * #%L
 * Maven Schema Bean Generator
 * %%
 * Copyright (C) 2013 - 2020 Cybercon GmbH
 * %%
 * 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.
 * #L%
 */

import static org.bndly.schema.beans.SchemaBeanGeneratorMojo.upperCaseFirstLetter;

import org.bndly.code.common.CodeGenerationContext;
import org.bndly.code.model.CodeBlock;
import org.bndly.code.model.CodeBracket;
import org.bndly.code.model.CodeLine;
import org.bndly.code.renderer.ImportResolver;
import static org.bndly.schema.beans.SchemaServiceImplGeneratorMojo.SERVICE_IMPL_SUFFIX;
import org.bndly.schema.model.Schema;
import org.bndly.schema.model.Type;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

/**
 * Generates Service Layer Client Interfaces from Schema
 */
@Mojo(
		name = "generateServices"
)
public class SchemaServiceInterfaceGeneratorMojo extends AbstractSchemaBasedBeanGeneratorMojo {

	private static final String GENERIC_RESOURCE_SERVICE = "org.bndly.common.service.shared.api.GenericResourceService";

	/**
	 * The package name to use for looking schema bean interfaces.
	 */
	@Parameter
	protected String schemaBeanSourcePackage;

	/**
	 * The package name to use for looking custom service interfaces.
	 */
	@Parameter
	public String customServiceInterfacesPath;

	/**
	 * The package name to use for service implementations to generate spring context file.
	 */
	@Parameter
	public String springBeanServiceImplementationPackage;

	/**
	 * A path to a file that should contain an example of a spring bean definition to use the services provided by the current maven module.
	 */
	@Parameter
	public String springBeanDefinitionPath;

	protected static final String SERVICE_INTERFACE_PREFIX = "Default";
	protected static final String CUSTOM_INTERFACE_PREFIX = "Custom";
	protected static final String SERVICE_INTERFACE_SUFFIX = "Service";

	private final List customApiList = new ArrayList<>();

	@Override
	protected void doCodeGenerationWithSchema(Schema schema, CodeGenerationContext ctx, File path) throws IOException, MojoExecutionException, MojoFailureException {
		ctx.setImportResolver(new ImportResolver() {
			@Override
			public String resolveImport(String simpleName) {
				if (simpleName == null) {
					throw new IllegalArgumentException("can not resolve import when simpleName is null.");
				}
				return simpleName;
			}
		});

		loadCustomServiceInterfaceNamesToList(customServiceInterfacesPath);

		OutputStream os = null;
		XMLStreamWriter xmlStream = null;
		try {
			if (springBeanDefinitionPath != null && springBeanServiceImplementationPackage != null) {
				Path springBeanDefPath = Paths.get(springBeanDefinitionPath);
				Path springBeanFolder = springBeanDefPath.getParent();
				if (Files.notExists(springBeanFolder)) {
					Files.createDirectories(springBeanFolder);
				}
				if (Files.notExists(springBeanDefPath)) {
					Files.createFile(springBeanDefPath);
				}
				os = Files.newOutputStream(springBeanDefPath, StandardOpenOption.WRITE);
				xmlStream = XMLOutputFactory.newInstance().createXMLStreamWriter(os, "UTF-8");
			}

			if (xmlStream != null) {
				xmlStream.setDefaultNamespace("http://www.springframework.org/schema/beans");
				xmlStream.setPrefix("beans", "http://www.springframework.org/schema/beans");
				xmlStream.setPrefix("xmlns", "http://www.w3.org/2001/XMLSchema-instance");
				xmlStream.setPrefix("context", "http://www.springframework.org/schema/context");
				xmlStream.writeStartDocument();
				xmlStream.writeStartElement("beans");
				xmlStream.writeDefaultNamespace("http://www.springframework.org/schema/beans");
				xmlStream.writeNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
				xmlStream.writeNamespace("context", "http://www.springframework.org/schema/context");
				xmlStream.writeNamespace("util", "http://www.springframework.org/schema/util");
				xmlStream.writeAttribute("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd");
			}
			List types = schema.getTypes();
			if (types != null) {
				if (xmlStream != null) {
					xmlStream.writeStartElement("http://www.springframework.org/schema/util", "list");
					xmlStream.writeAttribute("id", schema.getName() + "ServiceStubs");
					for (Type type : types) {
						if (!(type.isAbstract() || type.isVirtual())) {
							xmlStream.writeEmptyElement("ref");
							xmlStream.writeAttribute("bean", getServiceBeanName(type) + "Stub");
						}
					}
					xmlStream.writeEndElement();
				}
				for (Type type : types) {
					if (!(type.isAbstract() || type.isVirtual())) {
						importStateHolder.reset();

						// Generate default service interfaces
						CodeBlock defaultServiceCode = generateCodeForType(type, ctx);
						writeCodeToFile(defaultServiceCode, path, type, SERVICE_INTERFACE_PREFIX, SERVICE_INTERFACE_SUFFIX);

						// Generate Full-API-Interface for custom and default service interfaces
						CodeBlock fullAPIServiceCodeOfType = generateCodeForFullApiInterface(type, ctx);
						writeCodeToFile(fullAPIServiceCodeOfType, path, type, SERVICE_INTERFACE_SUFFIX);

						// Write spring bean definition
						if (xmlStream != null) {
							writeSpringBeanDefinition(xmlStream, type, ctx);
						}
					}
				}
			}
			if (xmlStream != null) {
				xmlStream.writeEndDocument();
			}
		} catch (XMLStreamException ex) {
			throw new MojoExecutionException("failed to create xml stream", ex);
		} finally {
			if (os != null) {
				try {
					os.close();
				} catch (Exception e) {
					// silently close
				}
			}
		}
	}

	private CodeBlock generateCodeForType(Type type, CodeGenerationContext ctx) {
		String tName = type.getName();

		getLog().info("generating java service client interface for type " + tName);
		CodeBlock block = ctx.create(CodeBlock.class);
		block.line("package ").append(ctx.getBasePackage()).append(";");
		block.line("");
		block.line("import ").append(schemaBeanSourcePackage).append(".").append(tName).append(";");
		block.line("import ").append(GENERIC_RESOURCE_SERVICE).append(";");
		block.line("");
		block.line("public interface ")
			.append(SERVICE_INTERFACE_PREFIX).append( upperCaseFirstLetter(tName) ).append(SERVICE_INTERFACE_SUFFIX)
			.append(" extends ")
			.append("GenericResourceService<").append(tName).append(">");

		CodeBracket bracket = block.createContained(CodeBracket.class);
		bracket.line("");

		return block;
	}

	protected static String lowerCaseFirstLetter(String string) {
		if (string.length() > 1) {
			string = string.substring(0, 1).toLowerCase() + string.substring(1);
		} else {
			string = string.toLowerCase();
		}
		return string;
	}

	private void loadCustomServiceInterfaceNamesToList(String path) throws MojoExecutionException {

		DirectoryStream.Filter filter = new DirectoryStream.Filter() {
			@Override
			public boolean accept(Path file) throws IOException {
				if (file != null) {
					return file.getFileName().toString().startsWith(CUSTOM_INTERFACE_PREFIX) && file.toString().endsWith(SERVICE_INTERFACE_SUFFIX + ".java");
				} else {
					return false;
				}
			}
		};

		try (DirectoryStream customServiceDir = Files.newDirectoryStream(Paths.get(path), filter)) {
			for (Path p : customServiceDir) {
				int end = p.getFileName().toString().length() - ".java".length(); //
				String customClazzName = p.getFileName().toString().substring(0, end);

				customApiList.add(customClazzName);
			}
		} catch (IOException io) {
			throw new MojoExecutionException("No custom interfaces found!", io);
		}
	}

	private CodeBlock generateCodeForFullApiInterface(Type type, CodeGenerationContext ctx) {
		String tName = type.getName();

		getLog().info("generating " + tName + "Service FULL API-Interface for default and custom services");

		CodeBlock block = ctx.create(CodeBlock.class);
		block.line("package ").append(ctx.getBasePackage()).append(";");
		block.line("");

		CodeLine line = block.createContained(CodeLine.class);
		line.append("public interface ")
				.append(upperCaseFirstLetter(tName)).append(SERVICE_INTERFACE_SUFFIX)
				.append(" extends ")
				.append(SERVICE_INTERFACE_PREFIX).append(upperCaseFirstLetter(tName)).append(SERVICE_INTERFACE_SUFFIX);

		String customClazzName = getCustomServiceInterfaceClassNameForType(type);
		if (customClazzName != null) {
			line.append(", ").append(customClazzName);
		}

		CodeBracket bracket = block.createContained(CodeBracket.class);

		bracket.line("String NAME = \"").append(getServiceBeanName(type)).append("\";");

		return block;
	}

	private String getServiceBeanName(Type type) {
		return lowerCaseFirstLetter(type.getSchema().getName()) + type.getName() + SERVICE_INTERFACE_SUFFIX;
	}

	private String getFullApiName(Type type, CodeGenerationContext ctx) {
		return ctx.getBasePackage() + "." + getSimpleFullApiName(type);
	}

	private String getSimpleFullApiName(Type type) {
		return upperCaseFirstLetter(type.getName()) + SERVICE_INTERFACE_SUFFIX;
	}

	private String getCustomServiceInterfaceClassNameForType(Type type) {
		for (String customClazzName : customApiList) {
			int end = customClazzName.length() - SERVICE_INTERFACE_SUFFIX.length(); //
			String typeName = customClazzName.substring(CUSTOM_INTERFACE_PREFIX.length(), end);

			if (type.getName().equals(typeName)) {
				return customClazzName;
			}
		}
		return null;
	}

	private void writeSpringBeanDefinition(XMLStreamWriter xmlStream, Type type, CodeGenerationContext ctx) throws XMLStreamException {
		String stubBeanName = getServiceBeanName(type) + "Stub";

		String customServiceInterfaceClassNameForType = getCustomServiceInterfaceClassNameForType(type);

		xmlStream.writeStartElement("bean");
		xmlStream.writeAttribute("name", stubBeanName);
		xmlStream.writeAttribute("class", "org.bndly.common.service.setup.SchemaServiceStub");

		xmlStream.writeEmptyElement("property");
		xmlStream.writeAttribute("name", "schemaName");
		xmlStream.writeAttribute("value", type.getSchema().getName());

		xmlStream.writeEmptyElement("property");
		xmlStream.writeAttribute("name", "fullApiClassName");
		xmlStream.writeAttribute("value", getFullApiName(type, ctx));

		if (customServiceInterfaceClassNameForType != null) {
			xmlStream.writeEmptyElement("property");
			xmlStream.writeAttribute("name", "customServiceClassName");
			xmlStream.writeAttribute("value", springBeanServiceImplementationPackage + "." + CUSTOM_INTERFACE_PREFIX + type.getName() + SERVICE_IMPL_SUFFIX);
		}

		xmlStream.writeEmptyElement("property");
		xmlStream.writeAttribute("name", "genericServiceClassName");
		xmlStream.writeAttribute("value", springBeanServiceImplementationPackage + "." + SERVICE_INTERFACE_PREFIX + type.getName() + SERVICE_IMPL_SUFFIX);

		xmlStream.writeEndElement();

		xmlStream.writeStartElement("bean");
		xmlStream.writeAttribute("name", getServiceBeanName(type));
		xmlStream.writeAttribute("class", getFullApiName(type, ctx));
		xmlStream.writeAttribute("factory-bean", stubBeanName);
		xmlStream.writeAttribute("factory-method", "getFullApi");
		xmlStream.writeEndElement();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy