Chapter 1. Migration Guide

Preamble
Prerequisites
Questions to ask
Prepare your extension project
Prepare eXo XML customizations
Portal Migration
Portal declarations
Portal metadata descriptors
Resource Bundles
ApplicationRegistry service configurations
API changes
Custom portlets migration
ECM Migration
ECM portlets
ECM actions
ECM drives
Publication lifecycle
ECM APIs changes
ECM templates migration
ECM UI extension migrations
Graphic Chart Migration
Changes in web.xml of webapps
new deployment descriptor : gatein-resource.xml
Javascripts
CSS
Organization Data Model Migration
Data Migration
Server Environment Migration
Data sources

This guide will help you migrating your project from eXo All In One 1.6 to eXo Platform 3.0.

You will need to create a brand new project and migrate your old one into it. Platform introduced a new way to package your project customization called extensions. By using WAR extension configuration approach, you will be able to overload eXo default files defined in WARs, such as gtmpl, favicon, xml, jsp files. If you don't understand what that means, get back to Developers Guide to learn how to bootstrap the basic structure for your extension project.

The extension mechanism allows to load xml configuration files from your extension war. Previously, you had to wire the loading of your xml configurations through the external configuration directory via import statements. Now, you can place all your xml files in your extension webapp. eXo Platform will read and load from your WEB-INF/conf/configuration.xml.

Read more on this in the Developers Guide.

Tip

In most cases, you should not even need to do it. But if you are still tempted to alter an xml file of portal.war, that's simple : don't do it! The extension mechanism lets you virtually override any file from portal.war by placing a file in your extension at the exact same location. For example to override, portal.war!WEB-INF/conf/database/configuration.xml, simply place a your file in myextension.war!WEB-INF/conf/database/configuration.xml.

Note

Be careful about the files shifting to the WAR extension, use war: prefix to redefine the moved Metadata & Gtmpl files, such as:

  • CLV Templates

  • Locales config file definition

  • Repository configuration

  • NodeTypes Templates

  • Gtmpl files

Although it is not strictly required, eXo recommends you to use Apache Maven 2.2 to build your project. You will benefit of the eXo maven repository for all eXo libraries and their dependencies.

ECM dependencies have changed significantly. The table below should help you to match old and new GAVs :

AIO PLF

<dependency>
  <groupId>org.exoplatform.ecm.dms.core</groupId>
   <!-- any artifact -->
  <artifactId>*</artifactId>
  <version>${org.exoplatform.ecm.dms.version}</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>org.exoplatform.ecms</groupId>
  <artifactId>exo-ecms-core-services</artifactId>
  <version>${org.exoplatform.ecms.version}</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>org.exoplatform.ecm.dms.core</groupId>
  <artifactId>exo.ecm.dms.core.webui.ext</artifactId>
  <version>${org.exoplatform.ecm.dms.version}</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>org.exoplatform.ecms</groupId>
  <artifactId>exo-ecms-core-webui</artifactId>
  <version>${org.exoplatform.ecms.version}</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>org.exoplatform.ecm.dms.core</groupId>
  <artifactId>exo.ecm.dms.core.portlet.ecm.core.main</artifactId>
  <version>${org.exoplatform.ecm.dms.version}</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>org.exoplatform.ecms</groupId>
  <artifactId>exo-ecms-core-webui-explorer</artifactId>
  <version>${org.exoplatform.ecms.version}</version>
  <scope>provided</scope>
</dependency>

In AIO, ECM was composed of 3 modules with a separate lifecycle (DMS, WCM and Workflow) and each artifact had a different version. In Platform, the ECM subsystem is now called eXo Content and the 3 modules follow the same lifecycle. So their artifacts hold the same version.

So, if your AIO project had defined properties like org.exoplatform.ecm.dms.version or org.exoplatform.ecm.wcm.version, replace them by org.exoplatform.ecms.version.

As you know, most of the customization in AIO is done via xml files. It is still the case in PLF, but they have evolved. The list of available components and plugins has changed, and the xml files layout has also evolved.

Customizing pages and navigations is done by the component named org.exoplatform.portal.config.UserPortalConfigService.

In AIO, you had to redefine the component entirely. In Platform, there is now an external-component-plugin, that you can call from your extension in order to add your own customizations. See this chapter for further information.

Transform your component declaration into an external-component-plugin declaration

AIO PLF

<component>
	<key>org.exoplatform.portal.config.UserPortalConfigService</key>
	<type>org.exoplatform.portal.config.UserPortalConfigService</type> 
	...
		<value-param>
			<name>default.portal</name>
			<description>The default portal</description>
			<value>defaultPortalName</value>
		</value-param>
	...
</component>

<external-component-plugins>
	<target-component>org.exoplatform.portal.config.UserPortalConfigService</target-component>
	... (same thing)
		<!-- If you define a new "default.portal" value in your extension,
		it will be considered as prior to any other "default.portal" definition -->
		<value-param>
			<name>default.portal</name>
			<description>The default portal</description>
			<value>defaultPortalName</value>
		</value-param> 
	... (same thing)
</external-component-plugins>

Use Migration Tools (see, the Migration Tools Guide, section Portal Configuration Migration ) to export and convert automatically AIO Portal metadata descriptors. The following paragraphs describe more about what was changed in PLF Portal metadata descriptors:

The navigation.xml file contains the navigations of your portal. In AIO, a node could be visible or invisible. Gatein has extended the concept of visibility. AIO's visibility maps as follow in PLF :

AIO PLF

<visible>false</visible>

<visibility>HIDDEN</visibility>

<visible>true</visible>

<visibility>DISPLAYED</visibility>

Tip

Refer to this Gatein Objects Schema file to see the possible "visibility" element values

pages.xml contains the definition of the structure of each page.

In AIO, the instance-id was a string identifier for the window, indicating what portlet to instantiate. In Platform, this has been simplified largely, only the application and the portlet names are needed:

AIO PLF

<application>
  <instance-id>owner-type#owner-id:/application/portlet/window-id</instance-id>
</application>

<portlet-application>
  <portlet>
    <application-ref>application</application-ref>
    <portlet-ref>portlet</portlet-ref>
    <preferences><!-- ... --></preferences>
  </portlet>
  <!-- ... -->
</portlet-application>

The portlet-preferences.xml is used to configure portlet instances preferences. This will not be necessary in PLF, because the portlet preferences are now inlined in pages.xml.

To migrate your portlet-preferences.xml, extract the <portlet-preferences> blocks and insert them in the corresponding <portlet-application> of pages.xml.

AIO (portlet-preferences.xml) PLF (pages.xml)

<portlet-preferences>
  <owner-type>portal</owner-type>
  <owner-id>classic</owner-id>
  <window-id>portal#classic:/web/NavigationPortlet/toolbar</window-id>
  <preference>
    <name>useAJAX</name>
    <value>true</value>
    <read-only>false</read-only>
  </preference>
</portlet-preferences>

<portlet-application>
  <portlet>
    <application-ref>web</application-ref>
    <portlet-ref>NavigationPortlet</portlet-ref>
    <preferences>
      <preference>
        <name>useAJAX</name>
        <value>true</value>
        <read-only>false</read-only>
      </preference>
    </preferences>
  </portlet>
  <!-- ... -->
</portlet-application>

In Platform, the edition of group and portal navigations is implemented by portlets. Hence, if you want to enable that feature in your portal, you will need to add these pages in pages.xml and link them to navigation nodes in navigation.xml. Read more about it in GateIn Reference Guide.

Group navigations are managed by the GroupNavigationPortlet :

<page>
  <name>groupnavigation</name>
  <title>Group Navigation</title>
  <access-permissions>*:/platform/users</access-permissions>
  <edit-permission>*:/platform/administrators</edit-permission>
  <portlet-application>
    <portlet>
      <application-ref>exoadmin</application-ref>
      <portlet-ref>GroupNavigationPortlet</portlet-ref>
    </portlet>
    <title>Group Navigation</title>
    <access-permissions>Everyone</access-permissions>
    <show-info-bar>false</show-info-bar>
    <show-application-state>false</show-application-state>
    <show-application-mode>false</show-application-mode>
  </portlet-application>
</page>

Portal navigations are managed by the PortalNavgationPortlet:

<page>
  <name>portalnavigation</name>
  <title>Portal Navigation</title>
  <access-permissions>*:/platform/users</access-permissions>
  <edit-permission>*:/platform/administrators</edit-permission>
  <portlet-application>
    <portlet>
      <application-ref>exoadmin</application-ref>
      <portlet-ref>PortalNavigationPortlet</portlet-ref>
    </portlet>
    <title>Portal Navigation</title>
    <access-permissions>Everyone</access-permissions>
    <show-info-bar>false</show-info-bar>
    <show-application-state>false</show-application-state>
    <show-application-mode>false</show-application-mode>
  </portlet-application>
</page>

Warning

Since you may not want to have them in your portal, these pages are not automatically added by the Migration Tool, it should be added manually !

You may have defined some custom Resource Bundles, it is no longer used/defined in a component configuration, you 've to change it into an external component plugin. In this external component plugin you will have to define only your own Resource Bundles, which means, Resource Bundles declaration shouldn't be declared in your configuration:

AIO PLF

<component>
<key>org.exoplatform.services.resources.ResourceBundleService</key>
<type>org.exoplatform.services.resources.jcr.ResourceBundleServiceImpl</type>
<init-params>
...
</init-params>
</component>

<external-component-plugins>
 <!-- New Resource Bundles declaration (plugin the list of Resource Bundles) -->
 <target-component>org.exoplatform.services.resources.ResourceBundleService</target-component>
 <component-plugin>
	<!-- The name of the plugin -->
	<name>Sample ResourceBundle Plugin</name>
	<!-- The name of the method to call on the ResourceBundleService in order to register the ResourceBundles -->
	<set-method>addResourceBundle</set-method>
	<!-- The full qualified name of the BaseResourceBundlePlugin -->
	<type>org.exoplatform.services.resources.impl.BaseResourceBundlePlugin</type>
	<init-params>
	<!-- Keep only your own Resource Bundles declarations -->
...
	</init-params>
 </component-plugin>
</external-component-plugins>

If you have made some modifications in Resource Bundles in your AIO Server, you could extract all the Resource Bundles stored in the JCR by using the Migration Tools (see the Migration Tools Guide, section Resource Bundles Migration).

The Application Registry application can be configured to predefine categories and permissions on portlets and gadgets. If you have customized this configuration, you need to migrate.

AIO PLF

<component>
  <key>org.exoplatform.application.registry.ApplicationRegistryService</key>
<!-- AIO "ApplicationRegistryService" implementation -->
  <type>org.exoplatform.application.registry.impl.ApplicationRegistryServiceImpl</type>
...
<field name="applicationType">
	<string>portlet</string>
</field>
...
<field name="applicationGroup">
	<string>exoadmin</string>
</field>
...
</component>

<component>
  <key>org.exoplatform.application.registry.ApplicationRegistryService</key>
<!-- PLF "ApplicationRegistryService" implementation -->
  <type>org.exoplatform.application.registry.jcr.ApplicationRegistryServiceImpl</type>
...
<!-- "applicationType" is replaced by "type"-->
<field name="type">
	<string>portlet</string>
</field>
...
<!-- "applicationGroup" is replaced by "contentId"-->
<field name="contentId">
	<string>exoadmin</string>
</field>
...
</component>

You may have modified the applications and gadgets categories, by using the "Application Registry Portlet", this configuration is stored in the JCR. In order to extract this new configuration, you could use the Migration Tools ( see Migration Tools Guide, section Application Registry Migration).

If your code uses eXo APIs, you may be affected by some API changes.

In your gtmpl files, replace Groovy markers :

AIO PLF
$someVariable <%=someVariable%>

If you have implemented your own portlets, you will need to edit their web.xml :

  <servlet>
    <servlet-name>GateInServlet</servlet-name>
    <servlet-class>org.gatein.wci.api.GateInServlet</servlet-class>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet>
    <servlet-name>RestServer</servlet-name>
    <description>eXo - Platform REST Server</description>
    <servlet-class>org.exoplatform.services.rest.servlet.RestServlet</servlet-class>
    <load-on-startup>4</load-on-startup>
  </servlet>

The descriptor file of struts portlet (WEB-INF/portlet.xml) needs be to updated :

AIO PLF

<portlet>
  ...
  <init-param>
    <name>ServletContextProvider</name>
    <value>org.exoplatform.ServletContextProviderImpl</value>
  </init-param>
  ...
</portlet> 

<portlet>
   ...
   <init-param>
     <name>ServletContextProvider</name>
     <value>org.gatein.pc.bridge.GateInServletContextProvider</value>
   </init-param>
   ...
 </portlet> 

If you have made some live changes on ECM Administration templates and configurations, you will need to use the Migration Tools in order to extract the whole configurations in a Kernel's source format. See the Migration Tools Guide for more informations, sections:

If you have defined an ECM action, or Taxonomy Action then you have to change the type and the value of the field lifecyclePhase as follow:

AIO PLF

  <external-component-plugins>
    <target-component>org.exoplatform.services.cms.actions.ActionServiceContainer</target-component>
...
		<object type="org.exoplatform.services.cms.actions.impl.ActionConfig$Action">
...
		<!--AIO "lifecyclePhase" field declaration-->
		   <field name="lifecyclePhase">
			 <string>add</string>
		   </field>
...
		</object>
...
</external-component-plugins>

<external-component-plugins>
    <target-component>org.exoplatform.services.cms.actions.ActionServiceContainer</target-component>
...
		<object type="org.exoplatform.services.cms.actions.impl.ActionConfig$Action">
...
		<!--PLF "lifecyclePhase" field declaration-->
		  <field name="lifecyclePhase">
			<collection type="java.util.ArrayList">
			 <value><string>node_added</string></value>
			</collection>
		  </field>
...
		</object>
...
</external-component-plugins>

And same thing for TaxonomyPlugin declaration:

AIO PLF

<external-component-plugins>
  <target-component>org.exoplatform.services.cms.taxonomy.TaxonomyService</target-component>
...
        <object type="org.exoplatform.services.cms.actions.impl.ActionConfig$TaxonomyAction">
...
         <!--AIO "lifecyclePhase" field declaration-->
         <field name="lifecyclePhase">
           <string>add</string>
         </field>
...
		</object>
...
</external-component-plugins>

<external-component-plugins>
  <target-component>org.exoplatform.services.cms.taxonomy.TaxonomyService</target-component>
...
  <object type="org.exoplatform.services.cms.actions.impl.ActionConfig$TaxonomyAction">
...
	 <!--PLF "lifecyclePhase" field declaration-->
	 <field name="lifecyclePhase">
	  <collection type="java.util.ArrayList">
	   <value>
		<string>node_added</string>
	   </value>
	  </collection>
	 </field>
...
	</object>
...
</external-component-plugins>

org.exoplatform.services.cms.actions.impl.ActionConfig can now accept a set of mixins. An interesting one is mix:affectedNodetype. It allows to filter the nodetype on which the action is triggered.

allowCreateFolders field declaration has been modified in the ECMS Drives declaration configuration:

AIO PLF

   <external-component-plugins>
      <target-component>org.exoplatform.services.cms.drives.ManageDriveService</target-component>
      <component-plugin>
...
		<type>org.exoplatform.services.cms.drives.impl.ManageDrivePlugin</type>
...
			<!-- AIO "allowCreateFolders" field declaration -->
                <field name="allowCreateFolder"><string>Both</string></field>
...
      </component-plugin>
</external-component-plugins>

   <external-component-plugins>
      <target-component>org.exoplatform.services.cms.drives.ManageDriveService</target-component>
...
		<type>org.exoplatform.services.cms.drives.impl.ManageDrivePlugin</type>
...
            <!-- PLF "allowCreateFolders" field declaration -->
            <field name="allowCreateFolders">
              <string>nt:folder,nt:unstructured</string>
            </field>
...
   </external-component-plugins>

AIO PLF

[...]
<type>org.exoplatform.services.cms.drives.impl.NewGroupListener</type>
[...]
<value-param>
 <name>allowCreateFolder</name>
 <value>Both</value>
</value-param>
[...]

[...]
<type>org.exoplatform.services.cms.drives.impl.NewGroupListener</type>
[...]
<value-param>
  <name>allowCreateFolder</name>
  <value>nt:folder,nt:unstructured</value>
</value-param>
<value-param>
  <name>allowNodeTypesOnTree</name>
  <value>*</value>
</value-param>
[...]

In Platform, the default publication public has changed to a new one, more flexible called Authoring plugin. The old StageAndVersion is now deprecated. It is recommended that you upgrade to the new plugin. Read more about the new Authoring extension in ECMS reference guide.

To migrate gracefully, it is possible to define a lifecycle that behaves identically to the StageAndVersion plugin. Use the Migration Tools (see Migration Tools Guide, section WCM Contents Publication Migration).

Replace folksonomy service :

AIO PLF
import org.exoplatform.services.cms.folksonomy.FolksonomyService org.exoplatform.services.cms.folksonomy.NewFolksonomyService

For ECM groovy templates, follow this :

AIO PLF
uicomponent.isShowHeader() uicomponent.isShowField(UIPCLVPortlet.SHOWHEADER)

or uicomponent.isShowField(UICLVPortlet.PREFERENCESHOWHEADER) |

uicomponent.showRefreshButton(_) uicomponent.isShowField(UIPCLVPortlet.SHOWREFRESHBUTTON)

or isShowField(UICLVPortlet.PREFERENCESHOWREFRESHBUTTON) |

uicomponent.getPortletPreferences() portletRequest.getPreferences()  
UICLVPortlet.SHOWXX_ UICLVPortlet.PREFERENCESHOWXX_  
UICLVPortlet.SHOWTHUMBNAILSVIEW UICLVPortlet.PREFERENCESHOWILLUSTRATION  
def viewNode = Utils.getNodeView(node);

def contentNodeLocation = NodeLocation.getNodeLocationByNode(node);
def viewNode = Utils.getViewableNodeByComposer(contentNodeLocation.getRepository(), contentNodeLocation.getWorkspace(), contentNodeLocation.getPath());

 

In Platform, css and javascript integration adopts a new descriptor. Please refer to the skinning paragraph of the Developer guide. You can also read the Skinning & Javascript Configuration Chapters in the Reference guide of GateIn.

Web resources WARs (images, css and Javascript) had some specific listeners declared in its web.xml files, this should be modified as following table shows

AIO PLF

<filter>
	<filter-name>ResourceRequestFilter</filter-name>
	<filter-class>org.exoplatform.portal.application.ResourceRequestFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>ResourceRequestFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
 <!-- PLF don't use this part anymore -->
 <listener>
   <listener-class>org.exoplatform.portal.webui.skin.SkinConfigListener</listener-class>
 </listener>
 <listener>
   <listener-class>org.exoplatform.portal.webui.javascript.JavascriptListener</listener-class>
 </listener>

<filter>
	<filter-name>ResourceRequestFilter</filter-name>
	<filter-class>org.exoplatform.portal.application.ResourceRequestFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>ResourceRequestFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

Note

Lean more about ResourceRequestFilter (section "3.1.5.2. Resource Request Filter").

In AIO, you used to load a javascript file thank to a descriptor called conf/script/groovy/JavascriptScript.groovy, it can be directly mapped to a <javascript> block in gatein-resources.xml.

AIO PLF

JavascriptService.addJavascript("module.name", "/path/to/file.js", ServletContext);

<javascript>
 <param>
	<js-module>module.name</js-module>
	<js-path>/path/to/file.js</js-path>
 </param>
</javascript>

In AIO, you used to declare a css thank to a descriptor called conf/script/groovy/SkinConfigScript.groovy. The content of this file is mapped to different blocks in gatein-resources.xml.

AIO PLF
 

SkinService.addPortalSkin("skinModule","skinName", "/path/to/file.css", ServletContext);

<portal-skin>
  <skin-name>skinName</skin-name>
  <skin-module>skinModule</skin-module>
  <css-path>/path/to/file.css</css-path>
</portal-skin>

 

SkinService.addSkin(
    "applicationName/portletName",
    "skinName",
    "/path/to/file.css",
    ServletContext
);

<portlet-skin>
  <application-name>applicationName</application-name>
  <portlet-name>portletName</portlet-name>
  <skin-name>skinName</skin-name>
  <css-path>/path/to/file.css</css-path>
</portlet-skin>

 

SkinService.addTheme("styleName", ["Theme1","Theme2"]);

<window-style>
 <style-name>styleName</style-name>
  <style-theme>
   <theme-name>Theme1</theme-name>
  </style-theme>
  <style-theme>
   <theme-name>Theme2</theme-name>
  </style-theme>
</window-style>

Once their content is migrated, you can delete your JavascriptScript.groovy and SkinConfigScript.groovy files.