I have several projects built by maven, and I want to share some common properties among them - spring version, mysql driver version, svn base url, etc. - so I can update them once and it will be reflected on all projects.
I thought of having a single super pom with all the properties, but if I change one of the problem I need to either increment its version (and to update all the poms inherit from it) or to delete it from all the developers' machines which I don't want to do.
Can specify these parameters externally to the pom? I still want to have the external location definition in a parent pom.
What you can do is to use the Properties Maven plugin. This will let you define your properties in an external file, and the plugin will read this file.
With this configuration :
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0-alpha-1</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>my-file.properties</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
and if you have, in your properties file the following lines:
spring-version=1.0
mysql-version=4.0.0
then it's the same thing as if you wrote, in your pom.xml, the following lines:
<properties>
<spring-version>1.0</spring-version>
<mysql-version>4.0.0</mysql-version>
</properties>
Using this plugin, you will have several benefits:
Set easily a long list of properties
Modify the values of these properties without modifying the parent pom.xml.
Note that the original idea I have here is something that I am doing, but that I may have found a much better idea which I've also listed out below. I wanted to keep both ideas here for completeness in case the newer idea does not work.
I think you can solve this problem using the parent pom, but you need to have a maven repository and a CI build tool.
I've got several projects which all inherit base properties from a parent POM. We use Java 1.5, so that build property is setup there. Everything is UTF-8. All of the reports that I wish to run, Sonar setup, etc, is inside the parent POM.
Assuming your project is in version control and you've got a CI tool, when you check in, your CI tool can build to POM project and deploy the SNAPSHOT to the maven repos. If your projects point to the SNAPSHOT version of the parent POM, they'll check the repository to validate that they have the latest version...if not they download the latest version. So if you update the parent, all of the other projects will update.
The trick, I suppose is releasing with a SNAPSHOT. I'd say your releases are going to come much less frequently than your changes. So you perform a release of your POM, then update your POMs that inherit from them and check them into version control. Let the devs know that they need to do an update and go from there.
You could just trigger builds there forcing the new POMs into the repository and then have all of the devs pick up the changes automagically upon build.
I've removed the LATEST/RELEASE keywords idea because they do not work for parent POMs. They only work for dependencies or plugins. The problem area is in DefaultMavenProjectBuilder. Essentially it has trouble determining which repository to look for the parent to determine what the latest or release version is. Not sure why this is different for dependencies or plugins though.
It sounds like these would be less painful than having to update the POMs on every change to the parent POM.
I think the properties-maven-plugin is the right approach long term, but as you responded to that answer it doesn't allow the properties to be inherited. There are some facilities in maven-shared-io that allow you to discover resources on the project classpath. I've included some code below that extends the properties plugin to find properties files in the dependencies of the plugin.
The configuration declares a path to a properties file, because the descriptor project is declared on the plugin configuration, it is accessible to the ClasspathResourceLocatorStrategy. The configuration can be defined in a parent project, and will be inherited by all the child projects (if you do this avoid declaring any files as they won't be discovered, only set the filePaths property).
The configuration below assumes that there is another jar project called name.seller.rich:test-properties-descriptor:0.0.1 that has a file called external.properties packaged into the jar (i.e. it was defined in src/main/resources).
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-ext-maven-plugin</artifactId>
<version>0.0.1</version>
<executions>
<execution>
<id>read-properties</id>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
</execution>
</executions>
<configuration>
<filePaths>
<filePath>external.properties</filePath>
</filePaths>
</configuration>
<dependencies>
<!-- declare any jars that host the required properties files here -->
<dependency>
<groupId>name.seller.rich</groupId>
<artifactId>test-properties-descriptor</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
</plugin>
The pom for the plugin project looks like this:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-ext-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<version>0.0.1</version>
<dependencies>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0-alpha-1</version>
</dependency>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-shared-io</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
</project>
The mojo is a copy of the properties plugin's ReadPropertiesMojo, with an additional "filePaths" property to allow you to define the relative path to the external properties file in the classpath, it makes the files property optional, and adds the readPropertyFiles() and getLocation() methods to locate the files and merge any filePaths into the files array before continuing. I've commented my changes to make them clearer.
package org.codehaus.mojo.xml;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.io.location.ClasspathResourceLocatorStrategy;
import org.apache.maven.shared.io.location.FileLocatorStrategy;
import org.apache.maven.shared.io.location.Location;
import org.apache.maven.shared.io.location.Locator;
import org.apache.maven.shared.io.location.LocatorStrategy;
import org.apache.maven.shared.io.location.URLLocatorStrategy;
import org.codehaus.plexus.util.cli.CommandLineUtils;
/**
* The read-project-properties goal reads property files and stores the
* properties as project properties. It serves as an alternate to specifying
* properties in pom.xml.
*
* #author Zarar Siddiqi
* #author Krystian Nowak
* #version $Id: ReadPropertiesMojo.java 8861 2009-01-21 15:35:38Z pgier $
* #goal read-project-properties
*/
public class ReadPropertiesMojo extends AbstractMojo {
/**
* #parameter default-value="${project}"
* #required
* #readonly
*/
private MavenProject project;
/**
* The properties files that will be used when reading properties.
* RS: made optional to avoid issue for inherited plugins
* #parameter
*/
private File[] files;
//Begin: RS addition
/**
* Optional paths to properties files to be used.
*
* #parameter
*/
private String[] filePaths;
//End: RS addition
/**
* If the plugin should be quiet if any of the files was not found
*
* #parameter default-value="false"
*/
private boolean quiet;
public void execute() throws MojoExecutionException {
//Begin: RS addition
readPropertyFiles();
//End: RS addition
Properties projectProperties = new Properties();
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.exists()) {
try {
getLog().debug("Loading property file: " + file);
FileInputStream stream = new FileInputStream(file);
projectProperties = project.getProperties();
try {
projectProperties.load(stream);
} finally {
if (stream != null) {
stream.close();
}
}
} catch (IOException e) {
throw new MojoExecutionException(
"Error reading properties file "
+ file.getAbsolutePath(), e);
}
} else {
if (quiet) {
getLog().warn(
"Ignoring missing properties file: "
+ file.getAbsolutePath());
} else {
throw new MojoExecutionException(
"Properties file not found: "
+ file.getAbsolutePath());
}
}
}
boolean useEnvVariables = false;
for (Enumeration n = projectProperties.propertyNames(); n
.hasMoreElements();) {
String k = (String) n.nextElement();
String p = (String) projectProperties.get(k);
if (p.indexOf("${env.") != -1) {
useEnvVariables = true;
break;
}
}
Properties environment = null;
if (useEnvVariables) {
try {
environment = CommandLineUtils.getSystemEnvVars();
} catch (IOException e) {
throw new MojoExecutionException(
"Error getting system envorinment variables: ", e);
}
}
for (Enumeration n = projectProperties.propertyNames(); n
.hasMoreElements();) {
String k = (String) n.nextElement();
projectProperties.setProperty(k, getPropertyValue(k,
projectProperties, environment));
}
}
//Begin: RS addition
/**
* Obtain the file from the local project or the classpath
*
* #throws MojoExecutionException
*/
private void readPropertyFiles() throws MojoExecutionException {
if (filePaths != null && filePaths.length > 0) {
File[] allFiles;
int offset = 0;
if (files != null && files.length != 0) {
allFiles = new File[files.length + filePaths.length];
System.arraycopy(files, 0, allFiles, 0, files.length);
offset = files.length;
} else {
allFiles = new File[filePaths.length];
}
for (int i = 0; i < filePaths.length; i++) {
Location location = getLocation(filePaths[i], project);
try {
allFiles[offset + i] = location.getFile();
} catch (IOException e) {
throw new MojoExecutionException(
"unable to open properties file", e);
}
}
// replace the original array with the merged results
files = allFiles;
} else if (files == null || files.length == 0) {
throw new MojoExecutionException(
"no files or filePaths defined, one or both must be specified");
}
}
//End: RS addition
/**
* Retrieves a property value, replacing values like ${token} using the
* Properties to look them up. Shamelessly adapted from:
* http://maven.apache.
* org/plugins/maven-war-plugin/xref/org/apache/maven/plugin
* /war/PropertyUtils.html
*
* It will leave unresolved properties alone, trying for System properties,
* and environment variables and implements reparsing (in the case that the
* value of a property contains a key), and will not loop endlessly on a
* pair like test = ${test}
*
* #param k
* property key
* #param p
* project properties
* #param environment
* environment variables
* #return resolved property value
*/
private String getPropertyValue(String k, Properties p,
Properties environment) {
String v = p.getProperty(k);
String ret = "";
int idx, idx2;
while ((idx = v.indexOf("${")) >= 0) {
// append prefix to result
ret += v.substring(0, idx);
// strip prefix from original
v = v.substring(idx + 2);
idx2 = v.indexOf("}");
// if no matching } then bail
if (idx2 < 0) {
break;
}
// strip out the key and resolve it
// resolve the key/value for the ${statement}
String nk = v.substring(0, idx2);
v = v.substring(idx2 + 1);
String nv = p.getProperty(nk);
// try global environment
if (nv == null) {
nv = System.getProperty(nk);
}
// try environment variable
if (nv == null && nk.startsWith("env.") && environment != null) {
nv = environment.getProperty(nk.substring(4));
}
// if the key cannot be resolved,
// leave it alone ( and don't parse again )
// else prefix the original string with the
// resolved property ( so it can be parsed further )
// taking recursion into account.
if (nv == null || nv.equals(nk)) {
ret += "${" + nk + "}";
} else {
v = nv + v;
}
}
return ret + v;
}
//Begin: RS addition
/**
* Use various strategies to discover the file.
*/
public Location getLocation(String path, MavenProject project) {
LocatorStrategy classpathStrategy = new ClasspathResourceLocatorStrategy();
List strategies = new ArrayList();
strategies.add(classpathStrategy);
strategies.add(new FileLocatorStrategy());
strategies.add(new URLLocatorStrategy());
List refStrategies = new ArrayList();
refStrategies.add(classpathStrategy);
Locator locator = new Locator();
locator.setStrategies(strategies);
Location location = locator.resolve(path);
return location;
}
//End: RS addition
}
Related
I'm trying to use the WinSCP.NET NuGet to upload some files to an SFTP through a Script Task component in SSIS. While writing the code everything went fine, but if after attempting to build, the WinSCP.NET dll seems to not be picked up breaking all of the references.
I've tried adding WinSCP path to my PATH variable (user). I've tried to add the local version of the WinSCPNET.dll to the GAC. I've tried to reinstall the package through NuGet. I've even tried to change the framework versions.
This is a problem I've had before with the WinSCP.NET DLL. Last time I ended up using a workaround by interfacing with the command line through C#. But I would like to use the DLL, as it's a much simpler implementation.
The code is basically the boilerplate from WinSCP, with some minor changes:
#region Namespaces
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using WinSCP;
#endregion
namespace ST_a1d3d6e0b5d54338bce6c79882c303c6
{
/// <summary>
/// ScriptMain is the entry point class of the script. Do not change the name, attributes,
/// or parent of this class.
/// </summary>
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
#region Help: Using Integration Services variables and parameters in a script
/* To use a variable in this script, first ensure that the variable has been added to
* either the list contained in the ReadOnlyVariables property or the list contained in
* the ReadWriteVariables property of this script task, according to whether or not your
* code needs to write to the variable. To add the variable, save this script, close this instance of
* Visual Studio, and update the ReadOnlyVariables and
* ReadWriteVariables properties in the Script Transformation Editor window.
* To use a parameter in this script, follow the same steps. Parameters are always read-only.
*
* Example of reading from a variable:
* DateTime startTime = (DateTime) Dts.Variables["System::StartTime"].Value;
*
* Example of writing to a variable:
* Dts.Variables["User::myStringVariable"].Value = "new value";
*
* Example of reading from a package parameter:
* int batchId = (int) Dts.Variables["$Package::batchId"].Value;
*
* Example of reading from a project parameter:
* int batchId = (int) Dts.Variables["$Project::batchId"].Value;
*
* Example of reading from a sensitive project parameter:
* int batchId = (int) Dts.Variables["$Project::batchId"].GetSensitiveValue();
* */
#endregion
#region Help: Firing Integration Services events from a script
/* This script task can fire events for logging purposes.
*
* Example of firing an error event:
* Dts.Events.FireError(18, "Process Values", "Bad value", "", 0);
*
* Example of firing an information event:
* Dts.Events.FireInformation(3, "Process Values", "Processing has started", "", 0, ref fireAgain)
*
* Example of firing a warning event:
* Dts.Events.FireWarning(14, "Process Values", "No values received for input", "", 0);
* */
#endregion
#region Help: Using Integration Services connection managers in a script
/* Some types of connection managers can be used in this script task. See the topic
* "Working with Connection Managers Programatically" for details.
*
* Example of using an ADO.Net connection manager:
* object rawConnection = Dts.Connections["Sales DB"].AcquireConnection(Dts.Transaction);
* SqlConnection myADONETConnection = (SqlConnection)rawConnection;
* //Use the connection in some code here, then release the connection
* Dts.Connections["Sales DB"].ReleaseConnection(rawConnection);
*
* Example of using a File connection manager
* object rawConnection = Dts.Connections["Prices.zip"].AcquireConnection(Dts.Transaction);
* string filePath = (string)rawConnection;
* //Use the connection in some code here, then release the connection
* Dts.Connections["Prices.zip"].ReleaseConnection(rawConnection);
* */
#endregion
/// <summary>
/// This method is called when this script task executes in the control flow.
/// Before returning from this method, set the value of Dts.TaskResult to indicate success or failure.
/// To open Help, press F1.
/// </summary>
public void Main()
{
// TODO: Add your code here
// User::FileName,$Package::SFTP_HostName,$Package::SFTP_Password,$Package::SFTP_PortNumber,$Package::SFTP_UserName
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = (string)Dts.Variables["$Package::SFTP_HostName"].Value,
UserName = (string)Dts.Variables["$Package::SFTP_Password"].Value,
SshHostKeyFingerprint = (string)Dts.Variables["$Package::SFTP_Fingerprint"].Value,
Password = (string)Dts.Variables["$Package::SFTP_Password"].GetSensitiveValue(),
PortNumber = (int) Dts.Variables["$Package::SFTP_PortNumber"].Value,
};
try
{
using (Session session = new Session())
{
// As WinSCP .NET assembly has to be stored in GAC to be used with SSIS,
// you need to set path to WinSCP.exe explicitly,
// if using non-default location.
session.ExecutablePath = (string)Dts.Variables["$Package::WinSCP_Path"].Value;
// Connect
session.Open(sessionOptions);
// Upload files
TransferOptions transferOptions = new TransferOptions();
transferOptions.TransferMode = TransferMode.Binary;
TransferOperationResult transferOperationResult = session.PutFiles(
(string)Dts.Variables["User::FileName"].Value, (string) Dts.Variables["$Package::SFTP_RemoteFileName"].Value,
true, transferOptions);
// Throw on any error
transferOperationResult.Check();
// Print results
bool fireAgain = false;
foreach (TransferEventArgs transferEvent in transferOperationResult.Transfers)
{
Dts.Events.FireInformation(0, null,
string.Format("Upload of {0} succeeded", transferEvent.FileName),
null, 0, ref fireAgain);
}
}
}
catch (Exception e)
{
Dts.Events.FireError(0, null,
string.Format("Error when using WinSCP to upload files: {0}", e),
null, 0);
Dts.TaskResult = (int)DTSExecResult.Failure;
}
Dts.TaskResult = (int)ScriptResults.Success;
}
#region ScriptResults declaration
/// <summary>
/// This enum provides a convenient shorthand within the scope of this class for setting the
/// result of the script.
///
/// This code was generated automatically.
/// </summary>
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
}
}
This should compile as is and allow me to run the SSIS, to upload the file. Instead the references break and I receive a lot of missing reference errors:
Error CS0246: The type or namespace name 'WinSCP' could not be found (are you missing a using directive or an assembly reference?)
Error: This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is ..\packages\WinSCP.5.15.0\build\WinSCP.targets.
I can indeed reproduce your problem, when I use WinSCP NuGet package. It looks like a problem between the NuGet package manager and SQL Server Data Tools. The file the error refers to actually does exist (in a path relative to the script task .csproj file).
Actually, it looks like it's not even recommended to use NuGet in SSIS. You should rather register the assembly to GAC:
How can I use NuGet with SSDT?
Creating a reference to a custom assembly from an SSIS Script Task - vb
SSIS Script Task cant find reference to assembly
And indeed, if I follow the WinSCP instructions for using the assembly from SSIS (using the GAC), it works just fine.
Make sure you have uninstalled the NuGet package.
Install WinSCPnet.dll to GAC or subscribe AppDomain.AssemblyResolve event.
And add WinSCPnet.dll to your script task project.
I'm trying to create a simple Junit5-Cucumber project (in Eclipse) that would be used for UI testing.
I took reference from this repo:https://github.com/cucumber/cucumber-java-skeleton
Issue: No definition found for Open the Chrome and launch the application (error happens to the Given, When and Then statements) in the test_features.feature file.
# test_features.feature
Feature: Reset functionality on login page of Application
Scenario: Verification of Reset button
Given Open the Chrome and launch the application
When Enter the username and password
Then Reset the credentials
# RunCucumberTest.java
package lpms.cucumber;
import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;
import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
#Suite
#IncludeEngines("cucumber")
#SelectClasspathResource("lpms/cucumber")
#ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty")
#ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "lpms.cucumber")
public class RunCucumberTest {
}
# StepDefinitions.java
package lpms.cucumber;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
public class StepDefinitions {
#Given("^Open the Chrome and launch the application$")
public void open_the_chrome_and_launch_the_application() throws Throwable
{
System.out.println("This step opens the chrome and launches the application");
}
#When("^Enter the username and password$")
public void enter_the_username_and_password() throws Throwable
{
System.out.println("This step enters the username and password on the login page");
}
#Then("^Reset the credentials$")
public void reset_the_credential() throws Throwable
{
System.out.println("This step clicks on the reset button.");
}
}
Project Structure
IMAGE OF MY PROJECT STRUCTURE
Solved!
It's a warning from Eclipse IDE, likely just a bug, because I can still get testing done.
Sidenote: Extremely useful guide for learning the latest cucumber: https://cucumber.io/docs/guides/10-minute-tutorial/
I had the same problem on my project and i'll post my solution here.
I've used Eclipse + Java 11 + SpringBoot 2.6.4
pom.xml dependencies
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<scope>test</scope>
<version>7.3.0</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit-platform-engine</artifactId>
<version>7.3.0</version>
<scope>test</scope>
</dependency>
pom.xml plugin in build section
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<properties>
<configurationParameters>
cucumber.junit-platform.naming-strategy=long
</configurationParameters>
</properties>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
After that, i've created a package in src/test/java called
filelife/skynet/cucumber
In this package i've created my steps class and my runner class; Steps class contains only some logging instrauctions, it doesn't verify nothing yet.
Steps class:
#Slf4j
public class SendMessagesOnServiceLimitsSteps {
#Given("A ServiceLimits Module with PosTXRate of {int} seconds")
public void a_service_limits_module_with_pos_tx_rate_of_seconds(Integer posTxRate) {
log.info("ServiceLimits PosTxRate {}", posTxRate);
System.out.println("Given Step");
}
#When("I keyOn the device")
public void i_key_on_the_device() {
System.out.println("Given Step");
}
#When("i wait for {int} seconds")
public void i_wait_for_seconds(Integer int1) {
System.out.println("Given Step");
}
#When("i keyOff the device")
public void i_key_off_the_device() {
System.out.println("Given Step");
}
#Then("PositionData messages should be {int} or {int}")
public void position_data_messages_should_be_or(Integer int1, Integer int2) {
System.out.println("Given Step");
}
#Then("device log print {string}")
public void device_log_print(String string) {
System.out.println("Given Step");
}
}
And my runner tests class:
#Suite
#IncludeEngines("cucumber")
#SelectClasspathResource("filelife/skynet/cucumber")
#ConfigurationParameter(
key = GLUE_PROPERTY_NAME,
value = "filelife.skynet.cucumber"
)
public class SkynetTest{
}
I've also created the same folder path (filelife/skynet/cucumber) in src/test/resources source folder and i've pasted my .feature file.
In the end, i've created 2 files:
cucumber.properties
junit-platform.properties
in same source folder src/test/resources containg, both of them, string:
cucumber.publish.quiet=true
This configuration works with:
mvn tests
and
right click on SkynetTest -> RunAs -> Junit Test
I'm browsing the PrimeFaces 6.1 source at GitHub.
The components only seem to have a renderer class and no component class. For example, the OutputLabelRenderer references
the OutputLabel, but the OutputLabel class is nowhere to be found. I expected it to be in the same folder as the renderer (as there was no import).
I did find this template file though. So it looks like it is generated somehow.
Where can I find the attributes for a PrimeFaces component?
After checking the pom.xml I've found this plugin:
<plugin>
<groupId>org.primefaces</groupId>
<artifactId>maven-jsf-plugin</artifactId>
<version>1.3.3-SNAPSHOT</version>
<executions>
<execution>
<id>generate-ui</id>
<phase>generate-sources</phase>
<configuration>
<uri>http://primefaces.org/ui</uri>
<shortName>p</shortName>
<templatesDir>src/main/java-templates</templatesDir>
<componentConfigsDir>target/resources-maven-jsf/ui</componentConfigsDir>
<standardFacesConfig>src/main/resources-maven-jsf/standard-faces-config.xml</standardFacesConfig>
<standardFaceletsTaglib>src/main/resources-maven-jsf/standard-facelets-taglib.xml</standardFaceletsTaglib>
<standardTLD>src/main/resources-maven-jsf/standard-tld.xml</standardTLD>
</configuration>
<goals>
<goal>generate-components</goal>
<goal>generate-facelets-taglib</goal>
</goals>
</execution>
...
</executions>
</plugin>
The definition (including the attributes) are located in the /src/main/resources-maven-jsf/ui/ folder. For examplate outputLabel.xml.
You can also download the sources JAR from Maven. It will include the component code. For example:
/*
* Generated, Do Not Modify
*/
/*
* Copyright 2009-2013 PrimeTek.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.primefaces.component.outputlabel;
import javax.faces.component.html.HtmlOutputLabel;
import javax.faces.context.FacesContext;
import javax.faces.component.UINamingContainer;
import javax.el.ValueExpression;
import javax.el.MethodExpression;
import javax.faces.render.Renderer;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.event.AbortProcessingException;
import javax.faces.application.ResourceDependencies;
import javax.faces.application.ResourceDependency;
import java.util.List;
import java.util.ArrayList;
import org.primefaces.util.ComponentUtils;
#ResourceDependencies({
})
public class OutputLabel extends HtmlOutputLabel {
public static final String COMPONENT_TYPE = "org.primefaces.component.OutputLabel";
public static final String COMPONENT_FAMILY = "org.primefaces.component";
public static final String DEFAULT_RENDERER = "org.primefaces.component.OutputLabelRenderer";
public enum PropertyKeys {
indicateRequired;
String toString;
PropertyKeys(String toString) {
this.toString = toString;
}
PropertyKeys() {}
public String toString() {
return ((this.toString != null) ? this.toString : super.toString());
}
}
public OutputLabel() {
setRendererType(DEFAULT_RENDERER);
}
public String getFamily() {
return COMPONENT_FAMILY;
}
public boolean isIndicateRequired() {
return (java.lang.Boolean) getStateHelper().eval(PropertyKeys.indicateRequired, true);
}
public void setIndicateRequired(boolean _indicateRequired) {
getStateHelper().put(PropertyKeys.indicateRequired, _indicateRequired);
}
public final static String STYLE_CLASS = "ui-outputlabel ui-widget";
public final static String REQUIRED_FIELD_INDICATOR_CLASS = "ui-outputlabel-rfi";
}
I have heard alot of success integration story when comes to Apache Camel with Fuse. HEnce. here Im just starting to explore the Fuse IDE, with just a simple task on top of my head, i would like to achieve:
Read a fix length file
Parse the fix length file
persist it to mysql database table
I am only able to get as far as:
Read the fix length file (with Endpoint "file:src/data/Japan?noop=true")
Define a Marshal with Bindy and Define a POJO package model with #FixedLengthRecord annotation
then i am stuck... HOW TO persist the POJO into mysql database table? I can see some JDBC, IBatis and JPA end point, but how to accomplish that in Fuse IDE?
My POJO package:
package com.mbww.model;
import org.apache.camel.dataformat.bindy.annotation.DataField;
import org.apache.camel.dataformat.bindy.annotation.FixedLengthRecord;
#FixedLengthRecord(length=91)
public class Japan {
#DataField(pos=1, length=10)
private String TNR;
#DataField(pos=11, length=10)
private String ATR;
#DataField(pos=21, length=70)
private String STR;
}
Well you can use all of the following components to actually read and write from the database:
JDBC
IBATIS
MyBATIS
SPRING-JDBC
SQL
Custom Processor
I am going to show you how to use the custom processor to insert the rows into a table. The main reason for this is that you will get to work with the messages and exchange and this will give you more of a insight into Camel. All of the other components can be used by following the documentation on the camel site.
So lets review what you have. You are reading the file and converting the body to a bindy object. So for each line in your text file Camel will send a bindy object of class com.mbww.model.JAPAN to the next end point. This next end point needs to talk to the database. There is one problem I can spot immediately you are using a marshal you should be using a unmarshal.
The documentation clearly states: If you receive a message from one of the Camel Components such as File, HTTP or JMS you often want to unmarshal the payload into some bean so that you can process it using some Bean Integration or perform Predicate evaluation and so forth. To do this use the unmarshal word in the DSL in Java or the Xml Configuration.
Your bindy class looks good but it is missing getters and setters modify the class to look like this:
package com.mbww.model;
import org.apache.camel.dataformat.bindy.annotation.DataField;
import org.apache.camel.dataformat.bindy.annotation.FixedLengthRecord;
#FixedLengthRecord(length=91)
public class Japan {
#DataField(pos=1, length=10)
private String TNR;
#DataField(pos=11, length=10)
private String ATR;
#DataField(pos=21, length=70)
private String STR;
public String getTNR() {
return TNR;
}
public void setTNR(String tNR) {
TNR = tNR;
}
public String getATR() {
return ATR;
}
public void setATR(String aTR) {
ATR = aTR;
}
public String getSTR() {
return STR;
}
public void setSTR(String sTR) {
STR = sTR;
}
}
First you need to create a data source to your database in your route. First thing is to add the mysql driver jar to your maven dependencies open your pom.xml file and add the following dependency to it.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- use this version of the driver or a later version of the driver -->
<version>5.1.25</version>
</dependency>
Right now we need to declare a custom processor to use in the route that will use this driver and insert the received body into a table.
So lets create a new class in Fuse IDE called PersistToDatabase code below:
package com.mbww.JapanData;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Map;
import org.apache.camel.Body;
import org.apache.camel.Exchange;
import org.apache.camel.Handler;
import org.apache.camel.Headers;
import com.mbww.model.Japan;
import com.mysql.jdbc.Statement;
public class PersistToDatabase {
#Handler
public void PersistRecord
(
#Body Japan msgBody
, #Headers Map hdr
, Exchange exch
) throws Exception
{
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.out.println("Where is your MySQL JDBC Driver?");
e.printStackTrace();
return;
}
System.out.println("MySQL JDBC Driver Registered!");
Connection connection = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/databasename","root", "password");
} catch (SQLException e) {
System.out.println("Connection Failed! Check output console");
e.printStackTrace();
return;
}
if (connection != null) {
System.out.println("You made it, take control your database now!");
} else {
System.out.println("Failed to make connection!");
}
try {
PreparedStatement stmt=connection.prepareStatement("INSERT INTO JapanDate(TNR,ATR,STR) VALUES(?,?,?)");
stmt.setString(1, msgBody.getTNR());
stmt.setString(2, msgBody.getATR());
stmt.setString(1, msgBody.getSTR());
int rows = stmt.executeUpdate();
System.out.println("Number of rows inserted: "+Integer.toString(rows));
}
catch(Exception e){
System.out.println("Error in executing sql statement: "+e.getMessage() );
throw new Exception(e.getMessage());
}
}
}
This class is a POJO nothing fancy except the #Handler annotation on the PersistRecord. This annotation tells camel that the PersistRecord method/procedure will handle the message exchange. You will also notice that the method PersistRecord has a parameter of type Japan. As mentioned earlier when you call the conversion bean in your camel route it translates each line into a Japan object and passes it along the route.
The rest of the code is just how to handle the JDBC connection and calling a insert statement.
We are almost done just one last thing to do. We need to declare this class in our camel route xml. This file will typically be called camel-route.xml or blueprint.xml depending on your arch type. Open the source tab and add the following line <bean id="JapanPersist" class="com.mbww.JapanData.PersistToDatabase"/> before the <camelContext> tag.
This declares a new spring bean called JapanPersist based on the class we just added to the camel route. You can now reference this bean inside your camel route.
Thus the final route xml file should look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel="http://camel.apache.org/schema/blueprint"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">
<bean id="JapanPersist" class="com.mbww.JapanData.PersistToDatabase"/>
<camelContext trace="false" id="blueprintContext" xmlns="http://camel.apache.org/schema/blueprint">
<route id="JapanDataFromFileToDB">
<from uri="file:src/data/japan"/>
<unmarshal ref="Japan"/>
<bean ref="JapanPersist"/>
</route>
</camelContext>
</blueprint>
Or see screen shot below:
Once you understand this technique you can start scaling the solution by using a splitter, connection pooling and threading to do massive amount of concurrent inserts etc.
Using the technique above you learned how to inject your own beans into a camel route which give you the ability to work with the messages directly in code.
I have not tested the code so there will probably be a bug or two but the idea should be clear.
I am using Java EE 6 and need to load configuration from a ".properties" file. Is there a recommended way (best practice) to load the values from the configuration file using dependency injection? I found annotations for this in Spring, but I have not found a "standard" annotation for Java EE.
This guy have developed a solution from scratch:
http://weblogs.java.net/blog/jjviana/archive/2010/05/18/applicaction-configuration-java-ee-6-using-cdi-simple-example
"I couldn't find a simple example of how to configure your application
with CDI by reading configuration attributes from a file..."
But I wonder if there is a more standard way instead of creating a configuration factory...
Configuration annotation
package com.ubiteck.cdi;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.enterprise.util.Nonbinding;
import javax.inject.Qualifier;
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectedConfiguration {
/**
* Bundle key
* #return a valid bundle key or ""
*/
#Nonbinding String key() default "";
/**
* Is it a mandatory property
* #return true if mandator
*/
#Nonbinding boolean mandatory() default false;
/**
* Default value if not provided
* #return default value or ""
*/
#Nonbinding String defaultValue() default "";
}
The configuration factory could look like :
import java.text.MessageFormat;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
public class ConfigurationInjectionManager {
static final String INVALID_KEY="Invalid key '{0}'";
static final String MANDATORY_PARAM_MISSING = "No definition found for a mandatory configuration parameter : '{0}'";
private final String BUNDLE_FILE_NAME = "configuration";
private final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_FILE_NAME);
#Produces
#InjectedConfiguration
public String injectConfiguration(InjectionPoint ip) throws IllegalStateException {
InjectedConfiguration param = ip.getAnnotated().getAnnotation(InjectedConfiguration.class);
if (param.key() == null || param.key().length() == 0) {
return param.defaultValue();
}
String value;
try {
value = bundle.getString(param.key());
if (value == null || value.trim().length() == 0) {
if (param.mandatory())
throw new IllegalStateException(MessageFormat.format(MANDATORY_PARAM_MISSING, new Object[]{param.key()}));
else
return param.defaultValue();
}
return value;
} catch (MissingResourceException e) {
if (param.mandatory()) throw new IllegalStateException(MessageFormat.format(MANDATORY_PARAM_MISSING, new Object[]{param.key()}));
return MessageFormat.format(INVALID_KEY, new Object[]{param.key()});
}
}
Tutorial with explanation and Arquillian test
Even though it does not exactly cover your question, this part of the Weld documentation might be of interest for you.
Having mentioned this - no, there is no standard way to inject arbitrary resources / resource files. I guess it's simply beyond the scope of a spec to standardise such highly custom-dependent requirement (Spring is no specification, they can simply implement whatever they like). However, what CDI provides is a strong (aka typesafe) mechanism to inject configuration-holding beans on one side, and a flexible producer mechanism to read and create such beans on the other side. Definitely this is the recommended way you were asking about.
The approach you are linking to is certainly a pretty good one - even though it might me too much for your needs, depending on the kind of properties you are planning to inject.
A very CDI-ish way of continuing would be to develop a CDI extension (that would nicely encapsulate all required classes) and deploy it independently with your projects. Of course you can also contribute to the CDI-extension catalog or even Apache Deltaspike.
See #ConfigProperty of Apache DeltaSpike
The only "standard" way of doing this would be to use a qualifier with a nonbinding annotation member, and make sure all of your injections are dependent scoped. Then in your producer you can get a hold of the InjectionPoint and get the key off the qualifier in the injection point. You'd want something like this:
#Qualifier
public #interface Property {
#Nonbinding String value default "";
}
...
#Inject #Property("myKey") String myKey;
...
#Produces #Property public String getPropertyByKey(InjectionPoint ip) {
Set<Annotation> qualifiers = ip.getQualifiers
// Loop through qualifers looking for Property.class save that off
return ResourceBundle.getBundle(...).getString(property.key);
}
There are obviously some enhancements you can do to that code, but it should be enough to get you started down the right track.