public class HelloImplIT { @ClassRule public static AppServer appServer = new AppServer(); private static Hello sut; @BeforeClass public static void beforeClass() { sut = appServer.lookup("mavenit", HelloImpl.class, Hello.class); } @Test public void test() { Assert.assertEquals("Hello world!", sut.hello()); } }
Lean example of integration test Maven project for WildFly
TweetPosted on Monday Feb 23, 2015 at 11:20PM in Technology
I created similar example before, but it was redundant. I created more lean version so leave here with some notes. check my GitHub repository for details.
The project contains AppServer class, which extends ExternalResource
so it can be used with @Rule
annotation of JUnit as follows. note that mavenit
is the name of WAR file to be tested.
The project is easy to use with both of IDE and standalone. to run IT classes with IDE, you need to launch the application server, deploy the project as WAR package, then run a IT class from IDE. if you launched the server at not 8080
port, specify the port in app-server.port
system property.
To run IT classes in standalone Maven, use following command. it automatically launches WildFly instance with port offset of 30000
, deploy the WAR file, running IT classes, undeploy the WAR and stops WildFly. the command is intended to use from a Jenkins job.
mvn verify -Pintegration-test-with-wildfly \ -Djava.home=/Library/Java/JavaVirtualMachines/jdk8/Contents/Home/jre \ -Djboss-as.home=/Users/kyle/servers/wildfly-8.2.0.Final \ -Dwildfly.port=39990 \ -Dapp-server.port=38080 \ -Dwildfly.jvmArgs="-Djboss.server.base.dir=/Users/kyle/servers/wildfly-8.2.0.Final/standalone-mavenit -Djboss.socket.binding.port-offset=30000 -ea"
You can configure following parameters through command line argument:
-
JAVA_HOME
⇒-Djava.home
-
WildFly distribution ⇒
-Djboss-as.home
-
jboss.server.base.dir
⇒ inside-Dwildfly.jvmArgs
-
Port offset ⇒
-Djboss.socket.binding.port-offset
inside-Dwildfly.jvmArgs
. you need to set-Dwildfly.port
and-Dapp-server.port
accordingly.
Switching EJB remote interface to local interface accordingly to environment
TweetPosted on Monday Feb 23, 2015 at 01:33PM in Technology
Sometimes I need to create both of remote and local interface for EJBs. most of cases that were for integration testing. usually I used this way but that way still has some disadvantages as follows:
-
Annoying creation of both of interface (local and remote) is needed for all of each EJBs
-
Remote interface is redundant for most cases in production environment
To overcome these issues, I moved declarations of EJB from annotation to ejb-jar.xml
. XML files are easy to be filtered with build tools such as Maven plugins. consider following case:
Business interface
public interface MyBean { String hello(); }
Implementation
public class MyBeanImpl implements MyBean { @Override public String hello() { return "Hello"; } }
Injection point
@WebServlet(name = "MyServlet", urlPatterns = "/") public class MyServlet extends HttpServlet { @EJB MyBean myBean; ... }
Test class
public class MyBeanIT { static Context ctx; static MyBean myBean; @BeforeClass public static void before() throws Exception { Properties props = new Properties(); // ... put application server specific configuration here ... ctx = new InitialContext(props); myBean = (MyBean) ctx.lookup("ejb:/ejbxmltest//MyBeanImpl!org.nailedtothex.ejbxmltest.MyBean"); } @AfterClass public static void after() throws Exception { ctx.close(); } @Test public void test() throws Exception { org.junit.Assert.assertEquals("Hello", myBean.hello()); } }
ejb-jar.xml
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd" version="3.1"> <enterprise-beans> <session> <ejb-name>MyBeanImpl</ejb-name> <!-- Will be replaced by business-local with replacer-plugin for production --> <business-remote>org.nailedtothex.ejbxmltest.MyBean</business-remote> <ejb-class>org.nailedtothex.ejbxmltest.MyBeanImpl</ejb-class> <session-type>Stateless</session-type> </session> </enterprise-beans> </ejb-jar>
Remote interface and bean definition are presented in ejb-jar.xml
instead of annotations. that definition will be used by IDE as default. EJBs will be deployed with a remote interface to the application server in development environment so we can test the bean through remote interface. a trick is in a profile named business-local
in pom.xml
which intended to use for production environment:
<profile> <id>business-local</id> <build> <!-- Following plugin definitions will be ignored by IDEA --> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <warSourceExcludes>WEB-INF/ejb-jar.xml</warSourceExcludes> </configuration> </plugin> <plugin> <groupId>com.google.code.maven-replacer-plugin</groupId> <artifactId>replacer</artifactId> <version>1.5.3</version> <executions> <execution> <phase>prepare-package</phase> <goals> <goal>replace</goal> </goals> </execution> </executions> <configuration> <file>src/main/webapp/WEB-INF/ejb-jar.xml</file> <outputFile>target/${project.artifactId}/WEB-INF/ejb-jar.xml</outputFile> <token><![CDATA[(?<=</?)business-remote(?=>)]]></token> <value>business-local</value> </configuration> </plugin> </plugins> </build> </profile>
maven-replacer-plugin
replaces all of business-remote
elements by business-local
and put it into WEB-INF
directory in the target directory, and tell maven-war-plugin
to stop copying original WEB-INF/ejb-jar.xml
so all of remote interfaces will be local interfaces in production environment. it is disabled by default and you have to enable it with invoke Maven with -P business-local
argument. as to injection point (MyServlet
) we don’t need to make any modification. @EJB
or @Inject
will work nevertheless whether the interface is local or remote.
Actually I really want to use Arquillian instead of remote EJB but unfortunately it doesn’t have some flexibility on my use case such as control of deployment timing at the present time.
The project used in test is available in my GitHub repository. the test was done with WildFly 8.2.0.Final.
CAUTION: invoking EJBs via local interface will call by reference but invoking via remote interface will call by value. you will get serious problems that different behavior between invoking local and remote interfaces. consider keep using remote interface on production (with accepting some performance degradation) or using modern testing framework such as Arquillian if necessary.
Using DatabaseConfiguration of Commons Configuration
TweetPosted on Sunday Feb 22, 2015 at 10:50PM in Technology
Commons Configuration is a very useful library which manages configurations of an application. it supported various data stores including RDBMS. I’ve done some test against RDBMS so leave this posting as a note. test was done against Commons Configuration 1.10. the project which used in test is can be obtained from my GitHub repository.
app_config Table
Assume now we have following data in a table named app_config
.
name | value |
---|---|
anyJob.someProp1 |
abc |
anyJob.someProp2 |
def |
someJob.list |
1,2,3,4,5 |
someJob.someProp |
${anyJob.someProp1} |
Create Configuration instance
First, we need to create a DatabaseConfiguration
instance with following procedure:
Configuration config = new DatabaseConfiguration(dataSource, "app_config", "name", "value");
Obtain all of configuration
We can obtain all of configurations as follows:
// Obtain all of keys for (Iterator<String> keys = config.getKeys(); keys.hasNext(); ) { String key = keys.next(); Object value = config.getProperty(key); System.out.println(key + "=" + value); }
It produces:
anyJob.someProp1=abc anyJob.someProp2=gef someJob.list=[1, 2, 3, 4, 5] someJob.someProp=${anyJob.someProp1}
Obtain keys with particular prefix
Sometimes we’d like to filter using prefixes. this time we get keys prefixed with someJob
.
// Obtain keys with particular prefix for (Iterator<String> keys = config.getKeys("someJob"); keys.hasNext(); ) { String key = keys.next(); Object value = config.getProperty(key); System.out.println(key + "=" + value); }
It produces:
someJob.list=[1, 2, 3, 4, 5] someJob.someProp=${anyJob.someProp1}
Obtain list
We can obtain comma-separated values as List
as follows:
// Obtain list System.out.println(config.getList("someJob.list")); // [1, 2, 3, 4, 5] System.out.println(config.getString("someJob.list")); // 1
It produces:
[1, 2, 3, 4, 5] 1
Note that invocation of getString()
returns only the first value.
Obtain referencing value
The value ${anyJob.someProp1}
in the key someJob.someProp
resolved as abc
with getString()
method as follows:
// Referencing a value of other key System.out.println(config.getString("someJob.someProp")); // abc
It produces:
abc
Update
The library reflects a update of database immediately even after instantiation of DatabaseConfiguration
. consider following procedure was executed after instantiation of the configuration instance.
// update value of a row try (Connection cn = dataSource.getConnection()) { try (Statement st = cn.createStatement()) { st.executeUpdate("update app_config set value='hij' where name='anyJob.someProp1'"); } } // check updated value System.out.println(config.getString("anyJob.someProp1")); // hij System.out.println(config.getString("someJob.someProp")); // hij
It produces:
hij hij
Tags: commons
Using JSL Inheritance with JBeret
TweetPosted on Sunday Feb 22, 2015 at 06:28PM in Technology
In practical use, many boilerplate code will appear to Job definition XMLs of JSR352 batch such as definition of Listeners, Properties and attribute such as item-count
of chunk
element. to overcome this, there is the specification of JSL Inheritance but it deferred to next version of JBatch spec but JBeret supports this specification already. it’s very useful to eliminate annoying fragments appear repeatedly so I tested it. WildFly 8.2.0.Final was used for test.
Parent XML
Parent XML begins as follows:
<?xml version="1.0" encoding="UTF-8"?> <job id="parent" abstract="true" version="1.0" xmlns="http://xmlns.jcp.org/xml/ns/javaee"> <properties> <property name="someJobProp" value="someJobPropValue"/> </properties> <listeners> <listener ref="myJobListener"/> </listeners>
Declared property named someJobProp
become visible from child XMLs. myJobListener
will be included implicitly in child XMLs as well.
Parent XML has an abstract step element which will be referenced from child XMLs as follows. these properties and listeners for a step will be included implicitly too. we can override javax.transaction.global.timeout
at there. it will be affected to all of child steps.
<step id="mySimpleStep" abstract="true"> <properties> <property name="javax.transaction.global.timeout" value="1800"/> </properties> <listeners> <listener ref="myStepListener"/> </listeners> </step>
It is the same for chunk
based step. it can be used to eliminate defining item-count
for all of chunk steps.
<step id="myChunkStep" abstract="true"> <properties> <property name="javax.transaction.global.timeout" value="900"/> <property name="jberet.local-tx" value="true"/> </properties> <listeners> <listener ref="myStepListener"/> <listener ref="myChunkListener"/> </listeners> <chunk item-count="3"/> </step> </job>
Child XML
Child XML became very simple thanks to inheritance as follows. you need to specify parent
and jsl-name
attributes to reference its parent. parent
means the name of parent attribute. jsl-name
means XML file name of referencing element.
<?xml version="1.0" encoding="UTF-8"?> <job id="child" parent="parent" jsl-name="parent" version="1.0" xmlns="http://xmlns.jcp.org/xml/ns/javaee"> <step id="simpleStep" next="chunkStep" parent="mySimpleStep" jsl-name="parent"> <batchlet ref="myBatchlet"/> </step> <step id="chunkStep" parent="myChunkStep" jsl-name="parent"> <chunk> <reader ref="myItemReader"/> <writer ref="myItemWriter"/> </chunk> </step> </job>
Launching example job
-
Clone the repository
-
Build the WAR and deploy it to your WildFly
-
Run a JUnit test class named JobTest. it invokes the job through remote EJB invocation.
Log
You can see Properties, Listeners and item-count
attribute were inherited as expected.
(Batch Thread - 1) Job child starting (Batch Thread - 1) Job child, Step simpleStep starting (Batch Thread - 1) hello world! (Batch Thread - 1) jobProps: {someJobProp=someJobPropValue} (Batch Thread - 1) stepProps: {javax.transaction.global.timeout=1800} (Batch Thread - 1) Job child, Step simpleStep done (Batch Thread - 1) Job child, Step chunkStep starting (Batch Thread - 1) jobProps: {someJobProp=someJobPropValue} (Batch Thread - 1) stepProps: {jberet.local-tx=true, javax.transaction.global.timeout=900} (Batch Thread - 1) Job child, Step chunkStep chunk starting (Batch Thread - 1) write: [1, 2, 3] (Batch Thread - 1) Job child, Step chunkStep chunk done (Batch Thread - 1) Job child, Step chunkStep chunk starting (Batch Thread - 1) write: [4, 5, 6] (Batch Thread - 1) Job child, Step chunkStep chunk done (Batch Thread - 1) Job child, Step chunkStep chunk starting (Batch Thread - 1) write: [7, 8, 9] (Batch Thread - 1) Job child, Step chunkStep chunk done (Batch Thread - 1) Job child, Step chunkStep chunk starting (Batch Thread - 1) write: [0] (Batch Thread - 1) Job child, Step chunkStep chunk done (Batch Thread - 1) Job child, Step chunkStep done (Batch Thread - 1) Job child done
JDBC chunk oriented processing with jberet.local-tx
TweetPosted on Saturday Feb 21, 2015 at 11:40PM in Technology
As following URL said, in my understanding, each open, read, write and close of every JDBC resources (Connection, Statement and ResultSet) needs to be in it’s own transaction.
There are open
and close
methods in ItemReader
and ItemWriter
interface. these methods looks like good to create and dispose JDBC resources such as Connection
, Statement
and ResultSet
but we can’t go that way due to the JSR352 spec. before invocation of these methods, the framework starts a transaction, then commits a transaction after invocation finished. according to preceding URL said, these resources become unusable at readItem
and writeItems
method because these resources were created in another transaction.
It’s terrible to do open a cursor again and again at start of every chunk processing. to overcome this problem, JBeret supplied an implementation specific parameter named jberet.local-tx
. so I created a sample batch project to test that parameter. my test was done with WildFly 8.2.0.Final.
How the batch works
Entire project can be obtained from my GitHub repository. the batch has 2 steps as follows:
-
prepare
: createsSRC
andDEST
table, and insert 100 rows intoSRC
table. refer source of Batchlet for details. -
test
: loads data fromSRC
table, then simply writes data intoDEST
table as is using chunk oriented processing. this step has a propertyjberet.local-tx
withtrue
value.MyItemReader
creates and disposes JDBC resources inopen
andclose
method. refer source of MyItemReader and MyItemWriter for details.
How to run the batch
-
Define a H2 DataSource
xa-data-source add \ --name=MyDS \ --driver-name=h2 \ --jndi-name=java:jboss/datasources/MyDS \ --user-name=sa \ --password=sa \ --xa-datasource-properties={ \ "URL" => "jdbc:h2:/tmp/localtxtest;AUTO_SERVER=TRUE"}
-
Deploy the project
-
Access http://localhost:8080/localtxtest-1.0-SNAPSHOT/ to launch the batch through the Servlet which mapped at
/
-
Look your database to the check batch works expectedly
Notes
Actually, the problem ARJUNA016087
warning is disappeared in latest WildFly 8.2.0.Final without using jberet.local-tx
. but I don’t know whether it is intended to fix or simply by chance still. I’ll keep looking further of this discussion.