In my unit tests I want to create a tmp directory inside the ${project.build.directory}. How can I access the value of ${project.build.directory} inside my unit test?
One way, which I could think of, is to provide a filtered properties file in the test resources, which holdes that value. (I haven't tried yet, but I think that should work.)
Is there a direct way to access/ pass this property value?
I've used something like this with some success before. The unit test will still run even if not using Maven, the target directory will still get created two dirs up relative to the cwd of wherever the tests are run.
public File targetDir(){
String relPath = getClass().getProtectionDomain().getCodeSource().getLocation().getFile();
File targetDir = new File(relPath+"../../target");
if(!targetDir.exists()) {
targetDir.mkdir();
}
return targetDir;
}
I think using system properties is quite straightforward if you configure the surefire-plugin as explained here http://maven.apache.org/plugins/maven-surefire-plugin/examples/system-properties.html . Even the example there is answering your question directly:
<project>
[...]
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.9</version>
<configuration>
<systemPropertyVariables>
<propertyName>propertyValue</propertyName>
<buildDirectory>${project.build.directory}</buildDirectory>
[...]
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
[...]
</project>
Remember, that your unit tests don't have to be executed from Maven surefire plugin, so ${project.build.directory} property might not be available. To make your tests more portable I would rather recommend using File.createTempFile().
Related
I have a project compose by more than one module, and an integration test (in the test folder) where I want to run this script using the #sql annotation. By default the class path resource is used.
The test is inside this folder:
mainFolder/module1/src/test/java/com/.../.../controllers/TestClass.java
while the script is present in this folder:
mainFolder/scripts/postgres/script.sql
Basically I'm not sure which string (relative path) I should put in the value parameter of the #Sql annotation.
I am afraid that if your scripts are not copied to the class path there are not a lot of options.
Please, try something like:
#Sql("file:/path-to-mainFolder/mainFolder/scripts/postgres/script.sql")
As you can see in the documentation you can use any valid resource type.
Having said that, I think the best option is to make these resources available in the classpath. If you are using maven, you can use for instance the copy-resources goal of the maven resources plugin to copy your resources when running your tests:
<project>
...
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>copy-resources</id>
<!-- here the phase you need: validate, test-compile... -->
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/scripts</outputDirectory>
<resources>
<resource>
<!-- Depending on your project, try defining the scripts src location as you consider more appropriate -->
<directory>mainFolder/scripts/postgres/script.sql</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
...
</build>
...
</project>
With this setup, now you can define your script location as a classpath resource:
#Sql("/scripts/postgres/script.sql")
Perhaps, I am not sure about that, in a similar fashion you can use the plugin testResources goal as well.
If you want to use the scripts on your local environment machine you can also add the directory mainFolder/scripts/postgres as a dependency. In Intellij, you con go to Project Structure -> Modules -> your module -> dependencies -> + -> Jars or directories -> your script directory -> Classes.
Then you will be able to use
#Sql("/yourScript.sql")
In DevOps pipeline you would need to add the dedicated directory to your application server classpath.
Keep in mind that you will have to keep unique names for your sql files, because if you intend to use a structure with multiple directory levels, I think the jvm will load only the first entry. It is not a recommended approach, but it is fast if you only need to generate a report or test something on your local environment.
Another approach would be to specify at runtime -Xbootclasspath/a:. Then you would be able to use #Sql with relative paths as you initially wanted.
E.g.
-Xbootclasspath/a:path-to-mainFolder
then you would be able to use
#Sql("/scripts/postgres/script.sql")
import software.amazon.awscdk.services.lambda.Function;
Function helloLambda = new Function(helloStack, "hellocdkworld123", FunctionProps.builder()
.functionName("HelloLambda")
.code(Code.fromAsset("target/cdkhello-0.1.jar")) // <- x ?
.runtime(Runtime.JAVA_8)
.handler("com.myorg.functions.HelloLambda::sayHello") <- y?
.build());
There is also a possibility to reference it by S3 bucket. But when I run cdk bootstrap I get a generated bucket with generated name of the jar file. How should I be able to reference that before hand from code? Of course now I could write the exact bucket + file but then purpose of defining it from code is lost right?
First of all, assuming that the method that you want to execute when the Lambda is invoked is sayHello, from the com.myorg.functions.HelloLambda class, then that part of your solution is correct. The more difficult part is actually accessing the JAR with your Lambda code in it.
NOTE: I've updated my original answer with what I think is a better way to accomplish this. In order to avoid confusion and making this answer too wordy, I've removed the original answer, though much of it is common with this one. I credit this answer for helping to improve this answer.
Pass the path to the dependent resource's JAR to CDK
TL;DR
Create a new property for the full path to your Lambda JAR.
Associate dependency and execution related goals into the package phase of the build.
Update cdk.json to point to the the package phase.
Pass the full path via a system property to your CDK code.
Use the System property to pass to Code.asset(...).
Preparation
I've separated out the Lambda and the CDK infrastructure code into separate Maven modules. The intention being that once the Lambda code is compiled, packaged up into an uber JAR (its code plus all of its dependencies' code), the infrastructure module can refer to it as a dependency, passing the full path to the Lambda JAR to the App/Stack class to that it can use it as an asset.
Create a new property for the full path to your Lambda JAR.
In the properties section of your pom.xml, create a new property to refer to your Lambda JAR. Something like this:
<properties>
...
<lambda.jar>${GROUP_ID:ARTIFACT_ID:jar}</lambda.jar>
...
</properties>
Populate a property with the full path to your Lambda dependency's JAR, using the dependency plugin.
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<goals>
<goal>properties</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
...
<plugins>
</build>
This associates the properties goal with the process-resources phase. Whenever that phase of the build occurs, the property you've created previously will be populated with the full path to the JAR in your local repository.
Associate dependency and execution related goals into a single phase of the build.
When you create a new CDK Java project, it outputs a file called cdk.json, which points by default to the Maven exec:java goal. In order for your new lambda.jar property to be populated correctly, you need to associate the exec:java goal with the same phase as above.
<build>
<plugins>
...
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<mainClass>com.myorg.TestingApp</mainClass>
</configuration>
</plugin>
...
</plugins>
</build>
In order for your code to get access to the JAR file that you've generated, you need to create a System property (I couldn't get environment variables to work) to your App class. Your pom.xml started with something like this:
Pass the full path via a system property to your CDK code.
In the configuration section (after mainClass), add a system property for your assets directory, something like this:
<systemProperties>
<systemProperty>
<key>lambda.jar</key>
<value>${lambda.jar}</value>
</systemProperty>
</systemProperties>
Update cdk.json to point to the the common phase you've used.
Your cdk.json of your CDK project should be changed to point to the process-resources phase. Once done it will look like this:
{
"app": "mvn package"
}
It will cause both the goals to be run in succession, and upon execution the path to your Lambda's JAR will be passed as a system property.
Access the property from your App/Stack code.
Finally, now that the system property is created, you can access it from your code by calling System.getProperty("lambda.jar"). Something like this:
final Code code = Code.fromAsset(System.getProperty("lambda.jar"));
You can then use the code reference wherever needed when defining your Lambda functions.
I want to write an E2E test for a Maven project. The flow would be to run the resulting JAR and verify it's output. The question is how can I obtain the path to the output JAR from an integration test (written in JUnit run by the Failsafe plugin).
You can either use command line or make an entry in pom.xml to tell where you want jar file saved.
on command line
-DoutputDirectory=<specify_your_path>
On in your pom.xml
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>x.y.z</version>
<configuration>
<outputDirectory>/your/path/for/jar</outputDirectory>
</configuration>
</plugin>
</plugins>
</build>
Now on how to read path in test : The unit test will still run even if not using Maven, the target directory will still get created two dirs up relative to the current working directory of wherever the tests are run. You can use the method below to get the target directory in test.
public File targetDir(){
String relPath = getClass().getProtectionDomain().getCodeSource().getLocation().getFile();
File targetDir = new File(relPath+"../../target");
if(!targetDir.exists()) {
targetDir.mkdir();
}
return targetDir;
}
How can I write equivalent maven plugin for the following gradle plugin defined?
/*
* Plugin to copy system properties from gradle JVM to testing JVM
* Code was copied from gradle discussion froum:
* http://forums.gradle.org/gradle/topics/passing_system_properties_to_test_task
*/
class SystemPropertiesMappingPlugin implements Plugin{
public void apply(Project project){
project.tasks.withType(Test){ testTask ->
testTask.ext.mappedSystemProperties = []
doFirst{
mappedSystemProperties.each{mappedPropertyKey ->
def systemPropertyValue = System.getProperty(mappedPropertyKey)
if(systemPropertyValue){
testTask.systemProperty(mappedPropertyKey, systemPropertyValue)
}
}
}
}
}
}
It really depends on what exactly you want to achieve.
In case you want to help with writing a maven plugin in general, you'll have to read the documentation.
In case you want to filter system properties that Maven JVM passes to your test JVM, I don't see any other option than extending the maven-surefire-plugin plugin and add there an option to do such mapping. (Note that by default Maven passes all its System Properties to the test JVM.) That is definitely doable but maybe you can achieve your goal with something maven already offers.
You can definitely pass additional system properties to your test JVM from Maven by using:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version>
<configuration>
<systemPropertyVariables>
<propertyName>propertyValue</propertyName>
<anotherProperty>${myMavenProperty}</buildDirectory>
</systemPropertyVariables>
</configuration>
</plugin>
as documented http://maven.apache.org/surefire/maven-surefire-plugin/examples/system-properties.html.
In this case you can set the value of anotherProperty from command line by invoking maven
mvn test -DmyMavenProperty=theValueThatWillBePassedToTheTestJVMAsProperty_anotherProperty
You can also use Surefire argline to pass multiple properties to the JVM. For instance
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version>
<configuration>
<argLine>${propertiesIWantToSetFromMvnCommandLine}</argLine>
</configuration>
</plugin>
and execute maven as follows
mvn test -DpropertiesIWantToSetFromMvnCommandLine="-Dfoo=bar -Dhello=ahoy"
in this case, you'll see properties foo and hello with values bar and ahoy, respectively, in your test JVM.
Due to the way my build system is designed (RTC Build Engine), I would like to provide maven with property values via a properties file, instead of specifying -Dkey=value for every property.
I found a couple of questions on S.O. (How to set build properties from a file in Maven POM? and How to read an external properties file in Maven) that relate precisely to this question, but they are relatively old, and both require custom plugins to work (in alpha state).
I realize that passing parameters to Maven like this is probably not the best solution, but the other option is specifying everything on the command line via -D settings which is not ideal either.
Furthermore, given that this properties file is only really used by the build engine (and not by the individual developer), I don't truly believe it belongs in the pom. But I cannot find any other mechanism that would allow me to specify a plugin to use - settings.xml does not permit specifying plugins.
Is my only choice in this case to use a plugin and specify it in the project pom?
in the pom you can place...
<properties>
<core-version>1234</core-version>
<lib-version>1234</lib-version>
<build-version>9999</lib-version>
<build-date>20150101</build-date>
</properties>
with all the properties you require.
Or you can use...
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0-alpha-2</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>dev.properties</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
and the file dev.properties will contain the properties
core-version=1234
lib-version=1234
build-version=9999
build-date=20150101
...
Or... you can inject the properties using a settings.xml file as shown here
You may also find the Maven build number plugin useful... here
The best in such cases is to upgrade to at least Maven 3.2.1 which supports defining such properties on the command line like the following:
mvn -Drevision=1234 -Dchangelist=WhatEver -Dsha1=XXXX clean package
But you can only use the above names.
Excerpt from release notes:
A simple change to prevent Maven from emitting warnings about versions
with property expressions. Allowed property expressions in versions
include ${revision}, ${changelist}, and ${sha1}. These properties can
be set externally, but eventually a mechanism will be created in Maven
where these properties can be injected in a standard way. For example
you may want to glean the current Git revision and inject that value
into ${sha1}. This is by no means a complete solution for continuous
delivery but is a step in the right direction.