miércoles, 6 de abril de 2016

Uso de maven en proyecto java

Maven is a framework, or an application if you may, which helps you manage a project’s lifecycle. For those new to Maven, usually we have our IDE such as EclipseNetBeans or SpringSource Tool Suite (STS), to handle the creation, compilation and deploying of projects. Instead, we can use Maven independent from the development environment (to some extent), to manage the project’s lifecycle. In this article we will see how Maven can help use in managing dependencies and creating a final product which can be deployed to a production system.
All code listed below is available at: http://code.google.com/p/java-creed-examples/source/checkout, under the maven project. Most of the examples will not contain the whole code and may omit fragments which are not relevant to the example being discussed. The readers can download or view all code from the above link.
This article focuses on how we can use Maven together with STS to manage our simple project. We will introduce what Maven can do and how can it be configured to aid us in our work. Here we will not show all Maven features, but focus only on those required to manage a standalone Java application.

Introduction to Maven

Maven is famous for its dependency management. What is that? In our projects we make use of third party JARs such as JUnit to name one. What do we usually do to use a third party JAR file or a group of JARs? We go on the Internet and look for it. We then download it and all of its dependencies. Note that a JAR may depend on another and we need to resolve all dependencies as otherwise our code may not compile. With Maven, it is all a different story. All we need is to go into the Maven Repository (a place where third party JARs can be downloaded from). Then we find our JAR and add it to the pom.xml file (will see later on). Maven will download this JAR and all its dependencies and adds them to the classpath for us.
But Maven is more than this. With Maven we can create projects with a predefined structure, test our code, and install/deploy it to name a few. One must note that Maven is a framework where new things (technically referred to as plugins) can be added. Therefore, one can program Maven to take care of other things that the standards features named before.
This article is more about how to use Maven to manage a Java application. The description is kept to a minimum in order not to end up with a too long and unfocused article.

Setting up the Project

Let say that we want to have an application that adds to inputs, say a and b, and prints the results as shown in the following figure.
Calculator Application
Calculator Application
This application is very trivial but it has all the prerequisites required to show most of the important Maven features when dealing with a Java standalone application. In this article we will do the following
  1. Start using Maven with an existing project
  2. Add all required dependencies
  3. Test our program
  4. Deploy our program, together with its dependencies
  5. Work with JAR files not available in a Maven repository
Before we hit to the road, let us have a look at the application without Maven. The following figure lists all classes and resources (such as the log4j.properties).
Project Structure
Project Structure
This is not a typical Maven folder structure, but we did this on purpose. We want to adopt Maven to an existing project and will keep the structure as is. One must note that the structure is irrelevant and once configure properly, it will work.
Here we have four source folders:
  1. src: where we have all our code
  2. resources: where we have configuration files such as the logging configuration
  3. test-src: where we have all our test classes
  4. test-resources: where we have the configuration to be used when testing. Note that we may need finer logging when testing, while in production we want to have a higher logging level not to impact the application performance.
To be fair, Maven organises the folders in a very similar way and unless forced to do otherwise, it is always recommended to follow Maven’s convention. We took this path to show the reader how Maven can be also adopted with an existing project with minimal refactoring.
It is now time to start working with Maven.

Make it a Maven Project

The first this one should do, if he/she would like to work with Maven within STS is to make a project as a Maven Project. I said: “would like to work with Maven within STS “, you can use Maven as a command prompt tool once you install it and modify the system path to find the mvn command. But this article is all about STS and Maven so we will restrict ourselves talking about these two, with one exception at the end, when we work with a third party JAR not available in the Maven repository.
STS 3.x allows the user to install Maven during the installation process. If your STS version does not have the Maven plugin installed, than it is recommend doing so before proceeding. Basically this is the m2eclipse Eclipse plugin and more information about this is found here.
In order to make a project a Maven Project all needs doing is to right-click on the project and select Configure and then select Convert to Maven Project. This will open the following dialog from where we can enter the basic, but required, information.
Create new POM
Create new POM
The Group Id is the company wide name. This is usually the company’s domain name. The Artifact Id is the project name. This uniquely identifies the project within the company. The Version describes the current version of the project and the Packaging is how we will deploy. All this can be modified at a later stage but Maven requires these before it can start. This will generate a Project Object Model (pom) file: pom.xml, as shown next.
<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>com.javacreed.examples</groupId>
  <artifactId>maven-examples</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <build>
    <sourceDirectory>src</sourceDirectory>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
We need to modify this, as for a start, we do not have all source folder added to the pom. This is very important as otherwise Maven will not pick the content of these source folders.
<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>com.javacreed.examples</groupId>
  <artifactId>maven-examples</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <build>
    <sourceDirectory>src</sourceDirectory>
    <resources>
      <resource>
        <directory>resources</directory>
      </resource>
    </resources>
    <testSourceDirectory>test-src</testSourceDirectory>
    <testResources>
      <testResource>
        <directory>test-resources</directory>
      </testResource>
    </testResources>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
Here we added the resources used when in production and the test classes and resources. The next step would be: manage the dependencies through Maven, as described next.

Observation

When editing the pom.xml, you may encounter the following problem:
Project configuration is not up-to-date with pom.xml. Run Maven->Update Project or use Quick Fix.
Simply, right click on the project and select Maven from the menu. Then select Update Project and follow the instructions. This is quite common and you may have to do it several times, especially when modifying the pom file.

Manage Dependencies with Maven

Assuming that the dependencies are only managed through Maven, the project will not compile. Our projects makes use of SLF4J for logging and JUnit for testing. We can add these by modifying the pom file as showed next.
<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>com.javacreed.examples</groupId>
  <artifactId>maven-examples</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <build>
    <sourceDirectory>src</sourceDirectory>
    <resources>
      <resource>
        <directory>resources</directory>
      </resource>
    </resources>
    <testSourceDirectory>test-src</testSourceDirectory>
    <testResources>
      <testResource>
        <directory>test-resources</directory>
      </testResource>
    </testResources>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.2</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.2</version>
    </dependency>
  </dependencies>
</project>
Here we just added three dependencies. These can all be found at: http://mvnrepository.com. Here we can search for most of the JARs (or as referred to by Maven: dependencies). One must note that we can search in other repositories, such as: http://ebr.springsource.com/repository/app/. But before we can use this repository (or any other repository), we must add the repository location to the pom file. We do not require to do this for our project as all of our dependencies can be found at http://mvnrepository.com. For completeness, the following lists shows what has to be added to the pom file (as described in here).
  • The bundle repository:
      <repository>
        <id>com.springsource.repository.bundles.release</id>
        <name>SpringSource Enterprise Bundle Repository - SpringSource Bundle Releases</name>
        <url>http://repository.springsource.com/maven/bundles/release</url>
      </repository>
    
      <repository>
        <id>com.springsource.repository.bundles.external</id>
        <name>SpringSource Enterprise Bundle Repository - External Bundle Releases</name>
        <url>http://repository.springsource.com/maven/bundles/external</url>
      </repository>
  • The library repository:
      <repository>
        <id>com.springsource.repository.libraries.release</id>
        <name>SpringSource Enterprise Bundle Repository - SpringSource Library Releases</name>
        <url>http://repository.springsource.com/maven/libraries/release</url>
      </repository>
    
      <repository>
        <id>com.springsource.repository.libraries.external</id>
        <name>SpringSource Enterprise Bundle Repository - External Library Releases</name>
        <url>http://repository.springsource.com/maven/libraries/external</url>
      </repository>
Please note that the above is not part of our example, and was added here to show than more repositories can be added.
It is also good to note that one of the dependencies has a scope, which is set to test>. This means that this dependency is only required for testing and it is not required for runtime. Only the classes under the test source folder make reference to this dependency. Why do we need to mark such dependencies? This plays a role when Maven managed our dependencies in deployment phase. In this phase Maven will ignore these dependencies, together with any classes under the test source folder and test resrouces.
Once Maven if finished to do its magic you will notice that a new library, called: Maven Dependencies, is added. Here we will find all dependencies added through Maven as shown next.
Maven Dependencies
Maven Dependencies

Observation

Note that the above figure shows five JARs when we only added three dependencies. This is because Maven has resolved the indirect dependencies (the dependencies of our dependencies and their dependencies too) automatically for us and added these to the project classpath.
When we add or remove dependencies, Maven downloads them in to our local PC (or as it is technically referred to: Local Meven Repository) for us and updates this library. It is important to manage all of our dependencies from Maven. Unfortunately this is not always the case especially with new releases of some dependencies. Furthermore, while Maven is very popular in managing dependencies, some JARs are not found in any repository and we need to work around that as we will see later on.
The project now compiles as it has all that it requires. We can run the project from STS through the Main class and works well. Now let us see how to use Maven to manage the other parts of the project lifecycle.

Building the project with Maven

So far we only used the Maven ability to manage our dependencies, which is pretty cool. It alone is already worth giving Maven a try. All projects in this website that require dependencies are managed through Maven. Like that when someone downloads the examples, he or she does not have to worry about the dependencies as Maven will handle that.
If you right-click on the pom.xml file and select Run As menu, you will see a list of options as shown in the following figure.
Maven run as menu options
Maven run as menu options
If you select the Maven install option, Maven will compile and test our code and if all goes well it creates the JAR file in the target directory. The Maven clean option will empty the target directory and the Maven test will simply run the test cases.

Observation

When using a command for the first time, Maven downloads several things. When we originally installed Maven, Maven only installed the bare minimum. Anything required is downloaded and installed on the fly.
It looks like we have everything we need and the JAR file was compiled, tested and deployed through Maven. But if we try to run the JAR file (by double clicking on it) nothing will happen. If we try to execute it from the command prompt we will get the following error.
C:\javacreed\maven\target>java -jar maven-examples-0.0.1-SNAPSHOT.jar
no main manifest attribute, in maven-examples-0.0.1-SNAPSHOT.jar
What did we miss? So far we modified the pom to be able to compile and test and these phases work well. The pom is not ready to deploy our application properly, as we will see in the next section.

Add main class to pom

One of Maven’s strengths is deployment. Through the pom file we can instruct Maven about how to deploy our project, such as which class is the main class as we will see in this section. This can be achieved by simply adding the following to the pom.
Do not rush adding this to the pom file, as it is not complete and it still missing the dependencies.
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <configuration>
    <archive>
      <manifest>
        <mainClass>com.javacreed.maven.examples.Main</mainClass>
      </manifest>
    </archive>
  </configuration>
</plugin>
Here we are telling Maven to modify the MANIFEST.MF file, which was too generated by Maven as shown next. Maven will generate the following manifest including the Main-Class as highlighted in the following example.
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: Albert
Build-Jdk: 1.7.0_02
Main-Class: com.javacreed.maven.examples.Main
We are far from ready as so far we instructed Maven to simply add the main class to the manifest. We need to also modify the manifest to include our dependencies. As is, our project will fail to run as the dependencies are not available at runtime as shown next.
C:\javacreed\maven\target>java -jar maven-examples-0.0.1-SNAPSHOT.jar
Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
        at com.javacreed.maven.examples.CalculatorImpl.(CalculatorImpl.java:8)
        at com.javacreed.maven.examples.CalculatorFrame.(CalculatorFrame.java:15)
        at com.javacreed.maven.examples.Main$1.run(Main.java:12)
        at java.awt.event.InvocationEvent.dispatch(Unknown Source)
        at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
        at java.awt.EventQueue.access$000(Unknown Source)
        at java.awt.EventQueue$3.run(Unknown Source)
        at java.awt.EventQueue$3.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
        at java.awt.EventQueue.dispatchEvent(Unknown Source)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 17 more
We have to also include the dependencies as we will see in the following section. So far we simply told Maven to add the selected class as the projects main class.

Add Runtime Dependencies

We have two ways to add the dependencies to the generated JAR file. We can either package everything as one JAR, having all dependencies extracted and re-packages with the generated JAR. Alternatively, we can have our dependencies copied to a folder relative to the JAR and modify the manifest accordingly. I prefer the second option but we will see how to do them both.
  • Everything packaged into one JAR
    <plugin>
      <artifactId>maven-assembly-plugin</artifactId>
      <configuration>
        <archive>
          <manifest>
            <addClasspath>true</addClasspath>
            <mainClass>com.javacreed.maven.examples.Main</mainClass>
          </manifest>
        </archive>
        <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
      </configuration>
      <executions>
        <execution>
          <id>make-my-jar-with-dependencies</id>
          <phase>package</phase>
          <goals>
            <goal>single</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  • Copied into a library folder
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <executions>
        <execution>
          <id>copy-dependencies</id>
          <phase>prepare-package</phase>
          <goals>
            <goal>copy-dependencies</goal>
          </goals>
          <configuration>
            <outputDirectory>${project.build.directory}/lib</outputDirectory>
            <overWriteReleases>false</overWriteReleases>
            <overWriteSnapshots>false</overWriteSnapshots>
            <overWriteIfNewer>true</overWriteIfNewer>
          </configuration>
        </execution>
      </executions>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <configuration>
        <archive>
          <manifest>
            <addClasspath>true</addClasspath>
            <classpathPrefix>lib/</classpathPrefix>
            <mainClass>com.javacreed.maven.examples.Main</mainClass>
          </manifest>
        </archive>
      </configuration>
    </plugin>
Here we are instructing Maven how to deploy our application and its dependencies. Note that in the second example we have two plugins and not one.

Observation

The second example may cause a known issue with m2eclipse plugin. All you need is to include the following to the pom.
<pluginManagement>
  <plugins>
    <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
    <plugin>
      <groupId>org.eclipse.m2e</groupId>
      <artifactId>lifecycle-mapping</artifactId>
      <version>1.0.0</version>
      <configuration>
        <lifecycleMappingMetadata>
          <pluginExecutions>
            <pluginExecution>
              <pluginExecutionFilter>
                <groupId>
                  org.apache.maven.plugins
                </groupId>
                <artifactId>
                  maven-dependency-plugin
                </artifactId>
                <versionRange>
                  [2.1,)
                </versionRange>
                <goals>
                  <goal>
                    copy-dependencies
                  </goal>
                </goals>
              </pluginExecutionFilter>
              <action>
                <ignore></ignore>
              </action>
            </pluginExecution>
          </pluginExecutions>
        </lifecycleMappingMetadata>
      </configuration>
    </plugin>
  </plugins>
</pluginManagement>
When we run Maven Install, Maven will produce our JAR file and copy the libraries to the folder lib as described in the pom file. Maven will update the manifest as shown next.
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: Albert
Build-Jdk: 1.7.0_02
Main-Class: com.javacreed.maven.examples.Main
Class-Path: lib/slf4j-api-1.7.2.jar lib/slf4j-log4j12-1.7.2.jar lib/lo
 g4j-1.2.17.jar
Here we are telling Java to add the following three JAR files, which are primarily used for logging. Note that the JUnit JAR files are not added to the manifest. That is because, JUnit is only used for testing and Maven will not deploy the testing classes and resources as indicated by its scope.
So far we saw how to manage the dependencies that were added to Maven. But what if we need to include a JAR file which is not found in Maven repository? In the following and final section we will see how to work with a custom, non-Maven managed, dependency.

Include non-Maven Dependencies

The simplest way to work with non-Maven dependencies is to add the new JAR file to the local Maven repository. Maven has a local repository which caches all downloaded dependencies. It first looks here and if not found in the Maven local repository, will look into the repositories located on the Internet and those defined in the pom.
The following steps illustrate how to do this.
  1. First obtain the JAR file. In this example we are using a simple JAR file called xTools.jar. Note that if this JAR depends on other JARs, then all these JARs need to be added to local Maven repository.
  2. Install the JAR file into the local Maven repository using the following command
    mvn install:install-file -Dfile=xtool.jar -DgroupId=com.javacreed.examples -DartifactId=xtool -Dversion=1.0.0 -Dpackaging=jar
    The parameters are self-explanatory. The above command will produce something like the following.
    C:\javacreed\lib>mvn install:install-file -Dfile=xtool.jar -DgroupId=com.javacreed.examples -DartifactId=xtool -Dversion=1.0.0 -Dpackaging=jar
    [INFO] Scanning for projects...
    [INFO] Searching repository for plugin with prefix: 'install'.
    [INFO] ------------------------------------------------------------------------
    [INFO] Building Maven Default Project
    [INFO]    task-segment: [install:install-file] (aggregator-style)
    [INFO] ------------------------------------------------------------------------
    [INFO] [install:install-file {execution: default-cli}]
    [INFO] Installing C:\javacreed\lib\xtool.jar to C:\javacreed\.m2\repository\com\javacreed\examples\xtool\1.0.0\xtool-1.0.0.jar
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESSFUL
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 3 seconds
    [INFO] Finished at: Fri Jan 18 09:36:11 CET 2013
    [INFO] Final Memory: 3M/122M
    [INFO] ------------------------------------------------------------------------
  3. With the JAR within our local Maven repository, all we need to do is add the dependency to the pom, as we did with the others.
    <dependency>
      <groupId>com.javacreed.examples</groupId>
      <artifactId>xtool</artifactId>
      <version>1.0.0</version>
    </dependency>
This new dependency is treated like the others. When deploying our application, Maven will deploy it with the rest.
This concludes our article about how to use Maven with a standalone Java application. In this article we used a very simple Java application to demonstrate how to manage the dependencies with Maven and how to deploy our application together with its dependencies. Finally we saw how to use custom JAR which is not found in a Maven repository.

Funete:
http://www.javacreed.com/how-to-use-maven-with-an-application/

No hay comentarios:

Publicar un comentario