This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository observe. See https://gitlab.nuiton.org/codelutin/observe.git commit 65ab98e085ff54332dad2a497b37219ae2430b2a Author: Tony CHEMIT <chemit@codelutin.com> Date: Tue Sep 6 19:10:09 2016 +0200 Generate webmotion mapping → in this way we get a complete declarative mapping --- application-web/pom.xml | 20 ++ .../web/controller/v1/ConfigurationController.java | 2 +- .../src/main/{resources => webmotion}/mapping | 20 +- .../toolbox/GenerateWebmotionMappingMojo.java | 257 +++++++++++++++++++++ 4 files changed, 293 insertions(+), 6 deletions(-) diff --git a/application-web/pom.xml b/application-web/pom.xml index 30036cf..6a991b3 100644 --- a/application-web/pom.xml +++ b/application-web/pom.xml @@ -235,7 +235,27 @@ <!--<verbose>true</verbose>--> </configuration> </execution> + <execution> + <id>generate-webmotion-mapping</id> + <goals> + <goal>generate-webmotion-mapping</goal> + </goals> + <configuration> + <sourceApiPackageName>fr.ird.observe.services.service</sourceApiPackageName> + <targetApiPackageName>fr.ird.observe.application.web.controller.v1</targetApiPackageName> + <targetApiClassSuffix>Controller</targetApiClassSuffix> + <classTypeName>fr.ird.observe.services.ObserveService</classTypeName> + <!--<verbose>true</verbose>--> + </configuration> + </execution> </executions> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>services</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> </plugin> </plugins> diff --git a/application-web/src/main/java/fr/ird/observe/application/web/controller/v1/ConfigurationController.java b/application-web/src/main/java/fr/ird/observe/application/web/controller/v1/ConfigurationController.java index 353e7e8..89228cc 100644 --- a/application-web/src/main/java/fr/ird/observe/application/web/controller/v1/ConfigurationController.java +++ b/application-web/src/main/java/fr/ird/observe/application/web/controller/v1/ConfigurationController.java @@ -60,7 +60,7 @@ public class ConfigurationController extends ObserveWebMotionController { public RenderContent mapping() throws IOException { - try (InputStream mappingUrl = getClass().getResourceAsStream("/mapping")) { + try (InputStream mappingUrl = getClass().getResourceAsStream("/src/main/webmotion/mapping")) { String content = IOUtils.toString(mappingUrl); return toTextPlain(content); diff --git a/application-web/src/main/resources/mapping b/application-web/src/main/webmotion/mapping similarity index 68% rename from application-web/src/main/resources/mapping rename to application-web/src/main/webmotion/mapping index 2eedb26..b88acfe 100644 --- a/application-web/src/main/resources/mapping +++ b/application-web/src/main/webmotion/mapping @@ -45,11 +45,21 @@ default.render=fr.ird.observe.application.web.ObserveWebMotionRender [actions] GET /admin/configuration/{method} ConfigurationController.{method} -GET,POST,DELETE /api/v1/{service}/{method} {service}Controller.{method} -GET,POST,DELETE /api/v1/{package}/{service}/{method} {package}.{service}Controller.{method} -GET,POST,DELETE /api/v1/{package1}/{package2}/{service}/{method} {package1}.{package2}.{service}Controller.{method} -GET,POST,DELETE /api/v1/{package1}/{package2}/{package3}/{service}/{method} {package1}.{package2}.{package3}.{service}Controller.{method} -GET,POST,DELETE /api/v1/{package1}/{package2}/{package3}/{package4}/{service}/{method} {package1}.{package2}.{package3}.{package4}.{service}Controller.{method} + +# →→→ Legacy +#GET,POST,DELETE /api/v1/{service}/{method} {service}Controller.{method} +#GET,POST,DELETE /api/v1/{package}/{service}/{method} {package}.{service}Controller.{method} +#GET,POST,DELETE /api/v1/{package1}/{package2}/{service}/{method} {package1}.{package2}.{service}Controller.{method} +#GET,POST,DELETE /api/v1/{package1}/{package2}/{package3}/{service}/{method} {package1}.{package2}.{package3}.{service}Controller.{method} +#GET,POST,DELETE /api/v1/{package1}/{package2}/{package3}/{package4}/{service}/{method} {package1}.{package2}.{package3}.{package4}.{service}Controller.{method} +# ←←← Legacy + +# →→→ Generated mapping + +@ACTIONS@ + +# ←←← Generated mapping + diff --git a/toolbox-maven-plugin/src/main/java/fr/ird/observe/maven/plugins/toolbox/GenerateWebmotionMappingMojo.java b/toolbox-maven-plugin/src/main/java/fr/ird/observe/maven/plugins/toolbox/GenerateWebmotionMappingMojo.java new file mode 100644 index 0000000..ba9d798 --- /dev/null +++ b/toolbox-maven-plugin/src/main/java/fr/ird/observe/maven/plugins/toolbox/GenerateWebmotionMappingMojo.java @@ -0,0 +1,257 @@ +package fr.ird.observe.maven.plugins.toolbox; + +/* + * #%L + * ObServe :: Maven plugin + * %% + * Copyright (C) 2008 - 2016 IRD, Codelutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +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.reflections.Reflections; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; + +/** + * Generate webmotion mapping file. + * + * @author Tony Chemit - chemit@codelutin.com + * @since 5.0 + */ +@Mojo(name = "generate-webmotion-mapping", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyResolution = ResolutionScope.COMPILE) +public class GenerateWebmotionMappingMojo extends ToolboxMojoSupport { + + + @Parameter(property = "generateWebmotionMapping.sourceApiPackageName", required = true) + private String sourceApiPackageName; + + @Parameter(property = "generateWebmotionMapping.targetApiPackageName", required = true) + private String targetApiPackageName; + + @Parameter(property = "generateWebmotionMapping.targetApiClassSuffix", required = true) + private String targetApiClassSuffix; + + @Parameter(property = "generateWebmotionMapping.mappingTemplate", defaultValue = "${project.basedir}/src/main/webmotion/mapping", required = true) + private File mappingTemplate; + + /** + * To set the type of class to seek to generate the mapping. + */ + @Parameter(property = "generateWebmotionMapping.classTypeName") + private String classTypeName; + + /** + * Un flag pour activer le mode verbeux. + * + * @since 1.0.0 + */ + @Parameter(property = "generateWebmotionMapping.verbose", defaultValue = "${maven.verbose}") + private boolean verbose; + + /** + * A flag to skip the goal. + * + * @since 1.0.0 + */ + @Parameter(property = "generateWebmotionMapping.skip", defaultValue = "false") + private boolean skip; + + /** + * The root directory where to generated. + */ + @Parameter(property = "generateWebmotionMapping.outputDirectory", defaultValue = "${project.build.outputDirectory}", required = true) + private File outputDirectory; + + private Map<Class<?>, Class<?>> translationMap; + + @Override + protected Path createOutputFile() throws IOException { + + Files.createDirectories(outputDirectory.toPath()); + + return outputDirectory.toPath().resolve("mapping"); + + } + + @Override + protected boolean isSkip() { + return skip; + } + + @Override + protected void init() throws Exception { + + if (skip) { + return; + } + + super.init(); + + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + + try { + + ClassLoader cl = initClassLoader(getProject(), null, false, true, false, true, true); + Thread.currentThread().setContextClassLoader(cl); + + Class<?> classType = Class.forName(classTypeName); + + Map<Class<?>, Class<?>> translationMap = new LinkedHashMap<>(); + + Reflections targetReflections = new Reflections(targetApiPackageName); + + for (Class<?> serviceContract : new Reflections(sourceApiPackageName).getSubTypesOf(classType)) { + + Set<Class<?>> serviceImpls = (Set<Class<?>>) targetReflections.getSubTypesOf(serviceContract); + + for (Class<?> serviceImpl : serviceImpls) { + + if (serviceImpl.getSimpleName().endsWith(targetApiClassSuffix)) { + + if (isVerbose()) { + getLog().info("Found maching class to scan : " + serviceContract.getName() + " → " + serviceImpl.getName()); + } + translationMap.put(serviceContract, serviceImpl); + break; + } + } + + } + + getLog().info(translationMap.size() + " type(s) detected."); + + this.translationMap = Collections.unmodifiableMap(translationMap); + } finally { + Thread.currentThread().setContextClassLoader(contextClassLoader); + } + + } + + @Override + protected boolean checkSkip() { + if (skip) { + getLog().info("Skipping goal (skip flag is on)."); + return false; + } + + if (translationMap.isEmpty()) { + getLog().warn("Skipping goal (No matching class)."); + return false; + } + + return super.checkSkip(); + } + + @Override + public void doAction() throws Exception { + + if (isVerbose()) { + getLog().info("project = " + getProject()); + } + + getLog().info("Generate to " + getOutputFile()); + + Set<String> rules = new TreeSet<>(); + for (Map.Entry<Class<?>, Class<?>> entry : translationMap.entrySet()) { + + Class<?> sourceClass = entry.getKey(); + Class<?> targetClass = entry.getValue(); + + generateForClass(sourceClass, targetClass, rules); + + } + + getLog().info(rules.size() + " rule(s) detected."); + + StringBuilder rulesBuilder = new StringBuilder(); + for (String rule : rules) { + rulesBuilder.append(rule).append("\n"); + } + String rulesStr = rulesBuilder.toString(); + try (BufferedWriter writer = Files.newBufferedWriter(getOutputFile(), StandardCharsets.UTF_8)) { + + String content = new String(Files.readAllBytes(mappingTemplate.toPath())) + .replace("${project.version}", getProject().getVersion()) + .replace("${buildDate}", getProject().getProperties().getProperty("buildDate")) + .replace("${buildNumber}", getProject().getProperties().getProperty("buildNumber")); + + writer.append(content.replace("@ACTIONS@", rulesStr)); + + } + + } + + @Override + public boolean isVerbose() { + return verbose; + } + + @Override + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + private void generateForClass(Class<?> sourceClass, Class<?> targetClass, Set<String> rules) throws MissingMethodException, MismatchMethodParameterNameException, MissingClassException, IOException { + + Objects.requireNonNull(sourceClass); + Objects.requireNonNull(targetClass); + Objects.requireNonNull(rules); + + Method[] sourceDeclaredMethods = sourceClass.getDeclaredMethods(); + + if (isVerbose()) { + + getLog().info("Check " + sourceClass.getName()); + + } + + String classTargetName = "GET,POST,DELETE /api/v1/" + String.join("/", Arrays.asList(targetClass.getName().substring(targetApiPackageName.length() + 1).split("\\."))) + "/"; + + for (Method sourceMethod : sourceDeclaredMethods) { + + if (isVerbose()) { + getLog().info("Generate for " + sourceClass.getName() + "#" + sourceMethod.getName()); + } + + String targetRule = classTargetName + sourceMethod.getName(); + + // generate entry in mapping + rules.add(targetRule); + + } + + } + +} + -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.