I am writing test cases for a Spring batch application. I need to setup an HSQL database that mirrors a remote mainframe db2 database so I can run my tests quickly.
I have to setup the db table in such as way that the following SQL statement will succeed:
SELECT PROJ_TYP, SYS_CD, ... FROM CMNREF.CNTRCT_EXTRNL_KEY_REF_V WITH UR
I believe the CMNREF above is the Schema owner. How can I create that table and schema in HSQL db so this SELECT will work?
Update:
Doing some research on the topic here, I've learned that I need to do the following:
1) Use the SA default user to create new user CMNREF
2) Grant some ROLES to user CMNREF (i.e. SELECT)
However, I also noticed that the default user SA has the DBA role, which means it can do anything on that database.... so I wonder if I even need to bother with creating the 1 and 2 above....
My Test Case:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/load-BMS-data-job-launcher-context.xml" })
public class SimpleJobLaunchFunctionalTests {
#Autowired
private JobLauncherTestUtils jobLauncherUtils;
#Qualifier("jdbcTemplate")
#Autowired
private JdbcOperations jdbcTemplate;
#Qualifier("jdbcTemplateBMS")
#Autowired
private JdbcOperations jdbcTemplateBMS;
#Before
public void setUp() {
jdbcTemplateBMS.update("DELETE from CMNREF.CNTRCT_EXTRNL_KEY_REF_V");
jdbcTemplate.update("DELETE from SHADOW_BMS");
// jdbcTemplate.update("DELETE from CMNREF.CNTRCT_EXTRNL_KEY_REF_V");
Calendar calendar = Calendar.getInstance();
java.sql.Timestamp currentTimestamp = new java.sql.Timestamp(calendar.getTime().getTime());
// Insert one test data record
jdbcTemplate.update("INSERT INTO CMNREF.CNTRCT_EXTRNL_KEY_REF_V(PROJ_TYP_CD, SYS_CD, STAT_CK_CD, EXTRNL_KEY_CD, EXTRNL_SYS_CD, CNTRCT_NUM, PROJ_NUM, CNTRCT_LINE_ITM, CUST_NUM, CUST_CNTL_NUM, CUST_NM, CHRG_CD, PRDCT_ID, BILNG_CRNCY_CD, BILNG_ISO_CRNCY_CD, BILNG_CRNCY_DCM_NUM, CFTS_CRNCY_EXCH_RT, CFTS_CRNCY_EXCH_RT_EXPIR_DT, CHRG_CRNCY_CD, CHRG_ISO_CRNCY_CD, CTRY_CD, CMPNY_CD, OFFERING_CD, CNTL_GRP_CD, ORIG_CTRY_CD, ORIG_CMPNY_CD, ORIG_LOC_CD, NOTES_ID, INET_ID, CFTS_GBI_IND, CFTS_CHRG_TYP_CD, CFTS_SPCL_HNDL_CD, CC_PMT_METH_IND, STAT_CD, REFRESH_TMS) VALUES('PROJT1', 'SCD', 'STC', 'EXTKEY','EXTSYSCODE', 'CTR923','PROJN293', 23, 'CNUM32', 'CN', 'NAME THIS CUST', 'CHCD', '2903-920','BCD', 'BIC', 23, 1.345, '2017-01-23','CCC', 'CIC', 'CCD', 'IBM', '9203L-98', 'CTLGRP', 'USA', 'IBM001', 'OLC', 'me#us.ibm.com', 'ME/US/IBM/COM', 'G', 'T', 'H', 'P', 'OPEN', '2016-01-02 19:29:23.271' )");
}
My DB Script is there:
CREATE USER CMNREF PASSWORD 'pw';
CREATE SCHEMA CMNREF AUTHORIZATION DBA;
ALTER USER CMNREF SET INITIAL SCHEMA CMNREF;
SET SCHEMA CMNREF;
CREATE TABLE CMNREF.CNTRCT_EXTRNL_KEY_REF_V (
PROJ_TYP_CD VARCHAR(10),
SYS_CD VARCHAR(3),
STAT_CK_CD VARCHAR(12),
EXTRNL_KEY_CD VARCHAR(36),
EXTRNL_SYS_CD VARCHAR(10),
CNTRCT_NUM VARCHAR(15),
PROJ_NUM VARCHAR(8),
CNTRCT_LINE_ITM INTEGER,
CUST_NUM VARCHAR(8),
CUST_CNTL_NUM VARCHAR(2),
CUST_NM VARCHAR(35),
CHRG_CD VARCHAR(4),
PRDCT_ID VARCHAR(15),
BILNG_CRNCY_CD VARCHAR(3),
BILNG_ISO_CRNCY_CD VARCHAR(3),
BILNG_CRNCY_DCM_NUM SMALLINT,
CFTS_CRNCY_EXCH_RT DECIMAL,
CFTS_CRNCY_EXCH_RT_EXPIR_DT DATE,
CHRG_CRNCY_CD VARCHAR(3),
CHRG_ISO_CRNCY_CD VARCHAR(3),
CTRY_CD VARCHAR(3),
CMPNY_CD VARCHAR(10),
OFFERING_CD VARCHAR(8),
CNTL_GRP_CD VARCHAR(8),
ORIG_CTRY_CD VARCHAR(3),
ORIG_CMPNY_CD VARCHAR(10),
ORIG_LOC_CD VARCHAR(3),
NOTES_ID VARCHAR(100),
INET_ID VARCHAR(100),
CFTS_GBI_IND VARCHAR(1),
CFTS_CHRG_TYP_CD VARCHAR(1),
CFTS_SPCL_HNDL_CD VARCHAR(1),
CC_PMT_METH_IND VARCHAR(1),
STAT_CD VARCHAR(12),
REFRESH_TMS TIMESTAMP
) ;
My DataSources are defined here:
<!-- Initialise the database before every test case: -->
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="${batch.drop.script}"/>
<jdbc:script location="${batch.schema.script}"/>
<jdbc:script location="${batch.business.schema.script}"/>
</jdbc:initialize-database>
<!-- Initialize the mock BMS database -->
<jdbc:initialize-database data-source="BMSdataSource" ignore-failures="DROPS">
<jdbc:script location="${bmsmock.business.schema.script}"/>
</jdbc:initialize-database>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${batch.jdbc.driver}" />
<property name="url" value="${batch.jdbc.url}" />
<property name="username" value="${batch.jdbc.user}" />
<property name="password" value="${batch.jdbc.password}" />
<property name="maxActive" value="${batch.jdbc.pool.size}"/>
<property name="validationQuery" value="${batch.jdbc.validationQuery}"/>
<property name="testWhileIdle" value="${batch.jdbc.testWhileIdle}"/>
</bean>
<bean id="BMSdataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${bmsmock.jdbc.driver}" />
<property name="url" value="${bmsmock.jdbc.url}" />
<property name="username" value="${bmsmock.jdbc.user}" />
<property name="password" value="${bmsmock.jdbc.password}" />
<property name="maxActive" value="${bmsmock.jdbc.pool.size}"/>
<property name="validationQuery" value="${bmsmock.jdbc.validationQuery}"/>
<property name="testWhileIdle" value="${bmsmock.jdbc.testWhileIdle}"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" lazy-init="true">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Set up or detect a System property called "ENVIRONMENT" used to construct a properties file on the classpath. The default is "hsql". -->
<bean id="environment"
class="org.springframework.batch.support.SystemPropertyInitializer">
<property name="defaultValue" value="hsql"/>
<property name="keyName" value="ENVIRONMENT"/>
</bean>
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
depends-on="environment">
<property name="location" value="classpath:batch-${ENVIRONMENT}.properties" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="order" value="1" />
</bean>
My HSQLDB Properties are below:
batch.jdbc.driver=org.hsqldb.jdbcDriver
batch.jdbc.url=jdbc:hsqldb:mem:testdb;sql.enforce_strict_size=true;hsqldb.tx=mvcc
batch.jdbc.user=sa
batch.jdbc.password=
batch.jdbc.testWhileIdle=false
batch.jdbc.validationQuery=
batch.drop.script=classpath:/org/springframework/batch/core/schema-drop-hsqldb.sql
batch.schema.script=classpath:/org/springframework/batch/core/schema-hsqldb.sql
batch.business.schema.script=classpath:/db/custom-db-assets.sql
batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer
batch.database.incrementer.parent=columnIncrementerParent
batch.lob.handler.class=org.springframework.jdbc.support.lob.DefaultLobHandler
batch.jdbc.pool.size=6
batch.grid.size=6
batch.verify.cursor.position=true
batch.isolationlevel=ISOLATION_SERIALIZABLE
batch.data.source.init=true
batch.table.prefix=BATCH_
bmsmock.jdbc.driver=org.hsqldb.jdbcDriver
bmsmock.jdbc.url=jdbc:hsqldb:mem:testbms;sql.enforce_strict_size=true;hsqldb.tx=mvcc
bmsmock.jdbc.user=SA
bmsmock.jdbc.password=
bmsmock.jdbc.pool.size=6
bmsmock.jdbc.testWhileIdle=false
bmsmock.jdbc.validationQuery=
bmsmock.business.schema.script=db/mock-bms-tables.sql
It is a good idea to use a different user without the DBA role.
In HSQLDB a user and a schema are separate concepts, with no automatic schema per user. What you have done is fine. Alternatively you can
CREATE USER CMNREF PASSWORD 'pw'
CREATE SCHEMA CMNREF AUTHORIZATION CMNREF
With the above, the CMNREF user owns a schema of the same name. It can execute all statements in its own schema.
update: In your settings the SA user is used for all the operations. You need to run the user creation section of the DB script you quoted with the user SA, then run the tests with the user CMNREF.
My script (attached) shows exactly how to do it. It's right, nothing wrong with the script or schema definition. My problem was that my test case was refering to the wrong Jdbc template when inserting data into the CMNREF.C* table.... the correct stmt should have been:
jdbcTemplateBMS**.update("INSERT INTO CMNREF.CNTRCT_E
Also, I need to turn on the DB2 mode for my sql script to work properly
I am including a .properties file, which has a list of properties:
configuration.files = file1, file2
configuration.files.file1.source = config/filename1
configuration.files.file2.source = config/filename2
Now I need the paths for each file changed to something like this:
vendor/project/config/filename1
vendor/project/config/filename2
To achieve that, I tried to foreach this list and prepend that suffix and overriding the existing property:
<foreach list="${configuration.files}" target="_prepend-vendor-path" param="file" >
<property name="configuration.files.${file}.source" value="/vendor/project/${configuration.files.${file}.source}" override="true"/>
</foreach>
<target name="_prepend-vendor-path" >
<echo msg="${configuration.files.${file}.source}" />
</target>
This doesn't work and I can't figure out why. Is it even possible to use target names like ${suffix}.name ? If not, how could I achive my goal here?
I just did some workaround for this, writing out the properties and their values to a file and readin them after the loop has finished with override = true:
<target name="_prepend-vendor-path" >
<exec dir="${project.basedir}" command="echo configuration.files.${file}.source = /vendor/project/${configuration.files.${file}.source} >> ${project.temp.config}" passthru="true" checkreturn="true" />
</target>
and after the foreach simply:
<property file="${project.temp.config}" override="true"/>
For some reason the properties won't be overridden in the foreach and I just can't figgure out why, but this little trick made it for me.
You can suffix your property values from your file with the property task using a filterchain and a regular expression replacement:
<?xml version="1.0"?>
<project name="Phing Build Tests" default="append-custom-path" basedir=".">
<target name="append-custom-path">
<property file="prop.properties">
<filterchain>
<replaceregexp>
<regexp pattern="^(.*)" replace="vendor/project/$1"/>
</replaceregexp>
</filterchain>
</property>
<echo>${configuration.files.file1.source}</echo>
<echo>${configuration.files.file2.source}</echo>
</target>
</project>
In phing i can set this:
<property name="build.myArray" value="something1, something2, something3" />
And then retrieve each value like this:
<foreach list="${build.myArray}" param="replace.me" target="build:create-vhost" />
<target name="build:create-vhost">
<filterchain>
<replacetokens begintoken="#" endtoken="#">
<token key="REPLACE_ME" value="${replace.me}" />
</replacetokens>
</filterchain>
</target>
My question is can i do the same this but using array with keys and values?
Something like that:
<property name="build.myArray" value="myKey = something1, myKey2 => something2, myKey3 => something3" />
and use that key names later
Is is possible to do it?
No, you cannot use properties as array in phing.
I need to include all xml files inside a directory (I dont know the name, and count of files) in my current build.xml using ImportTask.
This is my build.xml file:
<?xml version="1.0" encoding="utf-8"?>
<project name="New Project" basedir="." default="myimport">
<target name="myimport" description="dummy to import other build files">
<if>
<isset property="file" />
<then>
<echo msg="Importing ${file}" />
<import file="${file}" />
</then>
</if>
</target>
<if>
<not>
<isset property="dummy.property" />
</not>
<then>
<echo msg="Now include all files in ./dev/build directory" />
<property name="dummy.property" value="true" />
<foreach param="msg" absparam="file" target="myimport">
<fileset dir="./dev/build/">
<include name="*.xml"/>
</fileset>
</foreach>
</then>
</if>
</project>
and an example file in target directory:
<?xml version="1.0" encoding="utf-8"?>
<project name="test" basedir="." default="dummy">
<target name="dummy" description="Dummy task">
<echo msg="Dummy task, just for test" />
</target>
<echo msg="Imported!" />
</project>
when I run phing -l the result is:
Buildfile: /home/f0rud/workspace/s/build.xml
[echo] Now include all files in ./dev/build directory
[foreach] Calling Buildfile '/home/f0rud/workspace/s/build.xml' with target 'myimport'
New Project > myimport:
[echo] Importing ./dev/build/test.xml
[echo] Imported!
Default target:
----------------------------------------------------------------------------
myimport dummy to import other build files
Main targets:
----------------------------------------------------------------------------
myimport dummy to import other build files
But there is no dummy (or test.dummy) target, why?
Note : There is a funny bug, if I remove the if part, I get Maximum function nesting level of '100' reached, aborting! error but thats not my problem (the if solve that problem.)
The problem is import work on global context.
When I call it inside a target, its not available in global context.
So I've written a simple Phing task to load all files of a fileset like so:
class ImportDirTask extends Task {
/** Array of filesets */
private $filesets = array();
/**
* Nested creator, adds a set of files (nested fileset attribute).
*/
function createFileSet() {
$num = array_push($this->filesets, new FileSet());
return $this->filesets[$num-1];
}
/**
* Parse a Phing build file and copy the properties, tasks, data types and
* targets it defines into the current project.
*
* #return void
*/
public function main () {
// filesets
foreach ($this->filesets as $fs) {
$ds = $fs->getDirectoryScanner($this->project);
$srcFiles = $ds->getIncludedFiles();
$srcDirs = $ds->getIncludedDirectories();
foreach ($srcFiles as $f)
{
$task = new ImportTask();
$task->setProject($this->project);
$task->init();
$task->setFile($this->file = $fs->getDir($this->project) . FileSystem::getFileSystem()->getSeparator() . $f);
$task->main();
}
}
} //end main
} //end ImportDirTask
It just work.
I am calling a target by means of phingcall command.
I want to pass back a status variable from the called target or at least change the existing value from the calling target.
Goal: I want to branch in my main target controlling logic if the sub target fails which I indicate with a property.
The code below does not work. Any idea how to make it work or an altertive approach for my goal?
Thanks,
Juergen
<target name="main">
<echo>target a</echo>
<echo>${bOk}</echo>
<exec command="echo 1" outputProperty="bOk" />
<echo>bOk is 1: ${bOk}</echo>
<phingcall inheritRefs="true" target="sub">
</phingcall>
<echo>bOk should now be 0: ${bOk}</echo>
</target>
<target name="sub">
<echo>target b</echo>
<echo>bOk is 1: ${bOk}</echo>
<exec command="echo 0" outputProperty="bOk" />
<echo>bOk now is 0: ${bOk}</echo>
</target>
The problem here is that
<echo>bOk should now be 0: ${bOk}</echo>
echos
bOk should now be 0: 1
Even with the great help of #phing IRC I couldn't solve the problem.
I decided to write a custom task to account for data passing between targets:
<?php
require_once "phing/Task.php";
class rvGlobalTask extends Task {
private static $bOk = 1;
private $sMode = null;
private $bValue = null;
private $outputProperty = null;
public function setSMode( $sMode ) {
$this->sMode = $sMode;
}
public function setBValue( $bValue ) {
$this->bValue = $bValue;
}
public function setOutputProperty( $outputProperty ) {
$this->outputProperty = $outputProperty;
}
public function main() {
if ( $this->sMode == "set" ) {
rvGlobalTask::$bOk = $this->bValue;
} else {
$this->project->setProperty(
$this->outputProperty,
rvGlobalTask::$bOk
);
}
}
}
?>
This works fine for my problem. Perhaps someone else finds this useful as well.
Here's how you use an ExecTask to capture output.
<?xml version="1.0" encoding="UTF-8"?>
<project name="example" default="check-composer">
<!-- set a property to contain the output -->
<property name="whichComposer" value="" />
<!-- check if composer (getcomposer.org) is installed globally -->
<target name="check-composer">
<!-- put the output of "which" in our property -->
<exec command="which composer" outputProperty="whichComposer" />
<!-- act on what we found out -->
<if>
<contains string="${whichComposer}" substring="composer" />
<then>
<echo>Composer installed at ${whichComposer}</echo>
</then>
<else>
<echo message="better install composer. ${whichComposer}"/>
</else>
</if>
</target>
</project>