Good news: The first release of JUnitRules was accepted by OSS Sonatype.
The artifact coordinate for Gradle is
‘com.coders-kitchen:JUnitRules:1.0.1′
Good news: The first release of JUnitRules was accepted by OSS Sonatype.
The artifact coordinate for Gradle is
‘com.coders-kitchen:JUnitRules:1.0.1′
I recently promoted the first release of JUnitRules at OSS Sonatype.
This release will include
I hope that my first release is accepted.
Hi,
here is an short introduction of how to use slf4j’s logback.xml configuration file within an EAR.
Everybody who uses slf4j (or other Logging Frameworks) knows the following
When you deliver the configuration file of the logging framework within an WAR works everything as expected and without any problems. But if you try this with an EAR something magically happens, the logging framework can’t find the configuration file. And it uses it’s default behavior – System.out.println often by default -.
Searching this issue with Google shows up several possible solutions, for example exchanging the complete logging mechanism of your application server, or put the configuration file into lib/classes (Glassfish). If you dig a little bit deeper you may also find, that you should put the file into a dedicated jar-archive which is deployed as an ordinary library or that you must put the file into the EAR and extend the classpath, via MANIFEST.MF.
The problems of these possible solutions are, when
After doing a little bit research and try-and-error, I could develop this working solution:
As usual you’ll find a minimal example on github.
As a last step a little bit
The cause for this issue depends on the way an EAR-Container is handled by the application servers. The servers uses a kind of sandboxing for the classloaders that are available for an EAR. This means, that the way of providing any kind of resource is affected.
The initial classloader of an EAR-container provides only access to resources, that are available outside of the EAR. That means only standard libraries, libraries provided by the server and everything in the <domain|node>/lib-folder.
A direct impact of this way is, that classes that were provided via the EAR/lib directory have only access to resources outside of the EAR. And here is the reason, why SLF4J can’t find the configuration, when it is located in the EAR-container and the SLF4J libraries were put into the EAR/lib folder.
On the other hand, the modules of an EAR have only, direct, access to resources, that can be referenced from within the EAR.
Since yesterday evening Coders Kitchen has it’s own group id on OSS Sonatype.
The group id is com.coders-kitchen
The artifacts I will distribute first are
Cheers
Peter
Today I would like to show you how to integrate MosKito into your CDI Application.
I will show you the required dependencies and modifications to some files you need to do before you can use MosKito with CDI.
For this tutorial
is required.
The example application provides a RESTful webservice, which takes a document (as plain text) and calculates some fancy statistics for it.
Let’s start with the
The MosKito stuff can be retrieved from this repository url http://nexus.anotheria.net/nexus/content/repositories/releases/ .
For MosKito we need moskito-core and moskito-cdi at least in version 2.1.4.
The following build-script snippet includes everything we need at the moment.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
repositories { mavenCentral() maven { url 'http://nexus.anotheria.net/nexus/content/repositories/releases/' } } project.ext { moskitoGroup = 'net.anotheria' moskitoVersion = '2.1.4' } configurations.all*.exclude group: 'blowfish' dependencies { compile 'javax:javaee-web-api:6.0' compile "${moskitoGroup}:moskito-core:${moskitoVersion}", "${moskitoGroup}:moskito-cdi:${moskitoVersion}" } |
As you may have noticed I excluded everything from group blowfish. It’s required to do this, otherwise we had some unresolvable dependencies.
Now let us define the RESTful services and the stub for the underlying service. (Notice: I omitted the imports to have less code)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
package com.coderskitchen.macdi.webservice; import ... /** * This class is the entry point for the restful webservice */ @Path("/document") @ManagedBean public class DocumentStatisticService { @Context UriInfo uriInfo; @Inject DocumentStatisticBoundary dsb; @Inject ProcessBoundary pb; @PUT @Consumes({MediaType.APPLICATION_JSON}) public Response uploadAndQueueDocument(String jsonDocument) { String document = jsonDocument; String newProcessId = pb.createNewProcessId(); dsb.createAndPersistStatistic(newProcessId, document); UriBuilder baseUriBuilder = uriInfo.getBaseUriBuilder(); UriBuilder uriBuilder = baseUriBuilder.path(newProcessId); URI build = uriBuilder.build(); Response r = Response.created(build).build(); return r; } @GET @Path("{processId}/{word}") @Produces({MediaType.APPLICATION_JSON}) public Response getCountingForWord(@PathParam("processId") String processId, @PathParam("word") String word) { Integer countingForWord = dsb.getCountingForWord(processId, word); SingleCount c = new SingleCount(); c.setCount(countingForWord); c.setWord(word); Response r; r = Response.ok(c).build(); return r; } @GET @Path("{processId}") @Produces({MediaType.APPLICATION_JSON}) public Response getWordStatistics(@PathParam("processId") String processId) { WordStatistics statistics = dsb.getWordStatistics(processId); Response r; r = Response.ok(statistics).build(); return r; } } |
|
1 2 3 4 5 6 7 8 9 10 11 |
package com.coderskitchen.macdi.process; /** * This class creates new ProcessId */ public class ProcessBoundary { public String createNewProcessId() { long timeInMs = System.currentTimeMillis(); return "ID" + timeInMs; } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
package com.coderskitchen.macdi.statistic; import ... @Stateless public class DocumentStatisticBoundary { public Integer getCountingForWord(String processId, String word) { return Math.random()*10000; } public HashMap<String, Integer> processDocumentAndCreateStatistic(String document) { HashMap<String, Integer> statistics = new HashMap<String, Integer>(); statistics.put("A", 0); statistics.put("B", 1); return statistics; } @Asynchronous public void createAndPersistStatistic(String processId, String document) { // Implemementation is available on git hub } public WordStatistics getWordStatistics(final String processId) { HashMap<String, Integer> statistics = new HashMap<String, Integer>(); statistics.put("A", 0); statistics.put("B", 1); WordStatistics stat = new WordStatistics(); stat.setStatistics(statistics); stat.setProcessId(processId); stat.setDocument("document"); return stat; } } |
At least we need two data transfer objects, one for the complete statistics and one for a single line
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
package com.coderskitchen.macdi.entity; import javax.xml.bind.annotation.XmlRootElement; import java.util.HashMap; @XmlRootElement public class WordStatistics { private String processId; private HashMap<String, Integer> statistics = new HashMap<String, Integer>(); private String document; public String getDocument() { return document; } public void setDocument(String document) { this.document = document; } public String getProcessId() { return processId; } public void setProcessId(String processId) { this.processId = processId; } public HashMap<String, Integer> getStatistics() { return statistics; } public void setStatistics(HashMap<String, Integer> statistics) { this.statistics = statistics; } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package com.coderskitchen.macdi.entity; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class SingleCount { private String word; private Integer count; public String getWord() { return word; } public void setWord(final String word) { this.word = word; } public Integer getCount() { return count; } public void setCount(final Integer count) { this.count = count; } } |
Ok, that was the easy part. But now the big question is, how to enable MosKito for the application. Because MosKito uses interceptors for a CDI-based application we need to activate them via the
The activation can be done very easily by adding the required interceptors to the beans.xml file in the interceptors section.
MosKito provides basically these interceptors:
Combining the things together will end in this extend beans.xml
|
1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0" encoding="UTF-8"?> <beans 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/beans_1_0.xsd"> <interceptors> <class>net.anotheria.moskito.integration.cdi.CallInterceptor</class> <class>net.anotheria.moskito.integration.cdi.CountInterceptor</class> <class>net.anotheria.moskito.integration.cdi.DaoCallInterceptor</class> <class>net.anotheria.moskito.integration.cdi.ServiceCallInterceptor</class> <class>net.anotheria.moskito.integration.cdi.WebCallInterceptor</class> </interceptors> </beans> |
Ok, now let’s put the things together.
But, what would we like to monitor? ‘uploadAndQueueDocument’ seems to be a good candidate for runtime monitoring the basic stuff like counting the calls and process time per request, whereas ‘getCountingForWord’ and ‘getWordStatistics’ looks like good candidates for counting.
Both types of monitoring can be enabled by using the specific annotations
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package com.coderskitchen.macdi.webservice; import ... /** * This class is the entry point for the restful webservice */ @Path("/document") @ManagedBean public class DocumentStatisticService { ... @PUT @Consumes({MediaType.APPLICATION_JSON}) @Monitor(MonitoringCategorySelector.WEB) public Response uploadAndQueueDocument(String jsonDocument) { ... } @GET @Path("{processId}/{word}") @Produces({MediaType.APPLICATION_JSON}) @Count() public Response getCountingForWord(@PathParam("processId") String processId, @PathParam("word") String word) { ... } @GET @Path("{processId}") @Produces({MediaType.APPLICATION_JSON}) @Count public Response getWordStatistics(@PathParam("processId") String processId) { ... } } |
As you may have noticed I used MonitoringCategorySelector.WEB for @Monitor, because it’s the monitoring from a webservice.
And that’s it. The configuration and other things could be found here.
The complete example is avaiable on github.
Hi,
because it’s relative hard to figure out, how to do it, I would like to show you how to combine JAX-RS and CDI.
You need
Let’s start with a minimal application “The Greeter”.
If you call “<your_host>/GreetMeWithJaxRSAndCDI/rest/greet/<NAME>” it should return “Hi, <NAME>. Today it’s the <DATE>”.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.coderskitchen.thegreeter.rest; @Path("/greet") public class Greeter { @Inject Greeting greeting; @GET @Path("{name}") public String greetSomeone(@PathParam("name")) String name) { return greeting.greetSomeone(name); } } |
That was easy. (I skipped the imports to increase the visibility) Now the Greeting class.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.coderskitchen.thegreeter.greetings; @Stateless public class Greeting { public String greetSomeone(String name) { StringBuilder builder = new StringBuilder(); builder = builder.append("Hi, "); builder = builder.append(name); builder = builder.append("\n"); builder = builder.append("Today it's the "); SimpleDateFormat sdf = new SimpleDateFormat("dd'/'MM'/'YYYY"); builder = builder.append(sdf.format(new Date())); return builder.toString(); } } |
Now let’s add an empty beans.xml and a web.xml with this content
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<web-app version="3.0" 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/web-app_3_0.xsd"> <context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> <param-value>Development</param-value> </context-param> <servlet> <servlet-name>Jersey Web Application</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> </servlet> <servlet-mapping> <servlet-name>Jersey Web Application</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping> </web-app> |
The important thing here is that you need to specify an servlet class which provides an entry point to an implementation of the JAX-RS api. Here I chose Jersey.
Before we can build this “beast” we need to add some dependencies.
|
1 2 |
providedCompile 'com.sun.jersey:jersey-server:1.1.5.1' providedcompile 'javax:javaee-web-api:6.0' |
So the complete build.gradle will look like this
|
1 2 3 4 5 6 7 8 9 10 |
apply plugin: 'war' repositories { mavenCentral() } dependencies { providedCompile 'com.sun.jersey:jersey-server:1.1.5.1' providedCompile 'javax:javaee-web-api:6.0' } |
Ok, let’s build, deploy and call it. If you use a standard glassfish installation, then this link should work http://localhost:8080/GreetMeWithJaxRSAndCDI/rest/greet/peter.
Hmm, doens’t seem to work, or? What’s the problem behind?
The problem here is, that CDI isn’t in place to instantiate the dependency.
Their are two solutions for this problem:
In this example I use the first approach. This means we need to add @ManagedBean to our service.
Ok, let’s do a retry. And violá it works.
Lessons learned
To use JAX-RS and CDI together you need to
I hope this will help you a little bit.
As usual you’ll find the project on http://github.com/coders-kitchen/GreetMeWithJaxRSAndCDI.
During the last days I started to enable a build-time setup for one of my Spring projects.
One thing I set at build time was the spring.profiles.default context parameter. For replacing wildcards during build time I added the expand() closure to the war-task of my Gradle build script.
This works fine til I wanted to add some access to the webservice the tool provided using jQuery. After adding the jQuery library the build fails with the following message
|
1 |
Could not copy file '/home/peter/Development/.../core/src/main/webapp/js/jQuery.js' to '/home/peter/Development/.../core/build/webapp/js/jQuery.js' |
When starting the build with –stacktrace again, I figured out, that the problem was, that the underlying Groovy implementation expects that, when expand is used after a ‘$’ sign a character or a ‘{‘ follows. jQuery has many ‘$’ signs, so a workaround is required.
Due to the fact, that expand() works in Gradle on every file on a Copy tasks, for example, and didn’t support single file expansion, I started to investigate how to workaround this issue.
I added two new tasks, one for copying the files, which needs to be replaced and the other for the javascript libraries.
|
1 2 3 4 5 6 7 |
task expandForWebApp(type: Copy) { dependsOn copyJsToWebApp from "$projectDir/src/main/webapp" into "$buildDir/webapp" exclude '**/*.js' expand(environment: curEnv) } |
|
1 2 3 4 5 |
task copyJsToWebApp(type: Copy) { from "$projectDir/src/main/webapp" into "$buildDir/webapp" include '**/*.js' } |
The last things I need to do were
Summarized I did the following changes:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
def curEnv = 'test' task productiveEnvironment { curEnv = 'productive' } task copyJsToWebApp(type: Copy) { from "$projectDir/src/main/webapp" into "$buildDir/webapp" include '**/*.js' } task expandForWebApp(type: Copy) { dependsOn copyJsToWebApp from "$projectDir/src/main/webapp" into "$buildDir/webapp" exclude '**/*.js' expand(environment: curEnv) } jettyRun { dependsOn expandForWebApp webAppSourceDirectory = file("$buildDir/webapp") } webAppDirName =file("$buildDir/webapp") war.dependsOn expandForWebApp |
Even if I think, that the missing possibility for expanding a single file directly is a little drawback withGradle, I’m very happy that Gradle provides me the possibilities to express the things I would like to do directly in the build script.
In Multi-Modul-Projects you have several options to define the directory structure.
One variant, we also use for our projects, is the following:
|
1 2 3 4 5 6 7 |
Project build.gradle > modules > module_1 > module_2 ... > module_n |
The simplest and fastest solution with Gradle is to define the following includes in the settings settings.gradle file:
|
1 2 3 4 |
include 'modules:module_1' include 'modules:module_2' ... include 'modules:module_n' |
This solution has, at least, to drawbacks. One is that you need to define inter-module dependencies in that way project(‘modules:module_k’). The other one is, that you need to use a prefix like this modules:module_k if you would like to start a specific task of one of the submodules.
With Gradle you can change the path for you subprojects:
|
1 2 3 4 5 6 7 |
def String[] modules = ['module_1', 'module_2', ..., 'module_n'] include modules modules.each { name -> def p = findProject(":${name}") p.projectDir = new File(settingsDir, "modules/${name}") } |
This has the advantage, that you can directly specify the module for tasks and depdencies.
Info: Projectpaths can only be changed in the settings.gradle file, in the usual build script this property is write only.
During the last weeks a colleague of mine came to my desk and ask for a solution in JEE6 for the following
The implementation that should be used for injection via @Inject must be determined during runtime by call parameters of the target bean.
JEE didn’t have, or to be precise I didn’t know a solution for that, a direct solution for that kind of problem.
So my colleague tried this
He create the required object by his own via new and used setters to manually inject the dependencies, like database connection, … .
However, he quickly realized, that this approach is only sufficient if you have very similar variants of objects (meaning, same dependencies and so on). So if you have an new implementation, which requires a totally different set of dependencies the trouble began.
Another problem with this approach is, that you create / destroy the objects at the JEE container past. So the container isn’t aware of it and you can’t additionally use the lifecycle methods like PostConstruct or PreDestroy.
A possible
is to use the class Instance with the qualifier @Any. Instance provides a generic container for any implementation of an interface which matches the interfaces and the provided qualifier (here @Any). Additionally this class provides for every available instance the methods isAmbiguous and isUnsatisfied to check if the dependencies are satisfied.
This object can now used to create a factory method which returns the requested object. The cool fact is, that this object is completely JEE aware.
Last but not least a simple
This example is around a simple tax calculator.
This calculator calculates the tax based upon a previous selection of the user. The available variants are “no taxes”, “fixed”, “progressive” and “unbelievable”).
I will demonstrate here only the main components. The complete project is available on GitHub.
The core component is this factory:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public TaxCalculationFactory { @Inject @Any Instance<TaxCalculation> taxCalculations; public TaxCalculation getTaxCalculationForName(String humanName) { TaxCalculation selectedTaxCalculation = null; Instance<TaxCalculation> selectedTaxCalculations = taxCalculations.select(new TaxCalculationNameLiteral(humanName)); for(TaxCalculation cal : selectedTaxCalculations) { selectedTaxCalculation = cal; } return selectedTaxCalculation; } @Produces public List<String> getAvailableTaxCalculationNames() { List<String> names = new ArrayList<String>(); for (TaxCalculation taxCalculation : taxCalculations) { String name = taxCalculation.getClass().getAnnotation(TaxCalculationName.class).value(); names.add(name); } return names; } } |
As you see here all implemented TaxCalculations are injected via the Instance object. The requested TaxCalculation is determined in the method getTaxCalculationForName via an AnnotationLiteral (see next code example).
The AnnotationLiteral is required to use the select method of Instance. This method returns all instances of the available implementations which matches the conditions:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class TaxCalculationNameLiteral extends AnnotationLiteral<TaxCalculationName> implements TaxCalculationName { final String expectedName; TaxCalculationNameLiteral(String expectedName) { this.expectedName = expectedName; } @Override public String value() { return expectedName; } } |
The qualifier can be realized like this
|
1 2 3 4 |
@Qualifier public @interface TaxCalculationName { String value(); } |
I hope this little example shows how JEE can be used to easy realize such a mechanism. This approach has also the benefit, that you can easily add new implementations of the TaxCalculation without changing the implementation.
The problem of my colleague had thus be solved in a very elegant matter.
In opposite to my approach, my colleague uses information from the database to determine the dynamic dependency.
I hope that I had shown you that JEE can also be easily used in a dynamic environment. Only a little bit knowledge and “creativity” is required.
During the implementation of some integrationstests for a Spring application I faced the problem, that the tests couldn’t be executed due to the following error:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Caused by: java.lang.NoSuchFieldError: TRACE at org.jboss.logging.Log4jLogger.translate(Log4jLogger.java:64) at org.jboss.logging.Log4jLogger.doLog(Log4jLogger.java:44) at org.jboss.logging.Logger.trace(Logger.java:128) at org.hibernate.internal.CoreMessageLogger_$logger.trace(CoreMessageLogger_$logger.java:417) at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1311) at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1737) at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:94) at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:905) at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:890) at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:74) at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:268) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:310) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452) |
After some investigation I figured out, that Hibernate has a non transitive dependency to Log4J log4j >= 1.2.12 – I use slf4j / logback for logging -.
After I added this dependency 'org.slf4j:slf4j-log4j12:1.6.1' to my testCompile everything works fine.