Cucumber - running parallel scenarios - ruby

I have a test in Cucumber which is using the Example Table format. Could I get Cucumber to run each line of the table in parallel.
Scenario Outline: e_5_1 Check Convicting Court Codes in English 2
Given I navigate to the customer portal search screen
When I enter DLN, NI and Postcode from row <user_row> and access Penalties and disqualifications
And click on the endorsement in order to confirm court description from rows <row1> to <row2>
Then I Logout
Examples:
| user_row | row1 | row2 |
| 2 | 2 | 51 |
| 52 | 52 | 98 |
| 99 | 99 | 148 |
| 149 | 149 | 198 |
Normally, I would say run test.feature for the line number and it would iterate through each row one at a time, or using the line number I could specify in which line the table row sits.
Could I get it to run all 4 rows in parallel at the same time?
Thanks in advance

You can use opensource plugin cucumber-jvm-parallel-plugin which has many advantages over existing solutions but its only for java. Available at maven repository
<dependency>
<groupId>com.github.temyers</groupId>
<artifactId>cucumber-jvm-parallel-plugin</artifactId>
<version>2.2.0</version>
</dependency>
First you need to add this dependency and plugin with required configuration in your project pom file.
<dependencies>
<dependency>
<groupId>com.github.temyers</groupId>
<artifactId>cucumber-jvm-parallel-plugin</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>net.masterthought</groupId>
<artifactId>cucumber-reporting</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
<plugin>
<groupId>com.github.temyers</groupId>
<artifactId>cucumber-jvm-parallel-plugin</artifactId>
<version>2.2.0</version>
<executions>
<execution>
<id>generateRunners</id>
<phase>generate-test-sources</phase>
<goals>
<goal>generateRunners</goal>
</goals>
<configuration>
<!-- Mandatory -->
<!-- comma separated list of package names to scan for glue code -->
<glue>foo, bar</glue>
<outputDirectory>${project.build.directory}/generated-test-sources/cucumber</outputDirectory>
<!-- The directory, which must be in the root of the runtime classpath, containing your feature files. -->
<featuresDirectory>src/test/resources/features/</featuresDirectory>
<!-- Directory where the cucumber report files shall be written -->
<cucumberOutputDir>target/cucumber-parallel</cucumberOutputDir>
<!-- comma separated list of output formats json,html,rerun.txt -->
<format>json</format>
<!-- CucumberOptions.strict property -->
<strict>true</strict>
<!-- CucumberOptions.monochrome property -->
<monochrome>true</monochrome>
<!-- The tags to run, maps to CucumberOptions.tags property you can pass ANDed tags like "#tag1","#tag2" and ORed tags like "#tag1,#tag2,#tag3" -->
<tags></tags>
<!-- If set to true, only feature files containing the required tags shall be generated. -->
<filterFeaturesByTags>false</filterFeaturesByTags>
<!-- Generate TestNG runners instead of default JUnit ones. -->
<useTestNG>false</useTestNG>
<!-- The naming scheme to use for the generated test classes. One of 'simple' or 'feature-title' -->
<namingScheme>simple</namingScheme>
<!-- The class naming pattern to use. Only required/used if naming scheme is 'pattern'.-->
<namingPattern>Parallel{c}IT</namingPattern>
<!-- One of [SCENARIO, FEATURE]. SCENARIO generates one runner per scenario. FEATURE generates a runner per feature. -->
<parallelScheme>SCENARIO</parallelScheme>
<!-- This is optional, required only if you want to specify a custom template for the generated sources (this is a relative path) -->
<customVmTemplate>src/test/resources/cucumber-custom-runner.vm</customVmTemplate>
</configuration>
</execution>
</executions>
</plugin>
Now add below plugin just below above plugin which will invoke runner classes generated by above plugin
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version>
<configuration>
<forkCount>5</forkCount>
<reuseForks>true</reuseForks>
<includes>
<include>**/*IT.class</include>
</includes>
</configuration>
</plugin>
Above two plugins will do magic for cucumber test running in parallel (provided you machine also have advanced hardware support).
Strictly provided <forkCount>n</forkCount> here 'n' is directly proportional to 1) Advanced Hardware support and 2) you available nodes i.e. registered browser instances to HUB.
One major and most important changes is your WebDriver class must be SHARED and you should not implement driver.quit() method, as closing is take care by shutdown hook.
import cucumber.api.Scenario;
import cucumber.api.java.After;
import cucumber.api.java.Before;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.events.EventFiringWebDriver;
public class SharedDriver extends EventFiringWebDriver {
private static WebDriver REAL_DRIVER = null;
private static final Thread CLOSE_THREAD = new Thread() {
#Override
public void run() {
REAL_DRIVER.close();
}
};
static {
Runtime.getRuntime().addShutdownHook(CLOSE_THREAD);
}
public SharedDriver() {
super(CreateDriver());
}
public static WebDriver CreateDriver() {
WebDriver webDriver;
if (REAL_DRIVER == null)
webDriver = new FirefoxDriver();
setWebDriver(webDriver);
return webDriver;
}
public static void setWebDriver(WebDriver webDriver) {
this.REAL_DRIVER = webDriver;
}
public static WebDriver getWebDriver() {
return this.REAL_DRIVER;
}
#Override
public void close() {
if (Thread.currentThread() != CLOSE_THREAD) {
throw new UnsupportedOperationException("You shouldn't close this WebDriver. It's shared and will close when the JVM exits.");
}
super.close();
}
#Before
public void deleteAllCookies() {
manage().deleteAllCookies();
}
#After
public void embedScreenshot(Scenario scenario) {
try {
byte[] screenshot = getScreenshotAs(OutputType.BYTES);
scenario.embed(screenshot, "image/png");
} catch (WebDriverException somePlatformsDontSupportScreenshots) {
System.err.println(somePlatformsDontSupportScreenshots.getMessage());
}
}
}
Considering you want to execute more than 50 threads i.e. same no of browser instances are registered to HUB but Hub will die if it doesn't get enough memory therefore to avoid this critical situation you should start hub with -DPOOL_MAX=512 (or larger) as stated in grid2 documentation.
Really large (>50 node) Hub installations may need to increase the jetty threads by setting -DPOOL_MAX=512 (or larger) on the java command line.
java -jar selenium-server-standalone-<version>.jar -role hub -DPOOL_MAX=512

Take a look at this. It might be the solution to your problem:
https://github.com/grosser/parallel_tests

Related

Mule SDK Plugin leads to java.lang.NoClassDefFoundError: org/json/JSONObject

I created a plugin on my own that uses a JSON library. The project builds, runs and tests in IntelliJ and on the console.
When I import my plugin to Anypoint Studio I'm getting this error:
ERROR 2021-06-04 06:41:34,587 [[MuleRuntime].uber.01: [p-buchzentrum].get:\delays:p-buchzentrum-config.BLOCKING #2eb68d5] [processor: p-buchzentrum-implementationSub_Flow1/processors/4; event: 23dce8c1-c4ef-11eb-96c7-00e04c682392] org.mule.runtime.core.internal.exception.OnCriticalErrorHandler:
********************************************************************************
Message : java.lang.NoClassDefFoundError: org/json/JSONObject
Element : p-buchzentrum-implementationSub_Flow1/processors/4 # p-buchzentrum:p-buchzentrum-implementation.xml:22 (Write)
Element DSL : <bookit:write doc:name="Write" doc:id="1b4c08ce-1f90-459b-821d-78ad2a4f0f19" payload="#[payload]"></bookit:write>
Error type : MULE:FATAL_JVM_ERROR
FlowStack : at p-buchzentrum-implementationSub_Flow1(p-buchzentrum-implementationSub_Flow1/processors/4 # p-buchzentrum:p-buchzentrum-implementation.xml:22 (Write))
at get:\delays:p-buchzentrum-config(get:\delays:p-buchzentrum-config/processors/0 # p-buchzentrum:p-buchzentrum.xml:130 (Flow Reference))
at p-buchzentrum-main(p-buchzentrum-main/processors/0 # p-buchzentrum:p-buchzentrum.xml:17)
(set debug level logging or '-Dmule.verbose.exceptions=true' for everything)
********************************************************************************
ERROR 2021-06-04 06:41:34,597 [[MuleRuntime].uber.01: [p-buchzentrum].get:\delays:p-buchzentrum-config.BLOCKING #2eb68d5] [processor: p-buchzentrum-implementationSub_Flow1/processors/4; event: 23dce8c1-c4ef-11eb-96c7-00e04c682392] org.mule.runtime.core.internal.exception.OnCriticalErrorHandler:
********************************************************************************
Message : java.lang.NoClassDefFoundError: org/json/JSONObject
Element : (None)
Element DSL : (None)
Error type : MULE:FATAL_JVM_ERROR
FlowStack : (None)
Both my plugin and my Anypoint studio project contain the library:
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
My Operations class does not expose any methods that take or return any JSONObjects:
public class BookitOperations {
#MediaType(value = ANY, strict = false)
public String read(String payload) {
return FileHandler.read(payload);
}
#MediaType(value = ANY, strict = false)
public String write(String payload) {
return FileHandler.write(payload);
}
}
I tried packing the maven dependency with the assembly-plugin:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
But unpacking the plugin with:
jar xf {my-connector.jar}
Shows that this has not worked.
Any help would be appriciated!
Cheers

Setting descriptions for mojos and parameters in maven plugin

I am writing a maven plugin and I would like to add some documentation for the available goals and parameters.
When I run mvn help:describe -Dplugin=myplugin -Ddetail it prints out available goals and parameters. However it lists (no description) everywhere. From searching the internet I could not figure out where such a description is to be set.
For reference, my plugin is written in scala and looks roughly like this.
import org.apache.maven.plugins.annotations.{ Component, Parameter }
class MyMojo extends AbstractMojo {
#Parameter(defaultValue = "false", readonly = false)
private var skipFormatting: Boolean = _
}
So my question would be: where can set a description, such that it will show up with mvn help:describe -Dplugin=myplugin?
I strongly recommend to use the following:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>default-descriptor</id>
<phase>process-classes</phase>
</execution>
<execution>
<id>generate-helpmojo</id>
<goals>
<goal>helpmojo</goal>
</goals>
</execution>
</executions>
</plugin>
which will generate the help part during the build process. And yes you have to add some javadoc like this:
#Mojo(name = "failure", defaultPhase = LifecyclePhase.NONE,
requiresDependencyResolution = ResolutionScope.NONE, threadSafe = true)
public class FailureMojo extends AbstractMojo {
I don't understand why you haven't have any annotations on your Mojo?
The documentation like this: https://maven.apache.org/plugins/maven-install-plugin/plugin-info.html will be generated from the javadoc on parameters etc. https://github.com/apache/maven-install-plugin/blob/master/src/main/java/org/apache/maven/plugins/install/InstallMojo.java#L69
Based on examples when running the help:describe on familiar plugins like the maven-jar-plugin, tt appears it's based on the Javadoc of the Mojo class.

How to precompile jsp in a spring boot application?

I'm using Spring boot and we were using Spring with Tomcat before that.
When we used Spring and Tomcat two years ago, we used a maven plugin to precompile the jsp.
It was really useful to avoid this compilation to be made for every first visits after a deployement.
However all maven plugin that we know dumps a web.xml file that list all jsp and associated generated servlets.
With Spring boot, it don't use web.xml anymore, so this file is ignored.
We still have the compilation and that's a security belt but there is a penalty for every first visit on each page.
Does anybody know if it's possible to precompile jsp in a Spring boot application ?
I got precompiling to work either at server start time (don't have to use JspC, so simpler build file) and at build time (much quicker server start time). I register the resulting servlets dynamically, so you don't have to manually change any files if you add/remove JSPs.
At server start time
Use ServletRegistration.Dynamic to register a JSP_SERVLET_CLASS Servlet for each JSP.
Use the initParameter jspFile to set the JSP filename (ref)
e.g. for SpringBoot in a ServletContextInitializer (ref):
#Bean
public ServletContextInitializer preCompileJspsAtStartup() {
return servletContext -> {
getDeepResourcePaths(servletContext, "/WEB-INF/jsp/").forEach(jspPath -> {
log.info("Registering JSP: {}", jspPath);
ServletRegistration.Dynamic reg = servletContext.addServlet(jspPath, Constants.JSP_SERVLET_CLASS);
reg.setInitParameter("jspFile", jspPath);
reg.setLoadOnStartup(99);
reg.addMapping(jspPath);
});
};
}
private static Stream<String> getDeepResourcePaths(ServletContext servletContext, String path) {
return (path.endsWith("/")) ? servletContext.getResourcePaths(path).stream().flatMap(p -> getDeepResourcePaths(servletContext, p))
: Stream.of(path);
}
At build time
Generate Java source files for each JSP and a web.xml with their servlet mappings using JspC (ref).
Then register these with the ServletContext (by parsing the web.xml with Tomcat's WebXmlParser, e.g. for SpringBoot:
#Value("classpath:precompiled-jsp-web.xml")
private Resource precompiledJspWebXml;
#Bean
public ServletContextInitializer registerPreCompiledJsps() {
return servletContext -> {
// Use Tomcat's web.xml parser (assume complete XML file and validate).
WebXmlParser parser = new WebXmlParser(false, true, true);
try (InputStream is = precompiledJspWebXml.getInputStream()) {
WebXml webXml = new WebXml();
boolean success = parser.parseWebXml(new InputSource(is), webXml, false);
if (!success) {
throw new RuntimeException("Error parsing Web XML " + precompiledJspWebXml);
}
for (ServletDef def : webXml.getServlets().values()) {
log.info("Registering precompiled JSP: {} = {} -> {}", def.getServletName(), def.getServletClass());
ServletRegistration.Dynamic reg = servletContext.addServlet(def.getServletName(), def.getServletClass());
reg.setLoadOnStartup(99);
}
for (Map.Entry<String, String> mapping : webXml.getServletMappings().entrySet()) {
log.info("Mapping servlet: {} -> {}", mapping.getValue(), mapping.getKey());
servletContext.getServletRegistration(mapping.getValue()).addMapping(mapping.getKey());
}
} catch (IOException e) {
throw new RuntimeException("Error registering precompiled JSPs", e);
}
};
}
Example Maven config to generate and compile the JSP classes, and generate the precompiled-jsp-web.xml:
<!-- Needed to get the jasper Ant task to work (putting it in the plugin's dependencies didn't work) -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina-ant</artifactId>
<version>8.0.32</version>
<scope>provided</scope>
</dependency>
<!-- ... -->
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>precompile-jsp-generate-java</id>
<!-- Can't be generate-sources because we need the compiled Henry taglib classes already! -->
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo message="Precompiling JSPs"/>
<property name="compile_classpath" refid="maven.compile.classpath"/>
<property name="target_dir" value="${project.basedir}/generated-sources/jspc" />
<path id="jspc_classpath">
<path path="${compile_classpath}"/>
</path>
<typedef resource="org/apache/catalina/ant/catalina.tasks" classpathref="jspc_classpath"/>
<mkdir dir="${target_dir}/java"/>
<mkdir dir="${target_dir}/resources"/>
<jasper
validateXml="false"
uriroot="${project.basedir}/src/main/webapp"
compilertargetvm="1.8"
compilersourcevm="1.8"
failonerror="true"
javaencoding="UTF-8"
webXml="${target_dir}/resources/precompiled-jsp-web.xml"
outputDir="${target_dir}/java/" >
</jasper>
<!-- Can't use Maven to compile the JSP classes because it has already compiled the app's classes
(needed to do that becuase JspC needs compiled app classes) -->
<javac srcdir="${target_dir}/java" destdir="${project.build.outputDirectory}" classpathref="jspc_classpath" fork="true"/>
<!-- Have to copy the web.xml because process-resources phase has already finished (before compile) -->
<copy todir="${project.build.outputDirectory}">
<fileset dir="${target_dir}/resources"/>
</copy>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
<!-- Not strictly necessary, because Ant does the compilation, but at least attempts to keep it in sync with Maven -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-precompiled-jsp-java-sources</id>
<phase>generate-sources</phase>
<goals><goal>add-source</goal></goals>
<configuration>
<sources>
<source>${project.basedir}/generated-sources/jspc/java</source>
</sources>
</configuration>
</execution>
<execution>
<id>add-precompiled-jsp-resources</id>
<phase>generate-resources</phase>
<goals><goal>add-resource</goal></goals>
<configuration>
<resources>
<resource>
<directory>${project.basedir}/generated-sources/jspc/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
Based on the excellent answer of paulcm I came up with my own solution as the above solution didn't work for me and I couldn't track down the error. Maybe the answer above is outdated for tomcat9. Or it had some problem with multi-module setup. However: All credits belong to paulcm
This is only the compile time solution.
Add these two plugins to your pom.xml
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jspc-maven-plugin</artifactId>
<version>9.4.15.v20190215</version>
<executions>
<execution>
<id>jspc</id>
<goals>
<goal>jspc</goal>
</goals>
<configuration>
<mergeFragment>true</mergeFragment>
<sourceVersion>1.8</sourceVersion>
<targetVersion>1.8</targetVersion>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webXml>${project.basedir}/target/web.xml</webXml>
</configuration>
</plugin>
Add an empty web.xml file
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
<session-config>
<cookie-config>
</cookie-config>
</session-config>
</web-app>
Add a Registry
import org.apache.tomcat.util.descriptor.web.ServletDef;
import org.apache.tomcat.util.descriptor.web.WebXml;
import org.apache.tomcat.util.descriptor.web.WebXmlParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.xml.sax.InputSource;
import javax.servlet.ServletRegistration;
import java.io.InputStream;
import java.util.Map;
#Configuration
public class PreCompileJspRegistry {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Bean
public ServletContextInitializer registerPreCompiledJsps() {
return servletContext -> {
InputStream inputStream = servletContext.getResourceAsStream("/WEB-INF/web.xml");
if (inputStream == null) {
logger.info("Could not read web.xml");
return;
}
try {
WebXmlParser parser = new WebXmlParser(false, false, true);
WebXml webXml = new WebXml();
boolean success = parser.parseWebXml(new InputSource(inputStream), webXml, false);
if (!success) {
logger.error("Error registering precompiled JSPs");
}
for (ServletDef def : webXml.getServlets().values()) {
logger.info("Registering precompiled JSP: {} = {} -> {}", def.getServletName(), def.getServletClass());
ServletRegistration.Dynamic reg = servletContext.addServlet(def.getServletName(), def.getServletClass());
reg.setLoadOnStartup(99);
}
for (Map.Entry<String, String> mapping : webXml.getServletMappings().entrySet()) {
logger.info("Mapping servlet: {} -> {}", mapping.getValue(), mapping.getKey());
servletContext.getServletRegistration(mapping.getValue()).addMapping(mapping.getKey());
}
} catch (Exception e) {
logger.error("Error registering precompiled JSPs", e);
}
};
}
}
A comment for "At server start time" outlined above: the servlet you create will by default be in development mode if the application is packaged in an executable jar, so you if you use it in production mode, you should also set development = false ++ to prevent the jsps from being compiled again:
reg.setInitParameter("genStringAsCharArray", "true");
reg.setInitParameter("trimSpaces", "true");
reg.setInitParameter("development", "false");

Generate DDL script at MAVEN build with Hibernate4 / JPA 2.1

It seems like the hibernate3-maven-plugin used to generate DDL create/drop scripts is not compatible any more with Hibernate 4.3 and newer versions (using JPA 2.1).
I use this plugin configuration :
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>hibernate3-maven-plugin</artifactId>
<version>3.0</version>
<executions>
<execution>
<id>generate-sql-schema</id>
<phase>process-sources</phase>
<goals>
<goal>hbm2ddl</goal>
</goals>
<configuration>
<hibernatetool>
<jpaconfiguration persistenceunit="${persistenceUnitName}" />
<hbm2ddl update="true" create="true" export="false"
outputfilename="src/main/sql/schema.sql" format="true"
console="true" />
</hibernatetool>
</configuration>
</execution>
</executions>
</plugin>
But I get the following error :
[ERROR] Failed to execute goal org.codehaus.mojo:hibernate3-maven-plugin:3.0:hbm2ddl (generate-sql-schema) on project my-project: There was an error creating the AntRun task.
An Ant BuildException has occured: java.lang.NoClassDefFoundError: org/hibernate/util/ReflectHelper: org.hibernate.util.ReflectHelper -> [Help 1]
This class as migrated to a new package : org.hibernate.internal.util.ReflectHelper
However i found no clear way to keep generating DDL create scripts at MAVEN build.
There is no hibernate4-maven-plugin, or any other official way to do it.
So what ? Isn't it a main feature that should be supported ? How to do it ?
As Hibernate 4.3+ now implements JPA 2.1 the appropriate way to generate DDL scripts is to use following set of JPA 2.1 properties :
<property name="javax.persistence.schema-generation.scripts.action" value="create"/>
<property name="javax.persistence.schema-generation.create-source" value="metadata"/>
<property name="javax.persistence.schema-generation.scripts.create-target" value="target/jpa/sql/create-schema.sql"/>
A nice summary of others properties and context of schema generation in JPA 2.1 can be found here :
https://blogs.oracle.com/arungupta/entry/jpa_2_1_schema_generation
And official JPA 2.1 specifications here :
https://jcp.org/aboutJava/communityprocess/final/jsr338/index.html
As this will be generated at runtime, you may want to execute this DDL generation at build.
Here is the JPA 2.1 approach to generate this script programmatically :
import java.io.IOException;
import java.util.Properties;
import javax.persistence.Persistence;
import org.hibernate.jpa.AvailableSettings;
public class JpaSchemaExport {
public static void main(String[] args) throws IOException {
execute(args[0], args[1]);
System.exit(0);
}
public static void execute(String persistenceUnitName, String destination) {
System.out.println("Generating DDL create script to : " + destination);
final Properties persistenceProperties = new Properties();
// XXX force persistence properties : remove database target
persistenceProperties.setProperty(org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO, "");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_DATABASE_ACTION, "none");
// XXX force persistence properties : define create script target from metadata to destination
// persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_CREATE_SCHEMAS, "true");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_SCRIPTS_ACTION, "create");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_CREATE_SOURCE, "metadata");
persistenceProperties.setProperty(AvailableSettings.SCHEMA_GEN_SCRIPTS_CREATE_TARGET, destination);
Persistence.generateSchema(persistenceUnitName, persistenceProperties);
}
}
As you can see it's very simple !
Now you can use this in an AntTask, or MAVEN build like this (for MAVEN) :
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>generate-ddl-create</id>
<phase>process-classes</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<!-- ANT Task definition -->
<java classname="com.orange.tools.jpa.JpaSchemaExport"
fork="true" failonerror="true">
<arg value="${persistenceUnitName}" />
<arg value="target/jpa/sql/schema-create.sql" />
<!-- reference to the passed-in classpath reference -->
<classpath refid="maven.compile.classpath" />
</java>
</target>
</configuration>
</execution>
</executions>
</plugin>
Note that the official hibernate-maven-plugin also may, or may not, do the trick in some way :
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-maven-plugin</artifactId>
<version>4.3.1.Final</version>
</dependency>
Enjoy ! :)

Annotation scan not scanning external jars in classpath

Issue: Spring Component Annotation scan not picking up the class annotated in the external jar which is not included in pom.xml. But i need to scan for classes with specific annotation from external jars. These external jars will be placed in the classpath but will not be known to my application during compile time.
1) We have a maven module(artifactId="metric_processor") which produces a jar file(metric_processor.jar) and has following classes
package com.metric;
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface ProcessMetric {
String name();
}
package com.metric;
public interface MetricProcessor {
int computeMetric();
}
package com.metric;
#ProcessMetric(name="LATENCY")
#Component
public class LatencyMetricProcessor implements MetricProcessor {
.....
}
2) We have another maven module ("artifactId="metric_processor_external") which produces a jar(metric_processor_external.jar) and includes "metric_processor" module as compile time scope.
package com.metric;
#ProcessMetric(name="TEST_METRIC_EXTERNAL")
#Component
public class TestMetricProcessor implements MetricProcessor {
....
}
3) We have a third(main) maven module(artifactId="main_application") which is a stand alone application(uses spring) which includes module "metric_processor" in compile scope. (But does not include "metric_processor_external"). The build plugin for the third module is
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.main.TriggerMetricProcessor</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Application context xml for this module is
<beans>
<context:component-scan base-package="com.metric">
<context:include-filter type="annotation" expression="com.metric.ProcessMetric" />
</context:component-scan>
<bean id="triggerMetricProcessor" class="com.main.TriggerMetricProcessor" />
</beans>
I have the following class which is the starting point of the application
package com.main;
import ...
public class TriggerMetricProcessor {
public static void main(String[] args) throws Exception {
ApplicationContext context =
new ClassPathXmlApplicationContext("application-context.xml");
TriggerMetricProcessor triggerMetricProcessor = (TriggerMetricProcessor) context.getBean("triggerMetricProcessor");
triggerMetricProcessor.initMetricProcessor(context);
}
private void initMetricProcessor(ApplicationContext context) {
GenericBeanFactoryAccessor beanFactoryAccessor = new GenericBeanFactoryAccessor(context);
final Map<String, Object> metricProcessors = beanFactoryAccessor.getBeansWithAnnotation(ProcessMetric.class);
for (final Object metricProcessor : metricProcessors.values()) {
final Class<? extends MetricProcessor> metricProcessorClass = (Class<? extends MetricProcessor>)metricProcessor.getClass();
final ProcessMetric annotation = metricProcessorClass.getAnnotation(ProcessMetric.class);
System.out.println("Found MetricProcessor class: " + metricProcessorClass + ", with name: " + annotation.name());
}
}
}
we compile the third module as
maven clean install assembly:single
This produces the jar file "main_application-with-dependencies.jar"
Then we run its as
java -cp "metric_process_external.jar" -jar main_application-with-dependencies.jar
Now the application finds only "LatencyMetricProcessor" and does not find the "TestMetricProcessor".
Can someone please help?
When you use the -jar option to execute a jar file, the -cp option is ignored.
The Oracle Java docs for the -jar option say:
-jar
Execute a program encapsulated in a JAR file. The first argument is
the name of a JAR file instead of a startup class name. In order for
this option to work, the manifest of the JAR file must contain a line
of the form Main-Class: classname. Here, classname identifies the
class having the public static void main(String[] args) method that
serves as your application's starting point. See the Jar tool
reference page and the Jar trail of the Java Tutorial for information
about working with Jar files and Jar-file manifests.
When you use this option, the JAR file is the source of all user
classes, and other user class path settings are ignored.
Also check out this post: stackoverflow.com/questions/5879925/in-linux-how-to-execute-java-jar-file-with-external-jar-files
So you'll need to specify the metric_process_external.jar in your manifest file using a Class-Path: header. You should be able to get your Maven assembly plugin to do that.
If that's not practical, you'll need to run your application without the -jar flag:
java -cp "metric_process_external.jar:main_application-with-dependencies.jar" com.main.TriggerMetricProcessor

Resources