Create a Custom Product

Create a Custom Product

1 Objective

This article explains how to create a custom product. As you know eXo platform provides several products which can be integrated in one final product and used by end users. The example custom product of this tutorial is composed by eXo Portal, eXo DMS and eXo CS.

2 Prerequisites

You should know javascript, maven, pom files and internationalization. You should have used the eXoBuild command. You should be familiar with the configuration of eXo products. You should have the corresponding structure for exoBuild on your computer, that means Maven, jdk1.5 etc. should be prepared. The best preparation is to build an eXo product from sources?. This article refers to your eXo path using .../eXoProjects/ .

3 Find a Project Name

If you create a customer project product for eXo platform use the a name of a Greek mythological figure Take the next name in this list: http://en.wikipedia.org/wiki/List_of_Greek_mythological_figures#Other_deities

Transform it in lower-case letters. This example project is called "achilles", whenever you see "achilles" in the configuration sample you must replace it by your project name. In this article both terms product and project refer to ~~achilles ~~ (that means the customized eXo product you are configuring and preparing).

Create your base folder:
.../eXoProjects/achilles/trunk
The "trunk" folder always contains the current development state. If you create later versions you can create branches which are called "2.1" for example, the folder would be called "eXoProjects/achilles/branches/2.1". For this configuration we use "trunk" for the folder name, and "1.0" for the version number. Later versions would use for example "2.1" in both cases.

4 Create Pom Files

The precise versions of the products are Portal 2.5.2, DMS 2.3, CS 1.2RC1. As maven depends on pom files we have to create two pom files.

4.1 Root-Pom

Copy the pom.xml of the portal in the version you are going to use:
*http://svn.exoplatform.org:3840/projects/portal/tags/2.5.2/pom.xml*
into
eXoProjects/achilles/trunk
We call this pom "root-pom".

Adapt the root-pom
Delete all modules except "web/portal"; modify the groupId, name and version.

<groupId>org.exoplatform.achilles</groupId>
<artifactId>config</artifactId>
<name>eXo achilles Config</name>
<version>1.0</version>
and in the properties section (at the end) add:
<org.exoplatform.achilles.version>1.0</org.exoplatform.achilles.version>
<org.exoplatform.dms.version>2.3</org.exoplatform.dms.version>
<org.exoplatform.cs.version>1.2-RC1</org.exoplatform.cs.version>
These properties are used by Maven during the build process. Maven searches the right versions of each component of your custom product. Maven calls alls these components artifacts. In fact each artifact can be composed by other artifact. Maven looks into the dependency section of each artifact (which you are going to define in the portal-pom) and finds all depending artifacts.

4.2 Portal-Pom

Copy the pom.xml.of your portal version :
http://svn.exoplatform.org:3840/projects/portal/tags/2.5.2/web/portal/pom.xml
into
eXoProjects/achilles/trunk/web/portal/
We call this pom "portal-pom".

Adapt the portal-pom
Adapt the following (modelVersion is always 4.0.0 and the packaging remains "war"):

<modelVersion>4.0.0</modelVersion>
  <artifactId>achilles.web.portal</artifactId>
  <packaging>war</packaging> 
  <version>${org.exoplatform.achilles.version}</version>
  <name>achilles web module</name>
You see that by using ${org.exoplatform.achilles.version} you reference to the version of "achilles", you provided this version in the root-pom.

Dependencies
Add the dependencies of the products you want to integrate, the order is important. The dependency section uses references to the version properties of the root-pom:

<dependencies>
    <dependency>
      <groupId>org.exoplatform.portal</groupId>
      <artifactId>exo.portal.web.portal</artifactId>
      <version>${org.exoplatform.portal.version}</version>
          <type>war</type>
         <scope>runtime</scope>      
    </dependency>
    <dependency>
      <groupId>org.exoplatform.ecm.dms.core</groupId>
      <artifactId>exo.ecm.dms.core.web.portal</artifactId>
      <version>${org.exoplatform.ecm.dms.version}</version>
          <type>war</type>
         <scope>runtime</scope>      
    </dependency>
    <dependency>
      <groupId>org.exoplatform.cs</groupId>
      <artifactId>exo.cs.web.portal</artifactId>
      <version>${org.exoplatform.cs.version}</version>
          <type>war</type>
         <scope>runtime</scope>      
    </dependency>
  </dependencies>

Priority
The files of each products are copied to your target product. In some cases Portal, DMS, and CS use the same folders and files name, which causes name conflicts. This is especially true for configuration and internationalization files. In case of name conflicts the files of the first dependency take priority over the later files.

Impose configuration
You can impose your configuration and internationalization files by creating your own files in the trunk folder of achilles. Your own files have the highest priority. They must be in the same folder and have the same name as the files of the dependency artifacts. Maven compares and copies file by file. That means you can only partially impose your configuration, the other file are copied by Maven from the dependency artifacts.

Add an empty build. By doing so the build of Maven's super-pom is not used:

<build>
</build>

5 Javascript Configuration

The build process of eXo product involves configuration of javascript files. This is a little bit weird, but there is no way for you to circumvent this javascript configuration.

5.1 Create a product javascript

The product javascript file for achilles is called "trunk.js" because it serves for the trunk version of achilles. The day you have several versions you will have files called "2.0.js" or "2.3.js". This file uses the method Module.GetModule(). This method is provided in a different file called "modules.js" that we modify in a later step.

Create a new folder:
.../eXoProjects/tools/trunk/build/src/main/javascript/eXo/products/achilles/trunk.js
This file is used by eXoBuild.bat. For example if you specify --version=2.0 the file "2.0.js" is used. If no version is specified "trunk.js" is used.

You can glimpse at the final trunk file?

Look for the product javascript files of the products you want to integrate and "merge" them. In our case that means we use:

/eXoProjects/tools/trunk/build/src/main/javascript/eXo/products/portal/2.5.2.js
/eXoProjects/tools/trunk/build/src/main/javascript/eXo/products/dms/2.3.js
/eXoProjects/tools/trunk/build/src/main/javascript/eXo/products/cs/1.2-RC1.js

We copy the content of .../portal/2.5.2.js in our trunk.js
Adapt the "product section" using the configuration for "achilles", there are some extra configuration for eXo DMS so we copy them from "products/dms/2.3.js" (info: serverPluginVersion is the version of the eXo Portal, we take the highest portal version of the three products)

product.name = "achilles" ;
  product.portalwar = "portal.war" ;
  product.codeRepo = "achilles/trunk" ;
  product.serverPluginVersion = "2.5.2"
  product.useContentvalidation = true;
  product.contentvalidationVersion = "2.3";
  product.workflowVersion = "1.0";

We merge the "var section". Again we take always the highest version of each product. Pay attention to add the variables for portal, dms, and cs when merging. At the end we add one line for our custom product (achilles):

  var tool =  Module.GetModule("tools/trunk") ;
  var kernel = Module.GetModule("kernel/tags/2.0.6") ;
  var core = Module.GetModule("core/tags/2.1.4") ;
  var ws = Module.GetModule("ws/tags/1.3.2");
  var eXoPortletContainer = Module.GetModule("portlet-container/tags/2.0.5", {kernel : kernel, core : core}) ;
  var eXoJcr = Module.GetModule("jcr/tags/1.10.2", {kernel : kernel, core : core, ws : ws}) ;
  var portal = Module.GetModule("portal/tags/2.5.2", {kernel : kernel, ws:ws, core : core, eXoPortletContainer : eXoPortletContainer, eXoJcr : eXoJcr });
  var dms = Module.GetModule("ecm/dms/tags/2.3", {kernel : kernel, core : core, eXoPortletContainer : eXoPortletContainer, ws : ws, eXoJcr : eXoJcr, portal : portal});
  var cs = Module.GetModule("cs/tags/1.2RC1", {kernel : kernel, ws : ws, core : core, eXoPortletContainer : eXoPortletContainer, eXoJcr : eXoJcr, portal : portal});
  var achilles = Module.GetModule("achilles/trunk", {kernel : kernel, core : core, eXoPortletContainer : eXoPortletContainer, eXoJcr : eXoJcr, portal : portal});

We merge the "product.addDependencies" section. Only delete the doubled entries. Each "addDependency" adds a webapp in your custom product. Put in comments all portal dependencies:

/* product.addDependencies(portal.web.portal) ; */
/* product.addDependencies(dms.web.dmsportal); */
/* product.addDependencies(cs.web.csportal) ; */
Add the portal dependency for achilles:
product.addDependencies(achilles.web.portal) ;

The "product.addServerPatch" section is identical in all three products. No need to adapt this section.

Give your product the correct name:

product.module = achilles ;

Adapt the "product.dependencyModule" section. Our achilles project depends on the same products as the portal, but also on dms and cs:

product.dependencyModule = [tool, kernel, core, eXoPortletContainer, 
                              ws, eXoJcr, portal, dms, cs];
We keep all the lines that contain "product.removeDependency" which come from Portal and CS:
product.removeDependency(new Project("javax.mail", "mail", "jar", "1.4"));
...

5.2 Create a module.js

.../eXoProjects/tools/trunk/build/src/main/javascript/eXo/modules/achilles/trunk/module.js

Create a framework of a module.js:

eXo.require("eXo.projects.Module") ;
eXo.require("eXo.projects.Product") ;

function getModule(params) {

  return module;
}

Copy the var section of the portal/module.js to achilles/trunk/module.js only two parameters are needed for the dependencies:

var jcr = params.eXoJcr;
var portal = params.portal;

Adapt the module section to your new product:

var module = new Module();
  module.version =  "1.0" ;
  module.relativeMavenRepo =  "org/exoplatform/achilles" ;
  module.relativeSRCRepo =  "achilles/trunk" ;
  module.name =  "achilles" ;

At the very end add

module.web = {};
  module.web.portal = 
    new Project("org.exoplatform.achilles", "exo.achilles.web.portal", 
                "exo-portal", module.version).
    addDependency(portal.web.eXoResources) .
    addDependency(portal.web.eXoMacSkin) .
    addDependency(portal.web.eXoVistaSkin) .
    addDependency(portal.webui.portal) .
    addDependency(jcr.frameworks.command) .
    addDependency(jcr.frameworks.web);

The syntax of new Project() is:
new Project("{maven-groupId}", "{maven-artifact-id}", "{ptype-extension}", maven-module-version)

The ptype-extension defines the extension for the product type:

  • exo-portlet or exo-portal: war
  • exo-ear-jar: jar
  • exopc-war: exopc-war
  • exo-ear-rar: rar
The maven-module-version must correspond to the version in the pom files.

Test your pom configuration:

mvn clean install

Look in target folder for results. .../eXoProjects/achilles/trunk/web/portal/target

6 Adapt configuration.xml

As you learned when defining the pom files you can impose your own configuration. In the case of the configuration.xml file each product provides its own file but only the configuration of the portal is copied. You need to merge the configuration of all products. Create new a folder:

.../eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/conf
and copy the configuration.xml of portal 2.5.2 to this new folder. Adapt your configuration.xml by including the import entries of the correspondent DMS and CS configuration files and delete any doubled entry.

Doubled Services
The ResourceBundleService is defined two times in two different configuration files. In order to reduce confusion, let's delete the doubled configuration. Copy common-configuration.xml from portal and delete the Resource Bundle part:

<key>org.exoplatform.services.resources.ResourceBundleService</key>
<type>org.exoplatform.services.resources.jcr.ResourceBundleServiceImpl</type>
...
(You find the file at ".../eXoProjects/portal/tags/2.5.2/web/portal/src/main/webapp/WEB-INF/conf/common/common-configuration.xml" and you have to copy it to .../eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/conf/common)

The ResourceBundleService is configured for a second time in resource-bundle-configuration.xml. This file will be copied by Maven from DMS to your custom product. You don't need to copy or edit it.

7 Adapt web.xml

Copy the web.xml of DMS (starting with DMS because it contains also portal) to:

eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/web.xml

Compare all details of your product's web.xml and the CS web.xml There is no differences that relevant for merging. So you can leave your web.xml unchanged.

8 First Launch

Launch exoBuild (do not forget to prepare the environment variables using "exoenv.bat" or "source exoenv.sh") :

exobuild --product=achilles --exclude=all --build --deploy
Launch the application server
.../exo-working/exo-tomcat/bin/eXo.bat run
You should see a running portal application. If you open the application registry you will find also the portlets of DMS and CS. But the navigation contains no links to these portlets, because only the portal navigation was copied to the target. You will also see some problems with the internationalization, we will tackle both problems.

9 Navigation

Create your own navigation: Copy all group nagivations of the DMS (because this is the most complete navigation and it already contains the portal navigation) to achilles:

.../eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/conf/portal/group
For the other navigations there is no need to copy them or edit them. In any case the final navigation depends on the needs of your project. During the project you will certainly adapt it several times to your requirements.

Now compare CS navigation with your new achilles navigation (that you jus have copied from DMS). Add the missing navigation nodes and pages, that means the nodes and pages that are defined in CS and not (yet) in achilles.

You will notice that the dashboard of Portal is missing in the navigation of ECM. If you need it you must copy it from the Portal navigation:
.../eXoProjects/portal/tags/2.5.2/web/portal/src/main/webapp/WEB-INF/conf/portal/group/platform/users/navigation.xml

Rebuild (using exobuild) the project and see if everything works fine. If you accidentally declare the same page two times you will see an error in your tomcat console.

10 Internationalization Issues

You are seeing that the menu items of the navigation are not properly shown in English There are only the keys (like "platform.users.collaboration.forum") that are used by internationalization (also called i18n).

The translation is usually done in locale files. These files contain properties, that means key-value pairs. These files are also copied to your final product and nearly everything goes smoothly but some navigation node translations have got lost. The reason is that all three products define navigation nodes for the same user group. The group concerned is the "platform/users" group. The English translation of the navigation nodes of this group are defined in

.../eXoProjects/portal/tags/2.5.2/web/portal/src/main/webapp/WEB-INF/classes/locale/navigation/group/platform/users_en.properties

If you look into the same folder of DMS and CS you will find similar locale files. In your custom product only the users_en.properties of portal is copied, because this is the first product in the dependency list in the portal-pom. The files of the other products are ignored (please read ahead to understand the presence of the xml files in the same folder).

Merge the locale files
DMS has the most complete translations, therefore copy the whole locale folder tree of DMS to your custom product. By doing so the you define an adapted configuration for Achilles:

.../eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/classes/locale/**

You will modify the files you just copied from DMS. Note that DMS does not use ".properties" files but ".xml" files that have the same purpose and the same content. As the other products use ".properties" files you will see in the target folder that you get a mixture of ".xml" and ".properties" files. See info box below.

Merge the properties of the "platform/users" group that are provided by each product. That means you open the user_en.properties of both products Portal and CS and you create all the navigation node translations that are missing. You must create a new xml structure and create new xml tags to repect the property structure. Please refer to XML Resource Bundles to see how to do it.

You must repeat this procedure also for the default file "users.xml" (just copy the English version) and for all languages you need ("users_fr.xml", "users_ar.xml", "users_vir.xml" etc.). Before you start, read a little bit ahead, because you first have to solve a litte issue.

Unsuitable property
CS uses a locale key structure in users.properties that is not possible to transform for the .xml file:

platform.users.collaboration=Collaboration Suite
platform.users.collaboration.forum=Forum Application
platform.users.collaboration.contact=Contact Application
...

The problem is that "platform.users.collaboration" and "platform.users.collaboration.forum" would be converted to so called "mixed content" in the collaboration tag. Mixed content is not good style and is not covered by the ResourceBundleService.

Therefore modify the key "platform.users.collaboration" in "platform.users.collaboration.title" in this file:
.../eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/conf/portal/group/platform/users/navigation.xml

<node>
  <uri>collaboration</uri> 
  <name>collaboration</name> 
  <label>#{platform.users.collaboration}</label> 
...
Adapt the locale files (users_en.xml etc.) so that they use the new key ("platform.users.collaboration.title").

XML and properties

If you mix xml and properties files in the same folder (for example: "users_en.xml" and "users_en.properties") the xml-file takes priority. The ".properties" file is ignored. This means if you create a .properties file in your achilles configuration the build will not work as you expect. The problem is that Maven copies the configuration files of all products, that means also the xml files of DMS.

These xml files take priority over your custom .properties file. Therefore you must use .xml files if one of the products to be integrated uses xml files.

Correct portal.classic.webexplorer

If you have tested the application you will see an other problem with the locale key portal.classic.webexplorer. This key is configured in the file:
http://wiki.exoplatform.com/xwiki/bin/edit/Products+Interop/Create+a+Custom+Product .../eXoProjects/portal/tags/2.5.2/web/portal/src/main/webapp/WEB-INF/classes/locale/navigation/portal/classic_en.properties

You remember that you copied the locale files from DMS to Achilles. Therefore you must add the property "portal.classic.webexplorer" to this file:
../eXoProjects/achilles/trunk/web/portal/src/main/webapp/WEB-INF/classes/locale/navigation/portal/classic_en.xml

Rebuild your project and nearly everything should work fine.

11 Tripled Folders

There are three folders for each user, and two folders for each group.

11.1 Tripled User Folders

This is due to the ecm.new.user.event.listener service. This service listens for new users and creates automatically user folders in the Collaboration Center drive.

  • In this drive open the users folder. You will see that 3 folders have been created for each user (distinguished by numbering [1] or [2]).
DMS and CS have configured the same plugin called "NewUserListener" as you can see here:

ProductConfig PathComponent/ServiceType
Portal.../WEB-INF/conf/jcr/component-plugins-configuration.xmlorg.exoplatform.services.jcr.ext. hierarchy.NodeHierarchyCreatororg.exoplatform.services.jcr.ext. hierarchy.impl.AddPathPlugin
DMS.../WEB-INF/conf/dms/organization-component-plugins-configuration.xmlorg.exoplatform.services. organization.OrganizationServiceorg.exoplatform.services. jcr.ext.hierarchy.impl.NewUserListener
CS.../WEB-INF/conf/cs/jcr-component-plugins-configuration.xmlorg.exoplatform.services. organization.OrganizationServiceorg.exoplatform.services. jcr.ext.hierarchy.impl.NewUserListener

The configuration looks like:

<component-plugin>
  <name>ecm.new.user.event.listener</name> 
  <set-method>addListenerPlugin</set-method> 
  <type>org.exoplatform.services.jcr.ext.hierarchy.impl.NewUserListener</type> 
  ...

You should delete or comment two of these NewUserListener configurations. Therefore copy the configuration files of DMS and CS to the archilles trunk and modify the files there.

In organization-component-plugins-configuration.xml there is a org.exoplatform.services.cms.queries.impl.NewUserListener which creates queries which are doubled you should comments these lines.

11.2 Doubled Group Folders

The drive.new.group.event.listener listens for new groups and creates automatically group folders in the Collaboration Center drive.

  • In this drive open the groups folder. You will see that 2 folders have been created for each group, (distinguished by numbering [1]).
The configuration look as follows:
<component-plugin>
  <name>drive.new.group.event.listener</name> 
  <set-method>addListenerPlugin</set-method> 
  <type>org.exoplatform.services.cms.drives.impl.NewGroupListener</type> 
...

You should delete or comment one of these configurations. Open the file organization-component-plugins-configuration.xml you have copied in the step before and comment the lines concerning NewGroupListener.

If you rebuild and run the application again you will see, that there is only one folder per user and per group.

Tags:
Created by Sören Schmidt on 03/27/2009
Last modified by Sören Schmidt on 08/03/2009

Products

generated on Thu Sep 02 15:47:09 UTC 2010

eXo Optional Modules

eXo Core Foundations


Copyright (c) 2000-2010. All Rights Reserved - eXo platform SAS
2.4.30451