Manage application config with Commons Configuration
TweetPosted on Wednesday Feb 26, 2014 at 01:53PM in Technology
Environment
- Commons Configuration 1.10
- WildFly 8.0.0.Final
- Oracle JDK7u51
Why go this way?
Various portable ways are available for manage configurations of the Java EE application. such as:
- Configuration table in DB
- I guess it's not bad, but I feel that is bit tedious work to coding.
- There's a way to use this mechanism via Commons Configuration, but I haven't tried it yet.
- Custom JNDI resources
- I thought that it's most efficient at first, but negative points here:
- Badness of lookup performance.
- Editing JNDI resources is very annoying.
- I thought that it's most efficient at first, but negative points here:
So sometimes I'm thinking of any better way, and I found Commons Configurations[1]. notable capabilities of Commons Configuration are:
- Simple API
- Auto-reloading and saving mechanism provided
- Support of various formats and placement styles
So I'd try it.
Prepare sample project and related resources
Create a system property
- Create a system property which specifies the path of configuration file.
- I'm using WildFly so go with jboss-cli:
[standalone@localhost:9990 /] /system-property=app.config:add(value=/Users/kyle/app.properties)
{"outcome" => "success"}
[standalone@localhost:9990 /]
Create the properties file
- Location is same as “app.config” system property.
hoge=fuge
pom.xml
<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>org.nailedtothex</groupId>
<artifactId>config</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
</dependencies>
</project>
Create an application-scoped bean
package org.nailedtothex.config;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
@Named
@ApplicationScoped
public class AppConfigBean {
private Configuration config;
@PostConstruct
void init() throws ConfigurationException {
String path = System.getProperty("app.config");
PropertiesConfiguration propConfig = new PropertiesConfiguration(path);
propConfig.setReloadingStrategy(new FileChangedReloadingStrategy());
config = propConfig;
}
public Configuration getConfig(){
return config;
}
}
- If you want to read the config at startup of application, I recommend that use of @javax.ejb.Singleton and @javax.ejb.Startup[2].
Create a servlet
- Get a configuration variable from singleton bean.
package org.nailedtothex.config;
import java.io.IOException;
import java.io.PrintWriter;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = "/*")
public class HelloServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Inject
private AppConfigBean bean;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try (PrintWriter pw = response.getWriter()) {
pw.write(bean.getConfig().getString("hoge"));
}
}
}
Access via browser

- It needs only very small and clean code.
Check runtime reloading
Edit app.properties:
kyle-no-MacBook:~ kyle$ echo hoge=FUGE > app.properties kyle-no-MacBook:~ kyle$
Reload browser:

- I just reloaded the browser and I didn't do any other operations, but config was updated immediately.
Collaboration with CDI
- There's a way to inject properties to CDI managed bean at [2]. that must be useful.
XMLConfiguration
- XMLConfiguration is good for hierarchical configuration.
- It depended on Commons Collection so don't forget to add the dependency.
pom.xml
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
XmlTest.java
package org.nailedtothex.config;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.configuration.XMLConfiguration;
public class XmlTest {
public static void main(String[] args) throws Exception {
XMLConfiguration config = new XMLConfiguration();
config.load(XmlTest.class.getResourceAsStream("/config.xml"));
String backColor = config.getString("colors.background");
String textColor = config.getString("colors.text");
String linkNormal = config.getString("colors.link[@normal]");
String defColor = config.getString("colors.default");
int rowsPerPage = config.getInt("rowsPerPage");
List<Object> buttons = config.getList("buttons.name");
System.out.println(backColor);
System.out.println(textColor);
System.out.println(linkNormal);
System.out.println(defColor);
System.out.println(rowsPerPage);
System.out.println(buttons);
System.out.println();
Object o;
Iterator it = config.getKeys("colors.");
while(it.hasNext()){
System.out.println(it.next());
}
}
}
config.xml
<?xml version="1.0" encoding="UTF-8"?>
<gui-definition>
<colors>
<background>#808080</background>
<text>#000000</text>
<header>#008000</header>
<link normal="#000080" visited="#800080"/>
<default>${colors.header}</default>
</colors>
<rowsPerPage>15</rowsPerPage>
<buttons>
<name>OK,Cancel,Help</name>
</buttons>
<numberFormat pattern="###\,###.##"/>
</gui-definition>
Log
#808080 #000000 #000080 #008000 15 [OK, Cancel, Help] colors.background colors.text colors.header colors.link[@normal] colors.link[@visited] colors.default
References
Tags: javaee
Mocking a unstable HTTP server with WireMock
TweetPosted on Wednesday Feb 26, 2014 at 10:12AM in Technology
Environment
- WireMock 1.4.3
- HttpClient 4.3.2
- Oracle JDK7u51
Why need it?
- Sometimes website scraping stops at unexpected error like socket timeout or internal server error.
- We prefer to retry several times at such occasion.
- So we have to develop some retrying mechanism, and we have to create a test and create the mock of server.
- So in this article, I'm going to try to create the mock with WireMock.
Requirements for client
- Retry if processing fails, for 3 times.
- Retry when timeout occurred.
- SocketTimeoutException
- Retry when server returned status code 5xx
Test cases
- Server returns code 200 without any problems.
- Server returns code 500 at first, then client will retry, server returns code 200.
- Server returns code 500 forever, then client will retry 3 times and give up.
- Server delays the response, then client will regard as timeout and retry, then server returns code 200.
- Server delays the response forever, then client will retry 3 times and give up.
Resources
pom.xml
<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>org.nailedtothex</groupId>
<artifactId>wiremock</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>1.43</version>
<classifier>standalone</classifier>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>fluent-hc</artifactId>
<version>4.3.2</version>
</dependency>
</dependencies>
</project>
RetryableHttpFetcher.java
package org.nailedtothex.wiremock;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ServiceUnavailableRetryStrategy;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
public class RetryableHttpFetcher {
private static final Logger log = Logger.getLogger(RetryableHttpFetcher.class.getName());
// these parameters would be better to retrieve through JNDI or any other mechanism
private int MAX_RETRIES = 3;
private int RETRY_INTERVAL = 1000;
private int TIMEOUT = 500;
private final ServiceUnavailableRetryStrategy MY_SERVICE_UNAVAILABLE_RETRY_STRATEGY = new ServiceUnavailableRetryStrategy() {
@Override
public boolean retryRequest(HttpResponse response, int executionCount,
org.apache.http.protocol.HttpContext context) {
boolean rc = response.getStatusLine().getStatusCode() >= 500 && executionCount <= MAX_RETRIES;
log.log(Level.INFO,
"retryRequest(): returning={0}, statusCode={1}, executionCount={2}, maxRetries={3}, interval={4}",
new Object[] { rc, response.getStatusLine().getStatusCode(), executionCount, MAX_RETRIES,
RETRY_INTERVAL });
return rc;
}
@Override
public long getRetryInterval() {
return RETRY_INTERVAL;
}
};
private final HttpRequestRetryHandler MY_HTTP_REQUEST_RETRY_HANDLER = new HttpRequestRetryHandler() {
@Override
public boolean retryRequest(IOException e, int executionCount, HttpContext context) {
log.log(Level.INFO, "retryRequest(): exception={0}, executionCount={1}, maxRetries={2}",
new Object[] { e.getClass(), executionCount, MAX_RETRIES });
if (executionCount > MAX_RETRIES) {
log.log(Level.INFO, "give up: {0}", executionCount);
return false;
}
if (e instanceof java.net.SocketTimeoutException) {
log.log(Level.INFO, "retry: {0}", e.getMessage());
return true;
}
log.log(Level.INFO, "not retry: {0}", e.getMessage());
return false;
}
};
private final RequestConfig MY_REQUEST_CONFIG = RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build();
public String fetchAsString(String url) throws ClientProtocolException, IOException {
try (CloseableHttpClient client = HttpClientBuilder.create()
.setDefaultRequestConfig(MY_REQUEST_CONFIG)
.setRetryHandler(MY_HTTP_REQUEST_RETRY_HANDLER)
.setServiceUnavailableRetryStrategy(MY_SERVICE_UNAVAILABLE_RETRY_STRATEGY)
.build()) {
try (CloseableHttpResponse res = client.execute(new HttpGet(url))) {
if (res.getStatusLine().getStatusCode() >= 400) {
throw new HttpResponseException(res.getStatusLine().getStatusCode(), res.getStatusLine()
.getReasonPhrase());
}
return EntityUtils.toString(res.getEntity());
}
}
}
}
RetryableHttpFetcherTest.java
package org.nailedtothex.wiremock;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.net.SocketTimeoutException;
import org.apache.http.client.HttpResponseException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.github.tomakehurst.wiremock.stubbing.Scenario;
public class RetryableHttpFetcherTest {
@Rule
public WireMockRule wireMockRule = new WireMockRule(18089);
private RetryableHttpFetcher instance;
@Before
public void init() {
instance = new RetryableHttpFetcher();
}
@Test
public void test1_ok() throws Exception {
stubFor(get(urlEqualTo("/hoge.txt")).willReturn(
aResponse().withStatus(200).withHeader("Content-Type", "text/plain").withBody("OK")));
String expected = "OK";
String actual = instance.fetchAsString("http://localhost:18089/hoge.txt");
assertThat(actual, is(expected));
}
@Test
public void test2_retryAt500() throws Exception {
stubFor(get(urlEqualTo("/500")).inScenario("retry at 500")
.whenScenarioStateIs(Scenario.STARTED)
.willSetStateTo("one time requested")
.willReturn(aResponse().withBody("error").withStatus(500)));
stubFor(get(urlEqualTo("/500")).inScenario("retry at 500")
.whenScenarioStateIs("one time requested")
.willReturn(aResponse().withBody("OK").withStatus(200)));
String actual = instance.fetchAsString("http://localhost:18089/500");
assertThat(actual, is("OK"));
}
@Test(expected = HttpResponseException.class)
public void test3_retryAt500GiveUp() throws Exception {
stubFor(get(urlEqualTo("/500"))
.willReturn(aResponse().withBody("500").withStatus(500)));
instance.fetchAsString("http://localhost:18089/500");
}
@Test
public void test4_retryAtTimeout() throws Exception {
stubFor(get(urlEqualTo("/timeout")).inScenario("retrying")
.whenScenarioStateIs(Scenario.STARTED)
.willSetStateTo("one time requested")
.willReturn(aResponse().withBody("error").withStatus(500).withFixedDelay(3000)));
stubFor(get(urlEqualTo("/timeout")).inScenario("retrying")
.whenScenarioStateIs("one time requested")
.willReturn(aResponse().withBody("OK").withStatus(200)));
String actual = instance.fetchAsString("http://localhost:18089/timeout");
assertThat(actual, is("OK"));
}
@Test(expected = SocketTimeoutException.class)
public void test5_retryAtTimeoutGiveUp() throws Exception {
stubFor(get(urlEqualTo("/timeout"))
.willReturn(aResponse().withBody("timeout").withStatus(500).withFixedDelay(Integer.MAX_VALUE)));
instance.fetchAsString("http://localhost:18089/timeout");
}
@Test(expected = HttpResponseException.class)
public void notFound() throws Exception {
instance.fetchAsString("http://localhost:18089/NOT_FOUND");
}
}
Test logs
- All tests were passed.
test1_ok
2 26, 2014 11:34:22 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$1 retryRequest 情報: retryRequest(): returning=false, statusCode=200, executionCount=1, maxRetries=3, interval=1,000
test2_retryAt500
2 26, 2014 11:35:32 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$1 retryRequest 情報: retryRequest(): returning=true, statusCode=500, executionCount=1, maxRetries=3, interval=1,000 2 26, 2014 11:35:33 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$1 retryRequest 情報: retryRequest(): returning=false, statusCode=200, executionCount=2, maxRetries=3, interval=1,000
test3_retryAt500GiveUp
2 26, 2014 11:35:51 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$1 retryRequest 情報: retryRequest(): returning=true, statusCode=500, executionCount=1, maxRetries=3, interval=1,000 2 26, 2014 11:35:52 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$1 retryRequest 情報: retryRequest(): returning=true, statusCode=500, executionCount=2, maxRetries=3, interval=1,000 2 26, 2014 11:35:53 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$1 retryRequest 情報: retryRequest(): returning=true, statusCode=500, executionCount=3, maxRetries=3, interval=1,000 2 26, 2014 11:35:54 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$1 retryRequest 情報: retryRequest(): returning=false, statusCode=500, executionCount=4, maxRetries=3, interval=1,000
test4_retryAtTimeout
2 26, 2014 11:36:12 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$2 retryRequest 情報: retryRequest(): exception=class java.net.SocketTimeoutException, executionCount=1, maxRetries=3 2 26, 2014 11:36:12 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$2 retryRequest 情報: retry: Read timed out 2 26, 2014 11:36:12 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$1 retryRequest 情報: retryRequest(): returning=false, statusCode=200, executionCount=1, maxRetries=3, interval=1,000
test5_retryAtTimeoutGiveUp
2 26, 2014 11:36:39 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$2 retryRequest 情報: retryRequest(): exception=class java.net.SocketTimeoutException, executionCount=1, maxRetries=3 2 26, 2014 11:36:39 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$2 retryRequest 情報: retry: Read timed out 2 26, 2014 11:36:40 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$2 retryRequest 情報: retryRequest(): exception=class java.net.SocketTimeoutException, executionCount=2, maxRetries=3 2 26, 2014 11:36:40 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$2 retryRequest 情報: retry: Read timed out 2 26, 2014 11:36:40 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$2 retryRequest 情報: retryRequest(): exception=class java.net.SocketTimeoutException, executionCount=3, maxRetries=3 2 26, 2014 11:36:40 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$2 retryRequest 情報: retry: Read timed out 2 26, 2014 11:36:41 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$2 retryRequest 情報: retryRequest(): exception=class java.net.SocketTimeoutException, executionCount=4, maxRetries=3 2 26, 2014 11:36:41 午前 org.nailedtothex.wiremock.RetryableHttpFetcher$2 retryRequest 情報: give up: 4
References
Tags: test
Job-wide artifact injection with CDI Producer
TweetPosted on Tuesday Feb 25, 2014 at 05:37PM in Technology
Environment
- jBeret 1.0.1Beta-SNAPSHOT
- WildFly 8.0.0.Final
Why need it?
- Logger injection through CDI is easy and useful, but for jBatch programming of some occasions, I guess that Job-wide Logger is better than typical class-wide logger. thus, I will try it this time.
- With CDI, we can reduce some annoying code, even related to Job Properties so I also will try to inject a job-level property to a Batchlet through Producer.
Sample project
- jBatch resources
- CDI resources
- Test class
How does it work?
- 2 Injections are declared in InjectBatchlet.
- Both of them will be produced by JobWideArtifactProducer.
- JobWideArtifactProducer creates:
- a logger. the name contains job name.
- a Date. it came from job-level property named “baseDate”.
Log
18:14:51,064 INFO [job.jobwideproducer] (batch-batch - 3) process(): baseDate=14/02/25 0:00
Remarks
- Injection of variables such as working directory of the job may be useful too.
References
Tags: jbatch
Static EJB Timer example
TweetPosted on Tuesday Feb 25, 2014 at 04:08PM in Technology
Environment
- WildFly8.0.0.Final
- Oracle JDK7u51
Resources
pom.xml
<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>org.nailedtothex</groupId>
<artifactId>ejbtimer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>ejb</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
TimerService.java
package org.nailedtothex.ejbtimer;
import javax.ejb.Schedule;
import javax.ejb.Singleton;
@Singleton
public class TimerService {
@Schedule(second = "*/1", minute = "*", hour = "*", persistent = false)
public void periodic() {
System.out.println("period");
}
@Schedule(dayOfMonth = "25", hour = "16", minute = "10", persistent = false)
public void schedule() {
System.out.println("schedule");
}
}
- Almost all of the code was taken from [1].
- There are some additional useful informations and conversations so I recommend to visit [1].
- According to [2], “persistent” attribute means whether timers that missed should fire or not during downtime of the application server.
- Default is true.
- I don't want many missed timers to fire at once when I launch the application server which stopped unexpectedly so I just set it false.
- Detail of specification of @Schedule here: [4]
Log
16:09:44,002 INFO [stdout] (EJB default - 8) period 16:09:45,001 INFO [stdout] (EJB default - 9) period 16:09:46,002 INFO [stdout] (EJB default - 10) period 16:09:47,002 INFO [stdout] (EJB default - 1) period 16:09:48,002 INFO [stdout] (EJB default - 2) period 16:09:49,002 INFO [stdout] (EJB default - 4) period 16:09:50,002 INFO [stdout] (EJB default - 3) period 16:09:51,002 INFO [stdout] (EJB default - 5) period 16:09:52,002 INFO [stdout] (EJB default - 6) period 16:09:53,002 INFO [stdout] (EJB default - 7) period 16:09:54,002 INFO [stdout] (EJB default - 8) period 16:09:55,002 INFO [stdout] (EJB default - 9) period 16:09:56,001 INFO [stdout] (EJB default - 10) period 16:09:57,001 INFO [stdout] (EJB default - 1) period 16:09:58,001 INFO [stdout] (EJB default - 2) period 16:09:59,002 INFO [stdout] (EJB default - 4) period 16:10:00,002 INFO [stdout] (EJB default - 5) period 16:10:00,003 INFO [stdout] (EJB default - 3) schedule 16:10:01,001 INFO [stdout] (EJB default - 6) period 16:10:02,002 INFO [stdout] (EJB default - 7) period 16:10:03,001 INFO [stdout] (EJB default - 8) period 16:10:04,001 INFO [stdout] (EJB default - 9) period 16:10:05,001 INFO [stdout] (EJB default - 10) period 16:10:06,002 INFO [stdout] (EJB default - 1) period 16:10:07,003 INFO [stdout] (EJB default - 2) period
References
Tags: ejb
Memo of useful commands
TweetPosted on Monday Feb 24, 2014 at 10:53AM in Technology
Environment
- Apache Maven 3.1.1
Common goals
clean
- Delete everything at target directory
compile
- Compile sources to class files
test
- Run JUnit test classes
package
- Make the JAR file or WAR file
install
- It does “package” goal and install the package to local repository so that other Maven project can reference it.
Execute Main class
mvn exec:java -Dexec.mainClass=org.sonatype.mavenbook.weather.Main -Dexec.args="70112"
Show dependencies
Command
mvn dependency:resolve
What will we got
[INFO] The following files have been resolved: [INFO] com.ibm.icu:icu4j:jar:2.6.1:compile [INFO] xml-apis:xml-apis:jar:1.0.b2:compile [INFO] xerces:xmlParserAPIs:jar:2.6.2:compile [INFO] oro:oro:jar:2.0.8:compile [INFO] log4j:log4j:jar:1.2.14:compile [INFO] velocity:velocity:jar:1.5:compile [INFO] dom4j:dom4j:jar:1.6.1:compile [INFO] commons-lang:commons-lang:jar:2.1:compile [INFO] xerces:xercesImpl:jar:2.6.2:compile [INFO] commons-collections:commons-collections:jar:3.1:compile [INFO] junit:junit:jar:3.8.1:test [INFO] jdom:jdom:jar:1.0:compile [INFO] xalan:xalan:jar:2.6.0:compile [INFO] jaxen:jaxen:jar:1.1.1:compile [INFO] commons-io:commons-io:jar:1.3.2:test [INFO] xom:xom:jar:1.0:compile
Show dependencies in tree style
Command
mvn dependency:tree
What will we got
[INFO] org.sonatype.mavenbook.custom:simple-weather:jar:0.8-SNAPSHOT [INFO] +- log4j:log4j:jar:1.2.14:compile [INFO] +- dom4j:dom4j:jar:1.6.1:compile [INFO] | \- xml-apis:xml-apis:jar:1.0.b2:compile [INFO] +- jaxen:jaxen:jar:1.1.1:compile [INFO] | +- jdom:jdom:jar:1.0:compile [INFO] | +- xerces:xercesImpl:jar:2.6.2:compile [INFO] | \- xom:xom:jar:1.0:compile [INFO] | +- xerces:xmlParserAPIs:jar:2.6.2:compile [INFO] | +- xalan:xalan:jar:2.6.0:compile [INFO] | \- com.ibm.icu:icu4j:jar:2.6.1:compile [INFO] +- velocity:velocity:jar:1.5:compile [INFO] | +- commons-collections:commons-collections:jar:3.1:compile [INFO] | +- commons-lang:commons-lang:jar:2.1:compile [INFO] | \- oro:oro:jar:2.0.8:compile [INFO] +- commons-io:commons-io:jar:1.3.2:test [INFO] \- junit:junit:jar:3.8.1:test
Verbose Output
Command
mvn install -X
What will we got
- It outputs tons of verbose information, such as why each jars are included or excluded.
How to skipping unit tests
mvn install -Dmaven.test.skip=true
Show description of a plugin
mvn help:describe -Dplugin=exec -Dfull
Create a jar with dependencies
mvn assembly:attached
- This might need additional configuration of the plugin in the pom.xml
Run Jetty and deploy the WAR
mvn jetty:run
- This might be useful for some proto-typing of servlet or jsp.
- This might need additional configuration of the plugin in the pom.xml
Project Hierarchy
- Every Maven projects can have children or its parent.
- It's useful for declare common dependencies (using dependencyManagement) or plugins (using pluginManagement).
- Child pom.xml can omit “version” element in dependencies that are declared in dependencyManagement of the parent pom.xml
Analyze “used undeclared” or “unused declared” dependencies
mvn dependency:analyze
Tags: maven
