Testing OpenLiberty with Arquillian (Remote)
Having heard many great reviews, I thought I’ll give Open Liberty a try.
In this post, I shall discuss the following:
- Setup of Open Liberty
- Setup JDBC connection
- Setup Arquillian
- Testing REST endpoint
Installing Open Liberty
At the time of writing, I am using Open Liberty 18.0.0.1, and I am using Java SE 1.8.0_172 (P.S. Keen to move on to Java 9 and Java 10, but I thought better wait for LTS Java 11).
Installation is very easy. Let’s assume we are going to create a running server name test
.
First, unpack your Open Liberty download. It will create a directory structure wlp
.
Navigate to bin
directory and run the following command:
./server create test
Now a server name test
has been created. To start:
./server start test
with the argument test being the name of the server.
Navigate to http://localhost:9080/test
to see the context root.
To stop,
./server stop test
Configure server.xml
Once you have start test
server, a directory will be created under the /usr/servers/test
, and inside that directory there is file named server.xml
. Let’s check it out.
<?xml version="1.0" encoding="UTF-8"?> <server description="new server"> <!-- Enable features --> <featureManager> <feature>jsp-2.3</feature> </featureManager> <!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" --> <httpEndpoint id="defaultHttpEndpoint" httpPort="9080" httpsPort="9443" /> <!-- Automatically expand WAR files and EAR files --> <applicationManager autoExpand="true"/> </server>
In this article we are using Java EE 7 Web Profile, to enable that, it is very simple to do that (and it is not even necessary to restart the server). Simply change the featureManager
.
<?xml version="1.0" encoding="UTF-8"?> <server description="new server"> <!-- Enable features --> <featureManager> <feature>webProfile-7.0</feature> </featureManager> <!-- the rest of the configuration omitted -->
You can check what features being loaded dynamically by looking at console.log
.
Configuring JDBC Datasource
Configure Datasource in server.xml
For this exercise, I am using MySQL 8.0. Setting up MySQL and its configuration is out-of-scope of this article.
Let’s assume we have created a new database, also named test
.
To set up your datasource, make the following modification to your server.xml
and restart (or not, not too sure on this one, but no harm in restarting).
Comments interleaved.
<?xml version="3.0" encoding="UTF-8"?> <server description="new server"> <!-- Enable features --> <featureManager> <feature>webProfile-7.0</feature> </featureManager> <!-- Declare the jar files for MySQL access through JDBC. --> <dataSource id="testDS" jndiName="jdbc/testDS"> <jdbcDriver libraryRef="MySQLLib"/> <properties databaseName="test" serverName="localhost" portNumber="3306" user="root" password="P4sswordGoesH3r3"/> </dataSource> <library id="MySQLLib"> <file name="/home/dwuysan/dev/appservers/wlp/usr/shared/resources/mysql/mysql-connector-java-8.0.11.jar"/> </library> <!-- Automatically expand WAR files and EAR files --> <applicationManager autoExpand="true"/> </server>
persistence.xml
OpenLiberty comes with EclipseLink bundled as JPA Provider. In this example I have not configured any EclipseLink’s properties.
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1"> <persistence-unit name="testPU"> <jta-data-source>jdbc/testDS</jta-data-source> <properties> </properties> </persistence-unit> </persistence>
And you can then call it in your Java EE application via:
@Stateless @LocalBean public class LogService { @PersistenceContext private EntityManager em; public Collection<Log> getLogs() { return this.em.createNamedQuery(Log.FIND_ALL, Log.class).getResultList(); } }
Setting up arquillian
In this article, we are going to implement Arquillian remote testing against a running OpenLiberty server.
Firstly, add arquillian to your pom.xml
.
Configure pom.xml
This is the pom.xml that have been modified:
<?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>id.co.lucyana</groupId> <artifactId>test</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>test</name> <properties> <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.arquillian</groupId> <artifactId>arquillian-bom</artifactId> <version>1.4.0.Final</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.arquillian.graphene</groupId> <artifactId>graphene-webdriver</artifactId> <version>2.3.2</version> <type>pom</type> <scope>test</scope> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <scope>test</scope> <version>3.12.0</version> </dependency> <dependency> <groupId>org.jboss.arquillian.junit</groupId> <artifactId>arquillian-junit-container</artifactId> <scope>test</scope> </dependency> <dependency> <!-- Arquillian WebSphere Liberty Profile support --> <groupId>io.openliberty.arquillian</groupId> <artifactId>arquillian-liberty-remote</artifactId> <version>1.0.0</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> <compilerArguments> <endorseddirs>${endorsed.dir}</endorseddirs> </compilerArguments> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.2.1</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <phase>validate</phase> <goals> <goal>copy</goal> </goals> <configuration> <outputDirectory>${endorsed.dir}</outputDirectory> <silent>true</silent> <artifactItems> <artifactItem> <groupId>javax</groupId> <artifactId>javaee-endorsed-api</artifactId> <version>7.0</version> <type>jar</type> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
Make modification to server.xml
The documentation provided here is quite self-explanatory. Please consult this documentation for more up-to-date information on how to enable remote-testing.
<?xml version="1.0" encoding="UTF-8"?> <server description="new server"> <!-- Enable features --> <featureManager> <feature>webProfile-7.0</feature> <feature>restConnector-2.0</feature> </featureManager> <!-- Declare the jar files for MySQL access through JDBC. --> <dataSource id="testDS" jndiName="jdbc/testDS"> <jdbcDriver libraryRef="MySQLLib"/> <properties databaseName="test" serverName="localhost" portNumber="3306" user="root" password="P4sswordGoesH3r3"/> </dataSource> <library id="MySQLLib"> <file name="/home/dwuysan/dev/appservers/wlp/usr/shared/resources/mysql/mysql-connector-java-8.0.11.jar"/> </library> <httpEndpoint httpPort="9080" httpsPort="9443" id="defaultHttpEndpoint" host="*" /> <!-- userName and password should also be set in arquillian.xml to these values --> <quickStartSecurity userName="admin" userPassword="admin" /> <!-- Enable the keystore --> <keyStore id="defaultKeyStore" password="password" /> <applicationMonitor updateTrigger="mbean" /> <logging consoleLogLevel="INFO" /> <!-- This section is needed to allow upload of files to the dropins directory, the remote container adapter relies on this configuration --> <remoteFileAccess> <writeDir>${server.config.dir}/dropins</writeDir> </remoteFileAccess> <!-- Automatically expand WAR files and EAR files --> <applicationManager autoExpand="true"/> </server>
Trust the server (i.e. certificate)
You need to have those keys trusted by your client as well, otherwise you’ll see SSL certificate trust errors, and you need to give permissions for the container adapter to write to the dropins directory” (Liberty-Arquillian 2018)
Once you have made all the necessary modification above, (and re-start the server), notice that there is a new directory created under <location of your OpenLiberty server>/usr/servers/test/resources/security
with test
being the name of the server we have initially created.
Notice there are two files being created, keys.jks
and ltpa.keys
. Now, we are interested in the keys.jks
.
So that we can run our test from Netbeans (Maven), the JDK must trust the running OpenLiberty.
Check the certificate
keytool -list -v -keystore key.jks Enter keystore password:
The password
here is basically what we created in our server.xml, particularly this line:
<!-- Enable the keystore --> <keyStore id="defaultKeyStore" password="password" />
so, enter the password, and the output should be as follows:
***************** WARNING WARNING WARNING ***************** * The integrity of the information stored in your keystore * * has NOT been verified! In order to verify its integrity, * * you must provide your keystore password. * ***************** WARNING WARNING WARNING ***************** Keystore type: jks Keystore provider: SUN Your keystore contains 1 entry Alias name: default Creation date: May 26, 2018 Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=localhost, OU=test, O=ibm, C=us Issuer: CN=localhost, OU=test, O=ibm, C=us Serial number: 2a6c5b27 Valid from: Sat May 26 12:24:30 WITA 2018 until: Sun May 26 12:24:30 WITA 2019 Certificate fingerprints: MD5: 63:92:B2:4A:25:E3:BB:3B:96:37:11:C1:A7:25:38:B5 SHA1: B6:38:95:88:FC:50:EC:A0:8E:41:4E:DE:B5:D4:8B:85:2E:61:A2:5F SHA256: 9C:7B:6A:FA:46:8C:50:F2:7D:7B:C4:24:4B:15:78:5A:34:25:C8:43:D1:AB:4D:EE:C7:00:4C:AF:30:F5:5C:92 Signature algorithm name: SHA256withRSA Subject Public Key Algorithm: 2048-bit RSA key Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 88 F2 C2 32 73 73 B6 66 8F FA 42 85 1F 43 A5 AF ...2ss.f..B..C.. 0010: 84 33 62 D5 .3b. ] ] ******************************************* *******************************************
Next, export the certificate
We now need to create a .cer
. Use the following command:
keytool -export -alias default -file testwlp.crt -keystore key.jks Enter keystore password:
Basically we are exporting the certificate of alias
into a file named testwlp.crt
. Now, a file named testwlp.crt
should be created.
Lastly, let’s trust that certificate by importing that certificate into the JDK cacert
keytool -import -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -alias testwlp -import -file testwlp.crt
P.S. Please note that, as pointed out by many experts (via Twitter), apparently there are many ways to ‘trust’ the server. I am sure there are better ways, and as much as possible I’d prefer not to touch any of the JDK’s files.
Create arquillian.xml
Now all those plumbing works done, let’s add arquillian.xml
accordingly.
<?xml version="1.0" encoding="UTF-8"?> <arquillian xmlns="http://jboss.org/schema/arquillian" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd"> <engine> <property name="deploymentExportPath">target</property> </engine> <container qualifier="liberty-remote" default="true"> <configuration> <property name="hostName">localhost</property> <property name="serverName">test</property> <!-- check the 'quickStartSecurity' on 'server.xml' --> <property name="username">admin</property> <property name="password">admin</property> <!-- check the 'server.xml' --> <property name="httpPort">9080</property> <property name="httpsPort">9443</property> </configuration> </container> <extension qualifier="webdriver"> <!--<property name="browser">firefox</property>--> <property name="remoteReusable">false</property> </extension> </arquillian>
Write a REST Test case
With all those setups done, you can now write an Arquillian Test case. Below is an example of a test case against a JAX-RS end point (Please excuse the simplicity of the test case, the point is to show how we can test using Arquillian-remote against OpenLiberty):
package id.co.lucyana.test.resource; import id.co.lucyana.test.entity.Log; import id.co.lucyana.test.services.LogService; import id.co.lucyana.test.util.ApplicationConfig; import java.net.URL; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.drone.api.annotation.Drone; import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.shrinkwrap.api.ArchivePaths; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.openqa.selenium.WebDriver; @RunWith(Arquillian.class) public class LogResourceTest { @Drone private WebDriver webDriver; @Deployment public static JavaArchive createTestArchive() { return ShrinkWrap.create(JavaArchive.class) .addClass(Log.class) .addClass(LogResource.class) .addClass(LogService.class) .addClass(ApplicationConfig.class) .addAsManifestResource("test-persistence.xml", ArchivePaths.create("persistence.xml")); } @Test @RunAsClient public void testLogResource(@ArquillianResource URL url) { this.webDriver.get(url.toString() + "resources/log"); String pageSource = this.webDriver.getPageSource(); System.out.println("RESULT: " + pageSource); Assert.assertTrue(true); } }
References
DigiCert, 2018, ‘How to install the trusted root into Java cacerts Keystore’, DigiCert, accessed on 20 June 2018
Liberty-Arquillian, 2018, ‘Arquillian Liberty Remote Documentation’, GitHub. Inc, accessed on 20 June 2018
SSLShopper, 2008, ‘The Most Common Java Keytool Keystore Commands’, SSLShopper, accessed on 20 June 2018
Published on Java Code Geeks with permission by Deny Wuysan, partner at our JCG program. See the original article here: Testing OpenLiberty with Arquillian (Remote) Opinions expressed by Java Code Geeks contributors are their own. |