From e8516e7e4f1fd901827e0ad9e0c2ff5ece59f97a Mon Sep 17 00:00:00 2001 From: Jean-Francois Denise Date: Mon, 15 Jan 2024 13:48:53 +0100 Subject: [PATCH 1/2] Arquillian plugin to use Glow online registry of feature-packs --- .../glow/plugin/arquillian/ScanMojo.java | 63 ++++++++++++++-- docs/guide/test-maven-plugin/index.adoc | 75 ++++++++++++++++++- tests/arquillian-plugin-tests/pom.xml | 27 ++++--- 3 files changed, 148 insertions(+), 17 deletions(-) diff --git a/arquillian-plugin/src/main/java/org/wildfly/glow/plugin/arquillian/ScanMojo.java b/arquillian-plugin/src/main/java/org/wildfly/glow/plugin/arquillian/ScanMojo.java index b5052dc8..67ea6e39 100644 --- a/arquillian-plugin/src/main/java/org/wildfly/glow/plugin/arquillian/ScanMojo.java +++ b/arquillian-plugin/src/main/java/org/wildfly/glow/plugin/arquillian/ScanMojo.java @@ -53,6 +53,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -73,6 +74,7 @@ import org.jboss.galleon.universe.FeaturePackLocation; import org.jboss.galleon.universe.UniverseResolver; import org.jboss.galleon.universe.maven.MavenChannel; +import org.wildfly.glow.ScanArguments; import org.wildfly.glow.error.IdentifiedError; import static org.wildfly.glow.plugin.arquillian.GlowArquillianDeploymentExporter.TEST_CLASSPATH; import static org.wildfly.glow.plugin.arquillian.GlowArquillianDeploymentExporter.TEST_PATHS; @@ -138,6 +140,7 @@ public void trace(Object s) { /** * List of feature-packs that are scanned and injected in the generated * provisioning.xml. + * This option can't be used when {@code server-version} or {@code preview} or {@code context} are used. */ @Parameter(required = false, alias = "feature-packs") List featurePacks = Collections.emptyList(); @@ -156,10 +159,10 @@ public void trace(Object s) { private List dependenciesToScan = Collections.emptyList(); /** - * Execution profiles. + * Execution profile. */ - @Parameter(alias = "profiles", required = false, property = "org.wildfly.glow.profiles") - Set profiles = Collections.emptySet(); + @Parameter(alias = "profile", required = false, property = "org.wildfly.glow.profile") + private String profile; /** * Do not lookup deployments to scan. @@ -216,7 +219,7 @@ public void trace(Object s) { @Parameter(property = "org.wildfly.glow.verbose") private boolean verbose = false; - /** + /** * A list of channels used for resolving artifacts while provisioning. *

* Defining a channel: @@ -253,8 +256,41 @@ public void trace(Object s) { @Parameter(alias = "channels", property = "org.wildfly.glow.channels") List channels; + /** + * A WildFly server version. The latest version is the default, only usable if no {@code feature-packs} have been set. + */ + @Parameter(alias = "server-version", property = "org.wildfly.glow.server-version") + private String serverVersion; + + /** + * Use WildFly Preview server, only usable if no {@code feature-packs} have been set. + */ + @Parameter(alias = "preview-server", property = "org.wildfly.glow.preview-server") + private boolean previewServer; + + /** + * Execution context, can be {@code cloud} or {@code bare-metal}, default value is {@code bare-metal}, + * only usable if no {@code feature-packs} have been set. + */ + @Parameter(alias = "context", property = "org.wildfly.glow.context") + private String context; + @Override public void execute() throws MojoExecutionException, MojoFailureException { + + // Check configuration + if (!featurePacks.isEmpty()) { + if (serverVersion != null) { + throw new MojoExecutionException("server-version can't be set when feature-packs have been set."); + } + if (context != null) { + throw new MojoExecutionException("context can't be set when feature-packs have been set."); + } + if (previewServer) { + throw new MojoExecutionException("preview-server can't be set when feature-packs have been set."); + } + } + // Make sure that the 'hidden' properties used by the Arguments class come from the Maven configuration HiddenPropertiesAccessor.setOverrides(systemPropertyVariables); try { @@ -343,16 +379,29 @@ public void execute() throws MojoExecutionException, MojoFailureException { throw new MojoExecutionException(ex.getLocalizedMessage(), ex); } } - Arguments arguments = Arguments.scanBuilder(). + Set profiles = new HashSet<>(); + if (profile != null) { + profiles.add(profile); + } + ScanArguments.Builder argumentsBuilder = Arguments.scanBuilder(). setExecutionProfiles(profiles). setBinaries(retrieveDeployments(paths, classesRootFolder, outputFolder)). - setProvisoningXML(buildInputConfig(outputFolder, artifactResolver)). setUserEnabledAddOns(addOns). setConfigName(configName). setSuggest((enableVerboseOutput || getLog().isDebugEnabled())). setJndiLayers(layersForJndi). setVerbose(verbose || getLog().isDebugEnabled()). - setOutput(OutputFormat.PROVISIONING_XML).build(); + setOutput(OutputFormat.PROVISIONING_XML). + setTechPreview(previewServer). + setExecutionProfiles(profiles). + setExecutionContext(context).setVersion(serverVersion); + + if (!featurePacks.isEmpty()) { + argumentsBuilder.setProvisoningXML(buildInputConfig(outputFolder, artifactResolver)); + } + + Arguments arguments = argumentsBuilder.build(); + try (ScanResults results = GlowSession.scan(artifactResolver, arguments, writer)) { boolean skipTests = Boolean.getBoolean("maven.test.skip") || Boolean.getBoolean("skipTests"); diff --git a/docs/guide/test-maven-plugin/index.adoc b/docs/guide/test-maven-plugin/index.adoc index 03c5b34d..866e1174 100644 --- a/docs/guide/test-maven-plugin/index.adoc +++ b/docs/guide/test-maven-plugin/index.adoc @@ -110,7 +110,80 @@ the dependencies coordinates (`groupId:artifactId`) can be configured in the plu ---- -### A pom.xml file extract +### Specifying an execution context + +By default the `bare-metal` context is used. In case you need to produce a server to be executed on the cloud, +use the `cloud` element. + +### Specifying an execution profile + +In case you want to produce an High Available WildFly server, use the `ha` element. + +### Using WildFly Preview server + +In case you want to use a WildFly Preview server, use the `true` element. + +### Specifying a WildFly server version + +By default the latest WildFly server version is used. +In case you want to use a specific WildFly version, set the `server version` element. + +### Using a specific set of Galleon feature-packs + +By default the latest WildFly Galleon feature-pack and extra Galleon feature-packs are used. +You have the ability to set a list of feature-packs using the `` element. + +### A simple pom.xml file extract + +In this extract, the latest WildFly server version is used. + +[source,xml,subs=attributes+] +---- +... + + + + org.wildfly.glow + wildfly-glow-arquillian-plugin + 1.0.0.Beta4 + + [cdi, ee-concurrency, ee-integration, elytron, messaging-activemq, naming, servlet, undertow]==>ee-core-profile-server,ee-concurrency,messaging-activemq + + + + scan + + scan + + test-compile + + + + + org.wildfly.plugins + wildfly-maven-plugin + 5.0.0.Alpha2 + + true + ${project.build.directory}/wildfly + ${project.build.directory}/glow-scan/provisioning.xml + + + + test-provisioning + + provision + + test-compile + + + + + +... +---- + +### A custom pom.xml file extract In this extract, WildFly 30.0.1.Final is used, the `ssl` add-on is enabled, expected discovery and expected errors are configured. The WildFly Maven Plugin is used to provision the server used by tests. diff --git a/tests/arquillian-plugin-tests/pom.xml b/tests/arquillian-plugin-tests/pom.xml index 6fca88ff..9c4bab10 100644 --- a/tests/arquillian-plugin-tests/pom.xml +++ b/tests/arquillian-plugin-tests/pom.xml @@ -44,15 +44,6 @@ org.wildfly.glow wildfly-glow-arquillian-plugin ${project.version} - - - - org.wildfly - wildfly-galleon-pack - 29.0.1.Final - - - scan @@ -65,6 +56,24 @@ []==>ee-core-profile-server + + scan-custom + + scan + + test-compile + + + + org.wildfly + wildfly-galleon-pack + 29.0.1.Final + + + true + []==>ee-core-profile-server + + From 33e4a8e0d31f84a5368751a43373fca597b7c750 Mon Sep 17 00:00:00 2001 From: Jean-Francois Denise Date: Mon, 15 Jan 2024 15:59:56 +0100 Subject: [PATCH 2/2] Allow to not check errors in test plugin, fixed verbose, better display when java package matches --- .../glow/plugin/arquillian/ScannerMain.java | 9 +- .../glow/plugin/arquillian/ScanMojo.java | 150 ++++++++++-------- .../org/wildfly/glow/DeploymentScanner.java | 4 +- docs/guide/test-maven-plugin/index.adoc | 30 ++++ docs/guide/wildfly-maven-plugin/index.adoc | 13 ++ tests/arquillian-plugin-tests/pom.xml | 18 ++- 6 files changed, 150 insertions(+), 74 deletions(-) diff --git a/arquillian-plugin-scanner/src/main/java/org/wildfly/glow/plugin/arquillian/ScannerMain.java b/arquillian-plugin-scanner/src/main/java/org/wildfly/glow/plugin/arquillian/ScannerMain.java index 94d894de..901259c8 100644 --- a/arquillian-plugin-scanner/src/main/java/org/wildfly/glow/plugin/arquillian/ScannerMain.java +++ b/arquillian-plugin-scanner/src/main/java/org/wildfly/glow/plugin/arquillian/ScannerMain.java @@ -53,9 +53,9 @@ public static void main(String[] args) throws Exception { boolean verbose = Boolean.parseBoolean(args[4]); // ClassLoader to load the Scanner from the classpath (equivalent to application cp). // Delegates to the application classpath to resolve Java API. - URLClassLoader cpLoader = buildClassLoader(cpArray, Thread.currentThread().getContextClassLoader(), verbose); + URLClassLoader cpLoader = buildClassLoader(cpArray, Thread.currentThread().getContextClassLoader()); // ClassLoader to load the test classes, delegate to cpLoader - URLClassLoader testLoader = buildClassLoader(urlsArray, cpLoader, verbose); + URLClassLoader testLoader = buildClassLoader(urlsArray, cpLoader); Class exporterClass = Class.forName("org.wildfly.glow.plugin.arquillian.GlowArquillianDeploymentExporter", true, cpLoader); Constructor ctr = exporterClass.getConstructor(List.class, ClassLoader.class, Path.class, Boolean.TYPE); Object obj = ctr.newInstance(classes, testLoader, outputFolder, verbose); @@ -64,12 +64,9 @@ public static void main(String[] args) throws Exception { System.exit(0); } - private static URLClassLoader buildClassLoader(String[] cpUrls, ClassLoader parent, boolean verbose) throws Exception { + private static URLClassLoader buildClassLoader(String[] cpUrls, ClassLoader parent) throws Exception { List urls = new ArrayList<>(); for (String s : cpUrls) { - if (verbose) { - System.out.println("URL " + s); - } urls.add(new File(s).toURI().toURL()); } URL[] cp = urls.toArray(new URL[0]); diff --git a/arquillian-plugin/src/main/java/org/wildfly/glow/plugin/arquillian/ScanMojo.java b/arquillian-plugin/src/main/java/org/wildfly/glow/plugin/arquillian/ScanMojo.java index 67ea6e39..fa052b8c 100644 --- a/arquillian-plugin/src/main/java/org/wildfly/glow/plugin/arquillian/ScanMojo.java +++ b/arquillian-plugin/src/main/java/org/wildfly/glow/plugin/arquillian/ScanMojo.java @@ -145,12 +145,6 @@ public void trace(Object s) { @Parameter(required = false, alias = "feature-packs") List featurePacks = Collections.emptyList(); - /** - * Enable verbose output (containing identified errors, suggestions, ...). - */ - @Parameter(alias = "enable-verbose-output", property = "org.wildfly.glow.enable-verbose-output") - boolean enableVerboseOutput = false; - /** * GroupId:ArtifactId of dependencies that contain test classes to scan for * Arquillian deployments. @@ -216,6 +210,9 @@ public void trace(Object s) { @Parameter(alias = "expected-errors", property = "org.wildfly.glow.expected-errors") private List expectedErrors = Collections.emptyList(); + /** + * Enable verbose output (containing identified errors, suggestions, ...). + */ @Parameter(property = "org.wildfly.glow.verbose") private boolean verbose = false; @@ -275,6 +272,13 @@ public void trace(Object s) { @Parameter(alias = "context", property = "org.wildfly.glow.context") private String context; + /** + * By default the execution is aborted when unknown errors are reported. You can disable the check done for errors by + * setting this option to true. + */ + @Parameter(alias = "check-errors", property = "org.wildfly.glow.check-errors") + private boolean checkErrors = true; + @Override public void execute() throws MojoExecutionException, MojoFailureException { @@ -388,12 +392,11 @@ public void execute() throws MojoExecutionException, MojoFailureException { setBinaries(retrieveDeployments(paths, classesRootFolder, outputFolder)). setUserEnabledAddOns(addOns). setConfigName(configName). - setSuggest((enableVerboseOutput || getLog().isDebugEnabled())). + setSuggest((verbose || getLog().isDebugEnabled())). setJndiLayers(layersForJndi). setVerbose(verbose || getLog().isDebugEnabled()). setOutput(OutputFormat.PROVISIONING_XML). setTechPreview(previewServer). - setExecutionProfiles(profiles). setExecutionContext(context).setVersion(serverVersion); if (!featurePacks.isEmpty()) { @@ -404,64 +407,86 @@ public void execute() throws MojoExecutionException, MojoFailureException { try (ScanResults results = GlowSession.scan(artifactResolver, arguments, writer)) { - boolean skipTests = Boolean.getBoolean("maven.test.skip") || Boolean.getBoolean("skipTests"); - if (skipTests) { - getLog().warn("Tests are disabled, not checking for expected discovered layers."); - } else { - if (expectedDiscovery != null) { - String compact = results.getCompactInformation(); - if (!expectedDiscovery.equals(compact)) { - throw new MojoExecutionException("Error in glow discovery.\n" - + "-Expected: " + expectedDiscovery + "\n" - + "-Found : " + compact); - } - } - if (results.getErrorSession().hasErrors()) { - if (expectedErrors.isEmpty()) { - results.outputInformation(writer); - throw new MojoExecutionException("An error has been reported and expected-errors has not been set."); - } - List errors = new ArrayList<>(); - for (IdentifiedError err : results.getErrorSession().getErrors()) { - if (!err.isFixed()) { - errors.add(err); + boolean skipTests = Boolean.getBoolean("maven.test.skip") || Boolean.getBoolean("skipTests"); + if (skipTests) { + getLog().warn("Tests are disabled, not checking for expected discovered layers."); + } else { + if (expectedDiscovery != null) { + String compact = results.getCompactInformation(); + if (!expectedDiscovery.equals(compact)) { + throw new MojoExecutionException("Error in glow discovery.\n" + + "-Expected: " + expectedDiscovery + "\n" + + "-Found : " + compact); } } - if (expectedErrors.size() != errors.size()) { - List descriptions = new ArrayList<>(); - for (IdentifiedError err : errors) { - descriptions.add(err.getDescription()); + if (results.getErrorSession().hasErrors()) { + boolean errorsFound = false; + if (expectedErrors.isEmpty()) { + results.outputInformation(writer); + errorsFound = true; + String msg = "An error has been reported and expected-errors has not been set."; + if (checkErrors) { + throw new MojoExecutionException(msg); + } else { + getLog().warn(msg); + return; + } } - throw new MojoExecutionException("Number of expected errors mismatch. Expected " - + expectedErrors.size() + " reported " + errors.size() + ".\n" - + "Reported Errors " + descriptions + "\n" - + "Expected Errors " + expectedErrors); - } - Iterator it = errors.iterator(); - while (it.hasNext()) { - IdentifiedError err = it.next(); - if (expectedErrors.contains(err.getDescription())) { - it.remove(); + List errors = new ArrayList<>(); + for (IdentifiedError err : results.getErrorSession().getErrors()) { + if (!err.isFixed()) { + errors.add(err); + } } - } - it = errors.iterator(); - if (it.hasNext()) { - StringBuilder builder = new StringBuilder(); + if (expectedErrors.size() != errors.size()) { + List descriptions = new ArrayList<>(); + for (IdentifiedError err : errors) { + descriptions.add(err.getDescription()); + } + String msg = "Number of expected errors mismatch. Expected " + + expectedErrors.size() + " reported " + errors.size() + ".\n" + + "Reported Errors " + descriptions + "\n" + + "Expected Errors " + expectedErrors; + errorsFound = true; + if (checkErrors) { + throw new MojoExecutionException(msg); + } else { + getLog().warn(msg); + } + } + Iterator it = errors.iterator(); while (it.hasNext()) { IdentifiedError err = it.next(); - builder.append(err.getDescription()).append("\n"); + if (expectedErrors.contains(err.getDescription())) { + it.remove(); + } + } + it = errors.iterator(); + if (it.hasNext()) { + StringBuilder builder = new StringBuilder(); + while (it.hasNext()) { + IdentifiedError err = it.next(); + builder.append(err.getDescription()).append("\n"); + } + errorsFound = true; + String msg = "The following errors are unexpected:\n" + builder.toString(); + if (checkErrors) { + throw new MojoExecutionException(msg); + } else { + getLog().warn(msg); + } + } + if(!errorsFound) { + getLog().info("Expected errors found in glow scanning results. " + + " The test execution should fix them (eg: add missing datasources)"); + } + } else { + if (!expectedErrors.isEmpty()) { + throw new MojoExecutionException("expected-errors contains errors but no error reported."); } - throw new MojoExecutionException("The following errors are unexpected:\n" + builder.toString()); - } - getLog().info("Expected errors found in glow scanning results. " - + " The test execution should fix them (eg: add missing datasources)"); - } else { - if (!expectedErrors.isEmpty()) { - throw new MojoExecutionException("expected-errors contains errors but no error reported."); } } - } - if (enableVerboseOutput || getLog().isDebugEnabled()) { + if (verbose || getLog().isDebugEnabled()) { results.outputInformation(writer); } else { results.outputCompactInformation(writer); @@ -527,7 +552,7 @@ private Process startScanner(Path outputFolder, } Path lst = outputFolder.resolve(TEST_PATHS); Files.write(lst, pathList.toString().getBytes()); - if (enableVerboseOutput) { + if (verbose) { getLog().info("SCANNER: Test elements: " + pathList); getLog().info("SCANNER: Classes: " + classesLst); } @@ -536,10 +561,10 @@ private Process startScanner(Path outputFolder, collectCpPaths(System.getProperty("java.home"), Thread.currentThread().getContextClassLoader(), cp, - enableVerboseOutput, + verbose, reducedCp, getLog()); - if (enableVerboseOutput) { + if (verbose) { getLog().info("SCANNER: classpath: " + cp); getLog().info("SCANNER: bootstrap classpath: " + reducedCp); } @@ -559,7 +584,7 @@ private Process startScanner(Path outputFolder, cmd.add(lst.toAbsolutePath().toString()); cmd.add(classesLst.toString()); cmd.add(outputFolder.toAbsolutePath().toString()); - cmd.add(enableVerboseOutput || getLog().isDebugEnabled() ? "true" : "false"); + cmd.add(verbose || getLog().isDebugEnabled() ? "true" : "false"); final ProcessBuilder builder = new ProcessBuilder(cmd) .redirectError(ProcessBuilder.Redirect.INHERIT) .redirectOutput(ProcessBuilder.Redirect.INHERIT); @@ -577,9 +602,6 @@ private static void collectCpPaths(String javaHome, ClassLoader cl, StringBuilde for (URL url : ((URLClassLoader) cl).getURLs()) { final String filePath; File file; - if (enableVerboseOutput) { - log.info("SCANNER: CP file url " + url); - } try { file = new File(url.toURI()); filePath = file.getAbsolutePath(); diff --git a/core/src/main/java/org/wildfly/glow/DeploymentScanner.java b/core/src/main/java/org/wildfly/glow/DeploymentScanner.java index 1bf3506b..620ac1dc 100644 --- a/core/src/main/java/org/wildfly/glow/DeploymentScanner.java +++ b/core/src/main/java/org/wildfly/glow/DeploymentScanner.java @@ -175,7 +175,7 @@ private void scanAnnotations(DeploymentScanContext ctx) throws IOException { if (l != null) { ctx.layers.addAll(l); //System.out.println("Find an annotation " + ai.name().packagePrefix() + " layer being " + l); - LayerMapping.addRule(LayerMapping.RULE.ANNOTATION, l, ai.name().packagePrefix()); + LayerMapping.addRule(LayerMapping.RULE.ANNOTATION, l, ai.name().packagePrefix() + ".*"); } else { // Pattern? for (String s : ctx.mapping.getAnnotations().keySet()) { @@ -578,7 +578,7 @@ private Set lookup(String className, DeploymentScanContext ctx) { String pkgPrefix = className.substring(0, index); l = map.get(pkgPrefix); if (l != null) { - LayerMapping.addRule(LayerMapping.RULE.JAVA_TYPE,l, pkgPrefix); + LayerMapping.addRule(LayerMapping.RULE.JAVA_TYPE,l, pkgPrefix + ".*"); } } if (l == null) { diff --git a/docs/guide/test-maven-plugin/index.adoc b/docs/guide/test-maven-plugin/index.adoc index 866e1174..5249f962 100644 --- a/docs/guide/test-maven-plugin/index.adoc +++ b/docs/guide/test-maven-plugin/index.adoc @@ -34,6 +34,13 @@ The left part of the arrow contains the list of the discovered Layers according The right part is what will get provisioned. Composed of a base Layer (always `ee-core-profile-server`) and a list of the discovered Layers that has been cleaned-up to avoid to include dependencies. +Note: In case the `ha` profile is enabled, you need to prefix the expected discovery with the `[ha]` prefix. For example: + + +``` +[ha][cdi, ee-integration, ejb, ejb-lite, elytron-oidc-client, naming, servlet]==>ee-core-profile-server,ejb,elytron-oidc-client +``` + BTW: The link:https://github.com/wildfly/wildfly/tree/main/testsuite/integration[WildFly integration tests] contain a lot of examples of WildFly Glow scanning executions that you can use as a starting-point. @@ -61,6 +68,12 @@ That is generally an error to ignore, the test itself should add the datasource * `no default datasource found error`: It has been identified that a default datasource is required. That can be fixed by adding the add-on `h2-database:default`. +You can disable the check for errors by setting `false`. + +Note: When `true` is set, the list of expected errors can be different (it contains more details on the error), +make sure to suppress the expected errors when debugging or set `false`. + + ### Selecting a surefire execution to scan You can select a given surefire execution to scan. To do so add the following element to the plugin configuration: @@ -133,6 +146,23 @@ In case you want to use a specific WildFly version, set the `ser By default the latest WildFly Galleon feature-pack and extra Galleon feature-packs are used. You have the ability to set a list of feature-packs using the `` element. + +### Understanding which rule selected a given Galleon layer + +When setting `true`, the set of rules that selected a given layer are printed in the console. Output example: + +---- +... +layers inclusion rules +* ee-core-profile-server + - BASE_LAYER +* ee-concurrency + - JAVA_TYPE: [jakarta.enterprise.concurrent.*] +* undertow-https + - ADD_ON +... +---- + ### A simple pom.xml file extract In this extract, the latest WildFly server version is used. diff --git a/docs/guide/wildfly-maven-plugin/index.adoc b/docs/guide/wildfly-maven-plugin/index.adoc index 98bcfff2..f6311fba 100644 --- a/docs/guide/wildfly-maven-plugin/index.adoc +++ b/docs/guide/wildfly-maven-plugin/index.adoc @@ -255,3 +255,16 @@ You can print the rules that selected the Galleon Layers. To do so set the ` ---- +An example of output: + +---- +... +layers inclusion rules +* ee-core-profile-server + - BASE_LAYER +* ee-concurrency + - JAVA_TYPE: [jakarta.enterprise.concurren.*] +* undertow-https + - ADD_ON +... +---- diff --git a/tests/arquillian-plugin-tests/pom.xml b/tests/arquillian-plugin-tests/pom.xml index 9c4bab10..75a3ae7a 100644 --- a/tests/arquillian-plugin-tests/pom.xml +++ b/tests/arquillian-plugin-tests/pom.xml @@ -52,10 +52,24 @@ test-compile - true + true []==>ee-core-profile-server + + scan-cloud-ha-preview + + scan + + test-compile + + true + cloud + true + ha + [ha][]==>ee-core-profile-server + + scan-custom @@ -70,7 +84,7 @@ 29.0.1.Final - true + true []==>ee-core-profile-server