ToC
Maven and Eclipse
One of the most interesting features of Maven is its ability to work with multi-module projects and the options for executing pre-defined targets to perform certain clearly defined tasks, such as code compilation and packaging.
Unfortunately, the current version of Eclipse does not include the necessary mechanisms to support multi-module projects, so that integration with the Maven mechanisms are complex and require the use of certain strategies to simulate such behavior.
Maven uses a Project Object Model (POM) to describe the software project to be built, the structure of the project and its sub-modules, their dependencies on other modules and external components, and the build order of the elements. Using these defined characteristics, you can get Eclipse to use the information in the POMs to simulate multi-module behavior.
Working with Maven in Eclipse
When working with Eclipse, as mentioned above, we will have to follow certain guidelines that will allow us to make use of the functionality Maven offers.
The first thing to note is the naming convention of the directories. The names applied to directories are not random, as they indicate the hierarchical structure of the project. Thus, the name of the parent project will be the name of the artifact_id, and its submodules will include the parent name (for example, if the parent project is org.gvsig.example, the submodule Example1 must be kept in the directory org.gvsig.example/org.gvsig.example.example1, and Example2 in org.gvsig.example/org.gvsig.example.example2. If Example1 has a submodule Example1a, it would be stored in org.gvsig.example/org.gvsig.example.example1/org.gvsig.example.example1a and so on). This gives the project a tree structure:
- org.gvsig.ejemplo
- org.gvsig.ejemplo.ejemplo1
- org.gvsig.ejemplo.ejemplo1.ejemplo1a
- org.gvsig.ejemplo.ejemplo2
- org.gvsig.ejemplo.ejemplo1
The parent nodes are not recognized as Java projects by Eclipse, because they are simply containers of the submodules. These projects contain the POM file with its definition and description of the submodules. In this way, you can launch commands at the parent module to run on that level and all its child nodes automatically (install, compile, etc.).
On the other hand, the child nodes (or leaves of the tree) are Java projects, with their usual project structure. These nodes can take advantage of the parent configuration (dependencies, properties, attributes, ...) if that parent is specified in the POM, simplifying its configuration.
Developing a gvSIG project
When we develop a gvSIG project there should be a strict separation between the API and the implementation of that API. To accomplish this, a basic template for gvSIG projects has been prepared which takes advantage of the Maven capabilities to manage multi-module projects. This template will guide us on how to organize the project, keeping strictly separated the logic of the extension from the user interface, and the API of the logic from its implementation, and the API of the interface from its implementation, in different Eclipse projects and separate jars.
The basic structure of a project will be:
- library
- tags
- branches
- trunk
- org.gvsig.example
- org.gvsig.example.lib
- org.gvsig.example.lib.api
- org.gvsig.example.lib.spi
- org.gvsig.example.lib.impl
- org.gvsig.example.prov
- org.gvsig.example.prov.provider1
- org.gvsig.example.swing
- org.gvsig.example.swing.api
- org.gvsig.example.swing.impl
- org.gvsig.example.main
- org.gvsig.example.lib
- org.gvsig.example
- extensión
- tags
- branches
- trunk
- org.gvsig.example.app
- org.gvsig.example.app.extensión
- org.gvsig.example.app
Compared to gvSIG projects prior to the 2.0 version, this project structure would seem rather complex, but we will look into it with more detail.
The first thing to note is that the project is oriented as a multi-module SVN project compared to what we used to see in the development of gvSIG 1.X. On the one hand we have the development of the library and on the other, the extension(s) for gvSIG. This has been organized like this because normally the changes in the logic of our extension usually have a life cycle that is clearly separated from the integration into gvSIG. After development of the extension has been completed, it is easier to do the modifications from one gvSIG version to the next in the parts of the code that contains the integration with gvSIG, while the logic remains unchanged except for some bug corrections. To achieve this independence, the SVN repository is separated into two main groups:
Library
This contains the logic and user interface that are independent of gvSIG as an application. This does not mean that they can’t depend on some gvSIG libraries such as the ones for data access, geometry or mapcontext. If they need to depend on those libraries, they can be used.
Extension.
This is the part of the code that depends on the gvSIG application, Andami and other gvSIG extensions. This is where the Extension classes are located, as well as the configuration of our Andami plug-in.
In the library we will find the following:
org.gvsig.example.lib
This provides the API, SPI, and the implementation of the API.
org.gvsig.example.prov
If our library needs to interact with a service provider, this is where the different implementations of these service providers would be located.
org.gvsig.example.swing
If our project includes a user interface for the library's logic, it would be located here only. The projects located into org.gvsig.example.lib must not depend on the user interface and, therefore, not on this project.
org.gvsig.example.main
You may find it strange to find a main module within the library, but it is actually quite useful. This is a module that provides a test application that you can use to launch the user interface and the logic of the library without having to start gvSIG to test it. This allows for a more agile development of our project, postponing the integration with gvSIG until it is in a more advanced stage of development.
And finally we come to the separation of some of these modules into API, SPI and implementation. This separation can be found in org.gvsig.example.lib and org.gvsig.example.swing.
Imagine that we are developing a network analysis extension for gvSIG. What projects would we be working with?
In principle, our project would not require special service providers. It would consist of the logic, the user interface, and the actual plugin for gvSIG. We would have the following projects:
- org.gvsig.networkAnalysis.lib.api
- org.gvsig.networkAnalysis.lib.impl
- org.gvsig.networkAnalysis.swing.api
- org.gvsig.networkAnalysis.swing.impl
- org.gvsig.networkAnalysis.main
- org.gvsig.networkAnalysis.app.extensión
Basically we would have these six projects, and each of them would generate its own jar, corresponding to the name of the project. Each of the projects will be will be located in its corresponding folder, and it is important that they have the correct name. This is because when you import these projects into Eclipse, we find that the folder tree is flattened; but thanks to the naming convention, these projects retain their consistency in our workspace.
Although it seems obvious, it should be pointed out which dependencies between those projects are acceptable. The implementation will depend on the API, but not vice versa. Also, our API should be concise and complete. Complete because the client of this API should not need to know the implementation in order to use it, and concise because the details of the implementation do not need to be exposed; the less details are exposed, the easier and safer the maintenance will be.
On the other hand, there is the user interface. This, whether the implementation or its API, should have no dependencies on the implementation of the library, or the logic. It should only depend on its API.
And finally, the test implementation and the extension can only depend on the API of the library and the user interface API.
It should also be noted that pom.xml files are found in each of the different folder. So there is a pom.xml in org.gvsig.example that allows us to interact with all the submodules of our project, and it contains the general configuration of the project. For example, through the pom.xml we can directly build the Eclipse projects for each of our modules. In turn, org.gvsig.example.swing and org.gvsig.example.lib have their own pom.xml files with their specific configuration. Finally, each of the final projects will have their own pom.xml file. Each of these pom.xml will be configured indicating the pom of the higher level folder as the parent pom to inherit the common configurations.
We have mentioned API several times. But what do we mean by API? How do we define it?
The question is not as obvious as it seems. When you ask several developers how they define an API module we can end up with very different answers. So, in gvSIG, how do we define an API? Normally, the API consists of a set of Java interfaces. There may be an abstract class that provides a default implementation for something that should be extended by the API user, but it never contains a class with its implementation. As usual, there are exceptions to this. By their nature, exceptions that trigger this API will be implemented in the API. This set of interfaces, packaged in a project and its jar, is the only dependency that the API user must depend on.
When using swing there will be differences. Swing lacks interfaces to handle graphic components, and therefore when it comes to defining the API of the swing part of our project, instead of using interfaces, we use abstract classes that extend the different swing components, adding abstract methods from our project domain. Usually there are abstract classes that extend JPanel, and these define the user interfaces associated with components of our project.
What does the implementation consist of?
Well, there we have a set of classes that implement the interfaces defined in the API. Normally there will be a manager which will serve as entry point to the functionality of our project and its configuration.
It is helpful to review the tools that org.gvsig.tools provides to manage the separation between API and implementation using the Library, Locators and Manager, and the tools that are available for the management of service providers, so that new projects will not need to reinvent each time how to implement these tools, and to harmonize their use throughout gvSIG.
We must also consider the recommendations when naming the classes and interfaces. The project structure described here is designed to follow a naming system such as described in the document nomenclature for classes and interfaces. So the API interfaces are named using meaningful names from the extension domain, without using any kind of prefix or suffix, while the classes that are found in the implementation project and which implement the API interfaces, are named by adding Default as a prefix to the name of the interface that they implement.
Before moving on to the creation of a gvSIG project according to the structure described above, it is recommended to review the documentation about org.gvsig.tools.service that describes in more detail the classes and interfaces to create, and how they are stored in a Maven subproject.