package testutil; import java.util.logging.*; public class LoggingConfig { public LoggingConfig() { try { // Load a properties file from class path that way can't be achieved with java.util.logging.config.file /* final LogManager logManager = LogManager.getLogManager(); try (final InputStream is = getClass().getResourceAsStream("/logging.properties")) { logManager.readConfiguration(is); } */ // Programmatic configuration System.setProperty("java.util.logging.SimpleFormatter.format", "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL %4$-7s [%3$s] (%2$s) %5$s %6$s%n"); final ConsoleHandler consoleHandler = new ConsoleHandler(); consoleHandler.setLevel(Level.FINEST); consoleHandler.setFormatter(new SimpleFormatter()); final Logger app = Logger.getLogger("app"); app.setLevel(Level.FINEST); app.addHandler(consoleHandler); } catch (Exception e) { // The runtime won't show stack traces if the exception is thrown e.printStackTrace(); } } }
java.util.logging: Programmatic configuration and logging with Java8 lambdas
TweetPosted on Monday Mar 30, 2015 at 06:24PM in Technology
Configuration
We can put configurations in following places (ordered by priority):
-
java.util.logging.config.class
system property. specify FQCN of a class here -
java.util.logging.config.file
system property. specify the path to a property file here -
$JAVA_HOME/lib/logging.properties
file. put JRE-wide configuration here
Use of java.util.logging.config.class
is most flexible. in that way we can simply put a config class under src/test/java
for unit tests. we need to put configuration procedures in the default constructor. an implementation would be something like following:
If you prefer configuration in a property file rather than a config class, following will work as the same to config class:
handlers=java.util.logging.ConsoleHandler java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL %4$-7s [%3$s] (%2$s) %5$s %6$s%n java.util.logging.ConsoleHandler.level=FINEST app.level=FINEST
To set system property for every test execution, put following plugin
definition to your pom.xml
. note that IntelliJ IDEA respects this configuration in its test execution.
<plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.18.1</version> <configuration> <systemPropertyVariables> <java.util.logging.config.class> testutil.LoggingConfig </java.util.logging.config.class> <!-- If you prefer a file this will work too --> <!--<java.util.logging.config.file>--> <!--src/test/resources/logging.properties--> <!--</java.util.logging.config.file>--> </systemPropertyVariables> </configuration> </plugin>
Logging examples
Application class:
public class MyClass { private static final Logger log = Logger.getLogger(MyClass.class.getName()); public String hello(String greetings, String name) { // entering will be logged as FINER log.entering(MyClass.class.getName(), "hello", new Object[]{greetings, name}); // lambdas log.finest(() -> "finest: " + LocalDateTime.now()); log.finer(() -> "finer: " + LocalDateTime.now()); log.fine(() -> "fine: " + LocalDateTime.now()); log.info(() -> "info: " + LocalDateTime.now()); log.warning(() -> "warning: " + LocalDateTime.now()); log.severe(() -> "severe: " + LocalDateTime.now()); // exception logging // throwing will be logged as FINER log.throwing(MyClass.class.getName(), "hello", new Exception("test")); // exception + message logging with lambda log.log(Level.FINEST, new Exception("test"), () -> String.format("arg=%s", name)); // exception + parameter logging with LogRecord final LogRecord record = new LogRecord(Level.FINEST, "arg={0}"); record.setThrown(new Exception("test")); record.setLoggerName(log.getName()); // logger name will be null unless this record.setParameters(new Object[]{name}); log.log(record); final String rc = greetings + ", " + name; // exiting will be logged as FINER log.exiting(MyClass.class.getName(), "hello", rc); return rc; } }
Test:
public class MyClassTest { @org.junit.Test public void testHello() throws Exception { MyClass sut = new MyClass(); Assert.assertEquals("Hello, Kyle", sut.hello("Hello", "Kyle")); } }
Output:
2015-03-30 18:01:19.362 FINER [app.MyClass] (app.MyClass hello) ENTRY Hello Kyle 2015-03-30 18:01:19.425 FINEST [app.MyClass] (app.MyClass hello) finest: 2015-03-30T18:01:19.425 2015-03-30 18:01:19.427 FINER [app.MyClass] (app.MyClass hello) finer: 2015-03-30T18:01:19.427 2015-03-30 18:01:19.428 FINE [app.MyClass] (app.MyClass hello) fine: 2015-03-30T18:01:19.428 2015-03-30 18:01:19.429 INFO [app.MyClass] (app.MyClass hello) info: 2015-03-30T18:01:19.429 2015-03-30 18:01:19.431 WARNING [app.MyClass] (app.MyClass hello) warning: 2015-03-30T18:01:19.430 2015-03-30 18:01:19.433 SEVERE [app.MyClass] (app.MyClass hello) severe: 2015-03-30T18:01:19.433 2015-03-30 18:01:19.434 FINER [app.MyClass] (app.MyClass hello) THROW java.lang.Exception: test at app.MyClass.hello(MyClass.java:27) at app.MyClassTest.testHello(MyClassTest.java:10) ... 2015-03-30 18:01:19.439 FINEST [app.MyClass] (app.MyClass hello) arg=Kyle java.lang.Exception: test at app.MyClass.hello(MyClass.java:30) at app.MyClassTest.testHello(MyClassTest.java:10) ... 2015-03-30 18:01:19.442 FINEST [app.MyClass] (app.MyClass hello) arg=Kyle java.lang.Exception: test at app.MyClass.hello(MyClass.java:34) at app.MyClassTest.testHello(MyClassTest.java:10) ... 2015-03-30 18:01:19.447 FINER [app.MyClass] (app.MyClass hello) RETURN Hello, Kyle
Tags: logging
Modifying persistence.xml to execute drop-and-create dynamically
TweetPosted on Monday Mar 23, 2015 at 03:19PM in JPA
I need that for Arquillian testing so I created an utility method for that.
Tags: arquillian jpa
Enabling RequestDumpingHandler of Undertow
TweetPosted on Friday Mar 20, 2015 at 05:01PM in WildFly
Tested with WildFly 8.2.0.Final. Issue following command via jboss-cli and restart the server:
/subsystem=undertow/configuration=filter/custom-filter=request-dumper:add(class-name=io.undertow.server.handlers.RequestDumpingHandler, module=io.undertow.core) /subsystem=undertow/server=default-server/host=default-host/filter-ref=request-dumper:add
Following log will be dumped to the console:
----------------------------REQUEST--------------------------- URI=/batcheetest/jbatch/batchee/execution/start/myjob characterEncoding=null contentLength=95 contentType=[application/json] header=Accept=*/* header=Content-Type=application/json header=Content-Length=95 header=User-Agent=curl/7.30.0 header=Host=localhost:8080 locale=[] method=POST protocol=HTTP/1.1 queryString= remoteAddr=/127.0.0.1:57668 remoteHost=localhost scheme=http host=localhost:8080 serverPort=8080 --------------------------RESPONSE-------------------------- contentLength=-1 contentType=application/json header=Connection=keep-alive header=X-Powered-By=Undertow/1 header=Server=WildFly/8 header=Transfer-Encoding=chunked header=Content-Type=application/json header=Date=Fri, 20 Mar 2015 07:58:13 GMT status=200 ==============================================================
I’m disappointed that there is no dump of request body :(
Building environment specific artifacts with classifier
TweetPosted on Friday Mar 20, 2015 at 03:09PM in Maven
Consider following multi-module Maven project:
-
classifier
: The parent & aggregator project -
persistence
: A jar project which holds JPA entities and a persistence descriptor (persistence.xml
) -
web
: A war project which depends onpersistence
project
persistence
project contains following persistence descriptor:
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" 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"> <persistence-unit name="myPU"> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <properties> <property name="javax.persistence.schema-generation.database.action" value="${javax.persistence.schema-generation.database.action}"/> </properties> </persistence-unit> </persistence>
I need to set javax.persistence.schema-generation.database.action
property as drop-and-create
for development environment, but none
for production environment. in such case, using profiles and filtering might be a solution, but its shortcoming is that we can hold only one (among environment specific builds) artifact in the local repository because these builds has same coordinate. it may bring unexpected result such as deploying development build to the production environment by some accident. in such case, using classifier would be a better solution.
Preparation
First, let’s enable resource filtering in persistence
project.
<build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <plugins> <plugin> <artifactId>maven-resources-plugin</artifactId> <executions> <!-- Filter and copy resources under src/main/resources into target/classes (default location) --> <execution> <id>default-resources</id> <phase>process-resources</phase> <goals> <goal>resources</goal> </goals> <configuration> <filters> <filter>${basedir}/filters/dev.properties</filter> </filters> </configuration> </execution> </executions> </plugin> </plugins> </build>
Then put filters/dev.properties
:
javax.persistence.schema-generation.database.action=drop-and-create
Set a dependency in web
project:
<dependencies> <dependency> <groupId>org.nailedtothex.examples.classifier</groupId> <artifactId>persistence</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
Add configurations for producing artifacts for production
Now we can make artifacts that holds filtered persistence.xml
for development environment. next, let’s add configurations for producing artifacts for production environment with prod
classifier.
Put following profile definition into persistence
project to make the project to produce both of development and production (with prod
classifier) artifacts:
<profile> <id>prod</id> <properties> <filteredResources>target/filtered-classes</filteredResources> </properties> <build> <plugins> <plugin> <artifactId>maven-resources-plugin</artifactId> <executions> <!-- Filter and copy resources under src/main/resources into target/filtered-classes/prod --> <execution> <id>prod-resources</id> <phase>process-resources</phase> <goals> <goal>resources</goal> </goals> <configuration> <outputDirectory>${filteredResources}/prod</outputDirectory> <filters> <filter>${basedir}/filters/prod.properties</filter> </filters> </configuration> </execution> <!-- Copy classes under target/classes into target/filtered-classes/prod --> <!-- Existing files will not be overwritten. --> <!-- see http://maven.apache.org/plugins/maven-resources-plugin/resources-mojo.html#overwrite --> <execution> <id>copy-classes-prod</id> <phase>process-classes</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory>${filteredResources}/prod</outputDirectory> <resources> <resource> <directory>${project.build.outputDirectory}</directory> <filtering>false</filtering> </resource> </resources> </configuration> </execution> </executions> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <executions> <!-- Create the production jar with files inside target/filtered-classes/prod --> <execution> <id>prod-jar</id> <phase>package</phase> <goals> <goal>jar</goal> </goals> <configuration> <classifier>prod</classifier> <classesDirectory>${filteredResources}/prod</classesDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile>
Also put filters/prod.properties
:
javax.persistence.schema-generation.database.action=none
Issue following command:
$ mvn clean install -P prod
Result:
... [INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ persistence --- [INFO] Building jar: /Users/kyle/src/classifier/persistence/target/persistence-1.0-SNAPSHOT.jar [INFO] [INFO] --- maven-jar-plugin:2.6:jar (prod-jar) @ persistence --- [INFO] Building jar: /Users/kyle/src/classifier/persistence/target/persistence-1.0-SNAPSHOT-prod.jar [INFO] [INFO] --- maven-install-plugin:2.4:install (default-install) @ persistence --- [INFO] Installing /Users/kyle/src/classifier/persistence/target/persistence-1.0-SNAPSHOT.jar to /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/persistence/1.0-SNAPSHOT/persistence-1.0-SNAPSHOT.jar [INFO] Installing /Users/kyle/src/classifier/persistence/pom.xml to /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/persistence/1.0-SNAPSHOT/persistence-1.0-SNAPSHOT.pom [INFO] Installing /Users/kyle/src/classifier/persistence/target/persistence-1.0-SNAPSHOT-prod.jar to /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/persistence/1.0-SNAPSHOT/persistence-1.0-SNAPSHOT-prod.jar ...
You can see both of artifacts are installed as expected:
$ unzip -p /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/persistence/1.0-SNAPSHOT/persistence-1.0-SNAPSHOT.jar META-INF/persistence.xml <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" 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"> <persistence-unit name="myPU"> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <properties> <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/> </properties> </persistence-unit> </persistence> $ unzip -p /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/persistence/1.0-SNAPSHOT/persistence-1.0-SNAPSHOT-prod.jar META-INF/persistence.xml <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" 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"> <persistence-unit name="myPU"> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <properties> <property name="javax.persistence.schema-generation.database.action" value="none"/> </properties> </persistence-unit> </persistence>
So what is needed for web
project? put following profile as well:
<profile> <id>prod</id> <dependencies> <dependency> <groupId>org.nailedtothex.examples.classifier</groupId> <artifactId>persistence</artifactId> <version>1.0-SNAPSHOT</version> <classifier>prod</classifier> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-war-plugin</artifactId> <executions> <execution> <id>default-war</id> <phase>package</phase> <goals> <goal>war</goal> </goals> <configuration> <packagingExcludes>WEB-INF/lib/persistence-1.0-SNAPSHOT-prod.jar</packagingExcludes> </configuration> </execution> <execution> <id>prod-war</id> <phase>package</phase> <goals> <goal>war</goal> </goals> <configuration> <classifier>prod</classifier> <packagingExcludes>WEB-INF/lib/persistence-1.0-SNAPSHOT.jar</packagingExcludes> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile>
Then you will get both of artifacts installed after issuing mvn clean install -P prod
as follows:
$ unzip -l /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/web/1.0-SNAPSHOT/web-1.0-SNAPSHOT.war | grep persistence 3521 03-20-15 14:25 WEB-INF/lib/persistence-1.0-SNAPSHOT.jar $ unzip -l /Users/kyle/.m2/repository/org/nailedtothex/examples/classifier/web/1.0-SNAPSHOT/web-1.0-SNAPSHOT-prod.war | grep persistence 3513 03-20-15 14:25 WEB-INF/lib/persistence-1.0-SNAPSHOT-prod.jar
Arquillian EJB-JAR/EAR testing examples
TweetPosted on Friday Mar 20, 2015 at 10:33AM in Arquillian
There are plenty of examples of Arquillian testing with WAR deployments, but not for other deployments such as EJB-JAR or EAR. so I created some examples. these examples were tested against Arquillian 1.1.7.Final, using WildFly 8.2.0.Final as remote container. the entire project can be obtained from GitHub.
Testing against EJB-JAR deployment
Assume we have a simple EJB in a EJB-JAR project as follows:
@Stateless @LocalBean public class SomeEjb { public String hello(String name) { return "Hello, " + name; } }
Test class:
@RunWith(Arquillian.class) public class EjbJarIT { @Deployment public static Archive<?> createDeploymentPackage() { final Archive archive = ShrinkWrap.create(JavaArchive.class).addClass(SomeEjb.class); return archive; } @EJB private SomeEjb someEjb; @Test public void test() { Assert.assertEquals("Hello, Kyle", someEjb.hello("Kyle")); } }
The deployment will be a WAR through Arquillian’s automatic enrichment process while the method annotated as @Deployment
produced JavaArchive
.
Testing against EAR deployment
Assume we have a simple EAR project which depends on the preceding EJB-JAR project.
Test class:
@RunWith(Arquillian.class) public class EarIT { @Deployment public static Archive<?> createDeploymentPackage() throws IOException { final JavaArchive ejbJar = ShrinkWrap.create(JavaArchive.class, "ejb-jar.jar").addClass(SomeEjb.class); // Embedding war package which contains the test class is needed // So that Arquillian can invoke test class through its servlet test runner final WebArchive testWar = ShrinkWrap.create(WebArchive.class, "test.war").addClass(EarIT.class); final EnterpriseArchive ear = ShrinkWrap.create(EnterpriseArchive.class) .setApplicationXML("test-application.xml") .addAsModule(ejbJar) .addAsModule(testWar); return ear; } @EJB private SomeEjb someEjb; @Test public void test() { Assert.assertEquals("Hello, Kyle", someEjb.hello("Kyle")); } }
test-application.xml
which will be embed as application.xml
:
<application> <display-name>ear</display-name> <module> <ejb>ejb-jar.jar</ejb> </module> <module> <web> <web-uri>test.war</web-uri> <context-root>/test</context-root> </web> </module> </application>
Also I have an another example that uses the EAR which Maven has produced because creating EAR with ShrinkWrap would be annoying in some complex cases. the @Deployment
method will embed the test WAR into the EAR, and add a module
element into existing application.xml
before returning the archive to Arquillian runtime. the @Deployment
method would be something like this:
... @Deployment public static Archive<?> createDeploymentPackage() throws IOException { final String testWarName = "test.war"; final EnterpriseArchive ear = ShrinkWrap.createFromZipFile( EnterpriseArchive.class, new File("target/ear-1.0-SNAPSHOT.ear")); addTestWar(ear, EarFromZipFileIT.class, testWarName); ...
Tags: arquillian ear ejb