Friday, August 24, 2007

Log4J - Extending the Appender classes to wrap messages

Here's the problem: I want my Log4J output (hell, any logging output, Log4J or otherwise)to be easy to look at. I don't want the format to be "busy". I want things to line up so that the information I'm looking for is easily spotted. I don't think I'm alone in this idea and to drive this point home, it's partially the reason why Log Factor 5 and Chainsaw exists, in my opinion.

To illustrate, this is what I'm talking about as motivation to get order into log files.


I don't know about you, but looking at that is not my idea of a good time, especially if it happens to be 4:00AM. All I'm asking for is a little order in my chaos.

Formatting the message body
I'm not going to go into the detail surrounding Log4J message formatting. You can get that information from here.
What I am referring to is how to use the formatting instructions presented at the above link to help accomplish a more formatted message body.

*NOTE*
The example log-files presented below will not be properly formatted if you are using Internet Explorer. However, when using Firefox the formatting is presented properly. (You're missing out if you're using I.E.)

Limiting the message body to a certain length so as to maintain a degree of uniformity serves to help the ol' Mark-IV eyeball make sense out of the log entries. To illustrate, shown below is the log-file layout that tries to maintain a certain place for each logging element and where the message body is limited to say- - - 80-bytes.


.... ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7.... ....8
2007-08-24 13:39:06 [DEBUG] Implementation title : log4j at line [ 62] of class [Log4JTest.run()] [Logger:log4j.test.Log4JTest]
2007-08-24 13:39:06 [DEBUG] Implementation vendor : "Apache Software Foundation" at line [ 63] of class [Log4JTest.run()] [Logger:log4j.test.Log4JTest]
2007-08-24 13:39:06 [DEBUG] Implementation version : 1.2.14 at line [ 64] of class [Log4JTest.run()] [Logger:log4j.test.Log4JTest]



But what about when the message body is more than 80-bytes? That would hork-up the "pretty factor", huh? Now it was apparent that when a message body is more than 80 bytes, it should be wrapped to the next line, BUT, maintain the same information about where the logging event (line number, mainly) took place AND the developer should not have to deal with that issue. All the developer should do is what they were used to doing in the first place... mainly this:

log.info(theReallyLongMessage);


The whole solution

Extend the ConsoleAppender class in log4j. The idea being pursued here is to intercept the message, testing the message body length and if greater than 80-bytes, breaking up the message body into 80-byte segments and reconstituting the message segment into a new LoggingEvent object. Simple, really.


/*
* History of modification
*
* History of modification
*
* Date Project By Description
* -------- ----------- ----------- -----------------------------------------------
*
*
*/
package com.log4j.extensions;

/**
* This class provides the means for wrapping an arbitrary logging event-line, so
* that the body of the message - this is, "%m" - is wrapped to the next line(s).
*
* Keep in mind we aren't interested in formatting or altering the pattern layout
* as the logging event has already been formatted. The only aspect this class
* deals with is the actual length of the message; if it's greater than 80 bytes,
* the message will be broken up into 80-byte segments and sent to the ConsoleAppender.
*
*
*
*/


import java.util.ArrayList;

import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;

public class FoldingConsoleAppender extends ConsoleAppender {

private final static int LINE_LIMIT = 80;
private final static String HEADER = "THE FOLLOWING LINES WERE SPLIT INTO " +
LINE_LIMIT + " BYTE SEGMENTS TO FIT INTO THIS SPACE.";
private final static String FOOTER = "FOLDING COMPLETED";
private final static String NULL_MESSAGE = "The initial message value of null has been replaced with this message.";

/**
*
* Overrides the parent class method 'subAppend()' so that the Logging event can
* be intercepted to inspect the lenght of the message (%m).
*
*/
protected void subAppend(LoggingEvent event) {

//Take the logging event and inspect the message. If greater than 80-bytes
//create a CustomLoggingEvent with the 80-byte portion and send to the
//parent class subAppend() method. Repeat for the succeeding lines required
//to complete the process.

if ((String)event.getMessage() == null) {
String catClass = event.fqnOfCategoryClass;
Logger logger = Logger.getLogger(event.getLoggerName());
long ts = event.timeStamp;
Level level = event.getLevel();
Object message = NULL_MESSAGE;
LoggingEvent cle = new LoggingEvent(catClass, logger, ts, level, message, null);
super.subAppend(cle);
return;
}

if (((String)event.getMessage()).length() > LINE_LIMIT) {
Object m = event.getMessage();

String line = (String) event.getMessage();

if (line.length() > LINE_LIMIT) {
//Call foldLine to break up the message into 80-byte segments which
//have been placed into the array list.
ArrayList al = this.foldLine(line, LINE_LIMIT);

//For every 80-byte segment in the ArrayList, create a CustomLoggingEvent
//object using information from the original LoggingEvent but put the
//80-byte line into the new CustomLoggingEvent
for (int i = 0; i < al.size(); i++) {
Object o = al.get(i);
String catClass = event.fqnOfCategoryClass;
Logger logger = Logger.getLogger(event.getLoggerName());
long ts = event.timeStamp;
Level level = event.getLevel();
Object message = al.get(i);
LoggingEvent cle = new LoggingEvent(catClass, logger, ts, level, message, null);
super.subAppend(cle);
}

}

} else {
//Prints any log entry <= LINE_LIMIT bytes in length
super.subAppend(event);
}

}

/**
*
* Take the message body (%m) and divide it into 80-byte line-segments placing
* each segment (with preseding and ending note) into an ArrayList to be returned.
*
* @param line
* @param maxLen
* @return
*/
public ArrayList foldLine(String line, int maxLen) {

String foldedLine = null;
StringBuffer sb = new StringBuffer();
ArrayList al = new ArrayList();

int index = 0;
int lineLength = line.length();
int endIndex = index + maxLen;
al.add(HEADER);
while (index < lineLength) {
al.add(line.substring(index, endIndex));
index = index + maxLen;
endIndex = index + maxLen;
if (endIndex > lineLength) {
endIndex = lineLength;
}
}
al.add(FOOTER);

foldedLine = sb.toString();
return al;
}

}



Now that the class is created, place it into any package of your project. Next, modify your log4j.xml segment that describes the use of the ConsoleAppender as shown below.


Now, whenever the developer logs a message longer than 80-bytes:


//This demonstrates the idea of "folding" a line to ensure it fits inside the
//message space defined in the layout ( %-80m ) of the appender
String line = ".... ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7.... ....8.... ....9.... ....0";
log.info(line);


Instead of this message formatting .... (some messages appear before and after)



2007-08-24 10:26:19 [ERROR] 3. ERROR error message at line [ 39] of class [ArbitraryAppenderReference.log()] [Logger:Example_Logger]
2007-08-24 10:26:19 [FATAL] 4. FATAL error message at line [ 40] of class [ArbitraryAppenderReference.log()] [Logger:Example_Logger]
2007-08-24 10:26:19 [INFO ] .... ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7.... ....8.... ....9.... ....0 at line [ 43] of class [ArbitraryAppenderReference.log()] [Logger:Example_Logger]
2007-08-24 10:28:56 [INFO ] This logging entry is attached to appender 'Example_Logger'. at line [ 21] of class [ArbitraryAppenderReference.log()] [Logger:Example_Logger]
2007-08-24 10:28:56 [INFO ] and originated from class ArbitraryAppenderReference at line [ 22] of class [ArbitraryAppenderReference.log()] [Logger:Example_Logger]




... the message is formatted like this.


2007-08-24 10:26:19 [ERROR] 3. ERROR error message at line [ 39] of class [ArbitraryAppenderReference.log()] [Logger:Example_Logger]
2007-08-24 10:26:19 [FATAL] 4. FATAL error message at line [ 40] of class [ArbitraryAppenderReference.log()] [Logger:Example_Logger]
2007-08-24 13:39:06 [INFO ] THE FOLLOWING LINES WERE SPLIT INTO 80 BYTE SEGMENTS TO FIT INTO THIS SPACE. at line [ 33] of class [Log4JTest.run()] [Logger:log4j.test.Log4JTest]
2007-08-24 13:39:06 [INFO ] .... ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7.... ....8 at line [ 33] of class [Log4JTest.run()] [Logger:log4j.test.Log4JTest]
2007-08-24 13:39:06 [INFO ] .... ....9.... ....0 at line [ 33] of class [Log4JTest.run()] [Logger:log4j.test.Log4JTest]
2007-08-24 13:39:06 [INFO ] FOLDING COMPLETED at line [ 33] of class [Log4JTest.run()] [Logger:log4j.test.Log4JTest]
2007-08-24 10:28:56 [INFO ] This logging entry is attached to appender 'Example_Logger'. at line [ 21] of class [ArbitraryAppenderReference.log()] [Logger:Example_Logger]
2007-08-24 10:28:56 [INFO ] and originated from class ArbitraryAppenderReference at line [ 22] of class [ArbitraryAppenderReference.log()] [Logger:Example_Logger]




You can take the same FoldingConsoleAppender class above and use it as the basis to extend Log4J's FileAppender and DailyRollingFileAppender.

Thanks to Log4J's open source position !

Monday, August 20, 2007

Log4J all over again

I've not had to do Log4J configuration since before the time of the introduction of log4j.xml. Before that I used the simple log4j.properties variation.

But now, since I've run across an application using lo4j.xml I've had to learn how to configure Log4J using it. Initially, I thought "Argh" but as it turns out, it's not as bad as I had feared.

Having searched high and low on the internet, I found a lot of sites on the subject on log4j configuration. Some sites were good and other sites were not so good. All of them were broad; explained everything and then some. None offered real clarity. This was probably due to my impatience from just wanting a 1, 2, 3... list.

The log4j.xml file

Briefly, there's a layout to log4j.xml, shown below.


1. The class file
This is where the sequence of events begin. Somewhere, usually at the class level or perhaps in the constructor, you're going to have an entry such as this:

Logger log = Logger.getLogger(name);

The name may appear to be arbitrary, but its actual value is important. This is because it is linked to a named logger defined in log4j.xml. Many examples show the getLogger() method like this:

Logger log = Logger.getLogger(this.getClass().getName() );

Documentation such as this is a dis-service to both you and Log4j. The fact is that when getting a logger using the above approach returns the named logger only if one has been defined! If a logger having this name is not defined in log4j.xml a new logger instance will be created. Consequently, log entries will be sent to ALL log-files in your application. If you only have one then the problem is masked. Needless to say this approach should be avoided. Stick with names that are associated with named loggers you have defined in log4j.xml and your logging output to the correct log files will be much more predictable. Under this approach, you should expect to see something like this throughout your application:

Logger log = Logger.getLogger("Example_Logger");

Using named loggers you can create a separate set of log-files designed to contain entries for a given process.

2. Log4J looks-up the Logger Definition
Using the name ("Example_Logger") Log4J looks up logger information that were described using Logger Definitions.

From this lookup, Log4J obtains the name of the Appender.

3. Appender Definitions
Logger Definitions "point-to" Appenders which are described using Appender Definitions. Among other things, Appender Definitions define the log-file (and the filtering logging level) used to receive entries.


4. The Root Definition
The Root definition is used to define which logger is to descend from Log4J's root logger.

Friday, July 13, 2007

Initialization on Demand Holder (IODH) Idiom Singleton

I read this short article about the controversial Singleton Pattern that explains the popular form of the pattern which is seen in so many Java applicaitons is basically flawed.

The conclusion of the article illustrates another approach using an inner class which is guaranteed to execute only once ensuring there is only one instance created (and is thread-safe).

The Java Language Specification (JLS) guarantees the object "instance" would not be initialised until someone calls getInstance() method.


public class Singleton {
static class SingletonHolder {
static Singleton instance = new Singleton();
}

public static Singleton getInstance() {
return SingletonHolder.instance;
}
}


Click to read article
Wiki on Initialization on Demand Holder pattern

Tuesday, June 12, 2007

Eclipse Tip - Showing project path in title bar

To show the project's path in Eclipse's title bar, incorporate the '-showlocation' parameter when starting Eclipse. Right-click on the Eclipse icon to expose the properties and add it to the end of the start-command.




C:\Eclipse322\eclipse\eclipse.exe -showlocation

Wednesday, May 30, 2007

XStream

I haven't tried this one yet, but it could prove useful so I'm sticking it here.

XStream provides the means of serializing objects into XML and back again.

Read more here

Saturday, May 26, 2007

Subversion with Eclipse

Select link below

Link

Thursday, May 24, 2007

Declaring constants

Java transgression #3

Do not use interfaces to act as the container for constants. Interfaces should only be used to define types. Using an interface causes internal detail, such as constants, to leak into the class’s public API. Once something becomes part of the public API you can never get rid of it.

Consider an abstract class with public static final constants. Using an abstract class is an implementation of the “uses” association. With the use of an abstract class, the intent is clear and design abuse such as when using interfaces cannot propagate to other classes.

Resource

Configuration file abuse

Java transgression #2

For values that are deemed worthy of being placed into a “configuration file” e.g. config.xml or config.properties consider the nature and use of such a “configurable” value.

In a business world, when a value in the so-called config-file needs changing an application re-build and re-deploy is likely required. Therefore, it becomes no more advantageous than a class-level constant and defeats the implied intent of the value residing in the configuration file.

Consider using a database in concert with an administration tool that allows for values to be changed on-the-fly. This would reduce time and resources and improve “time-to-market”. In addition, such an approach would have the potential to prevent invalid values from being entered and thus inadvertently causing application failure.

Avoid the declaration of anonymous inner classes

Java Transgression #1

Action listener constructs associated with JButton, JTable, etc., (objects commonly seen within Swing applications) are frequently seen employing anonymous inner classes. If using Eclipse's VE and adding action listeners, it's hard to avoid as it places them in your application to provide function for the related component.

Refactoring results in the creation of a listener class, instantiating an object based upon that class and then passing it as the argument for the event listener.

Extending the listener class and assigning it to the component results in the elimination of inner classes altogether. The application will even load faster because the JVM doesn't spend time loading those inner classes.

In addition, employing anonymous inner classes kills any hope for object reuse.

Wednesday, May 23, 2007

Maven 2.0.6 Installation and proxy configuration

I ran into a very annoying situation while installing Maven 2.0.6 where the document I was following suggested running the following command as part of its quasi tutorial chapter:
  mvn archetype:create
-DarchetypeGroupId=org.apache.maven.archetypes
-DgroupId=com.mycompany.app
-DartifactId=my-app
 

After running it I was greeted with this jewel :

[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] ----------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ----------------------------------------------------------------------------
[INFO] The plugin 'org.apache.maven.plugins:maven-archetype-plugin' does not exist
or no valid version could be found
[INFO] ----------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1 second
[INFO] Finished at: Wed May 23 12:11:05 CDT 2007
[INFO] Final Memory: 4M/8M
[INFO] ------------------------------------------------------------------------


 

Never mind the part of the message suggesting to re-run using the -e switch. The suggestion to do so is really another waste of time offering no new insight as to the real nature of the error. (Typical of error messages, right?)

The appropriate error message required here for me would actually need to read like this:

"Hey, stupid! Maven is a build tool requiring an unfettered internet connection so that it can obtain resource files on an 'as needed' basis, sort of like a Just In Time (JIT) inventory system where parts are ordered as needed. What this means (in spite of what you may have read in the docs) is that you need to copy the settings.xml into your system's home directory.

That in turn means your settings.xml file should be located inside the ~/.m2/directory, where ~ is the current user's home. So on Windows that directory path should resemble c:\documents and settings\\ and on UNIX/Linux it's /home/."

While trying hard (and failing) to stay as far away from the topic of meaningful error messages, (without the insults.. ok, maybe benign ones here and there.) there's still a bit more information required here.

The settings.xml file that comes with Maven is in itself ineffective. This is because 99% of it is commented-out. So rather than deal with a large file, use this version for starters and change to match your environment.

First, notice the reference to the local repository. Yep, Maven needs that location. That's where it's going to place those things the absence of which prevent Maven from working. You can see it below and see the newly added contents created by Maven when I finally fixed this problem.



Second are the entries describing the proxy server name and its port. If you can't determine the name of the proxy server you will need to talk with your company's LAN personnel. Of note is the fact that user id and password are not required as hinted at by the related entries in the ship-with version of settings.xml. In my case, I didn't need them.

With this done, and relying on the fact that you have no other issues falling outside the scope of this post, when you rerun the "mvn archetype:create ... " command you should now be greeted with a list like the one I got, below.



C:\>mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.mycompany.app -DartifactId=my-app
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] org.apache.maven.plugins: checking for updates from central
[INFO] org.codehaus.mojo: checking for updates from central
[INFO] artifact org.apache.maven.plugins:maven-archetype-plugin: checking for updates from central
Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/1.0-alpha-4/maven-archetype-plugin-1.0-alpha-4.pom
1K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype/1.0-alpha-4/maven-archetype-1.0-alpha-4.pom
2K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/maven-parent/1/maven-parent-1.pom
6K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/apache/1/apache-1.pom
3K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/1.0-alpha-4/maven-archetype-plugin-1.0-alpha-4.jar
9K downloaded
[INFO] ----------------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO] task-segment: [archetype:create] (aggregator-style)
[INFO] ----------------------------------------------------------------------------
Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-core/1.0-alpha-4/maven-archetype-core-1.0-alpha-4.pom
1K downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-velocity/1.1.2/plexus-velocity-1.1.2.pom
7K downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-container-default/1.0-alpha-7/plexus-container-default-1.0-alpha-7.pom
1K downloaded
Downloading: http://repo1.maven.org/maven2/plexus/plexus-containers/1.0.2/plexus-containers-1.0.2.pom
471b downloaded
Downloading: http://repo1.maven.org/maven2/plexus/plexus-root/1.0.3/plexus-root-1.0.3.pom
5K downloaded
Downloading: http://repo1.maven.org/maven2/junit/junit/3.8.1/junit-3.8.1.pom
145b downloaded
Downloading: http://repo1.maven.org/maven2/plexus/plexus-utils/1.0.2/plexus-utils-1.0.2.pom
740b downloaded
Downloading: http://repo1.maven.org/maven2/classworlds/classworlds/1.1-alpha-2/classworlds-1.1-alpha-2.pom
3K downloaded
Downloading: http://repo1.maven.org/maven2/commons-collections/commons-collections/2.0/commons-collections-2.0.pom
171b downloaded
Downloading: http://repo1.maven.org/maven2/commons-logging/commons-logging-api/1.0.4/commons-logging-api-1.0.4.pom
168b downloaded
Downloading: http://repo1.maven.org/maven2/velocity/velocity/1.4/velocity-1.4.pom
2K downloaded
Downloading: http://repo1.maven.org/maven2/velocity/velocity-dep/1.4/velocity-dep-1.4.pom
1K downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-utils/1.1/plexus-utils-1.1.pom
767b downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus/1.0.4/plexus-1.0.4.pom
5K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/maven-model/2.0/maven-model-2.0.pom
2K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/maven/2.0/maven-2.0.pom
8K downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-utils/1.0.4/plexus-utils-1.0.4.pom
6K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/maven-artifact-manager/2.0/maven-artifact-manager-2.0.pom
1K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/maven-repository-metadata/2.0/maven-repository-metadata-2.0.pom
1K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/maven-artifact/2.0/maven-artifact-2.0.pom
723b downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-container-default/1.0-alpha-8/plexus-container-default-1.0-alpha-8.pom
7K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/wagon/wagon-provider-api/1.0-alpha-5/wagon-provider-api-1.0-alpha-5.pom
4K downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-container-default/1.0-alpha-9/plexus-container-default-1.0-alpha-9.pom
1K downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-containers/1.0.3/plexus-containers-1.0.3.pom
492b downloaded
Downloading: http://repo1.maven.org/maven2/dom4j/dom4j/1.6.1/dom4j-1.6.1.pom
6K downloaded
Downloading: http://repo1.maven.org/maven2/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.pom
365b downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-creator/1.0-alpha-4/maven-archetype-creator-1.0-alpha-4.pom
1K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-model/1.0-alpha-4/maven-archetype-model-1.0-alpha-4.pom
1K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/maven-project/2.0/maven-project-2.0.pom
1K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/maven-profile/2.0/maven-profile-2.0.pom
1K downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-archiver/1.0-alpha-5/plexus-archiver-1.0-alpha-5.pom
439b downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-components/1.1.4/plexus-components-1.1.4.pom
2K downloaded
Downloading: http://repo1.maven.org/maven2/oro/oro/2.0.8/oro-2.0.8.pom
140b downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/maven-plugin-api/2.0/maven-plugin-api-2.0.pom
601b downloaded
Downloading: http://repo1.maven.org/maven2/plexus/plexus-utils/1.0.2/plexus-utils-1.0.2.jar
156K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-creator/1.0-alpha-4/maven-archetype-creator-1.0-alpha-4.jar
21K downloaded
Downloading: http://repo1.maven.org/maven2/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar
306K downloaded
Downloading: http://repo1.maven.org/maven2/commons-logging/commons-logging-api/1.0.4/commons-logging-api-1.0.4.jar
25K downloaded
Downloading: http://repo1.maven.org/maven2/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar
106K downloaded
Downloading: http://repo1.maven.org/maven2/oro/oro/2.0.8/oro-2.0.8.jar
63K downloaded
Downloading: http://repo1.maven.org/maven2/velocity/velocity-dep/1.4/velocity-dep-1.4.jar
505K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-core/1.0-alpha-4/maven-archetype-core-1.0-alpha-4.jar
22K downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-archiver/1.0-alpha-5/plexus-archiver-1.0-alpha-5.jar
129K downloaded
Downloading: http://repo1.maven.org/maven2/velocity/velocity/1.4/velocity-1.4.jar
352K downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-velocity/1.1.2/plexus-velocity-1.1.2.jar
7K downloaded
Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetype/maven-archetype-model/1.0-alpha-4/maven-archetype-model-1.0-alpha-4.jar
15K downloaded
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-utils/1.1/plexus-utils-1.1.jar
164K downloaded
Downloading: http://repo1.maven.org/maven2/commons-collections/commons-collections/2.0/commons-collections-2.0.jar
88K downloaded
[INFO] Setting property: classpath.resource.loader.class => 'org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader'.
[INFO] Setting property: velocimacro.messages.on => 'false'.
[INFO] Setting property: resource.loader => 'classpath'.
[INFO] Setting property: resource.manager.logwhenfound => 'false'.
[INFO] **************************************************************
[INFO] Starting Jakarta Velocity v1.4
[INFO] RuntimeInstance initializing.
[INFO] Default Properties File: org\apache\velocity\runtime\defaults\velocity.properties
[INFO] Default ResourceManager initializing. (class org.apache.velocity.runtime.resource.ResourceManagerImpl)
[INFO] Resource Loader Instantiated: org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader
[INFO] ClasspathResourceLoader : initialization starting.
[INFO] ClasspathResourceLoader : initialization complete.
[INFO] ResourceCache : initialized. (class org.apache.velocity.runtime.resource.ResourceCacheImpl)
[INFO] Default ResourceManager initialization complete.
[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Literal
[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Macro
[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Parse
[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Include
[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Foreach
[INFO] Created: 20 parsers.
[INFO] Velocimacro : initialization starting.
[INFO] Velocimacro : adding VMs from VM library template : VM_global_library.vm
[ERROR] ResourceManager : unable to find resource 'VM_global_library.vm' in any resource loader.
[INFO] Velocimacro : error using VM library template VM_global_library.vm : org.apache.velocity.exception.ResourceNotFoundException: Unable to find resource 'VM_global_library.vm'
[INFO] Velocimacro : VM library template macro registration complete.
[INFO] Velocimacro : allowInline = true : VMs can be defined inline in templates
[INFO] Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions
[INFO] Velocimacro : allowInlineLocal = false : VMs defined inline will be global in scope if allowed.
[INFO] Velocimacro : initialization complete.
[INFO] Velocity successfully started.
[INFO] [archetype:create]
[INFO] Defaulting package to group ID: com.mycompany.app
[INFO] artifact org.apache.maven.archetypes:maven-archetype-quickstart: checking for updates from central
Downloading: http://repo1.maven.org/maven2/org/apache/maven/archetypes/maven-archetype-quickstart/1.0/maven-archetype-quickstart-1.0.jar
4K downloaded
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating Archetype: maven-archetype-quickstart:RELEASE
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.mycompany.app
[INFO] Parameter: packageName, Value: com.mycompany.app
[INFO] Parameter: basedir, Value: C:\
[INFO] Parameter: package, Value: com.mycompany.app
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: artifactId, Value: my-app
[INFO] ********************* End of debug info from resources from generated POM ***********************
[INFO] Archetype created in dir: C:\my-app
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10 seconds
[INFO] Finished at: Wed May 23 11:38:57 CDT 2007
[INFO] Final Memory: 4M/8M
[INFO] ------------------------------------------------------------------------


 

Now go visit your Maven repository and note the addition of new directories.

In addition, you should see a new project directory resembling this one.