Developing gCube Maven Components

From Gcube Wiki
Revision as of 16:49, 30 October 2017 by Gabriele.giammatteo (Talk | contribs) (Service Archive (SA))

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Common

Maven Coordinates

Maven components' coordinates must follow these conventions:

  • their groupId must be of the following form:
org.gcube.<class>
where class identifies a functionally correlated set of components and helps a range of users (developers, administrator) to discover components in various UIs/reports used within the system and in tools. Logically, the class corresponds to the gCube class of the component's, as specified in its gCube Software Profile. However, more specific, or else cross-class groups may be agreed upon among developers. In all cases, the group should align with official Maven guidelines. By agreement, for example, components in gCube class DataAccess declare a groupId of org.gcube.data.access and those in gCube class DataTransfer declare a groupId of org.gcube.data.transfer. On the other hand, also by agreement, components in gCube class InformationSystem declare a groupId of org.gcube.informationsystem.
  • their artifactId must follow Maven conventions (e.g. avoid capital letters or punctuation other than hyphens).

Parent

All gCube components must inherit from maven-parent. This ensure and promotes compliance with system-wide requirements, from the enforcement of minimal Java and Maven versions to generation and packaging of Javadoc documentation and component sources.

<parent>
 <artifactId>maven-parent</artifactId>
 <groupId>org.gcube.tools</groupId>
 <version>1.0.0</version>
 <relativePath />
</parent>


The maven-parent defines:

  • several system-wide properties like gcube.license, gcube.wikiRoot, distroDirectory
  • the default Java version (see more in Java Version)
  • the execution of some plugins that run for every component like: maven-javadoc-plugin, maven-source-plugin
  • the configuration of the maven-assembly-plugin to generate some gCube-specific packages: servicearchive, uberjar and source-package (see more in Artifact Types)
  • the logic to enable the generation of the source package

The maven-parent is at http://maven.research-infrastructures.eu/nexus/service/local/repositories/gcube-externals/content/org/gcube/tools/maven-parent/1.0.0/maven-parent-1.0.0.pom

The distro folder

This folder contains files that will be distributed along with the code in the distribution packages. Further details are in the Maven distro directory layout page.


Dependencies

Service and libraries that depend on other gCube components (Maven-based or Ant-based) can find them in the gCube Maven Repositories: gcube-snapshots and gcube-releases.

gCube Maven Repositories

As a one-off task, developers must configure these repositories in the ~/.m2/settings.xml file of their local Maven installation, as follows:

<settings 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/settings-1.0.0.xsd">
 
	<servers>
		<server>
			<id>gcube-snapshots</id>
			<username>gcube-user</username>
			<password>maven</password>
		</server>
	</servers>
 
 
	<profiles>
		<profile>
			<id>gcube</id>
			<repositories>
				<repository>
					<id>gcube-snapshots</id>
					<name>gCube Snapshots</name>
					<url>http://maven.research-infrastructures.eu/nexus/content/repositories/gcube-snapshots</url>
					<releases>
						<enabled>false</enabled>
					</releases>
					<snapshots>
						<enabled>true</enabled>
					</snapshots>
				</repository>
				<repository>
					<id>gcube-externals</id>
					<name>gCube Externals</name>
					<url>http://maven.research-infrastructures.eu/nexus/content/repositories/gcube-externals</url>
					<snapshots>
						<enabled>false</enabled>
					</snapshots>
					<releases>
						<enabled>true</enabled>
					</releases>
				</repository>
			</repositories>
 
			<pluginRepositories>
				<pluginRepository>
					<id>gcube-snapshots</id>
					<name>gCube Snapshots</name>
					<url>http://maven.research-infrastructures.eu/nexus/content/repositories/gcube-snapshots</url>
					<releases>
						<enabled>false</enabled>
					</releases>
					<snapshots>
						<enabled>true</enabled>
					</snapshots>
				</pluginRepository>
				<pluginRepository>
					<id>gcube-externals</id>
					<name>gCube Externals</name>
					<url>http://maven.research-infrastructures.eu/nexus/content/repositories/gcube-externals</url>
					<snapshots>
						<enabled>false</enabled>
					</snapshots>
					<releases>
						<enabled>true</enabled>
					</releases>
				</pluginRepository>
			</pluginRepositories>
 
		</profile>
	</profiles>
 
	<activeProfiles>
		<activeProfile>gcube</activeProfile>
	</activeProfiles>
</settings>


Note:Developers must not configure these repositories in their POMs, only in settings.xml.

This is to:

  • keep their projects independent from future changes in repository locations;
  • avoid interference with ETICS builds, where access to these repositories may comprise correct system integration;

With this settings.xml, developers will have access on their local development environment to development versions (gcube-snapshots) and externals (gcube-externals). This is the correct behaviour for most of use cases. If access to staging or releases repositories is necessary (e.g. patching a component already released in the branch where dependencies versions in the release are not in snapshots), developer should modify his settings.xml to point to gcube-staging(releases) instead of gcube-snapshots.

Third-party Repositories

Using the the settings.xml aforementioned, any dependency declared in pom.xml that can be found in one of gCube repositories or in Maven Central will be resolved at build time and deployment time without any further configuration. Frequently it may happens that a gCube component needs software stored on a public Maven repository not in on of those repositories. In this case, Maven provides an easy solution for declaring additional repositories for dependency resolution by adding a <repositories> section in the pom.xml of the project. E.g.:

<repositories>
    <repository>
    	<id>[repository-id]</id>    	<url>[repository URL]</url>    </repository>
</repositories>

Though technically possible to add any Maven repository in this way, in order to preserve the quality of gCube software it is required to check trustworthiness of its dependencies disallowing usage of certain external software. To enforce such a control over public Maven repositories, gCube components are allowed to use external repository not directly but only through a proxy repository (a.k.a. shadow repository) created on CNR's Nexus Repository Portal (http://maven.research-infrastructures.eu/nexus/).

List of all repositories in Nexus Portal is available at http://maven.research-infrastructures.eu/nexus/index.html#view-repositories

Note: if a component uses a public Maven repository directly, it will work for building but would fail in deployment phase. In fact, Software Gateway is only able to download software accessible from Nexus.

Request new proxy repository in Nexus

Proxy Repositories on Nexus are created by the Release Manager. To request a new proxy a new ticket must be created.

Details of ticket are:

  1. open a ticket in tracker "Support" in gCube ticketing system (at https://support.d4science.org/projects/gcube/)
  2. ticket's description must report:
    • URL of repository to add
    • organization which hosts the repository
    • which artifacts are needed from that repository
    • which components needs those artifacts
  3. Sprint field should be set to the gCube release in which the component is expected to be released
  4. Category field must report the gCube subsystem that needs the external repository
  5. Assignee is the Release Manager

An example of ticket for requesting a new repository mirror is reported in the figure below.

Ticket for requesting a new Mirror Repository

Release Manager will process the ticket checking that no issues are foreseen from the usage of software stored in the repository. If request is accepted, a new proxy repository is created and the repository's URL will be communicated publicly on gCube's developers mailinglist (a.k.a. TCOM mailinglist).

Knowing the mirror repository URL, developer can use it adding following lines in pom.xml. E.g.

<repositories>
    <repository>
    	<id>gcube-ext-osgeo</id>    	<url>http://maven.research-infrastructures.eu/nexus/content/repositories/osgeo/</url>    </repository>
</repositories>

Note: it is possible that software stored on proxy repositories is not included in search results on Nexus. This happens for those repositories that does not have indexes (or have indexes in a format that Nexus is not able to download). Anyway, once Nexus has downloaded artifacts from the original repository for the first time (e.g. because a compilation of a pom that uses the proxy repository), creates its own indexes and those artifacts will become available also for local search.

Dependency Versions

ETICS builds ensure correct integration of gCube components, regardless of the versions their dependencies. It also ensures that components are released with fixed versions for their dependencies, i.e. ensures build reproducibility.

Accordingly, developers can be liberal in the choice of dependency versions. i.e. rely on version ranges to:

  • minimise the requirements for version management;
  • be readily informed of any mis-alignment with their dependencies;

As we start adopting Maven, the lower-bound of version ranges will be a SNAPSHOT version. For example, a dependency on the streams library can be specified as follows:

<dependency>
    <groupId>org.gcube.data.access</groupId>
    <artifactId>streams</artifactId>
    <version>[1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)</version>
</dependency>

With this dependency, a component will be soon notified of a non-retro compatible change that, contrary to conventions, streams may have introduced in, say, version 1.3.0 (typically the day after at the latest). The notification will be delivered in the IDE, assuming the availability of Maven integration (e.g. m2e in Eclipse). The component is in fact free to open the range further if it wants to be aligned with its dependencies across major versions.

Common Dependencies

Many gCube components depend on gcf, which is now available as a Maven component since version 1.4. At the time of writing, the component has not been released yet. A dependency on it is then specified as follows:

<dependency>
    <groupId>org.gcube.core</groupId>
    <artifactId>gcf</artifactId>
    <version>[1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)</version>
    <scope>provided</scope>
</dependency>

The scope provided indicates that these dependency will be satisfied in the development environment (the gHN) and should not be included in GAR and SA archives.

Some components depend on the libraries available in all gHNs, though they do not depend on gcf itself. The subset of these libraries that are required for compilation and integration testing have been coalesced in a single Jar, distributed as a Maven component called ghn-core-runtime. A dependency on this Jar can be specified as follows:

<dependency>
    <groupId>org.gcube.distribution</groupId>
    <artifactId>ghn-core-runtime</artifactId>
    <version>1.0.0</version>
    <scope>provided</scope>
</dependency>

Note: a component that depends on gcf indirectly depends on gun-core-runtime.

Another common dependency is on the gCube gRS2, an Ant component that the build infrastructure make available in Maven repositories. A dependency on this component can be specified as follows:

<dependency>
    <groupId>org.gcube.execution</groupId>
    <artifactId>grs2library</artifactId>
    <version>[1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)</version>
</dependency>

Java Version

In Maven the Java version of the source code and of the generated classes can be controlled using the configuration of the maven-compiler-plugin. E.g.:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <source>1.7</source>    <target>1.8</target>  </configuration>
</plugin>


In gCube, this mechanism is exploited to enforce the usage of the same Java version for all components. In particular, the maven-parent defines the properties java_version, maven.compiler.source and maven.compiler.target and use these properties in the configuration of the maven-compiler-plugin.

...
<properties>
  <java_version>1.8</java_version>
  <maven.compiler.source>${java_version}</maven.compiler.source>
  <maven.compiler.target>${java_version}</maven.compiler.target>
  ...
</properties>
 
...
 
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <target>${maven.compiler.target}</target>    <source>${maven.compiler.source}</source>  </configuration>
</plugin>
...

All the gCube components inherit this default configuration from maven-parent and it is not recommended to override it in the component's pom.xml.

If it is really needed to override these values at component's pom.xml level, instead of specify a new configuration for the maven-compiler-plugin, the value of the property should be re-defined:

...
<properties>
  <maven.compiler.source>1.7</maven.compiler.source>  ...
</properties>


This configuration is flexible enough to allow to customize the Java compiler source and target values depending on different factors in the integration builds (e.g. during ETICS builds gCore components are built with Java 7, all the others with Java 8).


Artifact Types

There are four gCube-specific artifact types that are generated during the build of the gCube components. Each of them is summarized in the following subsections.

Source Package

The source package is an artifact with classifier src that contains the source code of the component plus the files in the distro directory (see Maven distro directory layout for more).

Its descriptor is at link.

It is automatically generated if the generateDistribution property is true.

Uberjar

The Uberjar is an artifact with classifier uberjar that contains the component classes plus all the jar files of the dependencies. This is not mandatory for all components, but only for those that needs to be deployed along with their dependenices.

Its descriptor is at link.

The generation of this package is not enabled by default. To enable it, bind its configuration to a build phase in the component pom.xml:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-assembly-plugin</artifactId>
	<executions>
		<execution>
			<id>make-uberjar</id>
			<phase>package</phase>
		</execution>
	</executions>
</plugin>

Service Archive (SA)

Whenever they are associated with a gCube Software Archive (SA), they must produce it as a secondary tar.gz artefact with classifier servicearchive. Note that the SA of Maven components does not need to include Javadoc documentation (as this documentation is produced and published directly as a Maven artefact);

  • for each deployable artefact they produce (primary or secondary), they must include the Maven coordinates of the arteact in the corresponding package section of the gCube Software Profile. Specifying package dependencies in the Software Profile, on the other hand, is no longer required;
  • whenever they are associated with a gCube Software Archive (SA), they must produce it as a secondary tar.gz artefact with classifier servicearchive. Note that the SA of Maven components does not need to include Javadoc documentation (as this documentation is produced and published directly as a Maven artefact);


The maven-parent defines a default descriptor to build a servicearchive (link). This descriptor fits most of single module gCube components that meet the requirements in Maven distro directory layout. If it does not fit your component, you will have to provide a custom descriptor.xml file and a custom configuration as described here.

The generation of the default servicearchvie is disabled by default (beacuse it is not fits all cases, please see above). To enable it, bind its configuration to a build phase in the component pom.xml:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-assembly-plugin</artifactId>
	<executions>
		<execution>
			<id>make-servicearchive</id>
			<phase>package</phase>
		</execution>
	</executions>
</plugin>

Javadoc

It is the default artifact (with classifier javadoc) generated by the maven-javadoc-plugin. It is automatically generated for all components.

Libraries

The streams library is a Maven component in gCube class DataAccess. We will use it as example of a library component in Maven.

Profile

The gCube Software profile of the library is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<Resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ID />
  <Type>Service</Type>
  <Profile>
    <Description>Embedded Domain-Specific Language for Stream Transformations</Description>
    <Class>DataAccess</Class>
    <Name>streams</Name>
     <Version>1.0.0</Version>
     <Packages>
       <Software>
         <Name>streams</Name>
         <Version>1.0.0-SNAPSHOT</Version>
         <MavenCoordinates>           <groupId>org.gcube.data.access</groupId>           <artifactId>streams</artifactId>           <version>1.0.0-SNAPSHOT</version>         </MavenCoordinates>         <Files>
           <File>streams-1.0.0-SNAPSHOT.jar</File>
         </Files>
       </Software>
     </Packages>
  </Profile>
</Resource>

Notice that:

  • the profile includes a single package and this package corresponds to the main build artefact of the component;
  • the whole profile and the package have usual gCube coordinates;
  • the package name is aligned with the Maven artifactId of the component;
  • the package version is aligned with the Maven version of the component;
  • the package includes MavenCoordinates that can be directly copied and pasted from the POM;
  • the package does not specify dependencies;
  • the package points to the main artefact of the Maven build;


The alignment between profile and POM can be exploited to simplify the management of the profile across different component versions. In particular, streams uses POM variables top define its profile:

<?xml version="1.0" encoding="UTF-8"?>
<Resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ID />
  <Type>Service</Type>
  <Profile>
    <Description>${description}</Description>
     <Class>DataAccess</Class>
     <Name>${artifactId}</Name>
     <Version>1.0.0</Version>
     <Packages>
       <Software>
         <Name>${artifactId}</Name>
         <Version>${version}</Version>
         <MavenCoordinates>
            <groupId>${groupId}</groupId>
            <artifactId>${artifactId}</artifactId>
             <version>${version}</version>
         </MavenCoordinates>
	 <Files>
            <File>${build.finalName}.jar</File>
         </Files>
       </Software>
     </Packages>
</Profile>
</Resource>

Note that the variables must be resolved (i.e. the profile is interpolated) before the profile can be used within the system. In particular, the main destination of the profile is the gCube Software Archive (SA) which packages the component for registration within the system.

Service Archive

streams is responsible for generating its own SA as part of its Maven build. This requires dedicated logic in the POM but has the advantage that:

  • SA validity can be verified locally;
  • no SA configurations are required in ETICS;
  • the required build logic can be easily reused across components;

streams uses the Maven Assembly Plugin to generate its own SA, with an approach that makes optimal use of Maven variable interpolation in static files such as README, svnpath.txt, MAINTAINERS, changelog.xml, etc. In particular, the Assembly plugin takes care of variable interpolation in the profile and other static files. The approach is best illustrated with a reference to the [sources] of the streams library.


As we discuss below, streams has the final requirement to produce an interpolated profile during ETICS builds, outside the context of the SA. streams meets this requirement with the Maven Resources Plugin. Again, consult the [sources] of the streams library to reuse this approach.

Services

The tree-manager service is a Maven component in gCube class DataAccess.

Project Structure

Differently from a library, the service is associated with a number of distinguished build artefacts:

  • the service implementation (a Jar);
  • the stubs automatically generated from the WSDL interfaces (a Jar);
  • the archive required for deployment in gCore (a Gar);
  • the archive required for registration in the system (a SA);

Producing these artefacts requires sharing a number of static and dynamic files (from profiles to WSDLs to pre-processed WSDLs). The usual Maven approach of mapping the artefacts onto different Maven projects is then genuinely difficult. It is instead more practical to use a single multi-module Maven project. The tree-manager uses this module structure:

  tree-manager  (parent module)------- (produces) --- (POM only)
         |
         |----- tree-manager-service   (service impl module)------- (produces) --- jar, gar, SA
         |                       |
         |                       | (depends on)
         |                       |
         |                      \/
         |----- tree-manager-stubs     (service stubs module)------- (produces) --- jar

As customary, the parent does not produce any artefact (is a POM-only project). It declares these coordinates in its POM:

<groupId>org.gcube.data.access</groupId>
<artifactId>tree-manager</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>

The parent physically includes the sub-modules;

<modules>
  <module>tree-manager-stubs</module>
  <module>tree-manager-service</module>
</modules>

Finally, the parent inherits from maven-parent:

<parent>
 <artifactId>maven-parent</artifactId>
 <groupId>org.gcube.tools</groupId>
 <version>1.0.0</version>
 <relativePath />
</parent>

The submodules inherit from the parent:

<parent>
  <artifactId>tree-manager</artifactId>
  <groupId>org.gcube.data.access</groupId>
  <version>1.0.0-SNAPSHOT</version>
  <relativePath>..</relativePath>
</parent>

and share its version and groupId. Their coordinates are then minimal:

<artifactId>tree-manager-service</artifactId>
<artifactId>tree-manager-stubs</artifactId>

The tree-manager-service module produces a Jar of its own implementation, as well the GAR and the SA of the whole services as secondary artefacts with classifiers of, respectively, gar and service archive.

The tree-manager-stubs module produces a Jar of its own implementation.

Finally, the parent module coordinates the build of its sub-modules and centralises the declaration of common dependencies and configuration. Refer to the [project sources] to reuse this approach.

Generating Stubs and Gars

The tree-manager-stubs uses the gCube Service Plugin to generate its classes from WSDL interfaces.

Similarly, the tree-manager-service uses the plugin to generate its secondary GAR artefact.

The configuration of the plugin is conveniently shared in the parent module.

Refer to the [project sources] of the streams for details.

Profile

The gCube Software profile (profile.xml) of a development version of the service is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<Resource>
	<ID></ID>
	<Type>Service</Type>
	<Profile>
		<Class>DataAccess</Class>
		<Name>tree-manager-service</Name>
		<Version>1.0.0</Version>
		<Packages>
			<Main>
				<Name>tree-manager-service</Name>
				<Version>1.0.0</Version>
				<MavenCoordinates>					<groupId>org.gcube.data.access</groupId>					<artifactId>tree-manager-service</artifactId>					<version>1.0.0-SNAPSHOT</version>				</MavenCoordinates>				<GARArchive>tree-manager-service-1.0.0-SNAPSHOT.gar</GARArchive>
				<PortType>
					<Name>gcube/data/tm/binder</Name>
				</PortType>
				<PortType>
					<Name>gcube/data/tm/reader</Name>
				</PortType>
				<PortType>
					<Name>gcube/data/tm/writer</Name>
				</PortType>
			</Main>
			<Software>
				<Name>tree-manager-stubs</Name>
				<Version>1.0.0</Version>
				<MavenCoordinates>					<groupId>org.gcube.data.access</groupId>					<artifactId>tree-manager-stubs</artifactId>					<version>1.0.0-SNAPSHOT</version>				</MavenCoordinates>				<Type>library</Type>
				<Files>
					<File>tree-manager-stubs-1.0.0-SNAPSHOT.jar</File>
				</Files>
			</Software>
		</Packages>
	</Profile>
</Resource>

Notice that:

  • the profile includes two packages and this package corresponds to the main build artefacts of the submodules;
  • the whole profile and the packages have usual gCube coordinates;
  • package names are aligned with the Maven artifactIds of the submodules;
  • package versions are aligned with the Maven versions of the submodules;
  • package include MavenCoordinatess that can be directly copied and pasted from the POM of the submodules;
  • packages do not specify dependencies;
  • packages points to the main artefact of the Maven build of the submodules;

Notice that, unlike libraries, services cannot easily use Maven variables in their profiles (as these artefacts are used for different purposes by different modules). Accordingly, developers needs to maintain versions used in profiles across releases. In particular, they need to:

  • replace SNAPSHOT versions with release versions in release branches;
  • advance SNAPSHOT versions in HEAD;

Profile Interpolation

The interpolation of profiles is required in order to keep information in profile in sync with pom.xml, in particular versions and filenames (that contain versions) that change at each release.

In order to generate profile.xml during build process you need to:

  1. add in the service's parent pom.xml the invocation to maven-resources-plugin
  2. place a template profile.xml in the ${distroDirectory}

Invocation to maven-resources-plugin looks like the following (to be placed in the <plugins> section inside the <build> section of the pom):

				<!-- interpolates profiles and copies from distribution location to configuration 
					location, where it is need for embedding into stub artifact, gar generation, 
					and service archive. -->
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-resources-plugin</artifactId>
					<version>2.6</version>
					<executions>
						<execution>
							<id>copy-profile</id>
							<goals>
								<goal>copy-resources</goal>
							</goals>
							<phase>process-resources</phase>
							<configuration>
								<outputDirectory>${configDirectory}</outputDirectory>
								<resources>
									<resource>
										<directory>${distroDirectory}</directory>
										<includes>
											<include>profile.xml</include>
										</includes>
										<filtering>true</filtering>
									</resource>
								</resources>
							</configuration>
						</execution>
					</executions>
				</plugin>

The template profile.xml looks like the following:

<?xml version="1.0" encoding="UTF-8"?>
<Resource>
	<ID></ID>
	<Type>Service</Type>
	<Profile>
		<Class>TestClass</Class>
		<Name>TestService</Name>
		<Version>1.0.0</Version>
		<Packages>
			<Main>
				<Name>TestService</Name>
				<Version>${project.version}</Version>				<MavenCoordinates>
					<groupId>org.example</groupId>
					<artifactId>test-service</artifactId>
					<version>${project.version}</version>				</MavenCoordinates>
				<GARArchive>>test-service-${project.version}.gar</GARArchive>			</Main>
			<Software>
				<Name>test-stubs</Name>
				<Version>${project.version}</Version>				<MavenCoordinates>
					<groupId>org.example</groupId>
					<artifactId>test-stubs</artifactId>
					<version>${project.version}</version>				</MavenCoordinates>
				<Type>library</Type>
				<Files>
					<File>test-stubs-${project.version}.jar</File>				</Files>
			</Software>
		</Packages>
	</Profile>
</Resource>

In the example, property ${project.version} will be substituted with the actual service's version at build-time. It is also possible, but not required, to parametrize also other values of profile.xml such as maven's coordinates and package names. You can see a more complex profile.xml template in the tree-manager project: http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/data-access/tree-manager/distro/profile.xml

Maven will copy the final profile.xml in ${configDirectory} during the process-resources phase of the build. From there it will be copied in the service-archive package.

Service Archive

tree-manager-service generates the service SA as part of its Maven build. This requires dedicated logic in the POM of the module and its parent.

As shown for libraries, the service uses the Maven Assembly Plugin to generate its own SA, with an approach that makes optimal use of Maven variable interpolation in static files such as README, svnpath.txt, MAINTAINERS, changelog.xml, etc. In particular, the Assembly plugin takes care of variable interpolation for these files. Refer to the [project sources] for details.

Portlets

The sample-portlet portlet is a Maven based sample portlet based on GWT. Refer to the project sources for details.

Project Structure

As for a library project, the portlet is associated with 2 distinguished build artefacts:

  • the archive required for deployment in a Liferay Container (a WAR);
  • the archive required for registration in the system (a SA);

In order to generate automatically the project structure and the base pom, we start by customizing the maven archetype liferay-portlet-archetype v. 6.0.6.

The generation of the project structure and the pom can be performed either from Eclipse ( by using the m2eclipse plugin) or by command line using the Maven SDK.

On Eclipse the following steps must be followed:

  • FIle -> New -> Project
  • From the wizard select Maven->Maven Project
  • From the Select Archetype step, Select 'All Catalogs' from the Catalogs combobox and type 'portlet' on the Filter field.
  • Select the liferay-portlet-archetype version 6.0.6 archetype ( if does not appear on the list just unselect 'show the last version of the Archetype only")
  • In the next step you have to provide Maven coordinates for your project
  • groupid : <your group id> e.g org.gcube.portlets.admin
  • artifactId : <your artifactId> e.g. sample-admin-portlet
  • version : <your project version> e.g. 1.0.0-SNAPSHOT ( use always SNAPSHOT versions for development versions of a project)
  • package: <your portlet code package> e.g. org.gcube.portlets.admin.sample

while from shell the following mvn command should be issued :

mvn archetype:generate \
    -DarchetypeArtifactId=liferay-theme-archetype \
    -DarchetypeGroupId=com.liferay.maven.archetypes \
    -DarchetypeVersion=6.0.6 \
    -DartifactId=<your artifactId> \
    -DgroupId=<your group id> \
    -Dversion=1.0.0-SNAPSHOT

Both approaches generate a project structure similar to the picture below:

Sample portlet project structure

In order to customize the build procedure to the gCube needs we should open the pom.xml and apply the following changes:

Configure the inheritance from maven-parent :

<parent>
 <artifactId>maven-parent</artifactId>
 <groupId>org.gcube.tools</groupId>
 <version>1.0.0</version>
 <relativePath />
</parent>

Configure some project properties:

<properties>
	<liferay.version>6.0.6</liferay.version>
	<liferay.auto.deploy.dir>${system.CATALINA_HOME}/../deploy</liferay.auto.deploy.dir>
	<distroDirectory>${project.basedir}/distro</distroDirectory>
	<configDirectory>${project.basedir}/config</configDirectory>
</properties>

Add the plugin for the generation of the SA, which has to be included under the build plugins section:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-assembly-plugin</artifactId>
	<version>2.2</version>
	<configuration>
            <descriptors>
	        <descriptor>${distroDirectory}/descriptor.xml</descriptor>
	   </descriptors>
	</configuration>
	<executions>
		<execution>
			<id>servicearchive</id>
			<phase>install</phase>
			<goals>
		          <goal>single</goal>
		       </goals>
		</execution>
	</executions>
</plugin>

In the case of a GWT portlet the gwt-maven-plugin should also be configured ( within build plugins section as well), by also selecting the proper gwt version ( in our example 2.4)

<plugin>
   <groupId>org.codehaus.mojo</groupId>
   <artifactId>gwt-maven-plugin</artifactId>
   <version>${gwt.version}</version>
   <executions>
	<execution>
         <goals>
	    <goal>compile</goal>
        </goals>
      </execution>
  </executions>
</plugin>


The proper GWT version should be added to the pom properties :

<gwt.version>2.4.0</gwt.version>

and GWT dependencies should be also configured:

<dependency>
            <groupId>com.google.gwt</groupId>
            <artifactId>gwt-user</artifactId>
            <version>${gwt.version}</version>
</dependency>
 <dependency>
            <groupId>com.google.gwt</groupId>
            <artifactId>gwt-servlet</artifactId>
            <version>${gwt.version}</version>
            <scope>provided</scope>
</dependency>

Refer to the [project sources] for further details on the project configuration.

Generating Artifacts

The sample-portlet uses the maven-compiler-plugin to compile the portlet code and the gwt-maven-plugin as well for GWT compilation.

The generation of a war package can be triggered by typing :

mvn package 

on your shell or running this build task within Eclipse.

In both cases the war package will be generated under the target folder, e.g.:

target/sample-admin-portlet-1.0.0-SNAPSHOT.war

The generation of all project artifacts ( war, source jar, javadoc, SA) can be triggered by the build task :

mvn install 

this will install as well all artifacts in your local repository, while the deployment of the artifacts to the remote gCube Maven repos can triggered by the build task:

mvn deploy 

Portlet dependencies

Specific portlet dependencies ( e.g. GWT, service stubs ) should be packaged within the portlet war. All dependencies specified within the pom file without the provided scope will be copied inside the WEB-INF/lib folder at packaging time.

For this reason all portlet specific dependencies should not specified that scope e.g:

<dependency>
	<groupId>org.gcube.externals</groupId>
	<artifactId>gwt-ext-patched</artifactId>
	<version>2.0.5</version>
</dependency>

On the other hand the system dependencies ( the one contained within the Portal bundle ) should not be included inside the portlet war. As said in this case the provided scope should be specified, e.g.

<dependency>
	<groupId>org.gcube.portal</groupId>
	<artifactId>custom-portal-handler</artifactId>
	<version>1.1.0-SNAPSHOT</version>
	<scope>provided</scope>
</dependency>
<dependency>
	<groupId>org.gcube.core</groupId>
	<artifactId>gcf</artifactId>
	<version>[1.4.0,1.5.0]</version>
	<scope>provided</scope>
</dependency>


A subset of these libraries ( contained in a GHN) that are required for compilation and integration testing have been coalesced in a single Jar, distributed as a Maven component called ghn-core-runtime. A dependency on this Jar can be specified as follows:

<dependency>
    <groupId>org.gcube.distribution</groupId>
    <artifactId>ghn-core-runtime</artifactId>
    <version>1.0.0</version>
    <scope>provided</scope>
</dependency>

Note: a component that depends on gcf indirectly depends on gun-core-runtime.

Include gCube Guided Tour using Maven

This particular component is not yet ported to maven so in order to be included in a maven based project it should be declared as system dependency as follows:

		<dependency>
   			<groupId>org.gcube.portletsuser</groupId>
   			<artifactId>gcube-guidedtour-widget</artifactId>
   			<version>1.0.0</version>
   			<scope>system</scope>
			<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/gcube-guidedtour-widget.jar</systemPath>		
   		</dependency>

together with the gCube Widget Library:

 
   		<dependency>
			<groupId>org.gcube.portlets.user</groupId>
			<artifactId>gcubewidgets</artifactId>
			<version>1.2.0</version>
			<scope>system</scope>
			<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/gCube-Widgets-Library.jar</systemPath>
		</dependency>

both components can be downloaded from the distribution repository [[1]].

In case the portlet depends on the gcf component, some dependency resolution changes are required to your portlet in order to properly compile ( because of libraries clashes)

The ghn-core-runtime component ( gcf dependency) should be excluded:

<dependency>
		<groupId>org.gcube.core</groupId>
		<artifactId>gcf</artifactId>
		<version>[1.5.1-SNAPSHOT,2.0.0)</version>
		<scope>provided</scope>
		<exclusions>
			<exclusion>
				<artifactId>ghn-core-runtime</artifactId>
				<groupId>org.gcube.distribution</groupId>
			</exclusion>
		</exclusions>
</dependency>


and some new dependencies should be added:

<dependency>
	<groupId>xerces</groupId>
	<artifactId>xercesImpl</artifactId>
	<version>2.9.1</version>
	<scope>provided</scope>
</dependency>
<dependency>
        <groupId>org.gcube.distribution</groupId>
	<artifactId>ws-addressing</artifactId>
	<version>1.0</version>
	<scope>provided</scope>
	</dependency>
<dependency>
       <groupId>org.gcube.distribution</groupId>
       <artifactId>axis-patched</artifactId>
       <version>1.2RC</version>
       <scope>provided</scope>
</dependency>

Profile

The gCube Software profile (profile.xml) of a development version of the portlet is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<Resource>
	<ID></ID>
	<Type>Service</Type>
	<Profile>
		<Class>PortletAdmin</Class>
		<Name>sample-admin-portlet</Name>
		<Version>1.0.0</Version>
		<Packages>
			<Software>
				<Name>sample-admin-portlet</Name>
				<Version>1.0.0</Version>
				<MavenCoordinates>					<groupId>org.gcube.portles.admin</groupId>					<artifactId>sample-admin-portlet</artifactId>					<version>1.0.0-SNAPSHOT</version>				</MavenCoordinates>                                <InstallScripts>
					<File>installPortlet.sh</File>
				</InstallScripts>
				<Files>
					<File>target/sample-admin-portlet-1.0.0-SNAPSHOT.war</File>
				</Files>
			</Software>
		</Packages>
	</Profile>
</Resource>

Notice that:

  • the whole profile and the packages have usual gCube coordinates;
  • package names are aligned with the Maven artifactId of the project;
  • package versions are aligned with the Maven version of the project;
  • package include MavenCoordinates that can be directly copied and pasted from the POM of the project;
  • packages do not specify dependencies;

The alignment between profile and POM can be exploited to simplify the management of the profile across different component versions. In particular, sample-portlet uses POM variables top define its profile:

<?xml version="1.0" encoding="UTF-8"?>
<Resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ID />
  <Type>Service</Type>
  <Profile>
    <Description>${description}</Description>
     <Class>PortletAdmin</Class>
     <Name>${artifactId}</Name>
     <Version>1.0.0</Version>
     <Packages>
       <Software>
         <Name>${artifactId}</Name>
         <Version>${version}</Version>
         <MavenCoordinates>
            <groupId>${groupId}</groupId>
            <artifactId>${artifactId}</artifactId>
             <version>${version}</version>
         </MavenCoordinates>
         <InstallScripts>
	     <File>installPortlet.sh</File>
	</InstallScripts>
	 <Files>
            <File>target/${build.finalName}.war</File>
         </Files>
       </Software>
     </Packages>
</Profile>
</Resource>

Note that the variables must be resolved (i.e. the profile is interpolated) before the profile can be used within the system. In particular, the main destination of the profile is the gCube Software Archive (SA) which packages the component for registration within the system.

Service Archive

sample-portlet generates the service SA as part of its Maven build. This requires dedicated logic in the POM of the project.

As shown for libraries, the portlet uses the Maven Assembly Plugin to generate its own SA, with an approach that makes optimal use of Maven variable interpolation in static files such as README, svnpath.txt, MAINTAINERS, changelog.xml, etc. In particular, the Assembly plugin takes care of variable interpolation for these files. Refer to the [project sources] for details.

Use Case: Create a new Mavenized GWT Portlet

In order to create a new Mavenized GWT Portlet this detailed guide can be also followed [2]

Widgets

Falls in this category of components mainly GWT widgets or so called GWT Libraries, which can be used to package classes for mutualization and/or modularization. A Widget is a java archive (JAR) containing both classes and java sources for later GWT compilation and a gwt.xml module descriptor.

In this section we refer to a sample Widget with maven structure (https://svn.research-infrastructures.eu/d4science/gcube/trunk/portal/mvn-widget-sample/)

Project Structure

As for a other project, the portlet is associated with 2 distinguished build artefacts:

  • the archive required for deployment (a JAR);
  • the archive required for registration in the system (a SA);

In order to generate automatically the project structure and the base pom, we start by customizing the maven archetype liferay-portlet-archetype v. 6.0.6.

The generation of the project structure and the pom can be performed either from Eclipse ( by using the m2eclipse plugin) or by command line using the Maven SDK.

On Eclipse the following steps must be followed:

  • FIle -> New -> Project
  • From the wizard select Maven->Maven Project
  • From the Select Archetype step, Select 'All Catalogs' from the Catalogs combobox and type 'portlet' on the Filter field.
  • Select the maven-archetype-quickstart version 1.1.0 archetype ( if does not appear on the list just unselect 'show the last version of the Archetype only")
  • In the next step you have to provide Maven coordinates for your project
  • groupid : <your group id> e.g org.gcube.portlets.admin
  • artifactId : <your artifactId> e.g. sample-admin-portlet
  • version : <your project version> e.g. 1.0.0-SNAPSHOT ( use always SNAPSHOT versions for development versions of a project)
  • package: <your portlet code package> e.g. org.gcube.portlets.admin.sample

while from shell the following mvn command should be issued :

mvn archetype:generate \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DarchetypeGroupId=com.liferay.maven.archetypes \
    -DarchetypeVersion=1.1.0 \
    -DartifactId=<your artifactId> \
    -DgroupId=<your group id> \
    -Dversion=1.0.0-SNAPSHOT

Both approaches generate a project structure similar to the picture below:

Sample widget project structure

In order to customize the build procedure to the gCube needs we should open the pom.xml and apply the following changes:

Configure the inheritance from maven-parent :

<parent>
 <artifactId>maven-parent</artifactId>
 <groupId>org.gcube.tools</groupId>
 <version>1.0.0</version>
 <relativePath />
</parent>

Configure some project properties:

<properties>
	<distroDirectory>${project.basedir}/distro</distroDirectory>
	<configDirectory>${project.basedir}/config</configDirectory>
</properties>

Add the plugin for the generation of the SA, which has to be included under the build plugins section:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-assembly-plugin</artifactId>
	<version>2.2</version>
	<configuration>
            <descriptors>
	        <descriptor>${distroDirectory}/descriptor.xml</descriptor>
	   </descriptors>
	</configuration>
	<executions>
		<execution>
			<id>servicearchive</id>
			<phase>install</phase>
			<goals>
		          <goal>single</goal>
		       </goals>
		</execution>
	</executions>
</plugin>

In the case of a GWT widget the gwt-maven-plugin should also be configured ( within build plugins section as well), by also selecting the proper gwt version ( in our example 2.4)

<plugin>
   <groupId>org.codehaus.mojo</groupId>
   <artifactId>gwt-maven-plugin</artifactId>
   <version>${gwt.version}</version>
   <executions>
	<execution>
         <goals>
	    <goal>compile</goal>
        </goals>
      </execution>
  </executions>
</plugin>


The proper GWT version should be added to the pom properties :

<gwt.version>2.4.0</gwt.version>

and GWT dependencies should be also configured:

<dependency>
            <groupId>com.google.gwt</groupId>
            <artifactId>gwt-user</artifactId>
            <version>${gwt.version}</version>
</dependency>
 <dependency>
            <groupId>com.google.gwt</groupId>
            <artifactId>gwt-servlet</artifactId>
            <version>${gwt.version}</version>
            <scope>provided</scope>
</dependency>

Refer to the [project sources] for further details on the project configuration.

Generating Artifacts

The sample-portlet uses the maven-compiler-plugin to compile the portlet code and the gwt-maven-plugin as well for GWT compilation.

The generation of a war package can be triggered by typing :

mvn package 

on your shell or running this build task within Eclipse.

In both cases the war package will be generated under the target folder, e.g.:

target/sample-admin-portlet-1.0.0-SNAPSHOT.war

The generation of all project artifacts ( war, source jar, javadoc, SA) can be triggered by the build task :

mvn install 

this will install as well all artifacts in your local repository, while the deployment of the artifacts to the remote gCube Maven repos can triggered by the build task:

mvn deploy 

Widget dependencies

The system dependencies ( the one contained within the Portal bundle ) should not be included inside the portlet war. As said in this case the provided scope should be specified, e.g.

<dependency>
	<groupId>org.gcube.portal</groupId>
	<artifactId>custom-portal-handler</artifactId>
	<version>1.1.0-SNAPSHOT</version>
	<scope>provided</scope>
</dependency>
<dependency>
	<groupId>org.gcube.core</groupId>
	<artifactId>gcf</artifactId>
	<version>[1.4.0,1.5.0]</version>
	<scope>provided</scope>
</dependency>

Profile

The gCube Software profile (profile.xml) of a development version of the widget is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<Resource>
	<ID></ID>
	<Type>Service</Type>
	<Profile>
		<Class>PortletAdmin</Class>
		<Name>sample-admin-portlet</Name>
		<Version>1.0.0</Version>
		<Packages>
			<Software>
				<Name>sample-admin-portlet</Name>
				<Version>1.0.0</Version>
				<MavenCoordinates>					<groupId>org.gcube.portles.admin</groupId>					<artifactId>sample-admin-portlet</artifactId>					<version>1.0.0-SNAPSHOT</version>				</MavenCoordinates>                                <InstallScripts>
					<File>installPortlet.sh</File>
				</InstallScripts>
				<Files>
					<File>target/sample-admin-portlet-1.0.0-SNAPSHOT.war</File>
				</Files>
			</Software>
		</Packages>
	</Profile>
</Resource>

Notice that:

  • the whole profile and the packages have usual gCube coordinates;
  • package names are aligned with the Maven artifactId of the project;
  • package versions are aligned with the Maven version of the project;
  • package include MavenCoordinates that can be directly copied and pasted from the POM of the project;
  • packages do not specify dependencies;

The alignment between profile and POM can be exploited to simplify the management of the profile across different component versions. In particular, sample-portlet uses POM variables top define its profile:

<?xml version="1.0" encoding="UTF-8"?>
<Resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ID />
  <Type>Service</Type>
  <Profile>
    <Description>${description}</Description>
     <Class>PortletAdmin</Class>
     <Name>${artifactId}</Name>
     <Version>1.0.0</Version>
     <Packages>
       <Software>
         <Name>${artifactId}</Name>
         <Version>${version}</Version>
         <MavenCoordinates>
            <groupId>${groupId}</groupId>
            <artifactId>${artifactId}</artifactId>
             <version>${version}</version>
         </MavenCoordinates>
         <InstallScripts>
	     <File>installPortlet.sh</File>
	</InstallScripts>
	 <Files>
            <File>target/${build.finalName}.war</File>
         </Files>
       </Software>
     </Packages>
</Profile>
</Resource>

Note that the variables must be resolved (i.e. the profile is interpolated) before the profile can be used within the system. In particular, the main destination of the profile is the gCube Software Archive (SA) which packages the component for registration within the system.

Service Archive

sample-portlet generates the service SA as part of its Maven build. This requires dedicated logic in the POM of the project.

As shown for libraries, the portlet uses the Maven Assembly Plugin to generate its own SA, with an approach that makes optimal use of Maven variable interpolation in static files such as README, svnpath.txt, MAINTAINERS, changelog.xml, etc. In particular, the Assembly plugin takes care of variable interpolation for these files. Refer to the [project sources] for details.