ECMASCRIPT 5 with wro4j and Google Closure Compiler - maven

We are using wro4j with Google Closure and Maven to minify our JS. By default it does not suport strict mode in the JS ("use strict";).. it just strips it out. Is there any configuration I can do in pom.xml or somewhere else to get it to leave use strict in there?
This is the configuration for google closure complier to do it:
--language_in=ECMASCRIPT5_STRICT
Not sure how to plug that in to Wro4j. Any ideas?

Create a custom implementation of the manager factory which adds ECMAScript5:
public class MyCustomWroManagerFactory
extends DefaultStandaloneContextAwareManagerFactory
{
#Override
protected ProcessorsFactory newProcessorsFactory()
{
final SimpleProcessorsFactory factory = new SimpleProcessorsFactory();
factory.addPreProcessor(
new GoogleClosureCompressorProcessor(
CompilerOptions.LanguageMode.ECMASCRIPT5_STRICT
)
);
return factory;
}
}
Reference it in the pom.xml as the value of the wroManagerFactory node:
<configuration>
<wroManagerFactory>com.mycompany.MyCustomWroManagerFactory</wroManagerFactory>
</configuration>
According to John Lenz from the Closure Compiler project, if you are using the Compiler API directly, you should specify a CodingConvention.
References
GoogleClosureCompressorProcessor.java - method setCompilerOptions
GoogleClosureCompressorProcessor.java - optionsPool method
Closure Compiler Service API Reference - language |  Closure Compiler  |  Google Developers

It's a bit more complicated in wro4j-maven-plugin 1.8, but not that bad.
You need to add two Java classes. First override newCompilerOptions of GoogleClosureCompressorProcessor like so:
package com.example.package.wro;
import com.google.javascript.jscomp.CheckLevel;
import com.google.javascript.jscomp.ClosureCodingConvention;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.DiagnosticGroups;
import java.nio.charset.Charset;
import org.apache.commons.lang3.CharEncoding;
import ro.isdc.wro.extensions.processor.js.GoogleClosureCompressorProcessor;
/**
* Custom processor overriding `newCompilerOptions` to add custom compiler options.
*
* Original author: Alex Objelean.
*/
public class CustomGoogleClosureCompressorProcessor extends GoogleClosureCompressorProcessor {
/**
* Encoding to use.
*/
public static final String ENCODING = CharEncoding.UTF_8;
#Override
protected CompilerOptions newCompilerOptions() {
final CompilerOptions options = new CompilerOptions();
// Set the language_in option on the Google Closure Compiler to prevent errors like:
// "JSC_TRAILING_COMMA. Parse error. IE8 (and below)"
options.setLanguageIn(CompilerOptions.LanguageMode.ECMASCRIPT5);
/**
* According to John Lenz from the Closure Compiler project, if you are using the Compiler API directly, you should
* specify a CodingConvention. {#link http://code.google.com/p/wro4j/issues/detail?id=155}
*/
options.setCodingConvention(new ClosureCodingConvention());
// use the wro4j encoding by default
//options.setOutputCharset(Charset.forName(getEncoding()));
setEncoding(ENCODING);
options.setOutputCharset(Charset.forName(ENCODING));
// set it to warning, otherwise compiler will fail
options.setWarningLevel(DiagnosticGroups.CHECK_VARIABLES, CheckLevel.WARNING);
return options;
}
}
You'll notice I've commented out the line getEncoding. This is because it's private. I also added setEncoding just in case.
Then we need the Custom manger:
package com.example.package.wro;
import ro.isdc.wro.manager.factory.standalone.DefaultStandaloneContextAwareManagerFactory;
import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.SimpleProcessorsFactory;
/**
* Custom manger adding custom processor.
*/
public class CustomWroManagerFactory extends DefaultStandaloneContextAwareManagerFactory {
#Override
protected ProcessorsFactory newProcessorsFactory() {
final SimpleProcessorsFactory factory = new SimpleProcessorsFactory();
factory.addPreProcessor(
new CustomGoogleClosureCompressorProcessor()
);
return factory;
}
}
And then use it in your pom.xml in wroManagerFactory. Something like so:
<plugin>
<groupId>ro.isdc.wro4j</groupId>
<artifactId>wro4j-maven-plugin</artifactId>
<version>1.8.0</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<!-- Google Closure Compiler -->
<!-- http://www.gzfs020.com/using-google-closure-compiler-with-wro4j-maven-plugin.html -->
<configuration>
<contextFolder>${basedir}/src/main</contextFolder>
<wroFile>${basedir}/src/main/config/wro.xml</wroFile>
<destinationFolder>${project.build.directory}/${project.build.finalName}/min</destinationFolder>
<!--
<wroManagerFactory>ro.isdc.wro.extensions.manager.standalone.GoogleStandaloneManagerFactory</wroManagerFactory>
-->
<wroManagerFactory>com.example.package.wro.CustomWroManagerFactory</wroManagerFactory>
</configuration>
</plugin>

Related

Accessing files in a Jar using ClassPathResource

I have a spring application that i must convert to jar. In this application I have a unit test:
#BeforeEach
void setUp() throws IOException {
//facturxHelper = new FacturxHelper();
facturxService = new FacturxService();
// String pdf = "facture.pdf"; // invalid pdfa1
String pdf = "resources/VALID PDFA1.pdf";
// InputStream sourceStream = new FileInputStream(pdf); //
InputStream sourceStream = getClass().getClassLoader().getResourceAsStream(pdf);
byte[] sourceBytes = IOUtils.toByteArray(sourceStream);
this.b64Pdf = Base64.getEncoder().encodeToString(sourceBytes);
}
#Test
void createFacturxMin() throws Exception {
// on va créer une facturX avec l'objet request
FacturxRequestMin request = FacturxRequestMin.builder()
.pdf(this.b64Pdf)
.chorusPro(Boolean.FALSE)
.invoiceNumber("FA-2017-0010")
.issueDate("13/11/2017")
.buyerReference("SERVEXEC")
.seller(TradeParty.builder()
.name("Au bon moulin")
.specifiedLegalOrganization(LegalOrganization.builder()
.id("99999999800010") .scheme(SpecifiedLegalOrganizationScheme.FR_SIRENE.getSpecifiedLegalOrganizationScheme())
.build())
.postalAddress(PostalAddress.builder()
.countryId(CountryIso.FR.name())
.build())
.vatId("FR11999999998")
.build())
.buyer(TradeParty.builder()
.name("Ma jolie boutique")
.specifiedLegalOrganization(LegalOrganization.builder()
.id("78787878400035")
.scheme(SpecifiedLegalOrganizationScheme.FR_SIRENE.getSpecifiedLegalOrganizationScheme())
.build())
.build())
.headerMonetarySummation(HeaderMonetarySummation.builder()
.taxBasisTotalAmount("624.90")
.taxTotalAmount("46.25")
.prepaidAmount("201.00")
.grandTotalAmount("671.15")
.duePayableAmount("470.15")
.build())
.build();
FacturXAppManager facturXAppManager = new FacturXAppManager(facturxService);
FacturxResponse facturxResponse = facturXAppManager.createFacturxMin(request);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(facturxResponse);
System.out.println(json);
}
The aim of the application is to create an xml and to embed it into the pdf file.
My issue is concerning an xml validation through xsd.
Here is an abstract of the code :
public static boolean xmlValidator(String fxGuideLine, String xmlString) throws Exception {
System.out.println("xmlValidator() called");
File xsdFile = null;
Source source = new StreamSource(new StringReader(xmlString));
// i removed a lot of if else statement concerning files which allow to validate xml
try {
xsdFile = new ClassPathResource(FacturxConstants.FACTUR_X_MINIMUM_XSD).getFile();
} catch (IOException e) {
throw new FacturxException(e.getMessage());
}
// validation du contenu XML
try {
SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsdFile);
Validator validator = schema.newValidator();
validator.validate(source);
return true;
} catch (SAXException | IOException e) {
throw new FacturxException(e.getLocalizedMessage());
}
...
}
In constants class, I added path to the xsd file:
public static final String FACTUR_X_MINIMUM_XSD = "resources/xsd/MINIMUM_XSD/FACTUR-X_MINIMUM.xsd";
In my POM file I do want to put the resources files in the built jar.
<build>
<finalName>${project.artifactId}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<outputDirectory> ${project.build.outputDirectory}\resources</outputDirectory>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.4.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
When I do a simple maven clean package, everything is running perfectly.
So far so good.
Next step is where my problem comes. Let's consider i want to use this dependency in an another application (a spring boot application). The previous jar compiled is a high level API that i want to integrate.
I launched the following command line :
mvn install:install-file -Dfile=myapi.jar -DgroupId=fr.myapi -DartifactId=graph-api-sharepoint -Dversion=1.0.0-SNAPSHOT -Dpackaging=jar
I do add my dependency correctly in my new project. that's perfect.
To check if my import worked correctly, i created a simple unit test with the same code (I do have a VALID PDFA1 in my resources folder. So far so good.
When running the test I do have the following error:
class path resource [resources/xsd/BASIC-WL_XSD/FACTUR-X_BASIC-WL.xsd] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/.m2/repository/fr/myapi/1.1.0/myapi-1.1.0.jar!/resources/xsd/BASIC-WL_XSD/FACTUR-X_BASIC-WL.xsd
How can i fix this issue ? I read many post but not fixes solved my issue. I do also think that i will have an issue also while compiling the springboot app as a jar
As mentionned, using a File won't work.
In the current code I updated it using InputStream:
InputStream is = new ClassPathResource(FacturxConstants.FACTUR_X_MINIMUM_XSD).getInputStream();
xsdSource = new StreamSource(is);
if my xsd path doesn't have resources:
public static final String FACTUR_X_MINIMUM_XSD = "xsd/MINIMUM_XSD/FACTUR-X_MINIMUM.xsd";
I have the following exception:
class path resource [xsd/MINIMUM_XSD/FACTUR-X_MINIMUM.xsd] cannot be opened because it does not exist
If i do put
public static final String FACTUR_X_MINIMUM_XSD = "resources/xsd/MINIMUM_XSD/FACTUR-X_MINIMUM.xsd";
the response is the following:
src-resolve: Cannot resolve the name 'ram:ExchangedDocumentContextType' to a(n) 'type definition' component.
I updated also the SchemaFactory and schema implementation:
SchemaFactory schemaFactory =
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsdSource);
Validator validator = schema.newValidator();
validator.validate(source);
return true;
public static final String FACTUR_X_MINIMUM_XSD = "resources/xsd/MINIMUM_XSD/FACTUR-X_MINIMUM.xsd";
Is wrong it should be (assuming src/main/resources/xsd is the actual location you are using).
public static final String FACTUR_X_MINIMUM_XSD = "/xsd/MINIMUM_XSD/FACTUR-X_MINIMUM.xsd";
Then your code is using a java.io.File which won't work, as a java.io.File needs to be a physical file on the file system. Which this isn't as it is inside a jar file. You need to use an InputStream.
public static boolean xmlValidator(String fxGuideLine, String xmlString) throws Exception {
System.out.println("xmlValidator() called");
Source source = new StreamSource(new StringReader(xmlString));
// i removed a lot of if else statement concerning files which allow to validate xml
try {
InputStream xsd = new ClassPathResource(FacturxConstants.FACTUR_X_MINIMUM_XSD).getInputStream();
StreamSource xsdSource = new StreamSource(xsd);
SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsdSource);
Validator validator = schema.newValidator();
validator.validate(source);
return true;
} catch (SAXException | IOException e) {
throw new FacturxException(e.getLocalizedMessage());
}
...
}
Which loads the schema using an inputstream.
Thanks to M. Deinum, I was able to find out a solution. I had to use indeed StreamSource. This didn't solve the following issue:
src-resolve: Cannot resolve the name 'ram:ExchangedDocumentContextType' to a(n) 'type definition' component.
As I used several xsd files, I implemented a way to retrieve a list of sources using PathMatchingResourcePatternResolver (from spring)
private static Source[] buildSources(String fxGuideLine, String pattern) throws SAXException, IOException {
List<Source> sources = new ArrayList<>();
PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
Resource[] resources = patternResolver.getResources(pattern);
for (Resource resource : resources) {
StreamSource dtd = new StreamSource(resource.getInputStream());
dtd.setSystemId(resource.getURI().toString());
sources.add(dtd);
}
return sources.toArray(new Source[sources.size()]);
}

OpenAPI generator returns 501 for implemented method

I've generated rest api with openAPI generator maven plugin and I've overridden the default method from MyApiDelegate interface, but POST request on /endpoint provides 501 NOT IMPLEMENTED as if I hadn't given my own implementation of that method in MyApiDelegateImpl.
Maven plugin configuration:
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>4.3.1</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<configOptions>
<inputSpec>${project.basedir}/src/main/resources/latest.yaml</inputSpec>
<generatorName>spring</generatorName>
<apiPackage>my.rest.api</apiPackage>
<modelPackage>my.rest.model</modelPackage>
<supportingFilesToGenerate>ApiUtil.java</supportingFilesToGenerate>
<delegatePattern>true</delegatePattern>
<useBeanValidation>false</useBeanValidation>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
/* code generated by plugin */
package my.rest;
public interface MyApiDelegate {
default Optional<NativeWebRequest> getRequest() {
return Optional.empty();
}
default ResponseEntity<Void> doSmth(Smth smth) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
}
package my.rest.api;
public interface MyApi {
default MyApiDelegate getDelegate() {
return new MyApiDelegate() {};
}
/*...Api operations annotations...*/
#RequestMapping(value = "/endpoint",
produces = { "application/json" },
consumes = { "application/json", "application/xml" },
method = RequestMethod.POST)
default ResponseEntity<Void> doSmth(#ApiParam(value = "" ,required=true) #RequestBody Smth smth) {
return getDelegate().doSmth(smth);
}
}
my implementation:
package my.rest.api;
#Service
#RequiredArgsConstructor
public class MyApiDelegateImpl implements MyApiDelegate {
private final MyService s;
#Override
public ResponseEntity<Void> doSmth(Smth smth) {
s.doIt(smth);
return ResponseEntity.ok().build();
}
}
How can I make my program use my own implementation of the method in concrete class, not the default implementation, which is provided in interface?
Implementing the MyApi interface directly and hence the method doSmth
in it, is one way of doing that. Your concrete class need not have all the web related annotations but just the paramters and return value like a normal method.
I don't understand how can an interface MyApiDelegate can be initialized but since getDelegate returns an implementation of it, the default implementation of doSmth is called which returns HttpStatus.NOT_IMPLEMENTED.
One more thing to take care of is making sure the deployment knows to use the implementation class. If you're using spring web than just marking your concrete class #RestController should suffice.

Declarative services in OSGI

I have created a (very) simple test to determine how to send and receive events using Apache Felix.
This is my sender:
package be.pxl;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import java.util.HashMap;
#Component(name = "be.pxl.Publisher", immediate = true)
public class Publisher {
EventAdmin admin;
#Activate
public void run(Object object) {
System.out.println("IN PUBLISHER");
Event event = new Event("event", new HashMap<String, Object>());
System.out.println("\tEVENT: " + event);
admin.postEvent(event);
System.out.println("\tADMIN: " + admin);
}
#Reference(name="be.pxl.admin", service = EventAdmin.class)
protected void setEventAdmin(EventAdmin admin) {
this.admin = admin;
}
}
This is my receiver:
package be.pxl;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import java.util.Dictionary;
import java.util.Hashtable;
#Component(name = "be.pxl.Subscriber", immediate = true)
public class Subscriber implements EventHandler {
private BundleContext context;
#Activate
public void run(Object object) {
System.out.println("IN SUBSCRIBER");
System.out.println("\tIN RUN METHOD");
String[] topics = new String[]{"event"};
Dictionary props = new Hashtable();
props.put(EventConstants.EVENT_TOPIC, topics);
System.out.println("\t\tCONTEXT: " + context);
context.registerService(EventHandler.class.getName(), this, props);
System.out.println("\t\tCONTEXT AFTER REGISTERSERVICE: " + context);
}
public void handleEvent(Event event) {
System.out.println("IN SUBSCRIBER");
String text = event.getProperty("text").toString();
System.out.println("\tEVENT CALLED: " + text);
}
#Reference(name="be.pxl.context", service=BundleContext.class)
protected void setBundleContex(BundleContext context) {
this.context = context;
}
}
This is the pom of my sender:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>be.pxl</groupId>
<artifactId>EventSender</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.event</artifactId>
<version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.component.annotations</artifactId>
<version>1.3.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
<version>3.2.100.v20100503</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.4.0</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Vendor>SmartCampus</Bundle-Vendor>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Export-Package>
be.pxl.*;version="1.0.0"
</Export-Package>
<Import-Package>
org.osgi.service.component.annotations
org.eclipse.osgi.service
org.osgi.core
org.osgi.service.event
</Import-Package>
<_dsannotations>*</_dsannotations>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
Everything compiles fine. I create it using mvn clean package, then I install this jar file in my apache felix container and start it. However, nothing happens. Nothing get pritns out.
Thanks in advance!
You appear to be most of the way there! As you've identified, Event Admin uses a whiteboard model to receive events. The important thing is that you need to tell the whiteboard which topics you want to listen to, which you do.
%%% Update %%%
Event admin topic names use a hierarchy of tokens separated by / characters. When publishing an event you do so to a specific topic, for example foo/bar/baz. When receiving events the EventHandler will be called for topics that match its registered interest(s). These interests can either be for a specific topic, or they can end with a * to indicate a wildcard match. For example foo/bar/* would receive events sent to foo/bar/baz and events sent to foo/bar/fizzbuzz.
%%% Back to the original %%%
There are, however a couple of issues with your code:
Firstly:
#Reference(name="be.pxl.context", service=BundleContext.class)
protected void setBundleContex(BundleContext context) {
this.context = context;
}
This is not how you access the BundleContext for your bundle. If you do need a BundleContext then it should be injected as a parameter into your #Activate annotated method. A BundleContext should never be registered as a service (it represents your bundle's private access to the OSGi framework), and it would not surprise me to find that this reference is unsatisfied in your example. You don't actually need the BundleContext however because...
Secondly:
#Activate
public void run(Object object) {
System.out.println("IN SUBSCRIBER");
System.out.println("\tIN RUN METHOD");
String[] topics = new String[]{"event"};
Dictionary props = new Hashtable();
props.put(EventConstants.EVENT_TOPIC, topics);
System.out.println("\t\tCONTEXT: " + context);
context.registerService(EventHandler.class.getName(), this, props);
System.out.println("\t\tCONTEXT AFTER REGISTERSERVICE: " + context);
}
This is not the right way to write an activate method (and as a result it may not be being called), nor should you be registering your component as a service here. When you make your class an #Component it will automatically be registered as a service using each directly implemented interface. This means that:
#Component(name = "be.pxl.Subscriber", immediate = true)
public class Subscriber implements EventHandler {
...
}
is already an OSGi EventHandler service!
You can add service properties to your component using the #Component annotation, or from the OSGi R7 release (due in a couple of months) using Component Property annotations. In this case you want to set your event.topics property like this:
#Component(property="event.topics=event")
You can then get rid of the activate method completely if you like.
Finally:
Event Admin is not a message queue, and your publisher is a one-shot send. Therefore if your publisher sends the event before the handler is fully registered then it will never receive the event. Consider making the publisher send periodic events, or be certain that the receiver starts before the publisher so that you see the message.
P.S.
It's not technically a problem, but I see that you're using version 2.4 of the maven-bundle-plugin. This is very old and the current released version of bnd is 3.5.0. The Bnd team have also started providing their own Maven plugins (such as the bnd-maven-plugin) that you might want to look at.

How to set up testsuite files for execution across environments

I have a test suite file that needs ability to be executed from both mvn command line (from Jenkins) as well as on-demand from Eclipse.
The test suite file must have ability to support parameters, ie:
<suite name="test run1">
<parameter name="testEnv" value="dev"></parameter>
<parameter name="proxyServer" value="x"></parameter>
<parameter name="proxyPort" value="y"></parameter>
If I leave as is, then mvn command line parameters don't work, as the values in the test suite file will override the parameters. i.e. this will not work:
mvn test ... -dtestEnv=E1QA -dproxyServer= -dproxyPort=
How can I write the test suite file so it supports both ad-hoc execution from Eclipse and mvn command line execution?
If you want configurable test property, use #DataProvider instead of hardcoding suite xml.
Provider class:
public class EnvProvider {
#DataProvider(name = "envProvider")
public static Object[][] createData() {
return new Object[][] { new Object[] {
System.getProperty("testEnv", "eclipse-default") }
};
}
Test method:
#Test(dataProvider = "envProvider", dataProviderClass = EnvProvider.class)
public void myTest(String currentEnv) {
System.out.println("Current env is : " + currentEnv);
}
pom.xml
<properties>
<testEnv>default-pom</testEnv>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<systemPropertyVariables>
<testEnv>${testEnv}</testEnv>
</systemPropertyVariables>
...
Result from eclipse right click
Current env is : eclipse-default
Result from mvn test
Current env is : default-pom
Result from mvn test -DtestEnv=jenkins
Current env is : jenkins
References: http://testng.org/doc/documentation-main.html#parameters-dataproviders
You can override the suite xml params with system level args if those are available - which would allow you to run from xml as well as from mvn.
All the params are basically assigned to some constant properties to be used across tests. The properties file is initialized in the onBeforeSuite method of the suite listener something to the effect
SuiteList extends SuiteListener{
public void onStart(ISuite suite) {
LProperties.loadProperties(suite);
}
//loadProperties implementation
LProperties{
public static void loadProperties(ISuite suite){
//This can read from a properties file and load properties
//or
// from the suite.getParameter - pick the params you need (reflection or list) and assign to the constants
//For all the properties, do System.getProperty and override values if found
}
Use LProperties constants in your tests.
based on a combination of above answers, I've figured it out.
First remove the hard-coded parameters in the test suite file.
Next, ensure the parameters are supported in the pom file.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.4.2</version>
<configuration>
<systemPropertyVariables>
<environment>${testEnv}</environment>
<environment>${proxyServer}</environment>
<environment>${proxyPort}</environment>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
Create constants file to contain default values for the parameters
public class Constants {
public static final String DEFAULT_TEST_ENV = "dev";
public static final String DEFAULT_PROXY_SERVER = "a-dev.phx.com";
public static final String DEFAULT_PROXY_PORT = "8585";
}
In the testNG setup() method, use System.getproperty():
#BeforeClass(alwaysRun = true)
protected static void setUp() throws Exception {
String testEnv = System.getProperty("testEnv", Constants.DEFAULT_TEST_ENV);
String proxyServer = System.getProperty("proxyServer", Constants.DEFAULT_PROXY_SERVER);
String proxyPort = System.getProperty("proxyPort", Constants.DEFAULT_PROXY_PORT);
System.out.println("testEnv: " + testEnv);
System.out.println("proxyServer: " + proxyServer);
System.out.println("proxyPort: " + proxyPort);
}
I could put the default values in a config file, but for now, constants file seems easiest in its own class.

How to merge files under different folders with same name using maven plugin

I have a requirement to merge all the files with same name under two different folders and output that to another folder.
Ex:
common
|
V1.sql
V2.sql
module
|
V1.sql
V2.sql
Now my target directory should be
target dir
|
V1.sql(has both the contents of common and module)
V2.sql(has both the contents of common and module)
I have looked at several maven plugins, but they dont seemed to support this. Please guide if anyone has come across such problem or implemented a plugin,
Not really pretty, but i achived this with the maven exec plugin:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<executions>
<execution>
<id>resourceMerge</id>
<goals>
<goal>java</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<mainClass>mavenProcessor.Resourcesmerger</mainClass>
<arguments>
<argument>${project.build.directory}/resourcesDefault</argument>
<argument>${project.build.directory}/resourcesProfile</argument>
<argument>${project.build.directory}/resourcesMerged</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
The Processor looks like this
package mavenProcessor;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
public class Resourcesmerger {
public static void main(String[] args) throws Throwable {
Path inputDir1 = new File(args[0]).toPath();
Path inputDir2 = new File(args[1]).toPath();
Path outputDir = new File(args[2]).toPath();
copyAppending(inputDir1, outputDir);
if (Files.exists(inputDir2)) {
copyAppending(inputDir2, outputDir);
}
}
private static void copyAppending(Path inputDir1, Path outputDir) throws IOException, FileNotFoundException {
List<Path> defaultResources = Files.walk(inputDir1).collect(Collectors.toList());
for (Path path : defaultResources) {
if (Files.isRegularFile(path)) {
Path relativePath = inputDir1.relativize(path);
Path targetPath = outputDir.resolve(relativePath);
targetPath.getParent().toFile().mkdirs();
try (FileOutputStream fos = new FileOutputStream(targetPath.toFile(), true)) {
System.out.println("Merge " + path + " to " + targetPath);
System.out.flush();
Files.copy(path, fos);
}
}
}
}
}

Resources