Nuxeo Book
Nuxeo Book
Nuxeo Book
Version 5.1
The Reference guide
5.1
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free
Documentation License, Version 1.2; with Invariant Section “Commercial Support”, no Front-Cover Texts, and
no Back-Cover Texts. A copy of the license is available at the URL: http://www.gnu.org/copyleft/fdl.html
Table of Contents
I. Introduction .................................................................................................................................. 1
1. Preface ................................................................................................................................. 2
1.1. What this Book Covers ............................................................................................... 2
1.2. What this book doesn't cover ....................................................................................... 2
1.3. Target Audience ......................................................................................................... 2
1.4. About Nuxeo ............................................................................................................. 2
1.5. About Open Source .................................................................................................... 2
2. Introduction .......................................................................................................................... 3
2.1. Enterprise Content Management .................................................................................. 3
2.1.1. Why ECM? ..................................................................................................... 3
2.2. The Nuxeo ECM platform .......................................................................................... 3
2.3. Introduction FAQ ....................................................................................................... 3
2.3.1. What are Nuxeo EP 5, Nuxeo EP and Nuxeo RCP? ........................................... 3
2.4. Intended audience ...................................................................................................... 3
2.5. What this book covers ................................................................................................ 3
3. Getting Started ...................................................................................................................... 5
3.1. Prerequisites .............................................................................................................. 5
3.2. Setting up your eclipse ............................................................................................... 5
3.2.1. Enabling maven project loading ....................................................................... 5
3.2.2. Enabling subversion checkout .......................................................................... 5
3.3. Learning from the project sample ................................................................................ 6
3.3.1. Checkout project ............................................................................................. 6
3.3.2. Understanding sample code .............................................................................. 7
3.3.3. Declaring book document type ......................................................................... 7
3.3.4. Regulating book states ..................................................................................... 8
3.3.5. Displaying book documents ............................................................................. 8
3.3.6. Making book documents indexable and searchable .......................................... 10
3.3.7. Enabling drag&drop creation ......................................................................... 11
3.3.8. Listening for events ....................................................................................... 11
3.4. Starting a new project ............................................................................................... 12
3.5. Using Documentation ............................................................................................... 12
3.6. Other IDEs: IntelliJ IDEA and NetBeans ................................................................... 12
3.6.1. IDEA ............................................................................................................ 12
3.6.2. NetBeans ...................................................................................................... 12
4. General Overview ............................................................................................................... 14
4.1. Introduction ............................................................................................................. 14
4.1.1. Architecture Goals ......................................................................................... 14
4.1.2. Main concepts and design .............................................................................. 17
4.2. Nuxeo Runtime: the Nuxeo EP component model ...................................................... 19
4.2.1. The motivations for the runtime layer ............................................................. 19
4.2.2. Extensible component model .......................................................................... 20
4.2.3. Flexible deployment system ........................................................................... 23
4.2.4. Extension points and Nuxeo 5 ........................................................................ 24
4.3. Nuxeo EP layered architecture .................................................................................. 25
4.3.1. Layers in Nuxeo EP ....................................................................................... 25
4.3.2. API and Packaging impacts ............................................................................ 27
4.3.3. Illustration of the layered architecture ............................................................. 27
4.4. Core Layer overview ................................................................................................ 27
4.4.1. Features of Nuxeo Core ................................................................................. 28
4.4.2. Nuxeo Core main modules ............................................................................. 29
4.4.3. Schemas and document types ......................................................................... 29
4.4.4. Life cycle associated to documents ................................................................. 30
4.4.5. Security model .............................................................................................. 31
4.4.6. Core events system ........................................................................................ 32
4.4.7. Query system ................................................................................................ 32
4.4.8. Versioning system ......................................................................................... 32
4.4.9. Repository and SPI Model ............................................................................. 32
Nuxeo EP 5.1 ii
Nuxeo Enterprise Platform - Version 5.1
Nuxeo EP 5.1 iv
Nuxeo Enterprise Platform - Version 5.1
Nuxeo EP 5.1 v
Nuxeo Enterprise Platform - Version 5.1
Nuxeo EP 5.1 vi
Nuxeo Enterprise Platform - Version 5.1
Nuxeo EP 5.1 ix
Nuxeo Enterprise Platform - Version 5.1
Nuxeo EP 5.1 x
Nuxeo Enterprise Platform - Version 5.1
Nuxeo EP 5.1 xi
Nuxeo Enterprise Platform - Version 5.1
Nuxeo EP 5.1 1
Chapter 1. Preface
• Third-party application developers, who will typically need both to customize the behavior of some
components and to write new components to extend the functionalities of the platform.
• System administrators, who need to understand how to configure the platform for the specific needs of
their hosting environment.
• Core developers, who work on the platform and need a reference documentation.
We fully understand that these different people with different concerns, and we plan to create, thanks to the
modular structure of this document, more targeted books for these different people.
Readers of this book are presumed familiar with the Java 5 language and with the Java EE and XML
technologies.
By entering the ECM field early in 2002, Nuxeo has established itself as the leader of open source ECM, with
customers for critical projects in the Government, Energy and Finance sectors. Nuxeo currently has 40
employees, about half of them developers.
Nuxeo has customers and partners in Europe, in Northern and Southern America, in Africa and in India.
Nuxeo fully embraces the open source vision, by fostering collaboration with a community of external
contributors.
Nuxeo EP 5.1 2
Chapter 2. Introduction
This chapter will give you an overview of ECM and the motivation for using the Nuxeo platform in your next
ECM project.
Nuxeo EP or "Enterprise Platform" is the server part of Nuxeo EP 5. It is a Java EE application intended to run
in a standard Java EE 5 application server like JBoss. It can be accessed by end users from a web browser, from
office productivity suites like MS-Office or OpenOffice.org, or from rich client developed using the Nuxeo
RCP technology (see below).
Nuxeo RCP or "Rich Client Platform" is a platform for building rich client applications, that usually connect to
a Nuxeo EP server.
Nuxeo EP and Nuxeo RCP run on top of a common runtime, "Nuxeo Runtime", and share a common set of
core components, called "Nuxeo Core".
Part 2:
Part 3:
Nuxeo EP 5.1 3
Introduction
Part 4:
Nuxeo EP 5.1 4
Chapter 3. Getting Started
This document describes how to setup an Eclipse project environment for working with Nuxeo EP. If you
encounter a problem don't forget you can share your experience on the ecm mailing list .
3.1. Prerequisites
We assume from now on that on your computer, you have the following ready-to-use environment:
• Java Development Kit (JDK) 1.5.x (Nuxeo EP is known to work now with Java 6 or OpenJDK, but this
is currently not supported)
• A Nuxeo EP installation from the last release ( download the installation wizard ). You can also
download a nightly build installer from http://www.nuxeo.org/static/snapshots/ but keep in mind
that this one may be broken, so don't erase your working setup with a nightly build: the result is not
guaranteed.
Note : The Nuxeo EP installer also installs and sets up the right version of JBoss on your computer, so you
don't need to install it by your own.
If some of these software are not already set up on your computer, please refer to the “Detailed Development
Software Installation Instructions” appendix in the nuxeo book, where you will find useful help.
Maven Integration for Eclipse provides tight integration for Maven into the IDE. It especially provides wizards
for creating new Maven projects and dependency management for build path based on Maven's pom file.
Nuxeo EP 5.1 5
Getting Started
From the Java perspective, in the package explorer view, located on the left side, you can now see the Nuxeo
project sample structure and you're able to browse all the classes without errors.
Now that you have created your project, you would want to see what your (not so tough) work produced?
Launch the dialog from Run > External tools > External tools configuration.... Select the “Ant Build” tool and
create a new configuration using the contextual menu. Select the file build.xml as buildfile and specify copy as
arguments.
You're now able to launch the copy of the generated plugin jar file using the external tools menu.
Open the internal web browser view and go to http://localhost:8080/nuxeo/ . Log in nuxeo with Admin
signature (Administrator/Administrator), if your project was correctly deployed, you should notice that the
default font and logo have changed.
Nuxeo EP 5.1 6
Getting Started
When developing, you will find it useful not to have to restart jboss when performing changes in xhtml pages.
Since releases 5.1.5 and 5.2-M2, you can add the line facelets.REFRESH_PERIOD=2 to the nuxeo.properties
file in the nuxeo.ear/config folder: pages will be refreshed within 2 seconds.
For hot deploying web sample resources, you should use the ant target web.
nuxeo-project-sample
`-- src
|-- main
| |-- java ❶
| | `-- org
| | `-- nuxeo
| | `-- project
| | `-- sample
| `-- resources
| |-- META-INF -- MANIFEST.MF ❷
| |-- OSGI-INF ❸
| | `-- l10n
| |-- directories
| |-- nuxeo.war ❹
| | |-- icons
| | |-- img
| | `-- incl
| | `-- tabs
| |-- schemas
| |-- themes
| '-- workflows
|
`-- test ❺
|-- java
| `-- org
| `-- nuxeo
| `-- ecm
| `-- sample
`-- resources
`-- META-INF
`-- OSGI-INF
❷ The main entry point is the MANIFEST.MF file. It defines the three variables Provide-Package ,
Require-Bundle and Nuxeo-Component that drives the Nuxeo EP runtime. They defines where to load
component definitions with their associated classes and resources.
❸ Component definition files are by convention located into the OSGI-INF folder. Nuxeo EP component
definition are mainly achieved by defining extension points.
Note
The project sample makes a component decomposition that is very atomic. At the opposite,
the whole extension points could be defined in a single component definition. It's an
architectural choice.
❶ Extension points may reference sample's specific classes. They are all being defined in the
src/main/java folder.
❹ Web book dedicated web templates are defined in the nuxeo.war folder.Note that these resources will be
written into the nuxeo.war folder at deployment time.
❺ Starting and stopping Nuxeo EP for testing features would be time consuming. Fixture code based on
Junit is located into the src/test folder.
...
<xs:element name="isbn" type="xs:string"/>
Nuxeo EP 5.1 7
Getting Started
Book schema and type are both declared in the types service using extensions. Here is the corresponding XML
snipset extracted from the core-types-contrib.xml file
...
<extension target="org.nuxeo.ecm.core.schema.TypeService"
point="schema">
<schema name="book" src="schemas/book.xsd" prefix="bk">
</extension>
...
<extension target="org.nuxeo.ecm.core.schema.TypeService"
point="doctype">
<doctype name="Book" extends="Document">
...
<schema name="book" >
...
</doctype>
</extension>
...
Book documents are displayed under folders or workspaces. Theses containment rules are declared to the types
service. Here is the XML snipset extracted from the ui-types-contrib.xml that defines them.
...
<extension target="org.nuxeo.ecm.platform.types.TypeService"
point="types">
...
<type id="Folder" coretype="Folder">
<subtypes>
<type>Book</type>
</subtypes>
</type>
<type id="Workspace" coretype="Workspace">
<subtypes>
<type>Book</type>
</subtypes>
Nuxeo EP 5.1 8
Getting Started
</type>
...
</extension>
...
ISBN book field is displayed onto book's content tab. This is achieved by registering into the web layout
service a new layout that includes the ISBN book number. Here is the XML snipset extracted from the
layouts-contrib.xml :
...
<extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager"
point="layouts">
...
<layout name="book">
...
<widget name="isbn" type="text">
<labels>
<label mode="any">ISBN</label>
</labels>
<fields>
<field>bk:isbn</field>
</fields>
</widget>
...
</layout>
...
</extension>
...
The types service is configured for displaying the book layout. Here is the XML snipset extracted from the
ui-types-contrib.xml :
...
<extension target="org.nuxeo.ecm.platform.types.TypeService"
point="types">
...
<type id="Book" coretype="Book">
...
<layouts mode="any">
<layout>heading</layout>
<layout>book</layout>
<layout>file</layout>
</layouts>
...
</type>
...
</extension>
...
Specific book behaviours are available onto book and workspace documents. Each corresponding tab is
declared to the actions service. Note that tab's templates are defined into the folder nuxeo.war/inc/tabs.
Note also that these templates get their model using the BookManagerBean which implements book behaviours.
Here is the XML snipset extracted from the actions-contrib.xml file :
...
<extension target="org.nuxeo.ecm.platform.actions.ActionService" point="actions">
...
<action id="tab_book_view"
link="/incl/tabs/book_view.xhtml" enabled="true"
label="Book" icon="/icons/file.gif" order="9">
...
</action>
...
<action id="tab_folder_books_list"
link="/incl/tabs/folder_books_view.xhtml" enabled="true"
label="Books" icon="/icons/file.gif" order="15">
...
</action>
...
</extension>
...
Book documents are subject to rights management and the corresponding tab should be displayed. ere is the
XML snipset extracted from the actions-contrib.xml file :
...
<extension target="org.nuxeo.ecm.platform.actions.ActionService" point="actions">
...
Nuxeo EP 5.1 9
Getting Started
Books are searched by keywords using using the query model service. Here is the XML snipset extracted from
the querymodel-contrib.xml file :
...
<extension
target="org.nuxeo.ecm.core.search.api.client.querymodel.QueryModelService"
point="model">
...
<queryModel name="BOOK">
...
<pattern>
SELECT * FROM Document WHERE ecm:primaryType = 'Book' AND
dc:subjects = ?
</pattern>
...
</queryModel>
...
</extension>
...
Query models are retrieved and invoked by the BookResultsProvider which provide the criterias from the
session context. The BookResultsProvider is registered into the results provider service. Here is the XML
snipset extracted from the resultsprovider-contrib.xml file :
...
<extension
target="org.nuxeo.ecm.webapp.pagination.ResultsProviderService"
point="model">
<resultsProvider name="BOOK" farm="bookResultsProvider" />
</extension>
...
Finally, books are searched and listed in the book's tab using the results provider cache. Here is the XHTML
snipset extracted from the folder_books_view.xhtnml :
...
<nxu:methodResult name="provider"
value="#{resultsProvidersCache.get('BOOK')}">
<h:form>
<ui:decorate template="/pagination/pageNavigationControls.xhtml">
<ui:param name="provider" value="#{provider}" />
</ui:decorate>
</h:form>
<h:dataTable var="bookinfo" value="#{provider.currentPage}"
...
</h:dataTable>
...
Nuxeo EP 5.1 10
Getting Started
...
<extension
target="org.nuxeo.ecm.platform.filemanager.service.FileManagerService"
point="plugins">
...
<plugin name="book_plugin"
class="org.nuxeo.project.sample.BookFileManagerPlugin">
<filter>image/gif</filter>
<filter>image/jpeg</filter>
</plugin>
...
</extension>
...
The BookTitleService is defined as a service. The BookTitleDescriptor defines the configuration format
suitable for this service. Here is the XML snipset extracted from the booktitle-service-contrib.xml file
...
<implementation class="org.nuxeo.project.sample.BookTitleServiceImpl" />
<service>
<provide interface="org.nuxeo.project.sample.BookTitleService" />
</service>
<extension-point name="title">
<object class="org.nuxeo.project.sample.BookTitleDescriptor" />
</extension-point>
...
<extension target="org.nuxeo.ecm.directory.sql.SQLDirectoryFactory"
point="directories">
<directory name="book_keywords">
<schema>vocabulary</schema>
<idField>id</idField>
<dataSource>java:/nxsqldirectory</dataSource>
<table>book_keywords</table>
<!-- one of "never", "on_missing_columns" or "always" -->
<createTablePolicy>on_missing_columns</createTablePolicy>
Nuxeo EP 5.1 11
Getting Started
<dataFile>directories/book_keywords.csv</dataFile>
</directory>
</extension>
Start the “new maven project” wizard from the menu File > New > Maven project. Enable your eclipse for
using Nuxeo EP's by addind a remote archetypes catalog using the configure dialog. The URL to be used is as
follow http://archiva.nuxeo.org/repository/nuxeo_release/. Select the archetype type nuxeo-archetype-start and
the nuxeo's version you want your project based. Set the artifact parameters according to your organisation
rules.
• The Nuxeo Book is getting to be the most complete source of information around Nuxeo EP, both for
beginners and advanced developer. It is a good start.
• The extension point documentation is also very useful: although you may find it rough, it is the best way
to evaluate the Nuxeo extensibility potential, and one should always start with a quick look around all the
extension points, to "think Nuxeo" before starting a new project, and not reinventing the wheel.
• The wiki: we try to reference all the documentation from the wiki welcome page, and you will find
tricks, howtos, etc. If you want to have a writer account to help update the content, ask on the Nuxeo's
mailing list.
3.6.1. IDEA
IntelliJ IDEA from Jetbrains is a very lovable IDE for Java that has many fans in the Java developers
community. It is unfortunately not open source.
To start using IDEA for coding on the Nuxeo project, you just need to type mvn idea:idea from your top-level
source directory: Maven will download all the dependencies, then generate the configuration files needed by
IDEA and you will be able open nuxeo-ecm.ipr from IDEA.
Note
At the time of this writing, IDEA will report some (spurious) compilation failures and you won't be
able to compile the whole application from IDEA. You can still use IDEA with the current
configuration to write Java code ("with pleasure"), and use Ant and/or Maven to build/deploy the
application.
3.6.2. NetBeans
NetBeans is an open source IDE from Sun.
Nuxeo EP 5.1 12
Getting Started
If you're using NetBeans 6 or later, you will be able to download a Maven 2 support plugin from NetBean's
"Update Center", and with it, start coding on Nuxeo EP very quickly.
Nuxeo EP 5.1 13
Chapter 4. General Overview
4.1. Introduction
This is the most standard use case. The web browser is used to navigate in content repositories.
• In order to be easily hostable, the platform need to be compatible with several application servers
In this case having a rich client that seamlessly communicates with other desktop applications and offers
a rich GUI is more comfortable than a simple web browser.
• Offline usage
In some cases, it is useful to be able to manipulate and contribute content without needing a network
connection.
• GUI and some services (like a local repository) need to be deployed on the client side
• Server hosts the collaborative services (like the workflow) and the central repository
• Distributed architecture
In order to be able to address the needs of large decentralized organizations, Nuxeo EP must provide a
way to be deployed on several servers on several locations
When building an business application, it can be useful to integrate services from Nuxeo EP in order to
address all content oriented needs of the application.
• Provide web service API to access generic ECM services (including repository)
Nuxeo EP 5.1 14
General Overview
• Provide EJB3 remoting API to access generic ECM services (including repository)
There are certainly a lot of other use cases, but mainly the constraints are:
• Be able to choose the deployment platform: POJO vs Java EE
• Eclipse RCP: a rich client solution that uses a POJO (OSGi) component model
• Be able to choose the deployment location of each component: client side vs server side
The idea is to be able to deploy a component on the server side or on the client side without having to
change its code or its packaging
Before building Nuxeo EP we worked during several years on the Zope platform with the CPS solution. CPS
was deployed for a lot different use cases and we learned a lot of good practices and design patterns. Even if
Nuxeo EP is a full rewrite of our ECM platform, we want to keep as much as possible of CPS good concepts.
• Concept of schemas and documents
Inside CPS most of the data manipulated was represented by a document object with a structure based on
schemas.
• Schemas enforce structure constraints and data integrity but also permit some flexibility.
When defining a schema you can specify what fields are compulsory, what are their data type, but you
can also define some flexible part of the schema.
Because the Document/Schema model is very flexible it can be used to manipulate different types of
data: like Users, Records and standards documents.
From the developer's perspective this permit using the same API and be able to reuse some UI
components
From the user's perspective it give the application some consistency: because the same features and
GUI can be used for all the data he manipulates.
Because CPS was very pluggable, it was possible to easily define different views for each document type
and also to let additional components contribute new actions or views on existing documents.
Nuxeo EP has a similar concept of views and actions, even if technically speaking the technologies are
different.
Because ECM applications make a very intensive use of the repository and often need to fetch a lot of
different documents to construct each page, the way the document retrieval is handled if very important
to have a scalable application.
Nuxeo EP 5.1 15
General Overview
• Distributed caching
CPS was constructed as a set of pluggable components relying on a common platform. This modularity has
been maintained in the new platform. Deploying a new feature or component on the new platform is as simple
as it was on the old one.
This requirement as a huge impact on the platform because the Java packaging model and the Java EE
constraints are not directly compatible with it.
Adding a new component should be as simple as dropping a file or an archive in some directory without having
to rebuild nor repackage the application.
This is important from the administrator point of view: be able to easily deploy new features.
This is also very important from the support point of view: be able to deploy customized components without
taking the risk of forking the platform and maintain the possibility to upgrade the standards components.
The CPS framework was powerful but we know it was very complex to use. Not only because of the unusual
CMF/Zope/Python programming model, but also because there was a lot of different concepts and you had to
understand them all to be able to leverage the platform when building a new application on top of it.
The idea is to clearly separate presentation, processing and storage so that developers can concentrate on
their task.
Nuxeo EP is constructed as a set of plugins so you can modify the behavior of the application by just
contributing a new plugin. This is simpler because for common tasks we will offer a simple plugin API
and the developer just has to implement the given interface without having to understand each part of the
platform.
We try to follow as much as possible all the Java standards when they are applicable. This will allow
experienced Java developers to quickly contribute to the Nuxeo EP platform.
We know what it's like to have to build and maintain an entire framework starting from the application server.
With the switch to the Java technology, we will use as much as possible existing open source components and
focus on integrating them seamlessly in the ECM platform. Nuxeo EP is a complete integrated solution for
building an ECM application, but Nuxeo won't write all infrastructure components. This approach will also
make the platform more standards compliant.
Thus developers can optimize their Java/JEE and open source experience to use Nuxeo EP.
Nuxeo EP 5.1 16
General Overview
Because ECM applications often need to be deeply integrated into the existing SI, Nuxeo EP will be easily
integrable
• API for each reusable service or component
Depending on the components, this API could be POJO, EJB3, or WebService, and in most cases it will
be available in the three formats.
This mainly means synchronous or asynchronous events listener that are a great place to handle
communication and synchronization between applications.
The Nuxeo EP platform was rewritten from the ground with the switch to Java. But we don't plan to do this
kind of work every couple of years, it wont be efficient neither for us, nor for the users of the platform. For that
reason, we choose innovative Java technologies like OSGi, EJB3, JSF, Seam ....
Nuxeo EP is built of several layers, following at least the 3 tiers standard architecture
• Presentation layer
• Service layer
Service stack that offers all generic ECM services like workflow, relations, annotations, record
management...
• Storage layer
Handles all storage-oriented services like document storage, versioning, life cycle ....
Depending on the components, their complexity and the needed pluggability, there can be more that 3 layers.
This layering of all the components brings Nuxeo EP the following advantages
• Choose the deployment target for each part of a component
By separating clearly the different parts of a feature, you can choose what part to deploy on the client and
what part to deploy on a server.
Because the service and storage layers are not bound to a GUI, they are more generic and then more
reusable
Nuxeo EP 5.1 17
General Overview
Thanks to this separation in component families you can easily extract from Nuxeo EP the components you
need for your application.
If you need to include Document storage facilities into your application you can just use Nuxeo EP Core: It will
offer you all the needed feature to store, version and retrieve documents (or any structured but flexible dataset).
If you also need process management and workflow you can also use Nuxeo EP Workflow service. And finally,
if you want to have a Web application to browse and manage your data, you can reuse the Nuxeo EP Web
layer.
The targeted platform do not provide the same mechanism to handle all the deployment tasks:
• Packaging (Java EE vs OSGi)
• Dependency management
• Extension management
Because of these differences, Nuxeo EP provides a unified deployment service that hides the specificity of the
target platform. This is also a way to add a pluggable component deployment system to some platform that
don't handle this (like Java EE).
This is one of the motivation for the Nuxeo Runtime that will be quickly introduce later in this document.
Inside Nuxeo EP each feature is implemented by a one or several reusable components and services. A feature
may be implemented completely at storage level, or may require a dedicated service and a dedicated GUI.
Nuxeo EP Web application is a default distribution of a set of ECM components. This can be used "as is" or
can be the base for making a business ECM application.
• If you need to remove a feature
• ...
This configuration may use an extension point to contribute the new behavior.
Nuxeo EP 5.1 18
General Overview
• ...
• If you need to add a completely new feature you can make your own component.
First check that there is no generic Nuxeo EP component available that could help you in your task (all
components are not deployed in the default webapp).
Here is a quick list of the Java technology we use inside Nuxeo EP platform:
• Java 5
• Jackrabbit JSR-170 repository for the default storage back end implementation
• ...
This paragraph will give you a quick overview of the Nuxeo Runtime, a more detailed technical presentation
can be found in an other chapter of this book.
Because most of Nuxeo EP components are shared by Nuxeo RCP (OSGI/RCP) and Nuxeo EP (Java EE), an
abstraction layer is required so the components can use transparently the components services independently
from the underlying infrastructure.
Nuxeo Runtime provides an abstraction layer on top of the target host platform. Depending on the target host
platform, this Runtime layer may be very thin.
Nuxeo Runtime already supports Equinox (Eclipse RCP OSGi layer) and JBoss 4.x (JMX). The port of Nuxeo
Runtime to other Java EE application server is in progress, we already have a part of Nuxeo EP components
Nuxeo EP 5.1 19
General Overview
that can be deployed on top of SUN Glassfish application server. Technically speaking, the port of Nuxeo
Runtime could be done on any JEE5 compliant platform and will be almost straightforward for any platform
that supports natively the OSGi component model.
Java EE is a great standard, but it was not designed for a component based framework: it is not modular at all.
• Java EE deployment model limitations
For example, the web.xml descriptor is a unique XML file. If you want to deploy an additional
component that needs to declare a new Java module you are stuck. You have to make one version of
the web.xml for your custom configuration. For Nuxeo EP platform, this constraint is not possible:
Because there are a lot of optional components, we can't have a fixed configuration that fits all.
There are too many optional components to build one static web.xml for each possible combination.
This problem with the web.xml is of course also true for a lot of standard descriptors (application.xml,
faces-config.xml, persistence.xml, ejb-jar.xml ....)
We have here the exact same problem than with the web.xml. additional components can contribute
new web pages, new web components ... We can have a monolithic web archive.
• No dependency declaration
Inside Java EE there is no standard way to declare the dependency between components.
Because Nuxeo EP is extensible and has a plugin model, we need that feature. A contribution is
dependent on the component it contribute to:
• The contribution must be deployed after the target component as it may override some configuration
If you take a .ear archive and want to add a new component, you have to rebuild a new ear.
Nuxeo Runtime provides an extensible component model that supports all these feature. It also handles the
deployment of these components on the target host platform.
This component model is heavily based on OSGi and provides the following features:
• Platform agnostic component model
Nuxeo EP 5.1 20
General Overview
Components explicitly declare their requirements and are deployed and activated by respecting the
inferred dependency chain.
Nuxeo Runtime components can be unit tested using JUnit without the need of a specific container.
OSGi ( Open Services Gateway initiative ) is a great standard for components based Java architecture.
A component gets activated only when the needed requirements are fulfilled
• Manage bundles
For Nuxeo EP, OSGi standard provides a lot of the needed features. This is the reason why Nuxeo Runtime is
based on OSGi, in fact Nuxeo Runtime component model is a subset of OSGi specification.
To ensure platform transparency, Nuxeo Runtime provides adapters for each target platform to help it support
OSGi components.
• This adopter layer is very thin on Equinox (Eclipse RCP) since the underlying platform is already OSGi
compliant.
• This adapter may be more complex for platform that are not aware of OSGi (JBoss 4.x or Glassfish)
In this case, the runtime adapter will handle all OSGi logic and deploy the components as native platform
components. For examples, on JBoss 4.x, Runtime components are deployed as JMX MBeans.
OSGi does not define a plugin model, but the Eclipse implementation (Equinox) does provide an extension
point system.
Because we used a lot the Eclipse Extension Point system and we liked it, Nuxeo Runtime also includes an
Extension Point system.
Nuxeo EP 5.1 21
General Overview
Each components can define extension points that other components can use to contribute configuration
or code.
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Nuxeo ECM Core
Bundle-SymbolicName: org.nuxeo.ecm.core;singleton:=true
Bundle-Version: 1.0.0
Bundle-Vendor: Nuxeo
Bundle-Localization: bundle
Bundle-Activator: org.nuxeo.ecm.core.NXCoreActivator
Bundle-ClassPath: ., lib/xsom.jar,
lib/connector-api.jar,
lib/java-cup-v11a.jar
Export-Package: org.nuxeo.ecm.core,
org.nuxeo.ecm.core.api,
org.nuxeo.ecm.core.api.local,
org.nuxeo.ecm.core.jca,
org.nuxeo.ecm.core.lifecycle,
org.nuxeo.ecm.core.model
Require-Bundle: org.nuxeo.ecm.core.api,
org.nuxeo.runtime
Nuxeo-Component: OSGI-INF/CoreService.xml,
OSGI-INF/TypeService.xml,
OSGI-INF/RepositoryService.xml,
OSGI-INF/CoreExtensions.xml,
OSGI-INF/SecurityService.xml
The XML descriptor will be used to define new extension points or contribute to existing one.
An extension point is a way to declare that your component can be customized from the outside:
• Contribute configuration
Extension points and contribution to extension points are defined using a XML descriptor that has to be
referenced in the MANIFEST.MF.
<component name="org.nuxeo.ecm.core.listener.CoreEventListenerService">
<require>org.nuxeo.ecm.core.repository.RepositoryService</require>
<implementation class="org.nuxeo.ecm.core.listener.impl.CoreEventListenerServiceImpl"/>
<extension-point name="listener">
<object class="org.nuxeo.ecm.core.listener.extensions.CoreEventListenerDescriptor"/>
</extension-point>
<extension target="org.nuxeo.ecm.core.listener.CoreEventListenerService" point="listener">
<listener name="nxruntimelistener" class="org.nuxeo.ecm.core.listener.impl.NXRuntimeEventListener" />
</extension>
<extension target="org.nuxeo.ecm.core.listener.CoreEventListenerService" point="listener">
Nuxeo EP 5.1 22
General Overview
This extension point let register plugins that will be invoked when a core event occurs.
The descriptor is simply defined by a Java class that uses annotations to defines how the XML descriptor will
be used to create an Object Descriptor instance to pass to the extension point registration.
The dependencies are declared in the MANIFEST.MF and can also be defined in XML descriptors that hold
contributions.
The Nuxeo Runtime orders the component deployment in order to be sure the dependencies are
respected. Components that have unresolved dependencies are simply not deployed
XML descriptors are referenced in the MANIFEST.MF. These descriptors make contributions to existing
extension points or declare new extension points.
Nuxeo Runtime let you declare template files (like web.xml, persistence.xml) and let other
component contribute to these files.
• Installation instructions
Some resources contributions (like i18n files or web pages) need more complex installation
instructions because they need archives and files manipulations. Nuxeo Runtime provide basic
commands to define how your components should be deployed
Nuxeo EP 5.1 23
General Overview
<extension target="faces-config#VALIDATOR">
<validator>
<validator-id>dueDateValidator</validator-id>
<validator-class>org.nuxeo.ecm.platform.workflow.web.ui.jsf.DueDateValidator</validator-class>
</validator>
</extension>
<install>
<!-- unzip the war template -->
<unzip from="${bundle.fileName}" to="/">
<include>nuxeo.war/**</include>
</unzip>
<!-- create a temp dir -->
<!-- be sure no directory with that name exists -->
<delete path="nxworkflow-client.tmp" />
<mkdir path="nxworkflow-client.tmp" />
<unzip from="${bundle.fileName}" to="nxworkflow-client.tmp">
<include>OSGI-INF/l10n/**</include>
</unzip>
<append from="nxworkflow-client.tmp/OSGI-INF/l10n/messages.properties"
to="nuxeo.war/WEB-INF/classes/messages.properties" addNewLine="true" />
<append from="nxworkflow-client.tmp/OSGI-INF/l10n/messages_en.properties"
to="nuxeo.war/WEB-INF/classes/messages_en.properties" addNewLine="true" />
<append from="nxworkflow-client.tmp/OSGI-INF/l10n/messages_fr.properties"
to="nuxeo.war/WEB-INF/classes/messages_fr.properties" addNewLine="true" />
<append from="nxworkflow-client.tmp/OSGI-INF/l10n/messages_de.properties"
to="nuxeo.war/WEB-INF/classes/messages_de.properties" addNewLine="true" />
<append from="nxworkflow-client.tmp/OSGI-INF/l10n/messages_it.properties"
to="nuxeo.war/WEB-INF/classes/messages_it.properties" addNewLine="true" />
<delete path="nxworkflow-client.tmp" />
</install>
</fragment>
Inside Nuxeo 5, extension points are used each time a behavior or a component needs to be configurable or
pluggable.
Here are some examples of extension points used inside the Nuxeo 5 platform.
• Schemas and document types
Inside Nuxeo 5 a document structure is defined by a set of XSD schemas. Schemas and Document Types
are defined using an extension point.
• Storage repository
Nuxeo core stores documents according to their type but independently of the low level storage
back-end. The default back-end is Jackrabbit JCR implementation. Nuxeo Core exposes an extension
point to define the storage back-end. We are working on an other repository implementation that will be
pure SQL based.
• Security Management
Nuxeo include a security manager that checks access rights on each single operation. The extension point
system allow to have different implementation of the security manager depending on the project
requirements:
• Enforce data integrity: store security descriptors directly inside the document
• Corporate security policy: implement a specific security manager that will enforce business rules
• Event handlers
Nuxeo platform lets you define custom Event handler for a very large variety of events related to content
or processes. The event handler extension mechanism gives a powerful way to add new behavior to an
existing application
Nuxeo EP 5.1 24
General Overview
• You can modify the behavior of the application without changing its code
• The development model is easy because you have a very simple Interface to implement and you can
use Nuxeo Core API to manipulate the data
Nuxeo 5 itself uses the Event Handler system for a lot of generic and configurable service
• Automatic versioning: create a new version when a document is modified according to business rules
• Meta-data automatic update: update contributor lists, last modification date ...
• Meta-data extraction / synchronization: extract Meta-Data from MS-Office file, Picture ....
Nuxeo 5 development model is heavily based on the usage of extension points. When a project requires specific
features we try as much of possible to include it as an extension of the existing framework rather than writing
separated specific component. This means make existing services more generic and more configurable and
implement the project specific needs as a configuration or a plugin of a generic component using Extension
Points.
From the logical point of view each layer is a group of components that provide the same nature of service:
• Storage oriented services: Nuxeo Core
Nuxeo core provides all the storage services for managing documents
• Repository service
• Versioning service
• Security service
• Lifecycle service
• ...
Nuxeo provides a stack of generic services that handle documents and provide content and process
management features. Depending on the project requirement only a part of the existing services can be
deployed.
Nuxeo EP 5.1 25
General Overview
• Notification service
• ...
These services can be very generic (like the action manager) but can also be directly tied to a type of
client (like the View generation can be bound to JSF/facelets for the web implementation)
Thanks to the Nuxeo Runtime remoting features it is very easy to split the components on several JVM. But
splitting some services can have a very bad effect on the global system performance.
Because of that, all the storage oriented services are inside the core. All components that have extensive usage
of the repository and need multiple synchronous interaction with it are located in the core. This is especially
true for all synchronous event handlers.
The services layer can itself be split in multiple deployment unit on multiple JVMs.
On the UI side all the services are logically deployed inside the same JVM. At least each JVM must have the
minimum set of services to handle user interaction for the given application.
Generic ECM services can depend on Core external API and can depend on external optional library (like
jBPM, Jena, OpenOffice.org ...).
UI services can rely on a client side API (like Servlet API) and share a common state associated to the user
session.
The Core layer is a POJO Layer with an optional EJB facade. The core can be embed in a client application.
The services are mostly implemented as POJO services so that they can be used as an embedded library. But
some of them can depend on typical Application Server infrastructure (like JMS or EJB).
Inside the UI Layer most service are dedicated to a target platform: web (JSF/Seam), Eclipse RCP or other.
Because the layer organization has several constraints, the implementation of a unique feature is spread across
several layers.
Typically a lot of transversal services is split in several sub-components in each layer in order to comply to
deployment constraint and also to provide better reusability. For example, the Audit service is made of 3 main
parts:
Nuxeo EP 5.1 26
General Overview
Generates HTML fragment that displays all events that occurred on a document.
Most of the components forming the core are exposed via the DocumentManager / CoreSession interface. The
interfaces and dependencies needed to access the Core services are packaged in a API package: even if there
are several Core component, you have only one dependency and API package.
The idea is that for accessing the core, you will only need to use the DocumentManager to manipulate
DocumentModels (the document object artifact). Some core services can be directly accessed via the
DocumentModel (like the life cycle or security data).
Each service exposes its own API and then has its own API package. Service related data (like process data,
relation data) are not directly hosted by the DocumentModel object but can be associated to it via adapters and
facets.
4.3.2.3. UI API
The web layer can be very specific to the target application. Nuxeo EP provides a default web application and a
set of base classes, utility classes and pluggable services to handle web navigation inside the content repository.
4.3.2.4. Packaging
Most features are made of several Java project and generate several Maven 2 artifact.
Nuxeo packaging and deployment system (Nuxeo Runtime, Platform API, Maven ...) leverage this separation
to help you distributing the needed deployment unit according to your target physical platform.
Nuxeo EP 5.1 27
General Overview
• Schema service
Lets you register XSD schemas and document types based on schemas.
• Repository service
Lets you define one or more repository for storing your documents.
• Versioning service
Nuxeo EP 5.1 28
General Overview
• Security service
• Lifecycle service
The repository service lets you define new document repositories. Defining separated repositories for your
documents is pretty much like defining separated Databases for your records.
Because Nuxeo Core defines a SPI on repository, you can configure how you want the repository to be
implemented. For now, default implementation uses JSR-170 (Java Content Repository) reference
implementation: Apache Jack Rabbit. In the future, we may provide other implementation of the Repository
SPI (like native SQL DB or Object Oriented DB).
Even if for now there is only one Repository implementation available, using JCR implementation, you can
configure how your data will be persisted: filesystem, xml or SQL Database. Please see "How to"s about
repository configuration.
Defines how security descriptors are stored in the repository (for now:
org.nuxeo.ecm.core.repository.jcr.JCRSecurityManager)
• A list of facets
Nuxeo EP 5.1 29
General Overview
For further explanation on Schemas and Document types, please see the dedicated section in this document.
The Nuxeo Core contains a LifeCycleManager service that exposes several extension points:
• one for contribution Life-Cycle management engine
(default one is called JCRLifeCycleManager and stores life-cycle related information directly inside the
JSR 170 repository)
Nuxeo EP 5.1 30
General Overview
Here is an example
<lifecycle name="default" lifecyclemanager="jcrlifecyclemanager"
initial="project">
<transitions>
<transition name="approve" destinationState="approved">
<description>Approve the content</description>
</transition>
<transition name="obsolete" destinationState="obsolete">
<description>Content becomes obsolete</description>
</transition>
<transition name="delete" destinationState="deleted">
<description>Move document to trash (temporary delete)</description>
</transition>
<transition name="undelete" destinationState="project">
<description>Recover the document from trash</description>
</transition>
<transition name="backToProject" destinationState="project">
<description>Recover the document from trash</description>
</transition>
</transitions>
<states>
<state name="project" description="Default state">
<transitions>
<transition>approve</transition>
<transition>obsolete</transition>
<transition>delete</transition>
</transitions>
</state>
<state name="approved" description="Content has been validated">
<transitions>
<transition>delete</transition>
<transition>backToProject</transition>
</transitions>
</state>
<state name="obsolete" description="Content is obsolete">
<transitions>
<transition>delete</transition>
<transition>backToProject</transition>
</transitions>
</state>
<state name="deleted" description="Document is deleted">
<transitions>
<transition>undelete</transition>
</transitions>
</state>
</states>
</lifecycle>
Nuxeo EP 5.1 31
General Overview
An ACP is a stack of ACL. We use ACP because security can be bound to multiples rules: there can be a
static ACL, an ACL that is driven by the workflow, and another one that is driven by business rules.
Separating ACLs allows to easily reset the ACP when a process or a rules does not apply any more.
Inside the repository each single document can have an ACP. By default security descriptors are inherited from
parent, but inheritance can be blocked when needed.
Security engine also lets you contribute custom policy services so that security management can include
business rules.
Security model and policy service are described in details later in this document.
As an example, part of the dublincore management logic is implemented as a CoreEvent listener: whenever a
document is created or modified, creation date, modification date, author and contributors fields are
automatically updated by a CoreEvent Listener.
This lets you define how versions and stored and what operations can be done on versions
This lets you define rules and logic that drives when new versions must be created and how versions
numbers are incremented.
Nuxeo repository is implemented using a SPI and extension point model: this basically means that a non JCR
based repository plugin can be contributed. In fact, we have already started a native SQL repository
implementation (that is not yet finished because we have no direct requirement for such a repository).
Nuxeo core can server several repository: it provides a extension point to declare additional repository: this
Nuxeo EP 5.1 32
General Overview
4.4.10. DocumentModel
Inside Nuxeo EP and especially inside the Core API the main data object is a Document.
Inside Nuxeo Core API, the object artifact used to represent a Document is called a DocumentModel.
the DocumentModel encapsulate all access to Document internal fields, the DocumentModel can be sent
over the network
When fetched from the Core, a DocumentModel does not carries all document related information. Some
data (called prefetch data) are always present, other data will be loaded (locally or remotely) from the
core when needed.
This feature is very important to reduce network and disk I/O when manipulating Document that
contains a lot of big blob files (like video, music, images ...) .
For files above 1 MB the DocumentModel uses the Core Streaming service.
In addition of the data oriented interface, a DocumentModel can be associated with one or several
Adapters that will expose a business oriented interface.
A DocumentModel can be located inside the repository using a DocumentRef. DocumentRef can be an IdRef
(UUID in the case of the JCR Repository Implementation) or PathRef (absolute path).
DocumentModels also holds information about the Type of the Document and a set of flags to define some
useful characteristics of the Document:
• isFolder
• isVersion
• isProxy
Nuxeo EP 5.1 33
General Overview
4.4.11. Proxies
A Proxy is a DocumentModel that points to a another one: very much like a symbolic link between 2 files.
Proxies are used when the same document must be accessible from several locations (paths) in the repository.
This is typically the case when doing publishing: the same document can be visible in several sections. In order
to avoid duplicating the data, we use proxies that point to same document.
A proxy can point to a checked out document (not yet associated to a version label) or to a versionned version
(typical use-case of the publishing).
The proxy does not store document data: all data access are forwarded to the source document. But the Proxy
does holds:
• its own security descriptors
• adding dedicated projects specific components when the requirements can not be integrated into a
generic component
This service layer provides the API used by client applications (Webapp or RCP based application) to do their
work.
This means that in this layer, services don't care about UI, navigation or pageflows: they simply explode an API
to achieve document oriented tasks.
As everything in the Nuxeo Platform the services use the Nuxeo Runtime component model.
Nuxeo EP 5.1 34
General Overview
This package is required to be able to call the service from the same JVM or from a remote JVM.
The Runtime component will implement the service business logic (ie: implement the API) and also
expose Extensions Points.
All the extensibility and pluggability is handled at runtime component level. This for example means that
the API can be partially implemented via plugins.
• integrate the service into JEE managed environment (JTA and JAAS)
• leverage some additional features of the application server like JMS and Message Driven Bean
Typically, the POJO module will be a Nuxeo Runtime Component that inherit from DefaultComponent,
provide extension points and implement a Service Interface.
public class RelationService extends DefaultComponent implements
RelationManager { ...}
The deployment descriptor associated to the component will register the component, declare it as service
provider and it may also declare extension points
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.relations.services.RelationService">
<implementation class="org.nuxeo.ecm.platform.relations.services.RelationService" />
<service>
<provide interface="org.nuxeo.ecm.platform.relations.api.RelationManager" />
</service>
<!-- declare here extension points -->
</component>
The facade will declare a EJB that implement the same service interface. In simple cases, the implementation
simply delegates calls to the POJO component.
The facade package will also contain a contribution to the Runtime Service management to declare the service
implementation.
<?xml version="1.0" encoding="UTF-8"?>
<component name="org.nuxeo.ecm.platform.relation.service.binding.contrib">
<extension target="org.nuxeo.runtime.api.ServiceManagement" point="services">
<documentation> Define the Relation bean as a platform service. </documentation>
<service class="org.nuxeo.ecm.platform.relations.api.RelationManager" group="platform/relations">
<locator>%RelationManagerBean</locator>
</service>
</extension>
</component>
Thanks to this declaration the POJO and the EJB Facade can now be used for providing the same interface
based on a configuration of the framework and not on the client code.
This configuration is used when deploying Nuxeo components on several servers: platform configuration
provides a way to define service groups and to bind them on physical servers.
Nuxeo EP 5.1 35
General Overview
The API package is unique for a given service, access to a remote EJB3 based service is the same as accessing
the POJO service.
From the client point of view, accessing a service is very simple and independent from service location and
implementation: this means not manual JNDI call. Everything is encapsulated in the Framework.getService
runtime API.
RelationManager rm = Framework.getService(RelationManager.class);
• This can be the local interface of the EJB3 service (using call by ref in JBoss)
• This can be the remote interface of the EJB3 service (using full network marshaling)
The choice of the implementation to return is left to the Nuxeo Runtime that will take the decision based on the
platform configuration.
The client can explicitly ask for the POJO service via the Framework.getLocalService() API: this is typically
used in the EJB Facade to delegate calls to the POJO implementation.
4.5.4. Adapters
DocumentModel adapters are a way to adapt the DocumentModel interface (that is purely data oriented) to a
more business logic oriented interface.
In the pure Service logic, adding a comment to a document would look like this:
CommentManager cm = Framework.getService(CommentManager.class);
cm.createComment(doc,"my comment");
List<DocumentModel> comments = cm.getComments(doc);
DocumentModel adapter give the possibility to have a more object oriented API:
CommentableDoc cDoc = doc.getAdapter(CommentableDoc);
cDoc.addComment("my comment");
List<DocumentModel> comments = cDoc.getComments();
The difference may seem small, but documentModel adapter can be very handy
• to have a more clean and clear code
DocumenModelAdapters are declared using an extension point that defines the interface provided by the
adapter and the factory class. DocumentModelAdapters can be associated to a Facet of the DocumentModel.
Nuxeo EP 5.1 36
General Overview
4.6.1.1. Requirements
• A standard framework
Nuxeo Web Layer uses JSF (SUN RI 1.2) and Facelets as presentation layer: JSF is standard and very
pluggable, Facelets is much more flexible and adapted to JSF than JSP.
NXThemes provides a flexible Theme and composition engine based on JSF and Facelets.
In the 5.1 version of the platform, Nuxeo Web Layer uses Apache Tomahawk and trinidad as components
library and AJAX4JSF for Ajax integration. In the 5.2 version we will move to Rich Faces.
Nuxeo Web Layer also uses Seam Web Framework to handle all the ActionListeners.
• Dependency injection
4.6.2.1. Requirements
Because this stack of services is very modular, so must be the web layer.
This basically mean that depending on the set of deployed services and on the configuration the web framework
must provide a way
• to add, remove or customize views
for example, if you don't need relations, you may want to remove the relations tab that is by default
available on document
Nuxeo EP 5.1 37
General Overview
the typical use case is removing actions that are bound to non deployed services or add new actions that
are specific to your project
you may want to change how some actions are handled by just overriding Nuxeo defaults
In order to fullfill these requirements, the key points of Nuxeo Web Framefulfill
• Context management to let components share some state
• Event system and dependency injection to let loosely coupled component collaborate
Inside the web framework, each component will need to know at least
• what is the current navigation context
This includes current document, current container, current Workspace, current Domain.
This information is necessary because most of the service will display a view on the current document,
and can fetch some configuration from the content hierarchy.
This includes identity and roles, but also its preferences and the set of documents he choose to work on
In some cases, this context information may be huge, and it's time consuming to recompute all this information
at each request.
Inside Nuxeo Web Framework, Seam context management is used to store these data. Depending on the
lifecycle of the data Session, Conversation or Event context are used.
At some point the components of the web framework need to interact with each other. But because components
can be present or not depending on the deployment scenario, they can't call each other directly.
For that matter, the Nuxeo Web Framework uses a lot of Seam features:
• Seam's context is used to share some state between the components
• Seam's dependency injection and Factory system is used to let component pull some data from each other
without having to know each other
In order to facilitate Nuxeo Services integration into the web framework, we use the Seam Unwrap pattern to
Nuxeo EP 5.1 38
General Overview
wrap Nuxeo Service into Seam Components that can be injected and manipulated as a standard Seam
component.
4.6.2.4. Deployment
The main components are webapp-core (default webapp base) and ui-web (web framework). On top of these
base components dedicated web components are deployed for each specific service.
For example, the workflow-service has its own web components package, so do relation-service, audit-service,
comment-service and so on.
Each package contains a set of views, actions, and ActionsListeners that are dedicated to one service and
integrate this service into the base webapp.
Because JEE standards require the webapp to be mono-bloc, we use the Nuxeo Runtime deployment service to
assemble the target webapp at deployment time.
This deployment framework let you: override resources, contribute XML descriptors like web.xml from several
components and manage deployment order.
Nuxeo EP 5.1 39
Chapter 5. Schemas and Documents
5.1. Introduction
This chapter presents the concepts of schemas and document types, which are used to define new documents in
Nuxeo EP 5.
5.1.1. Concepts
In Nuxeo EP 5, a fundamental entity is the document. A file, a note, a vacation request, an expense report, but
also a folder, a forum, can all be thought of as documents. Objects that contain documents, like a folder or a
workspace, are also themselves documents.
Any given document has a document type. The document type is specified at creation time, and does not change
during the lifetime of the document. When referring to the document type, a short string is often used, for
instance “Note” or “Folder”.
A document type is the grouping of several schemas. A schema represents the names and structure (types) of a
set of fields in a document. For instance, a commonly-used schema is the Dublin Core schema, which specifies
a standard set of fields used for document metadata like the title, description, modification date, etc.
To create a new document type, we start by creating one ore more schemas that the document type will use.
The schema is defined in a .xsd file (which obeys the standard XML Schema syntax) and is registered by a
contribution to a schema extension point.
The document type is registered through a contribution to another doctype extension point, and will specify
which schemas it uses. We also register the document type as a high-level type used by the EJB and rendering
layers.
“Core” document types and “ECM” component types should not be confused. The former live in the core
Nuxeo EP packages, the latter belong to the high level components. Apart from their definition, most of the uses
of "document types" refer to the “ECM” kind.
5.2. Schemas
A schema describes the names and types of some fields. The name is a simple string, like "title", and the type
describes what kind of information it stores, like a string, an integer or a date.
The two elements are sample1 and sample2. They are both of type xs:string, which is a standard type defined
by the XML Schema specification.
This schema has to be referenced by a configuration file so that the system knows it has to be read. The
configuration file will be placed in OSGI-INF/core-types-contrib.xml (the name is just a convention):
Nuxeo EP 5.1 40
Schemas and Documents
<?xml version="1.0"?>
<component name="org.nuxeo.project.sample.coreTypes">
<extension target="org.nuxeo.ecm.core.schema.TypeService" point="schema">
<schema name="sample" src="schemas/sample.xsd"/>
</extension>
</component>
We name our schema sample, and the .xsd schema was placed under resources/ which means that at runtime,
it will be available directly from the Jar, therefore we reference it through the path schemas/sample.xsd. The
schema is registered through an extension point of the Nuxeo component
org.nuxeo.ecm.core.schema.TypeService named schema. Our own extension component is given a name,
org.nuxeo.project.sample.coreTypes, which is not very important as we only contribute to existing
extension points and don't define new ones.
Finally, we tell the system that the OSGI-INF/core-types-contrib.xml file has to be read, by mentioning it in
the Nuxeo-Component part of the META-INF/MANIFEST.MF file of our bundle:
Manifest-Version: 1.0
Bundle-ManifestVersion: 1
Bundle-Name: NXSample project
Bundle-SymbolicName: org.nuxeo.project.sample;singleton:=true
Bundle-Version: 1.0.0
Bundle-Vendor: Nuxeo
Require-Bundle: org.nuxeo.runtime,
org.nuxeo.ecm.core.api,
org.nuxeo.ecm.core
Provide-Package: org.nuxeo.project.sample
Nuxeo-Component: OSGI-INF/core-types-contrib.xml
The document type is defined through a contribution to an other extension point, doctypes, of the same
extension component as before, org.nuxeo.ecm.core.schema.TypeService. We specify that our document
type, Sample, will be an extension of the standard system type Document, and that it will be composed of three
schemas, two standard ones and our specific one.
The Dublin Core schema that we use already contains standard metadata fields, like a title, a description, the
modification date, the document contributors, etc. Adding it to our document type ensures that a minimal level
of functionality will be present.
Nuxeo EP 5.1 41
Schemas and Documents
</type>
</extension>
</component>
As of this writing, the document type id has to be the same as the underlying core type; this restriction may be
lifted in the future. The type element will contain all the information for this type, this is described below.
This extension file is added to META-INF/MANIFEST.MF so that it will be taken into account by the deployment
mechanism:
Nuxeo-Component: OSGI-INF/core-types-contrib.xml,
OSGI-INF/ecm-types-contrib.xml
The label and icon are used by the user interface, for instance in the creation page when a list of possible types
is displayed. The icon is also used whenever a list of document wants to associate an icon with each.
The default view specifies the name of the Facelet to use for this document if nothing is specified. This
corresponds to a file that lives in the webapp, in this case view_documents.xhtml is a standard view defined in
the base Nuxeo bundle, that takes care of displaying available tabs, and the document body according to the
currently selected type. Changing it is not advised unless extremely nonstandard rendering is needed.
5.4.3. Layout
5.4.3.1. Configuration
Layouts are defined in a given mode (default modes are "create", "edit" and "view") ; layouts in the "any" mode
will be merged with layouts defined for specific modes.
The layout names refer to layouts defined on another extension point. Please see the layouts section for more
information.
Here is the old layout configuration that has been replaced by the above. If present, it is used instead of the new
configuration for compatibility purposes.
<layout>
<widget jsfcomponent="h:inputText"
schemaname="dublincore" fieldname="title"
required="true" />
<widget jsfcomponent="h:inputTextarea"
schemaname="dublincore" fieldname="description" />
Nuxeo EP 5.1 42
Schemas and Documents
<widget jsfcomponent="h:inputText"
schemaname="sample" fieldname="sample1" />
<widget jsfcomponent="h:inputText"
schemaname="sample" fieldname="sample2" />
</layout>
This section defines a series of widgets, that describe what the standard layout of this document type will be. A
layout is a series of widgets, which make the association between the field of a schema with a JSF component.
The layout is used by the standard Nuxeo modification and summary views, to automatically display the
document according to the layout rules. Note that the layout system is still young and is subject to minor
changes in the future. Here we define four widgets, displayed as simple input fields or as a text area.
This contributes a rule to an already existing type, Folder. The subtypes element specifies which types can be
created inside the type in which the element is embedded.
Here, we specify that our Sample type can be created in a Folder and a Workspace.
5.4.5. Summary
The final OSGI-INF/ecm-types-contrib.xml looks like:
<?xml version="1.0"?>
<component name="org.nuxeo.project.sample.ecm.types">
<extension target="org.nuxeo.ecm.platform.types.TypeService" point="types">
<type id="Sample" coretype="Sample">
<label>Sample document</label>
<icon>/icons/file.gif</icon>
<default-view>view_documents</default-view>
<layout>
<widget jsfcomponent="h:inputText"
schemaname="dublincore" fieldname="title"
required="true" />
<widget jsfcomponent="h:inputTextarea"
schemaname="dublincore" fieldname="description" />
<widget jsfcomponent="h:inputText"
schemaname="sample" fieldname="sample1" />
<widget jsfcomponent="h:inputText"
schemaname="sample" fieldname="sample2" />
</layout>
</type>
<type id="Folder" coretype="Folder">
<subtypes>
<type>Sample</type>
</subtypes>
</type>
<type id="Workspace" coretype="Workspace">
<subtypes>
<type>Sample</type>
</subtypes>
</type>
</extension>
</component>
Nuxeo EP 5.1 43
Part II. Platform Services
Nuxeo EP 5.1 44
Chapter 6. Actions, Views and JSF tags
6.1. Introduction
The User Interface framework uses different kinds of concepts to make the interface configurable in the same
way that the application itself is.
Links and pages that appear on the site can be the result of a "static" template written in the XHTML language,
but can also be the result of a configuration change, taken into account thanks to more "generic" XHTML
pages.
The following chapters will explain how configuration and templating combine to achieve the UI of the site.
6.2. Actions
6.2.1. Concepts
In this chapter, an action will stand for any kind of command that can be triggered via user interface interaction.
In other words, it will describe a link and other information that may be used to manage its display (the link
label, an icon, security information for instance).
<extension target="org.nuxeo.ecm.platform.actions.ActionService"
point="actions">
<action id="logout" link="#{loginLogoutAction.logout}"
label="command.logout">
<category>USER_SERVICES</category>
</action>
</extension>
The above action will be used to display a "logout" link on the site. Here are its properties:
• id: string identifying the action. In the example, the action id is "logout".
• label: the action name that will be used when displaying the link. In the example, the label is
"command.logout". This label is a message that will be translated at display.
• link: string representing the command the action will trigger. This string may represent a different
action given the template that will display the action. In the example, a JSF command link will be used,
so it represents an action method expression. The seam component called "loginLogoutAction" holds a
method named "logout" that will perform the logout and return a string for navigation.
• category: a string useful to group actions that will be rendered in the same area of a page. An action can
define several categories. Here, the only category defined is "USER_SERVICES". It is designed to group
Nuxeo EP 5.1 45
Actions, Views and JSF tags
all the actions that will be displayed on the right top corner of any page of the site.
Other properties can be used to define an action. They are listed here but you can have a look at the main
actions contributions file for more examples:
nuxeo-platform-webapp-core/srs/main/resources/OSGI-INF/actions-contrib.xml.
• filter-ids: id of a filter that will be used to control the action visibility. An action can have several
filters: it is visible if all its filters grant the access.
• filter: a filter definition can be done directly within the action definition. It is a filter like others and
can be referred by other actions.
• confirm: an optional Javascript confirmation string that can be triggered when executing the command.
• order: an optional integer used to sort actions within the same category. This attribute may be
deprecated in the future.
• enabled: boolean indicating whether the action is currently active. This can be used to hide existing
actions when customizing the site behavior.
Actions extension point provides merging features: you can change an existing action definition in your custom
extension point provided you use the same identifier. Properties holding single values (label, link for instance)
will be replaced using the new value. Properties holding multiple values (categories, filters) will be merged
with existing values.
Actions in the same category are supposed to be displayed in the same area of a page. Here are listed the main
default categories if you want to add an action there:
• USER_SERVICES: used to display actions in the top right corner of every page. The link attribute should
look like a JSF action command link. See the example above.
• VIEW_ACTION_LIST: used for tabs displayed on every document. As each tab is not displayed in a
different page, but just includes a specific template content in the middle of the page, the link attribute
has to be a template path. For instance:
<action id="TAB_VIEW" link="/incl/tabs/document_view.xhtml" enabled="true"
order="0" label="action.view.summary">
<category>VIEW_ACTION_LIST</category>
<filter-id>view</filter-id>
</action>
<action id="TAB_CONTENT" link="/incl/tabs/document_content.xhtml" order="10"
enabled="true" label="action.view.content">
<category>VIEW_ACTION_LIST</category>
<filter-id>view_content</filter-id>
</action>
• SUBVIEW_UPPER_LIST: used to display actions just below a document tabs listing. As USER_SERVICES,
these actions will be displayed using a command link, so the link attribute has to be an action method
expression. For instance:
<action id="newSection" link="#{documentActions.createDocument('Section')}"
enabled="true" label="command.create.section"
icon="/icons/action_add.gif">
<category>SUBVIEW_UPPER_LIST</category>
<filter id="newSection">
<rule grant="true">
<permission>AddChildren</permission>
<type>SectionRoot</type>
</rule>
</filter>
</action>
<action id="newDocument" link="select_document_type" enabled="true"
label="action.new.document" icon="/icons/action_add.gif">
<category>SUBVIEW_UPPER_LIST</category>
Nuxeo EP 5.1 46
Actions, Views and JSF tags
<filter-id>create</filter-id>
</action>
An action visibility can be controlled using filters. An action filter is a set of rules that will apply - or not -
given an action and a context.
Filters can be registered using their own extension point, or registered implicitly when defining them inside of
an action definition:
<filter id="view_content">
<rule grant="true">
<permission>ReadChildren</permission>
<facet>Folderish</facet>
</rule>
<rule grant="false">
<type>Root</type>
</rule>
</filter>
A filter can accept any number of rules. It will grant access to an action if, among its rules, no denying rule
(grant=false) is found and at least one granting rule (grant=true) is found. A general rule to remember is
that if you would like to add a filter to an action that already has one or more filters, it has to hold constraining
rules: a granting filter will be ignored if another filter is already too constraining.
The default filter implementation uses filter rules with the following properties:
• grant: boolean indicating whether this is a granting rule or a denying rule.
• permission: permission like "Write" that will be checked on the context for the given user. A rule can
hold several permissions: it applies if user holds at least one of them.
• facet: facet like "Folderish" that can be set on the document type
(org.nuxeo.ecm.core.schema.types.Type) to describe the document type general behavior. A rule can
hold several facets: it applies if current document in context has at least one of them.
• condition: EL expression that can be evaluated against the context. The Seam context is made available
for conditions evaluation. A rule can hold several conditions: it applies if at least one of the conditions is
verified.
• type: document type to check against current document in context. A rule can hold several types: it
Nuxeo EP 5.1 47
Actions, Views and JSF tags
applies if current document is one of them. The fake 'Server' type is used to check the server context.
• schema: document schema to check against current document in context. A rule can hold several
schemas: it applies if current document has one of them.
Filters do not support merging, so if you define a filter with an id that is already used in another contribution,
only the first contribution will be taken into account.
It is important to understand that an action does *not* define the way it will be rendered: This is left to pages,
templates and other components displaying it. Most of the time, actions will be rendered as command links or
command buttons.
For instance, actions using the USER_SERVICES category will be rendered as action links:
<nxu:methodResult name="actions"
value="#{webActions.getActionsList('USER_SERVICES')}">
<nxu:dataList layout="simple" var="action" value="#{actions}"
rowIndexVar="row" rowCountVar="rowCount">
<h:outputText value=" | " rendered="#{row!=(rowCount-1)}" />
<nxh:commandLink action="#{action.getLink()}">
<t:htmlTag value="br" rendered="#{row==(rowCount-1)}" />
<h:outputText value="#{messages[action.label]}" />
</nxh:commandLink>
</nxu:dataList>
</nxu:methodResult>
The nxu:methodResult tag is only used to retrieve the list of actions declared for the USER_SERVICES category.
The nxh:commandLink is used instead of a simple h:commandLink so that it executes commands that where
described as action expression methods.
Another use case is the document tabs: actions using the VIEW_ACTION_LIST category will be rendered as action
links too, but actions are managed by a specific seam component that will hold the information about the
selected tab. When clicking on an action, this selected tab will be changed and the link it points to will be
displayed.
6.3. Views
6.3.1. UI Views
First of all, we have to make the difference between a view in a standard JSF way (navigation case view id,
navigation case output) and views in Nuxeo 5 (document type view, creation view)
A standard JSF navigation rule can be defined in the OSGI-INF/deployment-fragment.xml files, inside the
faces-config#NAVIGATION directive.
<extension target="faces-config#NAVIGATION">
<navigation-case>
<from-outcome>create_document</from-outcome>
<to-view-id>/create_document.xhtml</to-view-id>
<redirect />
</navigation-case>
<navigation-case>
Nuxeo EP 5.1 48
Actions, Views and JSF tags
<from-outcome>view_documents</from-outcome>
<to-view-id>/view_documents.xhtml</to-view-id>
<redirect />
</navigation-case>
</extension>
A certain Nuxeo document type, can have defined a default view (used to view/edit the document) and a create
view (used to create the document). These views are specified in the OSGI-INF/ecm-types-contrib.xml file,
as in the following example.
The default view of a document is rendered as a list of tabs. As mentioned before, the document tabs are
defined as actions in the OSGI-INF/actions-contrib file, having as category VIEW_ACTION_LIST. A tab can be
added to a document default view as shown in the following example.
Example 6.6. Example of tab definition for a default view of a document type
• a scope (session or conversation), defined by <isSession/> tag - it defines if the memory storage occurs
in the Seam session context or in the Seam conversation context.
• a persistence (SQL directory or not present), defined by <persistent/> tag - the service persists only the
list of the document references, not the real documents; the lists of document references is persisted in a
SQL directory, which is generic and does not need any configuration.
The lists of documents can be invalidated when Seam events are raised. This is usefull, for example, for
resetting CURRENT_SELECTION lists when the user change the current folder or when a new search is performed.
Nuxeo EP 5.1 49
Actions, Views and JSF tags
When the File Manager service is called, it will detect the mime-type of the passed blob, and will try to find a
plugin to hadle the creation of a Nuxeo document model based on the detected mime-type.
Typical usages:
• txt/html/xml files - a Note document is created.
As a client of the File Manager service can be used the browser plugins (for Firefox and Internet Explorer)
which can be be downloaded through the links from the default Nuxeo 5 login page. These plugins enable the
user to create Nuxeo documents just by dragging & dropping folders/files to the browser. The plugins use a
restlet (HTTP API) to send files/folders to the Nuxeo 5 Platform. The restlets use the File Manager serivce in
order to create a Nuxeo document from the passed file.
Nuxeo EP 5.1 50
Actions, Views and JSF tags
Nuxeo EP 5.1 51
Chapter 7. Layouts
Let our artists go wild on imaginative page layouts.
-- Grant Morrison
7.1. Introduction
Layouts are used to generate pages rendering from an xml configuration.
In a document oriented perspective, layouts are mostly used to display a document metadata in different use
cases: present a form to set its schemas fields when creating or editing the document, and present these fields
values when simply displaying the document. A single layout definition can be used to address these use cases
as it will be rendered for a given document and in a given mode.
In this chapter we will see how to define a layout, link it to a document type, and use it in XHTML pages.
7.1.1. Layouts
A layout is a group of widgets that specifies how widgets are assembled and displayed. It manages widget rows
and has global control on the rendering of each of its widgets.
7.1.2. Widgets
It's all the same machine, right? The Pentagon, multinational corporations, the police! You do one little job, you
build a widget in Saskatoon and the next thing you know it's two miles under the desert, the essential
component of a death machine!
-- Holloway, Cube
A widget defines how one or several fields from a schema will be presented on a page. It can be displayed in
several modes and holds additional information like for instance the field label. When it takes user entries, it
can perform conversion and validation like usual JSF components.
7.1.4. Modes
The layout modes can be anything although some default modes are included in the application: create, edit,
view and search.
The widget modes are more restricted and widget types will usually only handle two modes: edit and view. The
widget mode is computed from the layout mode following this rule: if the layout is in mode create, edit or
search, the widget will be in edit mode. Otherwise the widget will be in view mode.
It is possible to override this behaviour in the widget definition, and state that, for instance, whatever the layout
mode, the widget will be in view mode so that it only displays read-only values. The pseudo-mode "hidden"
can also be used in a widget definition to exclude this widget from the layout in a given mode.
The pseudo mode "any" is only used in layouts and widgets definitions to set up default values.
Nuxeo EP 5.1 52
Layouts
Custom layouts can be contributed to the web layout service, using its extension point. The layout definition is
then available through the service to control how it will be displayed in a given mode.
Some jsf tags have been added to the Nuxeo ECM layout tag library to make then easily available from an
xhtml page.
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.forms.layouts.webapp">
<extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager"
point="layouts">
<layout name="heading">
<templates>
<template mode="any">/layouts/layout_default_template.xhtml</template>
</templates>
<rows>
<row>
<widget>title</widget>
</row>
<row>
<widget>description</widget>
</row>
</rows>
<widget name="title" type="text">
<labels>
<label mode="any">label.dublincore.title</label>
</labels>
<translated>true</translated>
<fields>
<field>dc:title</field>
</fields>
<properties widgetMode="edit">
<property name="required">true</property>
</properties>
</widget>
<widget name="description" type="textarea">
<labels>
<label mode="any">label.dublincore.description</label>
</labels>
<translated>true</translated>
<fields>
<field>dc:description</field>
</fields>
</widget>
</layout>
</extension>
</component>
• templates: list of templates to use for this layout global rendering. In the example, the layout template in
any mode is the xhtml file at "/layouts/layout_default_template.xhtml". Please refer to section about
custom layout templates for more information.
Nuxeo EP 5.1 53
Layouts
• rows: definition about what widgets will have to be displayed on this row. Each row can hold several
widgets, and an empty widget tag can be used to control the alignment. The widget has to match a widget
name given in this layout definition. In the example, two rows have been defined, the first one will hold
the "title" widget, and the second one will hold the "description" widget.
• widget: a layout definition can hold any number of widget definitions. If the widget is not referenced in
the rows definition, it will be ignored. Please refer the widget definition section.
• type: the widget type that will manage the rendering of this widget. In this example, the widget type is
"text". This widget type is a standard widget types, more information about widget types is available
here.
• labels: list of labels to use for this widget in a given mode. If no label is defined in a specific mode, the
label defined in the "any" mode will be taken as default. In the example, a single label is defined for any
mode to the "label.dublicore.title" message. If no label is defined at all, a default label will be used
following the convention: "label.widget.[layoutName].[widgetName]".
• translated: string representing a boolean value ("true" or "false") and defaulting to "false". When set as
translated, the widget labels will be treated as messages and displayed translated. In the example, the
"label.dublincore.title" message will be translated at rendering time. Default is true.
• fields: list of fields that will be managed by this widget. In the example, we handle the field "dc:title"
where "dc" is the prefix for the "dublincore" schema. If the schema you would like to use does not have a
prefix, use the schema name instead. Note that most of standard widget types only handle one field. Side
note: when dealing with an attribute from the document that is not a metadata, you can use the property
name as it will be resolved like a value expression of the form #{document.attribute}.
• properties: list of properties that will apply to the widget in a given mode. Properties listed in the "any"
mode will be merged with properties for the specific mode. Depending on the widget type, these
properties can be used to control what jsf component will be used and/or what attributes will be set on
these components. In standard widget types, only one component is used given the mode, and properties
will be set as attributes on the component. For instance, when using the "text" widget type, every
property accepted by the "<h:inputText />" tag can be set as properties on "edit" and "create" modes, and
every property accepted by the "<h:outputText />" tag can be set as properties. Properties can also be
added in a given widget mode.
• widgetModes: list of local modes used to override the local mode (from the layout).
• subWidgets: list of widget definitions, as the widget list, used to describe sub widgets use to help the
configuration of some complex widget types.
Here is a more complex layout contribution that shows the syntax to use for these additional properties:
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.forms.layouts.webapp">
Nuxeo EP 5.1 54
Layouts
<extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager"
point="layouts">
<layout name="complex">
<templates>
<template mode="any">/layouts/layout_default_template.xhtml</template>
</templates>
<rows>
<row>
<widget>identifier</widget>
</row>
</rows>
<widget name="identifier" type="text">
<labels>
<label mode="any">label.dublincore.title</label>
</labels>
<translated>true</translated>
<fields>
<field>uid:uid</field>
</fields>
<widgetModes>
<!-- not shown in create mode -->
<mode value="create">hidden</mode>
</widgetModes>
<properties widgetMode="edit">
<!-- required in widget mode edit -->
<property name="required">true</property>
</properties>
<properties mode="view">
<!-- property applying in view mode -->
<property name="styleClass">cssClass</property>
</properties>
</widget>
</layout>
</extension>
</component>
<layouts mode="any">
<layout>heading</layout>
<layout>note</layout>
</layouts>
Layouts are defined in a given mode; layouts in the "any" mode will be merged with layouts defined for
specific modes.
For instance, we can use the documentLayout tag to display the layouts of a document:
<div xmlns="http://www.w3.org/1999/xhtml"
xmlns:nxl="http://nuxeo.org/nxforms/layout">
<nxl:documentLayout mode="view" value="#{currentDocument}" />
Nuxeo EP 5.1 55
Layouts
</div>
We can also display a specific layout for a document, even if it is not specified in the document type definition:
<div xmlns="http://www.w3.org/1999/xhtml"
xmlns:nxl="http://nuxeo.org/nxforms/layout">
<nxl:layout name="heading" mode="view" value="#{currentDocument}" />
</div>
Please refer to the tag library documentation available at http://doc.nuxeo.org/5.1/tlddoc/ for nuxeo jsf tags.
7.5.1. text
The text widget displays an input text in create or edit mode, with additional message tag for errors, and a
regular text output in any other mode. Widgets using this type can provide properties accepted on a
<h:inputText /> tag in create or edit mode, and properties accepted on a <h:outputText /> tag in other
modes.
7.5.2. int
The int widget displays an input text in create or edit mode, with additional message tag for errors, and a
regular text output in any other mode. It uses a number converter. Widgets using this type can provide
properties accepted on a <h:inputText /> tag in create or edit mode, and properties accepted on a
<h:outputText /> tag in other modes.
7.5.3. secret
The secret widget displays an input secret text in create or edit mode, with additional message tag for errors,
and nothing in any other mode. Widgets using this type can provide properties accepted on a <h:inputSecret
/> tag in create or edit mode.
7.5.4. textarea
The textarea widget displays a textarea in create or edit mode, with additional message tag for errors, and a
regular text output in any other mode. Widgets using this type can provide properties accepted on a
<h:inputTextarea /> tag in create or edit mode, and properties accepted on a <h:outputText /> tag in other
modes.
7.5.5. datetime
The datetime widget displays a javascript calendar in create or edit mode, with additional message tag for
errors, and a regular text output in any other mode. It uses a date time converter. Widgets using this type can
provide properties accepted on a <nxu:inputDatetime /> tag in create or edit mode, and properties accepted
on a <h:outputText /> tag in other modes. The converter will also be given these properties.
7.5.6. template
The template widget displays a template content whatever the mode. Widgets using this type must provide the
Nuxeo EP 5.1 56
Layouts
path to this template; this template can check the mode to adapt the rendering.
Information about how to write a template is given in the custom widget template section.
7.5.7. file
The file widget displays a file uploader/editor in create or edit mode, with additional message tag for errors, and
a link to the file in other modes. Widgets using this type can provide properties accepted on a <nxu:inputFile
/> tag in create or edit mode, and properties accepted on a <nxu:outputFile /> tag in other modes.
7.5.8. htmltext
The htmltext widget displays an html text editor in create or edit mode, with additional message tag for errors,
and a regular text output in other modes (without escaping the text). Widgets using this type can provide
properties accepted on a <nxu:editor /> tag in create or edit mode, and properties accepted on a
<nxu:outputText /> tag in other modes.
7.5.9. selectOneDirectory
The selectOneDirectory widget displays a selection of directory entries in create or edit mode, with additional
message tag for errors, and the directory entry label in other modes. Widgets using this type can provide
properties accepted on a <nxd:selectOneListbox /> tag in create or edit mode, and properties accepted on a
<nxd:directoryEntryOutput /> tag in other modes.
7.5.10. selectManyDirectory
The selectManyDirectory widget displays a multi selection of directory entries in create or edit mode, with
additional message tag for errors, and the directory entries labels in other modes. Widgets using this type can
provide properties accepted on a <nxd:selectManyListbox /> tag in create or edit mode, and properties
accepted on a <nxd:directoryEntryOutput /> tag in other modes.
7.5.11. list
The list widget displays an editable list of items in create or edit mode, with additional message tag for errors,
and the same list of items in other modes. Items are defined using sub wigdets configuration. This actually a
template widget type whose template uses a <nxu:inputList /> tag in edit or create mode, and a table iterating
over items in other modes.
7.5.12. checkbox
The checkbox widget displays a checkbox in create, edit and any other mode, with additional message tag for
errors. Widgets using this type can provide properties accepted on a <h:selectBooleanCheckbox /> tag in
create, edit mode, and other modes.
Nuxeo EP 5.1 57
Layouts
<f:subview
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:nxl="http://nuxeo.org/nxforms/layout"
xmlns:nxu="http://nuxeo.org/nxweb/util"
xmlns:nxd="http://nuxeo.org/nxweb/document">
<f:subview rendered="#{layout.mode != 'edit' and layout.mode != 'create'}">
<table class="dataInput">
<tbody>
<nxl:layoutRow>
<tr>
<nxl:layoutRowWidget>
<td class="labelColumn">
<h:outputText value="#{widget.label}" rendered="#{!widget.translated}" />
<h:outputText value="#{messages[widget.label]}" rendered="#{widget.translated}" />
</td>
<td class="fieldColumn">
<nxl:widget widget="#{widget}" mode="#{widget.mode}" value="#{value}" />
</td>
</nxl:layoutRowWidget>
</tr>
</nxl:layoutRow>
</tbody>
</table>
</f:subview>
<f:subview rendered="#{layout.mode == 'edit' or layout.mode == 'create'}">
<table class="dataInput">
<tbody>
<nxl:layoutRow>
<tr>
<nxl:layoutRowWidget>
<td class="labelColumn">
<h:outputText value="#{widget.label}" rendered="#{!widget.translated}"
styleClass="#{nxu:test(widget.required, 'required', '')}" />
<h:outputText value="#{messages[widget.label]}" rendered="#{widget.translated}"
styleClass="#{nxu:test(widget.required, 'required', '')}" />
</td>
<td class="fieldColumn">
<nxl:widget widget="#{widget}" mode="#{widget.mode}" value="#{value}" />
</td>
</nxl:layoutRowWidget>
</tr>
</nxl:layoutRow>
</tbody>
</table>
</f:subview>
</f:subview>
This template is intended to be unused in any mode, so the layout mode is checked to provide a different
rendering in "edit" or "create" modes and other modes.
When this template is included in the page, several variables are made available:
• layout: the computed layout value ; its mode and number of columns can be checked on it.
• value or document: the document model (or whatever item used as value).
The layout system integration using facelets features requires that iterations are performed on the layout rows
and widgets. The <nxl:layoutRow> and <nxl:layoutRowWidget /> trigger these iterations. Inside the
layoutRow tag, two more variables are made available: layoutRow and layoutRowIndex. Inside the
layoutRowWidget, two more variables are made available: widget and widgetIndex.
These variables can be used to control the layout rendering. For instance, the default template is the one
Nuxeo EP 5.1 58
Layouts
applying the "required" style on widget labels, and translating these labels if the widget must be translated. It
also makes sure widgets on the same rows are presented in the same table row.
<f:subview xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a4j="https://ajax4jsf.dev.java.net/ajax"
xmlns:t="http://myfaces.apache.org/tomahawk"
xmlns:nxdir="http://nuxeo.org/nxdirectory">
<t:dataList id="#{widget.id}" var="listItem" value="#{field_0}"
layout="simple" styleClass="standardList">
<h:graphicImage value="/icons/html.png" />
<h:commandLink value="#{listItem}" immediate="true"
action="#{userManagerActions.viewUser}">
<f:param name="usernameParam" value="#{listItem}" />
</h:commandLink>
<br />
</t:dataList>
</f:subview>
This widget presents the contributors of a document with specific links on each on these user identifier
information.
Having a widget type just to perform this kind of rendering would be overkill, so using a widget with type
"template" can be useful here.
When this template is included in the page, the widget variable is made available:
• Use the widget id as identifier: the widget id is computed to be unique within the page, so it should be
used instead of fixed id attributes so that another widget using the same template will not introduce
duplicated ids in the jsf component tree.
• Use the variable with name following the field_n pattern to reference field values. For instance, binding
a jsf component value attribute to #{field_0} means binding it to the first field definition.
The standard widget type "list" is actually a widget of type "template" using a static template path:
/widgets/list_widget_template.xhtml. If this default behaviour does not suit your needs, you can simply copy
this template, make your changes, and use a widget of type "template" with the new template path.
This template assumes that each element of the list will be displayed using subwidgets definitions.
For instance, to handle a list of String elements, you can use the definition:
<widget name="contributors" type="list">
<fields>
<field>dc:contributors</field>
Nuxeo EP 5.1 59
Layouts
</fields>
<subWidgets>
<widget name="contributor" type="text">
<fields>
<field></field>
</fields>
</widget>
</subWidgets>
</widget>
The empty field definition in the subwidget is used to specify that each element of the list is itself the element
to display.
To handle a list of complex properties (each entry of the list is a map with keys 'name' and 'email' for instance),
you can use the definition:
<widget name="employees" type="list">
<fields>
<field>company:employees</field>
</fields>
<subWidgets>
<widget name="name" type="text">
<fields>
<field>name</field>
</fields>
</widget>
<widget name="email" type="text">
<fields>
<field>email</field>
</fields>
</widget>
</subWidgets>
</widget>
To handle a complex property (the value is a map with keys 'name' and 'email' for instance, you can use the
definition:
<widget name="manager" type="template">
<fields>
<field>company:manager</field>
</fields>
<properties mode="any">
<property name="template">
/widgets/complex_widget_template.xhtml
</property>
</properties>
<subWidgets>
<widget name="name" type="text">
<fields>
<field>name</field>
</fields>
</widget>
<widget name="email" type="text">
<fields>
<field>email</field>
</fields>
</widget>
</subWidgets>
</widget>
Nuxeo EP 5.1 60
Layouts
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.forms.layout.MyContribution">
<extension target="org.nuxeo.ecm.platform.forms.layout.WebLayoutManager"
point="widgettypes">
<widgetType name="customtype">
<handler-class>
org.myproject.MyCustomWidgetTypeHandler
</handler-class>
<property name="foo">bar</property>
</widgetType>
</extension>
</component>
Additional properties can be added to the type registration so that the same class can be reused with a different
behaviour given the property value.
The widet type handler is used to generate facelet tag handlers dynamically taking into account the mode, and
any other properties that can be found on a widget.
The best thing to do before writing a custom widget type handler is to go see how standard widget type
handlers are implemented, as some helper methods can be reused to ease implementation of specific
behaviours.
The field definition has to match a document property for which setters and getters will be available, or the
"value" property must be passed explicitely for the binding to happen. Depending on the widget, other kinds of
bindings can be done.
Nuxeo EP 5.1 61
Chapter 8. Event Listeners and Scheduling
8.1. Introduction
Events and event listeners have been introduced at the Nuxeo core level to allow pluggable behaviours when
managing documents (or any kinds of objects of the site).
Whenever an event happens (document creation, document modification, relation creation, etc...), an event is
sent to the event service that dispatches the notification to its listeners. Listeners can perform whatever action
when receiving an event.
8.2. Concepts
A core event has a source which is usually the document model currently being manipulated. It can also store
the event identifier, that gives information about the kind of event that is happening, as well as the principal
connected when performing the operation, an attached comment, the event category, etc..
Events sent to the event service have to follow the org.nuxeo.ecm.core.api.event.CoreEvent interface.
A core event listener has a name, an order, and may have a set of event identifiers it is supposed to react to. Its
definition also contains the operations it has to execute when receiving an interesting event.
Several event listeners exist by default in the nuxeo platform, for instance:
• DublincoreListener: it listens to document creation/modification events and sets some dublincore
metadata accordingly (date of creation, date of last modification, document contributors...)
• DocUidGeneratorListener: it listens to document creation events and adds an identifier to the document
if an uid pattern has been defined for this document type.
• DocVersioningListener: it listens to document versioning change events and changes the document
version numbers accordingly.
<?xml version="1.0"?>
<component name="DublinCoreStorageService" version="1.0.0">
<extension target="org.nuxeo.ecm.core.listener.CoreEventListenerService"
point="listener">
<listener name="dclistener"
class="org.nuxeo.ecm.platform.dublincore.listener.DublinCoreListener"
order="120" />
</extension>
</component>
Nuxeo EP 5.1 62
Event Listeners and Scheduling
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.uidgen.service.UIDGeneratorService">
<extension target="org.nuxeo.ecm.core.listener.CoreEventListenerService"
point="listener">
<listener name="uidlistener"
class="org.nuxeo.ecm.platform.uidgen.corelistener.DocUIDGeneratorListener"
order="10">
<eventId>documentCreated</eventId>
</listener>
</extension>
</component>
The only thing needed to add an event listener is to declare its name and its class. Sometimes the order in which
listeners are called matters so an integer order can be set to control it. A filtering on event ids can be done when
registering it too, though the notification method could handle it too.
For instance, the UIDgenerator service will only be notified when the event service receives a document
creation event.
Old school event listeners should still work ok for now, but should be migrated soon as the compatibility may
introduce bugs and will be removed shortly.
If your event listener does not care about the source, or the event it deals with is not a document, you do not
have to do anything.
In order to be sure that when an JMS event is received the associated DocumentModel is available, all
document oriented messages that may occur at core level are forwarded to the JMS topic when the session
repository is saved (ie: when data is committed).
Nuxeo EP 5.1 63
Event Listeners and Scheduling
In some cases, depending on own the Core API is used, some messages can be duplicated within the same
transaction (like modifying several times the same document), the JMSEventListener marks all duplicated
messages before sending them to JMS, its JMS messages receiver to choose to process or not the duplicated
messages.
During the forwarding on the JMS Topic, the coreEvents are converted to EventMessage. The main difference
is that the EventMessage does not contains the DocumentData (ie: all schemas and fields are unloaded), this is
done in order to avoid overloading JMS.
An important point to remember is that the MDB is executed asynchronously in a dedicated thread:
• there is no JAAS Session established: you can not access the repository without this
• the DocumentMessage is not bound to an existing CoreSession: you can not use the DocumenMessage to
do lazy loading (ie: DocumentMessage.getProperty())
So, in order to extract some document oriented properties of the document associated to the event, you must:
• Establish a JAAS Session
Nuxeo EP 5.1 64
Event Listeners and Scheduling
8.8. Scheduling
XXX TODO: FG
Nuxeo EP 5.1 65
Chapter 9. User Notification Service
9.1. Introduction
The notification framework provides a way to notify the users regarding different events as they happen in the
system.
• template: is a file in which is stored the text that is sent to an user - it may be dynamic
In order to define a notification, one must declare all these attributes in a contribution file.
In order to define a new channel (by default only email channel is used) the ChannelNotificator interface
must be implemented.
It has 2 methods:
• isInterestedInNotification(Notification docMessage) usually checks if the channel is right.
• sendNotification(DocumentMessage docMessage) sends the actual notification. For now only email
notification channel is implemented.
In order to define a new notification one must place a definition like this one in his contribution file :
<notification name="Modification" channel="email" enabled="true"
availableIn="workspace" autoSubscribed="false"
template="modif" subject="Document modified"
subjectTemplate="subjectModif"
label="label.nuxeo.notifications.modif">
<event name="documentModified"/>
<event name="contentSubdocumentModified"/>
</notification>
As you may see above a notification must declare a list of events to which it reacts. In our case
documentModified contentSubdocumentModified.
Nuxeo EP 5.1 66
User Notification Service
Also a notification must have a name that must be unique within the application. A label must also be specified
for i18n.
The attribute autoSubscribed is set to true when we want that a notification is sent to all concerned users. In
this case within the eventInfo map there must be loaded also the users that are concerned. For example if we
want that some users (ex: administrators or workflow manager) to get a notification each time a task is assigned
to them, we must use autoSubscribed="true" and put the usernames of all users in the eventInfo of the event
under the key recepients.
The attribute availableIn is used in order to restrict the scope of a notification. For example if we want to
define a notification that is triggered each time the document is modified, then it would not be used inside a
section, because sections contain documents that cannot be modified, only published. So in order to hide this
notification inside a section, we specify availableIn="workspace". The accepted values are workspace,
section and all.
The template attribute specifies the name of a template that will be used for generating the body of the
email(notification). This name is associated with a file using another extension point like this: <template
name="modif" src="templates/modif.ftl" />.
Inside a *.ftl file there may be inserted some dynamic parts like the document name, the user triggering the
event, etc. Any data that one wishes to put inside the body of the email, or its subject, he must put that data in
the eventInfo map under a unique key. Then inside the template file that data will be displayed using ${key}.
For the email notification a subject is used. This subject is a string but is also dynamic following the same rules
as the body inside the template files. For those who wants more parameterizable subject, you can use the
subjectTemplate attribute : it specfiies the name of the template used for generating the subject and into which
you can write dynamic content. As for the template attribute, the association between the name and the file is
done using an extension point like <template name="subjectModif"
src="templates/subjectModificationTemplate.ftl" />.
If both subject and subjectTemplate attributes are filled, the subjectTemplate attribute will be used to generate
the subject.
Note that if you are writing an HTML based template it will be advised to use HTML encoded letters when
there is accentuated letters (in french for example "é" will be "é"). The htmlEscape method is provided
while writing templates to transform accentued characters in data from the eventInfo map.
<p> Your comment here : ${htmlEscape(key)} </p>
Nuxeo EP 5.1 67
Chapter 10. Indexing & Searching
This chapter presents the architecture of the indexing and search service in Nuxeo EP 5.
10.1. Introduction
This chapter is under construction. XXX TODO: GR+JA
10.2. Configuration
For obvious performance and volume considerations, the search service doesn't index all the content of the
application, nor does it provide the full content in search results. This must be specified in the configuration,
along with what to do with the data.
The search service configuration is done by the standard Nuxeo Runtime extension point system. The target is
org.nuxeo.ecm.core.search.service For a complete example, check the default configuration file:
nuxeo-platform-search-core/OSGI_INF/nxsearch-contrib.xml (from Nuxeo EP sources).
10.2.1. Concepts
The main concepts are Resource and Field. A Resource holds several fields. It has a name and a prefix, which
is used, e.g, in queries. Resources are supposed to be unsplittable, but they are usually aggregated. It's up to the
backend to decide how to handle that aggregation; this goes beyond the scope of the present documentation.
At this point the search service handles documents only, so it's safe to say that documents correspond to
aggregates of resources, and resources correspond to schemas. In the future, there'll be more types of resources.
In this example, the Search service will index all schemas for documents of type DocType1, all schemas except
unwanted_schema for type DocType2 and only indexed_schema for type DocType3.
Each ot these indexable schemas will be processed according to the corresponding indexable schema resource
if available in the configuration, or to the default one .
In particular, the behavior of data from a given schema is homogeneous across all document types.
Nuxeo EP 5.1 68
Indexing & Searching
<extension target="org.nuxeo.ecm.core.search.service.SearchServiceImpl"
point="resource" type="schema">
<resource name="dublincore" prefix="dc" indexAllFields="true">
<excludedField>issued</excludedField>
<field name="title" (...) />
</resource>
<resource name="book" prefix="bk" type="schema">
<field name="barcode" (...) />
</resource>
</extension>
The name and prefix attributes are mandatory. They should match the ones from the schema extension point of
org.nuxeo.ecm.core.schema.TypeService as in, e.g.:
A missing prefix in the core configuration as in the common schema declaration in the example above will
default to the schema name.
The prefix is important, since all subsequent references to the fields (in queries and raw search results) take the
prefix:fieldName form.
The field type tells the search engine how to process the field. This is a mandatory, case-insensitive, attribute.
The following table summarizes the available types. The listed Java classes are guaranteed to work. The
backend might implement more converting facilities.
Nuxeo EP 5.1 69
Indexing & Searching
At indexing time, the contents of text fields goes through the process of tokenization and analysis, whose main
goal is to provide fulltext search capabilities, usually at the expense of exact matches.
Tokenization means converting the textual content in a stream of words (tokens). During the analysis step, this
raw stream is altered to better suit the needs of fulltext searches. It is for example common practice to strip the
stream of so-called stop words (most commons words in the language) and to degrade accented characters. One
can apply further linguistical processing, such as stemming or synonym analysis.
The name of analyzer to use on a given text field is specified through the analyzer attribute. The Search
Service acts as a pure forwarder, sending the raw text to the backend, along with the specified analyzer name.
The default value for the analyzer attribute is default. The attribute is simply ignored for other field types.
• To make it possible to provide the full field value in search results, one must set the stored attribute to
true. One must keep in mind that the purpose is to present the user a limited yet sufficient set of
information along with search results, i.e, in a swifter way than having to fetch it from the core, and not
to duplicate the content in the search databases.
• Depending on field types and on the processing that's been done by the backend, the possibility to use the
field value as a sort key might require some additional resources. To force the backend to give this extra
effort, set the sortable attribute to true.
• The binary attribute is used to mark binary fields, e.g, to trigger conversions (not used in 5.1M2).
• If there is only one relevant type from the table above, it is applied.
Example 10.1. Relying on automatic field configuration on all fields but one
Nuxeo EP 5.1 70
Indexing & Searching
Although the input of searchQuery is an already parsed NXQL statement, we'll use NXQL query strings in the
sequel for clarity. NXQL query strings are parsed by the method parse of the static
org.nuxeo.ecm.core.query.sql.SQLQueryParser class.
Although the Search Service is meant to provide an unified abstraction on the tasks of indexing and querying,
text fields have to be somewhat an exception. Indeed, search engines have very different capabilities,
depending on provided analyzers. They are nonetheless all expected to provide a direct syntax for full text
searches, that an end user can use from, e.g., an input box on a web page. Given the very special kind of
constraint that indexing a text field represents, it's not guaranteed that exact matches are supported.
See Section 10.4.4, “Text fields behavior” from the documentation of the Compass backend to get a more
concrete view on this (with examples).
10.3.2.1.1. Conclusions
• The backend uses the closest thing to exact matches it supports to treat = predicates.
• The syntax of LIKE predicates is backend dependent. It follows the backend's full text query syntax
• The CONTAINS from JCR is not supported. Use a LIKE statement on the main full-text field (see
Section 10.3.2.3, “Pseudo fields”).
Nuxeo EP 5.1 71
Indexing & Searching
On a multi-valued field, the = operator is true if the right operand belongs to the set of field values. The IN
operator is true if the intersection between the set of field values and the right operand is non empty.
This behavior is in conformance with the JCR specification, which states it the following more general terms:
In the WHERE clause the comparison operators function the same way they do in XPath when
applied to multi-value properties: if the predicate is true of at least one value of a multi-value
property then it is true for the property as whole.
The following fields are available on all document resources. They don't correspond to document fields and
aren't configurable, that's why they're called pseudo-fields.
The names of these fields are synchronized with constants from the class BuiltinDocumentFields. Any use
from java code should rely on these.
Nuxeo EP 5.1 72
Indexing & Searching
The contents of these files are covered in great details in the Compass 1.1 documentation. In the present
documentation, we'll focus on integration matters with the Nuxeo Search Service.
All Compass specific configuration files are relative to the classpath of the compass plugin. A default
configuration is provided for Nuxeo EP 5 WebApp. To customize it, one sadly has to put the configuration at
the right place within the backend's JAR.
Here is an ant fragment to perform this in a JBoss context, assuming that the Search Service has already been
installed in the application server and that the server's deployment directory is stored in the deploy.dir
property.
<copy todir="${deploy.dir}/nuxeo.ear/system/nuxeo-platform-search-compass-plugin-1.0.0-SNAPSHOT.jar"
overwrite="true" failonerror="false">
<fileset dir="src/main/resources">
<include name="myfile.xml" />
</fileset>
</copy>
The backend's JAR is included as a directory in nuxeo.ear during the Maven build of nuxeo-platform-ear for
this single purpose. This is prone to change in the future.
The Compass backend itself is registered against the Search Service through the searchEngineBackend
extension point of org.nuxeo.ecm.core.search.service.SearchServiceImpl. Your component can use the
configurationFileName element to specify a path to the master configuration file, like this:
10.4.2.1. Storage
Nuxeo EP 5.1 73
Indexing & Searching
Compass supports several storage possibilities, called connections in Compass configuration objects. The
configuration is done trough a Nuxeo Runtime extension point and possibly within the Compass master
configuration file. The extension point always takes precedence over the Compass file, but can be used to fall
back to Compass file, that offers currently more possibilites.
To set the connection to a file system Lucene store, put a file element in the contribution, and set the path
attribute to the target location. If the path doesn't start with /, it will be interpreted as being relative to Nuxeo
Runtime's home directory, e.g, /opt/jboss/server/default/data/NXRuntime in the default Nuxeo EP
installation on JBoss.
Other connection types, notably JDBC, are defined by the Compass configuration file, one has to put the
default XML element in the contribution, like this:
<extension target="org.nuxeo.ecm.core.search.backend.compass.CompassBackend"
point="connection">
<default/>
</extension>
The default connection is a relative file system one, hosted in the nxsearch-compass sub directory of Nuxeo
Runtime's home.
10.4.2.2. Analyzers
The master configuration file holds the definition and configuration of analyzers: a lookup name gets associated
to an analyzer type and options. The Compass backend makes Compass use directly the name declared in the
Search Service as the lookup name, configuration, therefore one has to ensure here that all of these do exist in
the Compass configuration.
Together with the registration itself comes the configuration of analyzers. For instance, an analyzer discarding
stop words might be given the full list of stop words.
Compass comes with a two predefined analyzers: default and search. You can reconfigure them as well.
See the relevant part in Compass documentation for details and sample configurations.
10.4.2.3. Converters
Lucene only knows about character strings. Therefore, typed data such as dates and integers must be converted
in strings to get into Lucene and back. Compass provides helpers for this and the Compass backend uses them
directly.
In the master configuration file, one register available converters in the form of a lookup name and a Java class.
Lots of converters are already registered by default, covering most basic types. The compass.cfg.xml file
shipping with the Compass backend redefines one (the date converter).
Currently, the Compass backend can't force Compass to use a given converter and/or analyzer on a given field.
It must therefore be specified in the mappings file, which is itself loaded from the master configuration file. The
default name for this file is nxdocument.cpm.xml.
Here's a sample, inspired from the mappings file provided with the backend.
Nuxeo EP 5.1 74
Indexing & Searching
<?xml version="1.0"?>
<!DOCTYPE compass-core-mapping PUBLIC
"-//Compass/Compass Core Mapping DTD 1.0//EN"
"http://www.opensymphony.com/compass/dtd/compass-core-mapping.dtd">
<compass-core-mapping>
<resource alias="nxdoc"
sub-index="nxdocs"
analyzer="default"
all="false">
<resource-id name="nxdoc_id"/>
<resource-property name="dc:created" converter="date"
store="yes" index="un_tokenized" />
<resource-property name="dc:title" analyzer="french" />
</resource>
</compass-core-mapping>
In Compass' terminology, fields are called properties. The name of the Compass property corresponding to a
Nuxeo indexed field coincides with the field's prefixed name.
• An exception to the above rules: you may experiment with the sub-index attribute, according to your
performance needs.
10.4.4.1. Indexing
At indexing time, the text field is analyzed using the analyzer from the Compass configuration file regardless
what has been configured through the Search Service extension point.
10.4.4.2. Searching
Equality statements in WHERE clauses are transformed into the closest thing that Lucene can provide on an
analyzed field, namely a phrase query.
On the other hand, LIKE clauses are directly fed to Lucene's QueryParser. To search documents whose title
starts with "nux", one may write
SELECT * FROM Document WHERE dc:title LIKE 'nux*'
The following two statements are equivalent. The second one is the QueryParser syntax for phrase queries.
... WHERE dc:title='Nuxeo Book'
... WHERE dc:title LIKE '"Nuxeo Book"'
Lucene's QueryParser syntax is really powerful, you can specify how close two words can be, apply fine
grained boosts for the relevance ordering, and more. The only restriction you have on LIKE statements for text
Nuxeo EP 5.1 75
Indexing & Searching
fields within the Compass backend is the choice of field. In other words, the colon character is escaped.
You would need to query date fields, like creation, modification or expiration date, that are provided by Nuxeo
Platform. In these cases, it would be interesting to use BETWEEN clauses, associated with DATE keyword,
which allows to convert strings as date values.
You should be aware of the following trap in Lucene queries: purely negative queries don't match anything,
even if they are themselves nested in a boolean query that has positive statements. The Compass backend uses
the standard way to circumvent this limitation, provided that the negative aspect can be seen from the NXQL
structure, i.e., not enclosed in a Lucene QueryParser literal.
Queries that should work as intended (the last three being equivalent):
... WHERE dc:title NOT LIKE 'book'
... WHERE dc:title LIKE 'nuxeo' AND dc:title NOT LIKE 'book'
... WHERE dc:title LIKE 'nuxeo' AND NOT dc:title LIKE 'book'
... WHERE dc:title LIKE '+nuxeo -book'
Nuxeo EP 5.1 76
Chapter 11. Look and feel
11.1. Introduction
The Nuxeo theme notion is wider than the notion attached to the same word in portal concepts. Indeed, the
Nuxeo Theme defines all the look and feel of your webapp: composition, layout and graphical appearance.
Nuxeo does not aim at developing a portal, i.e. a JSR 168 container, but it authorizes a kind of page and widget
management to get some flexibility in the design you want to give to your webapp. The tool to enable you
manage those aspects is "Nuxeo Theme editor", that you can make appear with the following command when
you are in Nuxeo Web app:
To switch to Nuxeo Theme editor click on the 'Themes Management' link in the user services panel.
Alternatively, you can type 'shift'+'alt'+'t'. To switch to Nuxeo Theme editor with Mozilla / Firefox < 2.0 type
'alt'+'t'
11.2. Principle
One special fragment is the Facelet region: in the properties tab of the editor you can specify the name of a
faces to directly integrate it into your page.
To use Nuxeo theme editor, you need to understand its model. The main entity is the theme. Then a theme may
have many pages. For each pages, you define a layout (a canvas) and add a list of fragments (widget). The
graphical editor uses a tab (theme) and sub-tab (page) system. When you want to add a new page or theme click
on "+" at the end of the tab list. For a page you have three possible views:
Nuxeo EP 5.1 77
Look and feel
• wysiwyg: you can move the widgets and evaluate the rendering.
• fragments: to put the widgets in their placeholder (= a cell). You can put many widgets in a place
holder.
• layout: you can create new rows and divide the rows into cells, and specify the width of each cell (just
edit the nn% on the screen).
You can click on a fragment to edit it. When you edit a fragment you have a multitab editor to specify:
• custom properties of the fragment: e.g. the text if it is a text fragment,
• then you specify the perspective in which the fragment can be seen.
One special fragment is the Facelet region: in the properties tab of the editor you can specify the name of a
faces to directly integrate it into your page.
11.3. Mechanism
Nuxeo Editor is done for not having to understand the underground mechanism. Yet it can be good to
understand the background to better leverage the tool and its possibilities. The Nuxeo component that manages
the customization and extension of Nuxeo EP 5 look and feel is: org.nuxeo.theme.services.ThemeService.
To register a whole theme (widget, style, layout etc. ...) you need to contribute to the extension point "theme"
this way:
<extension
target="org.nuxeo.theme.services.ThemeService" point="themes">
<theme> <src>META-INF/nxthemes-setup.xml</src>
</theme> </extension>
In the trunk, the default theme is in the webapp project. Having a look inside enables us to discover the main
features.
All this markup refers to an Element subtype in the java model: PageElement, CellElement... The fragment
element, the one that gives the widget oriented capacity to Nuxeo is typed: we have in the default distribution
"generic fragment", "action fragments". A typed fragment returns a model to be displayed and edit in the edit
mode of the fragments. This model is often (but not always) what we can see in the "properties" tab of the
fragment editor. For now there is in the default Nuxeo distribution:
• generic fragment (empty model)
• textual fragment
• region fragment
• action fragment
Nuxeo EP 5.1 78
Look and feel
The fragment can also receive a perspective attribute that specifies in which perspective it will be displayed
(the fourth tab in the fragment editor). You can then propose to Nuxeo user the same kind of experience you
have in Eclipse. The perspective are specified in the perspective extension point of !ThemeService component.
• their style
To do this, you put, inside the <theme> markup, children markup from those types:
• layout --> <layout element = ...>
Those 3 markups use the attribute element to get the reference to which element of your skeleton they will be
applied:
element="page[3]/section[3]/cell[1]"
This view is defined like this (with another extension point of !ThemeService: views):
<view
name="page frame"> <element-type>page</element-type>
<format-type>widget</format-type>
<class>org.nuxeo.theme.jsf.views.JSFView</class>
<template>nxthemes/jsf/widgets/page-frame.xml</template>
</view>
We can see that a view is associated to an element type. The element types contributions should be reserved to
Nuxeo only (one should manage with existing ones). The template markup gives the html/faces/text code to be
used for rendering the view. Notice that in the view, you can access the fragment model data through the EL
call nxthemesInfo.model.
<h:outputText escape="false"
value="#{nxthemesInfo.model.body}" />
The layout properties are given like this (still under the <theme> markup) :
<layout element="page[3]/section[3]/cell[1]">
<width>50%</width> <padding>20px</padding>
<margin>0</margin> </layout>
Nuxeo EP 5.1 79
Look and feel
Then comes the style properties. Again, you apply them to an element:
<style
element="page[1]/section[3]/cell[1]|page[3]/section[4]/cell[1]">
<selector path=""> <color>#757575</color>
<border-style>solid none none none</border-style>
<border-color>#003366</border-color>
<border-width>1px</border-width> <background> #FFF
url(/nuxeo/img/gray_gradient.gif) top left repeat-x
</background> <padding>5px 15px 5px 5px</padding>
</selector> <selector path="div">
<font-size>9px</font-size> </selector>
</style>
The selector specifies the markup to which the defined style is applied. The style definition used is the one of
the deeper upper-element that has a style definition specified. To be exhaustive, we need to present the filter
system (TODO)
So how to manage priority when more than one parameter is passed to the framework? The !ThemeService
component has another extension point to achieve this: the negotiation extension point. Not only can it be
used to select a theme but it also works with other objects as we will see later.
<negotiation object="theme"
strategy="nuxeo5">
<scheme>org.nuxeo.theme.jsf.negotiation.theme.RequestParameter</scheme>
<scheme>org.nuxeo.theme.jsf.negotiation.theme.CookieValue</scheme>
<scheme>org.nuxeo.theme.jsf.negotiation.theme.ViewId</scheme>
<!-- local theme (specific to nuxeo5) -->
<scheme>org.nuxeo.ecm.webapp.theme.LocalTheme</scheme>
<scheme>org.nuxeo.theme.jsf.negotiation.theme.DefaultTheme</scheme>
</negotiation>
As we can see in the example, the negotiation point defines the order in which the different methods for
obtaining the current theme information are applied. This negotiation feature also applies to other Nuxeo
Theme objects like the engine, the mode, the perspective.
Nuxeo EP 5.1 80
Look and feel
The engine, that you register in the ThemeService component through the "engines" extension point lets you
add for each type of element some "filters" that will do some work around the markup content at rendering
time. Nuxeo already uses filters like the style fitler, that put the style definition you chose, the layout filter, the
"drag'n drop" filter... One interesting use of the filters is illustrated with the Nuxeo Theme editor: when you
type shift+alt+t, the display changes and you are in the WYSIWYG Nuxeo theme editor. But all the
components that make your page are still here, they just look a bit different because, some different filters are
used. For instance, because of the drag'n drop filter presence, you can move the fragments.
Then, when you register the view associated to an element, you specify the resources it needs:
<view name="nuxeo5
clip board"> <format-type>widget</format-type>
<class>org.nuxeo.theme.jsf.views.JSFView</class>
<template>incl/user_clipboard.xhtml</template>
<resource>dragdrop.js</resource>
</view>
11.3.6. Application
The last concept you need to know about to completely control the look and feel of your application is the
application extension point:
<extension
target="org.nuxeo.theme.services.ThemeService" point="applications">
<application root="/nuxeo"> <negotiation>
<strategy>nuxeo5</strategy>
<default-engine>default</default-engine>
<default-theme>default/default</default-theme>
<default-perspective>default</default-perspective>
</negotiation> <resource-caching>
<lifetime>36000</lifetime> </resource-caching>
<view id="/create_relation_search_document_popup.xhtml">
<theme>default/popup</theme> </view> <view
id="/user_dashboard.xhtml">
<theme>default/user_dashboard</theme> </view> <view
id="/view_calendar.xhtml">
<perspective>view_calendar</perspective> </view>
<view id="/print.xhtml">
<perspective>print</perspective> </view>
</application> </extension>
As you can see in the example, an application is associated to a web-app root context. There you specify the
strategy (a negotiation grouping feature), the default engine, the default theme and perspective. You also
specify the caching policy and there you also declare the JSF view id / theme association that we went through
earlier in this tutorial.
Nuxeo EP 5.1 81
Look and feel
Eventually, all theme and styling work will be done in the Theme Editor. For now, we have to use both the
editor and the file theme-default.xml in
nuxeo/nuxeo-platform/nuxeo-platform-webapp-core/src/main/resources/META-INF/.
What can be done in the editor: page layout, widget moving, fragment styling, page/section/cell preset borders
and backgrounds
In addition to the theme-default.xml come palettes: a bunch of presets for colors, backgrounds, fonts and
other css attributes. Nuxeo EP 5 supports text palettes and GIMP/Photoshop palettes (for the colors).
When you add images or modify theme-default.xml, you have to redeploy your Nuxeo 5.
In case of doubt, try using the editor, because the produced code is much cleaner and compliant than anything
you would write manually :-)
• widgets in pages
This file is deployed in JBoss. If you modify the theme using the editor all changes will be lost so think of
downloading the theme to your Desktop, to replace the theme-default.xml in your local copy of Nuxeo EP 5.
A good way of working with this file is to add your working copy in theme-contrib.xml. It is possible in
NXThemes to load several themes and page.
<theme>
<src>file:///path/to/sources/nuxeo/nuxeo-platform/nuxeo-platform-webapp-core/src/main/resources/META-INF
</theme>
Nuxeo EP 5.1 82
Look and feel
After a redeployment, in the 'Manage Themes' section we now have a theme that can be reloaded directly from
the file-system!
• Modify the theme inside the editor, then go to Manage Themes tab and click on "Save" action. All
changes will be saved in the file.
• nxfonts.properties that contains default css font, small and 4 levels of titles
The easiest way for you to customize yout Nuxeo EP 5 app is to modify the existing palettes!
to
Then all the fonts of the app will be changed to your new value!
Currently in nxthemes-setup.xml we have a style named default buttons, which is defined as:
Nuxeo EP 5.1 83
Look and feel
Predefined styles are also a good way of efficiently changing the look of your application because you need to
change the CSS only once!
Later in the file we notice that the 'user services' fragment takes the default buttons style preset:
It means that the styles defined for the buttons will be applied to the 'user services' fragment (user links and
search in the banner).
As we explained earlier, layout editing and local styling can be done in the theme editor.
In the editor, click on an element you want to style, click "Edit" in the Menu. Here we chose the RSS/Atom link
button
Nuxeo EP 5.1 84
Look and feel
The existing selectors are on the right in the Properties box, otherwise move the mouse over the preview area
and click on an element to create a CSS selector path.
Nuxeo EP 5.1 85
Look and feel
We choose to change the small font preset to the default one. As you see, the Style picker shows all the palettes
and all the presets are rendered. We remove the background property for the syndication links button and add a
preset background-color, our RSS/Atom button is all changed now:
When you are done with managing your theme you might want to save it to your local copy of Nuxeo. Just go
in the Manage Themes tab, download the custom theme to your computer, then put it in your repository.
Nuxeo EP 5.1 86
Look and feel
You may want to modify an existing fragment to customize your project, let's say you want your compagny
logo instead of Nuxeo EP's and you own corporate links in the footer. We won't create & declare new
fragments (as we saw, fragments and their resources are defined in theme-contrib.xml), we'll use the
default-ones to override Nuxeo EP's, considering you have your own project using Nuxeo EP default as made
in the sample project.
Tip
This is a general principle for nuxeo.war folder. The contents of the /img/ folder of your app
are the contents of Nuxeo EP's default .../nuxeo.war/img folder. Every specific resource in
your.project/.../nuxeo.war/img come in addition of what is already in default
.../nuxeo.war/img if non-existing there with same filename, or come instead of what is
existing in default .../nuxeo.war/img if same filename.
<div xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"> <ui:insert
name="footer"> Copyright
<f:verbatim>&copy;</f:verbatim> 2006 Nuxeo.
Visit <!-- --> <h:outputLink
value="http://www.nuxeo.com"> <h:outputText
value="nuxeo.com" /> </h:outputLink> | Get <!--
--><h:outputLink
value="http://www.nuxeo.com/en/services/support/">
<h:outputText value="support" /> </h:outputLink> |
Join the <!-- --> <h:outputLink
value="http://www.nuxeo.org/sections/community">
<h:outputText value="community" /> </h:outputLink>
<br /> <h:form> <h:outputText
value="#{messages['label.selectLocale']}" />
<h:selectOneMenu value="#{localeSelector.localeString}"
styleClass="langSelect"> <f:selectItems
value="#{localeSelector.supportedLocales}"/>
</h:selectOneMenu> <h:commandButton
action="#{localeSelector.select}"
value="#{messages['command.changeLocale']}" class="langSubmit"
/> </h:form> </ui:insert>
</div
• Change from Copyright to <br /> by something like <a href="http://yoursite.com">My Corporate
Site</a>, save your changes
Nuxeo EP 5.1 87
Look and feel
• Do an ant on your projet, rerun your jboss and appreciate the changes...
Note
We assume you are familiar to Nuxeo EP way and have read the theme section above!
• You have a page called "default", toy with it, add sections and fragments, color the areas so your theme
is not empty. We recommend to add a region fragment with name set as body so the main content is
displayed.
Nuxeo EP 5.1 88
Look and feel
• You can add pages to your theme by clicking the plus tab next to the pages names.
You will need a few files/declaration to set your new theme. What we will be overriding in our project is
/nuxeo/nuxeo-platform/nuxeo-platform-webapp-core/src/main/resources/.
• The theme extension point contains your local theme file (if you plan to work that way)
Nuxeo EP 5.1 89
Look and feel
• The applications extension point set the negociation with your theme as default theme
Your new theme is now part of your project and set as default. Your can ant your app and re-run JBoss.
Tip
A good way to name your files is to add your project's name before the current Nuxeo filename.
For an example, theme-contrib.xml may become cuztom-theme-contrib.xml.
Your now all set to create your own design with all the tools explained in the sections above:
• Create fragments, declare theme in cuztom-theme-contrib.xml and drop them into your pages using the
theme editor
• Modify the structure of the pages with the theme editor and save your changes in your local files
• Add style in your local files and reload the theme using the Manage Theme tab in theme editor.
• Add images and icon into your nuxeo.war folder and call them in the style, in the actions-contrib.xml or
in the fragments.
Nuxeo EP 5.1 90
Chapter 12. Authentication, Users & Groups
Management
12.1. Introduction
In Nuxeo EP, the concept of a user is needed for two main reasons:
• Users are needed for authentication and authorization to work,
• Users have associated information that can be displayed, for instance to display someone's full name or
email address.
An abstraction, the UserManager, centralizes the way a Nuxeo EP application deals with users (and groups of
users). The UserManager is queried by the platform's LoginModule when someone attemps to authenticate
against the framework. It is also queried whenever someone wants the last name or email of a user for instance,
or to get all users having "Bob" as their first name.
12.2.1. Schemas
To define a new source of users, you simply define a new directory (or redefine the default one) to use different
connection and schema information. We'll use an example where the pet-name field is added to the user's
schema.
TODO: FG groups
Nuxeo EP 5.1 91
Authentication, Users & Groups Management
12.2.2. Directories
The user schema can now be used when we define a new directory, MyUserDirectory. A SQL directory is
defined like this:
<extension target="org.nuxeo.ecm.directory.sql.SQLDirectoryFactory" point="directories">
<directory name="MyUserDirectory">
<schema>myuser</schema>
<idField>username</idField>
<passwordField>password</passwordField>
<dataSource>java:/nxsqldirectory</dataSource>
<table>myusers</table>
<dataFile>myusers.csv</dataFile>
<createTablePolicy>on_missing_columns</createTablePolicy>
<references>
<inverseReference field="groups" directory="groupDirectory"
dualReferenceField="members" />
</references>
</directory>
</extension>
And we can provide a file, myusers.csv, which will be used to populate the table if it is missing:
username, password, firstName, lastName, company, email, petName
bob,bobSecret,Bob,Doe,ACME,bob@example.com,Lassie
If instead we had used an LDAP directory, the configuration would look like:
Detailed configuration on SQL Directories and LDAP Directories can be found in Chapter 17, Directories and
Vocabularies.
12.2.3. UserManager
We can now tell the UserManager that this directory should be the one to use when dealing with users:
<extension target="org.nuxeo.ecm.platform.usermanager.UserService" point="userManager">
<userManager>
Nuxeo EP 5.1 92
Authentication, Users & Groups Management
<users>
<directory>MyUserDirectory</directory>
<emailField>email</emailField>
<searchFields append="true">
<searchField>username</searchField>
<searchField>firstName</searchField>
<searchField>lastName</searchField>
<searchField>myfield</searchField>
</searchFields>
</users>
</userManager>
</extension>
This configuration also sets the email field, and search fields that have to be queried when searching for users.
It can be completed to set the anonmymous user, add virtual users, or set the group directory properties.
The anonymous user represents a special kind of virtual user, used to reprensent users that do not need to log in
the application. This feature is used in conjunction with the anonymous plugin (see next chapter).
Virtual users can be added for authentication. Properties are used to create the appropriate model as if user was
retrieved from the user directory. This is a convenient way to add custom users to the application when the user
directory (using LDAP for instance) cannot be modified. Virtual users with the "administrators" group will
have the same rights than the default administrator.
Nuxeo EP 5.1 93
Authentication, Users & Groups Management
The group directory can also be configured to define the groups hierarchy and the contained users. This
configuration has to match the user directory inverse references.
Every authenticated user will be placed in the configured default group. This group does not need to exist in the
backing group directory, nor does any other group listed in virtual users configuration.
12.3. Authentication
Users and groups and managed via the UserManagerService that handles the indirection to users and groups
directories (SQL or LDAP).
Nuxeo EP 5.1 94
Authentication, Users & Groups Management
Nuxeo authentication framework is pluggable so that you can contribute new plugin and don't have to rewrite
and reconfigure a complete JAAS infrastructure.
• nuxeo-ecm-web: for the web application on the top of the service stack
On JBoss application server, the JBoss Client Login module is used to propagate security between the web part
and the service stack.
12.3.2.1. NuxeoLoginModule
This means extract information from the CallBack stack and validate identity.
NuxeoLoginModule supports several types of CallBacks (including Nuxeo specific CallBack) and uses a
plugin system to be able to validate user identity in a pluggable way.
• Principal creation
For that NuxeoLoginModule uses Nuxeo UserManager service that does the indirection to the
users/groups directories.
When used in conjonction with UserIdentificationInfoCallback (Nuxeo custom CallBack system), the
Nuxeo EP 5.1 95
Authentication, Users & Groups Management
LoginModule will choose the right LoginPlugin according to the CallBack information.
Because validating User identity can be more complex that just checking login/password, NuxeoLoginModule
exposes an extension point to contribute new LoginPlugins
Typically, default implementation will extract Login/Password from UserIdentificationInfo and call the
checkUsernamePassword against the UserManager that will validate this information against the users
directory.
Other plugins can use other informations carried by UserIdentificationInfo (token, ticket ...) to validate the
identity against an external SSO system. The UserIdentificationInfo also carries the LoginModule plugin name
that must be used to validate identity. Even if technically, a lot of SSO system could be implemented using this
plugin system, most SSO implementations have be moved to the Authentication Plugin at the Web Filter level,
because they need a http dialog.
For now, the NuxeoLoginModule has only two way to handle validateUserIdentity:
• default
Uses UserManager
• Trusted_LM
This plugin assumes the user identity has already been validated by the authentication filter, so the
validatedUserIdentity will always return true. Using this LoginModule plugin, a user will be logged if
the user exists in the UserManager. This plugin is used for most SSO system in conjonction with a
Authentication plugin that will actually do the work of validating password or token.
You can also call the login method and pass it directly a CallBackHandler. This can be used in conjonction with
org.nuxeo.ecm.platform.api.login.UserIdentificationInfoCallbackHandler.
Nuxeo EP 5.1 96
Authentication, Users & Groups Management
This can be getting a userName/Password, getting a token in a cookie or a header, redirecting user to
another authentication server.
This means creating the needed callBacks and call the JAAS Login
In order to avoid recreating a login context for each request, the LoginContext is cached.
12.3.3.1. NuxeoAuthenticationFilter
The NuxeoAuthenticationFilter is one of the top level filter in Nuxeo Web Filters stack.
For each request it will try to find a existing LoginContext and create a RequestWrapper that will carry the
NuxeoPrincipal.
If no existing LoginContext is found it will try to prompt the client for authentication information and will
establish the login context.
If order to execute the task of prompting the client and retrieving UserIndetificationInfo, the filter will rely on a
set of configured plugins.
Typically, SSO AuthenticationPlugin will do all the work and will use the Trusted_LM LoginModule
Plugin.
AuthenticationPlugins that uses HTTP redirect in order to do the login prompt will let the Filter store the
first accessed URL in order to cleanly redirect the user to the page he asked after the authentication is
successful.
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.ui.web.auth.defaultConfig">
<extension
target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
point="authenticators">
Nuxeo EP 5.1 97
Authentication, Users & Groups Management
As you can see in the above example, the descriptor contains the parameters tag that can be used to embed
arbitrary additional configuration that will be specific to a given AuthenticationPlugin. In the above example, it
is used to define the field names and the JSP file used for form based authentication.
NuxeoAuthenticationFilter supports several authentication system. This is, for example, useful for having users
using Form based authentication and having RSS clients using Basic Authentication. Because of that
AuthenticationPlugin must be ordered. For that purpose, NuxeoAuthenticationFilter uses a dedicated extension
point that let you define the AuthenticationChain.
<component name="Anonymous.auth.activation">
<require>org.nuxeo.ecm.platform.ui.web.auth.defaultConfig</require>
<extension
target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
point="chain">
<authenticationChain>
<plugins>
<plugin>BASIC_AUTH</plugin>
<plugin>ANONYMOUS_AUTH</plugin>
<plugin>FORM_AUTH</plugin>
</plugins>
</authenticationChain>
</extension>
</component>
The NuxeoAuthenticationFilter will use this chain to trigger the login prompt. Typically, when authentication is
needed, the Filter will call the handleLoginPrompt method on the plugins in the order of the authentication
chain. When prompt is done, it will call the handleRetrieveIdentity method in the same order.
Some AuthenticationPlugin may choose not to trigger the LoginPrompt in all cases. For example, the
BasicAuthentication plugin only generates the prompt for specific urls like RSS feeds or restlet calls. This
allow the platform to be easily called by Restlets or RSS clients without bothering browser clients.
This is a standard form based authentication. Current implementation let you configure the name of the
Login and Password fields, and the name of the page used to display the login page
This plugin supports standard HTTP Basic Authentication. By default, this plugin only generates the
authentication prompt on configured URLs.
There are also additional components that provides other Authentication plugins (see below).
Nuxeo provides a set of other authentication plugins that are not installed by default with the standard Nuxeo
EP setup. These plugins can be downloaded and installed separately.
Nuxeo EP 5.1 98
Authentication, Users & Groups Management
This plugin implements a client for CAS2 SSO system (Central Authentication System V2):
In order to configure CAS2 Auth, you need to create an XML configuration file into
$JBOSS_HOME/server/default/deploy/nuxeo.ear/config
In order to configure this plugin, you need to create an XML configuration file into
$JBOSS_HOME/server/default/deploy/nuxeo.ear/config
Nuxeo EP 5.1 99
Authentication, Users & Groups Management
<component name="MyAPP.Mod_sso">
<require>org.nuxeo.ecm.platform.ui.web.auth.defaultConfig</require>
<require>org.nuxeo.ecm.platform.login.Proxy</require>
<extension
target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
point="authenticators">
<authenticationPlugin
name="PROXY_AUTH">
<loginModulePlugin>Trusting_LM</loginModulePlugin>
<parameters>
<!-- configure here the name of the http header that is used to retrieve user identity -->
<parameter name="ssoHeaderName">remote_user</parameter>
</parameters>
</authenticationPlugin>
</extension>
<!-- Include Proxy Auth into authentication chain -->
<extension
target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
point="chain">
<authenticationChain>
<plugins>
<!-- Keep basic Auth at top of Auth chain to support RSS access via BasicAuth -->
<plugin>BASIC_AUTH</plugin>
<plugin>PROXY_AUTH</plugin>
</plugins>
</authenticationChain>
</extension>
</component>
This plugging was partially contributed by Nuxeo EP users and has been reported to work by several users.
If you have troubles with latest version of IE on POST requests, please see JCIFS instructions on that
(http://jcifs.samba.org/src/docs/ntlmhttpauth.html#post).
In order to configure this plugin, you need to create an XML configuration file into
$JBOSS_HOME/server/default/deploy/nuxeo.ear/config
<plugin>BASIC_AUTH</plugin>
<plugin>NTLM_AUTH</plugin>
<plugin>FORM_AUTH</plugin>
</plugins>
</authenticationChain>
</extension>
</component>
In order to configure this plugin, you need to create an XML configuration file into
$JBOSS_HOME/server/default/deploy/nuxeo.ear/config
• configure the plugin via an XML descriptor (define who the anonymous user will be)
In order to configure this plugin, you need to create an XML configuration file into
$JBOSS_HOME/server/default/deploy/nuxeo.ear/config
13.1. Introduction
The Security Service provides an extension point to plug custom security policies that do not rely on the
document security settings. For instance, it can be used to define permissions according to the document
metadata, or to the logger user information.
13.2. Architecture
Policies are set in two places:
• in the core where custom checks of permission can be performed before resolving the document security
using its ACP.
• in the search where the query can be patched with custom constraints before processing it.
Policies are checked in the order given as a parameter when registering them.
On the core side, they are used when checking permissions: they can grant or deny access, in case following
policies - as well as the default security check relying on the ACP set on the document - will be ignored. They
can also return an undefined access, in case following policy checks will continue.
When defining a custom policy for the Read permission, queries to the search service have to be adapted to
have the same constraints: otherwise some queries will not return documents that the user can see, or will return
documents that the user cannot see. Search policies are used when performing any search.
These policies are set on different services and follow different interfaces. When deploying on multi machines
environment, where search and core may be hosted on different machines, the core and search policies will
have to be deployed on the corresponding machine.
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.core.securityPolicy.accessLevelContrib">
<extension target="org.nuxeo.ecm.core.security.SecurityService"
point="policies">
<policy name="accessLevel"
class="org.nuxeo.ecm.core.security.AccessLevelSecurityPolicy" order="0" />
</extension>
</component>
Here a specific check is done on all documents, regardless of the permission being checked. A property on the
document (securityLevel) is checked against a property set on the user model (accessLevel).
Note that the "unknown" access is returned when the policy should not interfere with the standard security
check.
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.searchPolicy.accessLevelContrib">
<extension target="org.nuxeo.ecm.core.search.service.SearchServiceImpl"
point="policies">
<policy name="access"
class="org.nuxeo.ecm.core.search.security.AccessLevelSearchPolicy"
order="20" />
</extension>
</component>
As a check is done on the core side regardless of the permission, it will also apply for the "Read" permission
which is checked when navigating to a document. Without this policy, searches would return documents that
the user may not be able to see, and woul generate a security exception when the user clicks on the document
link.
The same check than performed on the core is added here as a contraint to the search query. Note that the
principal attached to the query is kept: it is used to perform standard security checks, as the search indexes the
document ACP.
For additional information about NXQL queries, please refer to the Section 10.3, “Programmatic Searching”
section.
14.1. Introduction
TODO
14.2. Architecture
TODO
Once your workflow definition has been designed and is ready you can deploy it in Nuxeo workflow. Of
course, the target workflow engine backend plugin should be deployed and registered against the workflow
service.
In this case, the workflow definition will be deployed at application server deployment time (For now, this is
the case when the application server is starting up since hot deployment is not yet possible using Nuxeo
Runtime at the time of writing this document).It means this way of deploying workflow definition is not
suitable for all cases. See the next subsections for other ways of deploying workflow definitions.
Below is an example of a jPDL workflow definition contribution for the jBPM backend. This XML fragment
would be defined in a contribution registered as a component in a bundle:
<?xml version="1.0"?>
<component name="com.company.workflow.sample.contributions">
<extension target="org.nuxeo.ecm.platform.workflow.service.WorkflowService"
point="definition">
<definition>
<engineName>jbpm</engineName>
<mimetype>text/xml</mimetype>
<definitionPath>workflows/process_definition.xml</definitionPath>
</definition>
</extension>
</component>
• engineName: name specified for the target backend at workflow service registration time (see workflow
service backend extension point)
• mimetype: mimetype of the workflow definition. This is especially interesting in case of the format is
binary. (serialization issue at deployment time)
com.company.workflow /
META-INF /
workflows /
process_definition.xml
MANIFEST.MF
OSGI-INF /
workflow-definitions-contrib.xml
TODO
The field used for version is adaptable via the properties extension point of the
org.nuxeo.ecm.platform.versioning.service.VersioningService component. It allows to define which
properties should be used to set versions for a given document type.
<versioningProperties>
<majorVersion>my:major_version</majorVersion>
<minorVersion>my:minor_version</minorVersion>
<documentType>File</documentType>
<documentType>Note</documentType>
</versioningProperties>
The CoreEventListenerService is used to define which event to listen to. The default declaration is in
nuxeo-platform-versioning-core:
<listener name="versioninglistener"
class="org.nuxeo.ecm.platform.versioning.listeners.DocVersioningListener">
<eventId>lifecycle_transition_event</eventId>
<eventId>documentCreated</eventId>
<eventId>beforeDocumentModification</eventId>
<eventId>documentUpdated</eventId>
<eventId>documentRestored</eventId>
</listener>
<listener name="versioningChangelistener"
class="org.nuxeo.ecm.platform.versioning.listeners.VersioningChangeListener" />
To modify the version of a document when a life cycle state is reach you need to define rules with the
behavior(increment major, minor or do nothing) using the extension rules from
org.nuxeo.ecm.platform.versioning.service.VersioningService:
The default behavior for all type but File and Note is to increase the minor version for each life cycle change.
You need to override one of the default rules if you add a new type. The order is important, the list of rules is
read and the first match is used.
<versioningRuleEdit name="sampleEditRuleAnyState" action="ask_user"
lifecycleState="*">
<includeDocType>File</includeDocType>
<includeDocType>Note</includeDocType>
<includeDocType>MyNewType</includeDocType>
<option value="no_inc" default="true" />
This section shows how to use the versioning module, how to modify the version of a document automatically
or manually and how to use a document from a previous version.
16.1. Introduction
Events are logged (recorded) in an SQL table. The table name is NXP_LOGS. Events are not available in a flat
format, for example in a flat log text file.
16.2. Features
By default only document creation/deletion/modification events are logged. But it is possible to configure the
service to log other events.
16.4. Architecture
TODO
17.1. Introduction
TODO OG - General overview of the directory concept and goals
This code declares a directories node which defines a directory of users or of groups. The following
information are given to describe the directory:
• name: name of the server which will be used in the declaration of the directories
• schema: namee of the schema describing the user attributes in the directory
• dataSource: type of storage for the directory. In this example, the HSQLDB is used. Other RDBMS like
PostgreSQL can be used to store the datas by changing the local datasource.
• idField: the id field designs the primary key in the table, used for retrieving entries by id
• password: field from the table which contain the passwords, relative to the identfiant
• autoincrementIdField: boolean value which tells if the idField is automatically incremented - this value is
most of the time at false, because the identifiant is a string.
• dataFile: file from which data are getting to populate the table. Be careful to follow the structure of the
schema given above.
• createTablePolicy: indicates how the dataFile will be used to populate the table. Three values are
allowed:
• on_missing_columns: the dataFile is used to create missing columns, it means at creation of the table
or each time a new column is added, to follow the schema for example. Colums cannot be deleted.
• always: the dataFile is used to create the table as each restart of the application server
• querySizeLimit: the maximum number of results that the queries on this directory should return; if there
are more results than this, an exception will be raised
<extension target="org.nuxeo.ecm.directory.ldap.LDAPDirectoryFactory"
point="servers">
<server name="default">
<ldapUrl>ldap://localhost:389</ldapUrl>
<bindDn>cn=nuxeo5,ou=applications,dc=example,dc=com</bindDn>
<bindPassword>changeme</bindPassword>
</server>
</extension>
• ldapUrl:address of the LDAP server. A single server declaration can point to a cluster of replicated
servers. To leverage such a cluster and improve availibility, please provide one <ldapUrl/> tag for each
replica of the cluster. ldaps is the convention to use TLS/SSL connection.
These credentials are used by Nuxeo5 to browse directory and create/modify entries.
• searchBaseDn: entry point into the server's LDAP tree structure. Searches are only made below this root
node
• subtree: search in the whole subtree. Use this parameter when the [people] branch is nested.
• substringMatchType: defines who the query is built using wildcard characters. Three different values
can be provided:
• subany: wildcards are added around the string to match (as *foo*)
• subfinal: wildcard is added after the string (baz*). This is the default behaviour.
• readOnly: boolean value. This parameter allows to create new entries or modify existing ones in the
LDAP server
• creationBaseDn: entry point in the server's LDAP tree structure where new entries will be created. This
is useless to provided if readOnly attribute is set to false.
• creationClass: use as many tag as needed to specify which class are used to defined new people entries
in LDAP server.
The staticAttributeId attribute contains directly the value which can be read and manipulated.
This syntax should be understood as "the member groups value is an inverse reference on groupDirectory
directory using members reference". It is the group directory that stores all members for a given group. So the
groups of a member are retrieved by querying in which groups a member belongs to.
For example, it is useful to combine entries from LDAP directory with a standard directory provided by
Nuxeo5.
<component name="org.nuxeo.ecm.directory.multi.config">
<extension
target="org.nuxeo.ecm.directory.multi.MultiDirectoryFactory"
point="directories">
<directory name="multi">
<schema>schema</schema>
<idField>uid</idField>
<passwordField>password</passwordField>
<source name="sourceA" creation="true">
...
</source>
<source name="sourceB">
....
</source>
</directory>
</extension>
</component>
17.5.2. Sub-directories
</component>
Let's have a look at the schema attribute which can take two different values:
• vocabulary: this schema is provided to make default vocabulary
• xvocabulary: this schema is used to define linked vocabularies: when using xvocabulary schema, an
other attribute should be defined: parentDirectory points the parent directory name to which the current
one is relative.
18.1. Introduction
The org.nuxeo.ecm.platform.mimetype.* packages give all the tools to find the mimetype of a document.
The package provides two guessing approach: using file extensions and using the guessing library Jmimemagic
(third party tool providing been enhanced detection methods, based on the binary signature of files).
18.2. MimetypeRegistry
All the recognized mimetypes are stored in the MimetypeRegistry. Each mimetype definition is a contribution
to the mimetype extension point of the
org.nuxeo.ecm.platform.mimetype.service.MimetypeRegistryService component.
<component
name="org.nuxeo.ecm.platform.mimetype.service.MimetypeRegistryService">
<extension
target="org.nuxeo.ecm.platform.mimetype.service.MimetypeRegistryService"
point="mimetype">
<mimetype normalized="application/vnd.oasis.opendocument.text"
binary="true" iconPath="odt.png" oleSupported="true">
<mimetypes>
<mimetype>application/vnd.oasis.opendocument.text</mimetype>
</mimetypes>
<extensions>
<extension>odt</extension>
</extensions>
</mimetype>
</extension>
</component>
A mimetype node, bound to a MimetypeDescriptor defines a normalized mimetype with the following
informations:
• normalized: the mimetype entry that is described and that will be returned
• oleSupported: this file mimetype is supported by the oleExtract transform plugin - default is False
An other defined extension point is extension that allow to register extensions that are ambiguous to force
mimetype sniffing.
<component
name="org.nuxeo.ecm.platform.mimetype.service.MimetypeRegistryService">
<extension
target="org.nuxeo.ecm.platform.mimetype.service.MimetypeRegistryService"
point="extension">
<fileExtension name="xml" mimetype="text/xml" ambiguous="true" />
</extension>
</component>
At the MimetypeRegistry level, methods are provided to dynamically register (or unregister) mimetypes or
extensions by code
The registerMimetype method, with a MimetypeEntry argument, adds the given entry to the registry. After
adding the mimetype, one can see that the MimetypeEntry retrieved using the initial Normalized name is
correct. The getMimetypeSample method, helper for the TestMimetyepRegistryService test class, shows a
definition of a MimetypeEntry by code.
Based on the previous entry definition, two registries are built allowing to retrieve the normalized mimetype
through file extension (efficient) or by sniffing (accurate).
The file extension detection rely on the filename that has to be correct. As it is a simple string to associate to
the mimetype, we do not develop further. Just note that the filename has to be correctly named and have an
extension to retrieve a mimetype with this method.
The sniffing tries to analyse the file content itself in order to guess the mimetype. The third-party tool
Jmimemagic is used for this and widely enhanced on defining new supported mimetypes and detectors. The
Jmimemagic tool uses 2 files, magic.xml and magic_1_0.dtd. We redefine the XML one to add new detections
(no extension-point there defined from Jmimemagic too ;-) l).
Basically, the default mimetype sniffing is based on searching a sequence of characters (or binary values) at a
specified offset.
<match>
<mimetype>application/pdf</mimetype>
<extension>pdf</extension>
<description>PDF document</description>
<test offset="0" type="string" comparator="=">%PDF-</test>
</match>
A match is the definition of a magic entry. It contains a mimetype, an extension and a textual description of
the defined mimetype. A test node, containing the operation to perform is also defined. Here it declares that
for an application/pdf mimetype, the file has to contain the string %PDF- at offset 0.
if this method is usually suitable for a lot of files (i.e. one can find some invariants in the format), when used
with more complex ones, a simple offset (or a combination) is not enough and we have to refine the detection
algorithm. That is what detectors are made for and we have defined some for the 2 major office file formats,
MsOffice and OpenOffice.org.
<match>
<mimetype>application/zip</mimetype>
<extension>zip</extension>
<description>Zip archive data</description>
<test offset="0" type="string" comparator="=">PK\003\004</test>
<match-list>
<!-- opendocument & OOo 1.x -->
<match>
<mimetype>OOo</mimetype>
<extension>OOo</extension>
<description>OOo 1.x and OpenDocument file</description>
<test type="detector" offset="0" length="" bitmask="" comparator="=">
org.nuxeo.ecm.platform.mimetype.detectors.OOoMimetypeSniffer
</test>
</match>
</match>
First a simple offset detection is performed to qualify a zip file, then a sub-match is defined. The detector
type indicates that the org.nuxeo.ecm.platform.mimetype.detectors.OOoMimetypeSniffer has to be called
and this is its responsibility to give all the valid information (mimetype, extension, description) if the file is
of correct type.
For MS-Office files, the "magic numbers" (the value to be found at a certain offset) are not that clear, as the
magic number defined by Jmimemagic (based on the file Linux command resource file) is the same for all
MS-Office application. Then we invoke a detector for each component that uses the POI library to detect what
file we deal with.
<match>
<mimetype>application/msword</mimetype>
<extension>doc</extension>
<description>Microsoft Office Document</description>
<test offset="0" type="string" comparator="=">\320\317\021\340\241\261</test>
<match-list>
<!-- XLS file by detector -->
<match>
<mimetype>application/vnd.ms-excel</mimetype>
<extension>xls</extension>
<description>Excel File</description>
<test type="detector" offset="0" length="" bitmask="" comparator="=">
org.nuxeo.ecm.platform.mimetype.detectors.XlsMimetypeSniffer
</test>
</match>
<!-- PPT file by detector -->
<match>
<mimetype>application/vnd.ms-powerpoint</mimetype>
<extension>ppt</extension>
<description>Powerpoint File</description>
<test type="detector" offset="0" length="" bitmask="" comparator="=">
org.nuxeo.ecm.platform.mimetype.detectors.PptMimetypeSniffer
</test>
</match>
</match-list>
</match>
Once a Microsoft Office Document has been detected at offset 0 in the first match, two sub-matches
detectors are defined for application/vnd.ms-excel
(org.nuxeo.ecm.platform.mimetype.detectors.XlsMimetypeSniffer) and
application/vnd.ms-powerpoint (org.nuxeo.ecm.platform.mimetype.detectors.PptMimetypeSniffer). If
none returns a correct mimetype, the only possibility remains then application/msword. (This may be lightly
refactored for simplicity in a near future).
A detector is a class that implements net.sf.jmimemagic.MagicDetector. The public process method has to
detect if the file fulfills the condition. If successful, it returns the mimetypes supported by this file. The public
methods getHandledExtensions and getHandledTypes define the String arrays that are used by Jmimemagic
to build the final match.
Once available, directly or from a bean, any File or Blob can be analyzed and the information retrieved like the
mimetype name or the supported extensions list.
import org.nuxeo.ecm.platform.mimetype.ejb.MimetypeRegistryBean;
...
private MimetypeRegistryBean mimetypeRegistry;
...
public void testSniffWordFromFile() throws Exception {
File file = FileUtils.getResourceFileFromContext("test-data/hello.doc");
String mimetype = mimetypeRegistry.getMimetypeFromFile(file);
assertEquals("application/msword", mimetype);
List<String> extensions = mimetypeRegistry.getExtensionsFromMimetypeName(mimetype);
assertTrue(extensions.contains("doc"));
}
In the above example, the mimetypeRegistry object used is a MimetypeRegistryBean. The purpose is to sniff
the mimetype of a given file. The MS-Word file is first read and the getMimetypeFromFile method is called.
Once the mimetype is retrieved, the getExtensionsFromMimetypeName can be called and it gives the associated
extensions from the registry.
19.1. Introduction
Transforms are operations that perform any action modifying an input document. This can cover file conversion
as well as mail merging or information extraction.
The plugins are the engines that perform the needed transformations.
List<TransformDocument> transform(
Map<String, Serializable> options, TransformDocument... sources);
The transform method returns a list of TransformDocument and accepts an options map and
TransformDocument sources. TransformDocument object is defined in
org.nuxeo.ecm.platform.transform.interfaces.TransformDocument and holds the binary as well as other
information such as mimetype.
The options map holds all the necessary options to be passed to the plugin. Please note that the keys are the
plugin names. See the officeMerger plugin below for an implementation example.
The name attribute will be used to declare the transform chain. The class attribute is the class that will do the
effective job of the transformation while the destinationMimeType is the mime-type of the result of the
transform.
After attributes, <sourceMimeType> nodes define the allowed input mime-types the transform is supporting. In
the presented case, we can see that the any2odt plugin will be able to handle text, Microsoft office word , OOo
1.x and OpenDocument format (OOo2.x) files to output an application/vnd.oasis.opendocument.text
OpenDocument file. Options can also be added as we see for <option> tags with ooo_host_name and
ooo_host_port attributes.
Plugins can be combined to build transform chains. This chains are declared in a transformer which is a
contribution to the extension-point transformers of
org.nuxeo.ecm.platform.transform.service.TransformService component.
<extension target="org.nuxeo.ecm.platform.transform.service.TransformService"
point="transformers">
<transformer name="any2text"
class="org.nuxeo.ecm.platform.transform.transformer.TransformerImpl">
<plugins>
<plugin name="any2pdf"/>
<plugin name="pdf2text"/>
</plugins>
</transformer>
</extension>
A transformer is defined by its name. This is this name that will be used to initialize a transform service when
using it.
Then, plugins involved in the chain are listed. Wen can see that our any2txt transformer is composed with two
chained plugins: any2pdf then pdf2txt. Obviously, a single plugin for a transform is legal as we can see with
the use of our previous any2odt plugins.
<extension target="org.nuxeo.ecm.platform.transform.service.TransformService"
point="transformers">
<!-- This transformer uses a the OOo plugin to transform documents to ODT-->
<transformer name="any2odt"
class="org.nuxeo.ecm.platform.transform.transformer.TransformerImpl">
<plugins>
<plugin name="any2odt"/>
</plugins>
</transformer>
</extension>
We first get the TransformService that exposes all the available transforms. Then a specific transformer is
built with the getTransformerByName method of the service. The name is the one that has been declared in the
contribution to the transformers extension-point of the
org.nuxeo.ecm.platform.transform.service.TransformService component.
Then the transformer exposes a transform method that returns a list of TransformDocument. The arguments are
the:
• options: the plugin options (the keys are the plugin names) - we pass null here as we do not have any
There are three levels of options that overload. First the plugin options are the default. Then any option in the
transformer that define an option for this plugin overload them. Finally, any code-defined options are merged,
overloading any previous option that may have been already defined. Please note again that options are defined
on a plugin name basis.
• a blob
In our example, we use the second way and give a sourceStream and the mime-type of an ODF document.
Once the input list processed, the results list contains all the transformed files as TransformDocument instances
from which you can retrieve the SerializableInputStream stream with getStream method, the mime-type
using getMimetype and the blob with getBlob method.
An alternate way of using the transform is to call it directly from the service
The arguments are the same and the converter name is given as first argument. An other constructor using
blobs as input instead of TransformDocument is also available. Before calling a transform we can also check
that the source mime-type is supported by calling the isMimetypeSupportedByPlugin method. Be careful
though that this plugin name may be different than the transformer name.
Transforms can be called directly but are also part of the docModifier framework that reacting on events, can
call transforms to alter or generate new informations (see below oleExtract plugin or docModifier
documentation)
The transform call relays to the JOOoConvertPluginImpl that first connects to OOo on port and host defined
in the contribution (usually localhost:8100) or in the options (not defined in our example). It then acquire an
OpenOfficeDocumentConverter from JODconverter tool and then can call the convert method with the
requested target mimetype and source file. Note that in future version, the
StreamOpenOfficeDocumentConverter class will be used to avoid dealing with File objects. This limitation
will be solved when a new version of OpenOffice.org (2.3) will be out and solves a regression on loading
streams.
Before any call to the underlying OpenOffice.org converter, once in the transformation engine
JOOoConvertPluginImpl, the source document mimetype is tested. If it is the same than the requested
destinationMimetype defined by the plugin, the source file is returned immediately as result unless the
mimetype occurs in the <sourceMimeType> of the plugin to allow self-transformations. By default, any2pdf
plugin will return immediately if an application/pdf file is submitted while any OpenDocument
transformation (any2odt, any2ods, any2odp) will process the files as each one contains its own mimetype like
<sourceMimeType>application/vnd.oasis.opendocument.text</sourceMimeType>. This allow to clean and
validate files forged by hand and possibly apply automatic treatments at OpenOffice.org side.
Since OOo 2.3.0, it is possible to use InputStreams as source documents so that no more File objects are
needed. This is useful if we want to isolate the OOo server on a separate machine (and if needed use a farm
with load balancing if heavy work is needed) As this feature is only available with OOo2.3+ (due to a bug in
previous versions), there is a nuxeo.property to define the OOo version used.
org.nuxeo.ecm.platform.transform.ooo.version=2.3.0
At term, the JODConverter tool should be able to return the OOo version it is using, so that this property will
not be needed anymore. For the moment, let use this property. But as OOo stream loading has been reported as
less efficient than File/URL by JODConverter users, the streaming method is only used if OOo is really a
remote instance. This is analyzed if the ooo_host_name transformer option is 'localhost' or starts with
'127.0.0'. And of course, only if OOo declared version is greater than 2.3.0.
As the underlying engine is based on OpenOffice.org, one can extend the document converter by supporting
OOo loading options. The transform plugins options mechanism is fully supported, so they can be defined at
plugin, transformer and code level. The available fields to be passed to OOo is listed at MediaDescriptor IDL
reference and have to be bound to the plugin name.
Here is an example passing two options for loading conditions of a special document that is a password
protected and has an autostart bound event that delete the content of the file to put the word DELETED in the
document. This simulate any other heavy process such as document merging or automatic mail-merge from a
database.
First, we build the plugin options map. The Password field is in charge of sending the password string to the
OOo loader so that the file can be opened. The MacroExecutionMode field defines how macros and script are
handled at startup. By default, the consequence of the -headless OOo mode, is that the NEVER_EXECUTE = 0
value is used. One can change this default behaviour by using any value listed in the OOo MacroExecMode
constant group. One important thing to be noted is that type is important: Password require a String argument
while MacroExecutionMode requires a short one. The types have to be correct otherwise the field will not be
handled by OOo.
By default, JODConverter sets the ReadOnly option as true. As we want to modify the document, we will have
to set the ReadOnly flag to false. The Hidden flag has also to be set to false. This trick is due to a problem in
OpenOffice.org PDF engine that seems not to be able to handle document modification while Hidden. As an
headless server-deployed OOo instance, this should not be a major problem.
Once Options have been defined, they are globally merged to the options map under the plugin name key (here,
any2pdf)
Then the transform can be called as usual. Please note that mimetype of password document have to be passed
explicitly to the TransformDocumentImpl constructor as it can not be sniffed by the Mimetype service for the
moment.
Finally, as expected a PDF document is returned with its content changed to the DELETED string.
19.3.2. Pdfbox
19.3.3.1. Implementation
The purpose of this plugin is to extract all these Ole objects and provide them as standalone files so that they
can be checked individually. It is also extended to extract images. It has a classical Transform plugin structure,
the plugin name is oleExtractPlugin bound to
org.nuxeo.ecm.platform.transform.plugin.oleextract.impl.OfficeOleExtractorPluginImpl. The
transform name is oleExtract.
First, the transform service is classically called, the TRANSFORMER_NAME being set to oleExtract. After
processing, the only TransformDocument returned result contains a property ole:olecontents that gives the
list of embedded objects that have been extracted.
The olecontents schema is defined in olecontent.xsd. Each element of the list contains the following fields
<xs:complexType name="olecontent">
<xs:sequence>
<xs:element name="displayname" type="xs:string" />
<xs:element name="filename" type="xs:string" />
<xs:element name="mime-type" type="xs:string" />
<xs:element name="data" type="nxs:content"/>
<xs:element name="thumbnail-mime-type" type="xs:string"/>
<xs:element name="thumbnail-data" type="nxs:content"/>
</xs:sequence>
</xs:complexType>
Each olecontent element contains file datas (data & mime-type) and thumbnail ones (thumbnail-mime-type
& thumbnail-data). The displayname is the name retrieved in the office file if it was named otherwise the
internal one that has been given in the office file. The filename fields is built from the displayname and the
extension deduced from the mime-type.
A new FileWithOle doctype, based on File, is defined. It can be subtype of Workspace and Folder and is
defined as a contribution of org.nuxeo.ecm.platform.types.TypeService which allows to create new
documents based on it.
The oleExtract transform plugin has been bound to the oleExtractModifier docModifier. The contribution to
the extension point is defined in
<extension target="org.nuxeo.ecm.platform.modifier.service.DocModifierService"
point="docTypeToTransformer">
<documentation>
docModifier for oleExtract transform plugin.
</documentation>
<docModifier name="oleExtractModifier"
documentType="FileWithOle"
transformationPluginName="oleExtract"
sourceFieldName="file:content"
destinationFieldName="file:content">
<coreEvent>documentCreated</coreEvent>
<coreEvent>documentModified</coreEvent>
<customField name="olecontents:olecontents" transformParamName="ole:olecontents"/>
<customOutputField outputParamName="ole:olecontents" name="olecontents:olecontents"/>
</docModifier>
</extension>
This contribution reacts on document creation and modification. It receives the initial office file file:content
and gives back the olecontents:olecontents back mapped top the ole:olecontents we saw above. The
initial file is returned unchanged.
With this docModifier reacting on some events, the oleExtract results can now be integrated in the application.
The org.nuxeo.ecm.platform.transform.oleextract.action component defines an ActionService contribution
The TAB_OLEOBJECT action defines a new tab listing the olecontents:olecontents elements and providing
links for retrieving each file provided the document is of the current type FileWithOle.
OleExtract is based on the parsing of OpenDocument File format. If the submitted file is an ODF one, then it is
unzipped and processed without any connection to OpenOffice.org. If the file is not an ODF one, then a
converter plugin is used according to the source mime type. In this case, OpenOffice.org is required as a
JODConverter dependency.
Once we have an ODF file it is unzipped and its content.xml is parsed to find draw:object-ole, draw:object
and draw:image related tags. For each one, the name of the resource is retrieved if it exists. Then for each
found resource, the alternate view is retrieved so that a preview can be proposed when listing this content (still
under development)
ODF resources in an ODF file (think at a spreadsheet diagram embedded in a text document) are stored in
directories and flat XML form while other resources are stored in a binary format. So for ODF resources the
global manifest file is parsed to isolate the files with their correct manifest:media-type so that the new ODF
archive for the Ole object can be built. Once the new manifest file is created, the embedded ODF directory is
zipped and this binary form is returned.
The public method merge is available from OfficeMergerImpl and returns a SerializableInputStream
containing the resulting document.
OfficeMergerImpl merger = new OfficeMergerImpl();
SerializableInputStream result = merger.merge(sourceFiles, engineType, converter,
outlineRank, withPageBreaks);
SerializableInputStream result = merger.mergeStreams (sourceStreams, engineType,
converter, outlineRank, withPageBreaks);
Some options have been added to enhance the building of the main document. Here is the list of the arguments
of the merge method
• sourceFiles/sourceStreams: Ordered array of File objects or streams to be merged.
• engineType: Depending on the nature of source documents, the OpenOffice.org API to be used is
obviously not the same. If source files are text file, the user will probably want to have a text file as a
result while if he deals with slides, the results is expected as a presentation. This String argument tells
which engine to be used (text, presentation, spreadsheet- only text is already implemented) -
Default text
• converter: Once the document is built, the Document Converter plugin can be called automatically to
create the final document. This is the converter name that is expected (eg. any2pdf) and an exception is
raised if it does not exist or the mime type deduced from the engineType is not supported. If an empty
string is provided, no transform occur at the end of the merging - Default empty
• outlineRank: This is the rank (compared to the file list) where a Table Of Content may appear. For
example, if the value is 3, then the first two files of the file list are inserted, the T.O.C is built and
inserted and then the remaining files are processed. The Table of Content is refreshed at the end of the
whole insertion. A value of 0 means no T.O.C. - Default 0
• withPageBreaks: A boolean that adds or removes page breaks between file insertions - Default true
The plugin engine, the merge method is available directly but the principal use will occur through a Transform
call. The Transform name is officeMerger and the package name is
org.nuxeo.ecm.platform.transform.plugin.officemerger
<plugin name="OfficeMergerPlugin"
class="org.nuxeo.ecm.platform.transform.plugin.officemerger.impl.OfficeMergerImpl"
destinationMimeType="application/vnd.oasis.opendocument.text">
<sourceMimeType>application/msword</sourceMimeType>
<sourceMimeType>application/vnd.oasis.opendocument.text</sourceMimeType>
<sourceMimeType>application/vnd.sun.xml.writer</sourceMimeType>
<option name="ooo_host_name">localhost</option>
<option name="ooo_host_port">8100</option>
</plugin>
Note that OpenOffice.org has to be listening from incoming UNO connections on the specified interface
ooo_host_name and port ooo_host_port. If it is not the case, an OpenOfficeException exception will be
raised.
So, once defined, the officeMerger transform can be called passing the options like
Note that mergingOptions can be incomplete or even null. The options will then take their default values.
The results list contains the final merged document, and converted if requested, at first index.
The XSLT Plugin accepts XML documents as source files, and you must provide the XSL stylesheet as a
plugin's option named stylesheet. The XSL stylesheet must be provided as a Blob.
The resulting documents' mime-type is set depending of the method attribute of the xsl:output element,
specified in the XSL stylesheet. If there is no method attribute defined in the XSL stylesheet, a default value is
chosen for the method attribute as defined in the XSLT Specification.
If there is no XSL stylesheet provided to the plugin or if an error occurs during the transformation (corrupted
xml or xsl for instance), a TransformException is thrown.
20.1. Introduction
Relations in Nuxeo EP 5 follow concepts as described by the W3C Resource Description Framework (RDF).
The purpose is to provide content management relations (relations between documents of the site for instance)
as well as being able to share this information with third party applications, by following the RDF standards.
20.2. Concepts
There are a few jargon terms to understand when dealing with relations.
Let's consider a relation like "document A is a version of document B". This relation is described as a triplet or
statement: it has a subject, "document A", a predicate, "is version of", and an object, "document B".
The statement elements are more generally referred to as nodes. More specific kinds of nodes are literals and
resources. A subject and a predicate will always be resources, while the object may be also a literal. In a
relation like "document A has title 'documentation'", the object will be the literal string 'documentation'.
Literals are simple nodes, holding information like a string or a date. Resources refer to uniquely identifiable
objects, and often use a URI as identifier that looks like a URL. If this URI refers to an identified namespace,
we can make a difference between resources using it.
For instance, we can use the dcterms namespace to identify predicates: "http://purl.org/dc/terms/References",
"http://purl.org/dc/terms/IsBasedOn","label.relation.predicate.IsBasedOn",...
When defining a relation like "document A is a version of document B", we will then build a statement which
subject is a resource representing document A, which predicate is a resource representing the "is a version of"
information, and which object is a resource representing document B.
If we would like to state that this relation was created as a certain date, we will add a date property to the
statement. This can be seen as a relation where the subject would be the statement itself, the predicate a
resource representing the "was created at" information, and which object would be a literal representing the
given date.
20.3. Configuration
If you would only like to change the storage used for the default graph of Nuxeo EP 5, please refer to
Section 43.4.2, “Relation service configuration”.
The graph definition is made though an extension point. It holds configuration about where and how to store
relations. Here is an example contribution.
Example 20.1. Jena graph configuration for the Relation Service using PostgreSQL as storage
This graph uses a Jena graph. Jena is a RDF framework, a plugin has been developed to integrate it to the
nuxeo platform. The graph definition requires the plugin to be registered to the application.
For instance, the namespace "http://www.nuxeo.org/document/uid/" is used to identify documents using their
JCR unique identifier. We can register an adapter so that the resource can be transformed into the actual
document model it represents.
For example, the DocumentModelResourceAdapter class allows to get a DocumentModel object from a
resource, build with a namespace and a local name.
• methods to get all statements or statements which are matching a given pattern: this can be useful to
determine if a relation is incoming (document is the subject of the statement) or outgoing (the document
is the object of the resource)
• methods to get extra properties: for each relation, informations are added like creation date or the creator
of the relation.
Another interesting interface is NodeInfo: it is useful to qualify the type of resource you are handling with.
Some methods can determine if the node is a literal, a document, a blank node, etc ...
21.1. Introduction
The placeful configuration service allows configuration to be placed on a node in a repository. It is possible to
update, remove this configuration and merged it with all the configuration located from this node to the root of
the repository.
Placeful Configuration (PC in the rest of this chapter) is useful when you have a number of nodes much bigger
than the number of configuration. It allows to change and merge these configuration without having to travel
the repository tree.
You can then use the placeful configuration service. The following code snippet show the basic usage:
Path p1 = new Path("/mon/path");
RepositoryLocation repo = new RepositoryLocation("monrepo");
NuxeoPrincipal principal = new NuxeoPrincipalImpl("myself");
PlacefulConfigurationManager pcs = Framework.getService(PlacefulConfigurationManager.class);
LocalTheme lt1 = pcs.createConfigurationEntry(LocalTheme.class, p1, repo, principal);
lt1.setMode("myMode");
pcs.saveConfigurationEntry(lt1);
Map<String, Object> map = new HashMap<String, Object>();
map.put("mode", "myMode");
List<LocalTheme> list = pcs.getAllConfigurations(LocalTheme.class, map); ❶
LocalTheme lt = pcs.getConfiguration(LocalTheme.class, repo, p, principal);
LocalTheme ltMerge = pcs.getMergedConfiguration(LocalTheme.class, repo, p, principal);
pcs.removeConfiguration(LocalTheme.class, repo, p1, principal);
❶ Query the storage for all the PC that have those field/value.
Note that:
• you never create a PlacefulConfiguration yourself but ask the manager for one.
• You can not move a PC, you need to remove it and create a new one.
For more information on available methods and class, have a look at the Javadoc.
• The information relative to its "placefulness": the path, principal and repository.
To contribute a configuration to the service you only have to gives the information specific to the configuration.
The "placeful" part is taken care of by the service. You also need to give a way to merge the PC. We will create
a simple config as an exemple. A "Simple" PC that has only one field: value.
• Create an interface specific for your configuration.
interface SimpleConfig {
setValue();
getValue();
}
• Create the empty interface that will be manipulated by the user. It needs to extends
PlacefulConfiguration, Serializable and your specific interface.
• Create the implementation of the specific configuration. It needs to implement your specific interface
and PlacefulConfigurationConfig. The PlacefulConfigurationConfig interface adds the
getAssociatedInterface() methods. It returns the "user" interface:
• Create the class for the merge algorithm. It has to implement PlacefulConfigurationAlgorithm. Its
only method takes a PC and a storage and returns a merged PC. Our Simple configuration will do no
merge at all:
• Finally the "user" interface has to know the implementing and merge class. You add the
@configurationClass annotation on the "user" interface:
@ConfigurationClass(value=SimpleConfigImpl.class, mergeAlgorithm=SimpleMergeAlgorithm.class)
public interface Simple extends PlacefulConfiguration, SimpleConfig, Serializable {};
• If you want your PC to be usable by a Directory storage you also need to provide a schema
Section 21.4.2, “Directory storage”
In most case you want your merge algorithm to merge all entries from the root to the node of the configuration.
For this, you can extend the class AbstractBaseMergeAlgorithm. You then only need to add a method to merge
2 entries.
If your PC is a simple JavaBean, then you can extend BeanMergeAlgorithm . The only things you need to pass
is the class of your bean. For each property, the merge algorithm will return the nearest value from the node
that is not null.
It is assumed that an indexed field is a property of the PC and so available via getters. By default, all the
"placeful" values (principal, path, repository) are indexed and don't need to be added to the fields list.
To use directory storage you need to define a directory and the associated schema. The schema only needs to
include the base.xsd schemaLocation and the placefulConfiguration.xsd schemaLocation. You can also
add any String element that you want to be indexed. Note that the "placeful" fields general for all
configurations are included by default and don't need to be added.
Don't forget to declare the schema and directory to the extension point. It is assumed that each field to be
indexed is a property of the PC and can be accessed by getters.
<component
name="org.nuxeo.ecm.platform.placeful.configuration.defaultContrib">
<extension
target=
"org.nuxeo.ecm.platform.placeful.configuration.service.PlacefulConfigurationService"
point="storage"> ❶❷
<storageBackend name="RAM"
class=
"org.nuxeo.ecm.platform.placeful.configuration.storage.InMemoryPlacefulConfigurationStorage">
<properties>
<property name="fields">docId,theme,mode</property> ❸
</properties>
</storageBackend>
</extension>
<extension
target=
"org.nuxeo.ecm.platform.placeful.configuration.service.PlacefulConfigurationService"
point="configuration"> ❷
Now the basic Virtual Navigation configuration offers a navigation through coverage and through subjects. For
each document it is possible to determine a country that is relative to the document and one or many subjects
that corresponds well to it.
To activate the Virtual Navigation you have to add the Virtual Navigation addon in the plugin extension folder
of your JBoss server. After restarting the server you can see a new widget in the left hand corner of Nuxeo 5.
You can switch between navigation style easily.
The standard physical navigation is still here and the two Virtual Navigation are selectable on the right. As you
can see the Virtual Navigation keeps a folder based navigation but this tree browsing is built from vocabularies
defined in your Nuxeo 5 instance. Coverage and Subjects are 2 vocabularies that exist in the basic Nuxeo 5
configuration. You may know that tree based vocabularies are not limited in depth and you can have 2 or more
level in your vocabulary, for example Coverage allows to select a continent and a country.
To have good results with Virtual Navigation you have to fill meta-data linked to documents. The Meta-Data
view allows the user to provide his own meta-data. Select a country and/or subjects to make your Virtual
Navigation efficient. See below to view the Meta-Data configuration screen.
When selecting a node in a Virtual Navigation tree it will execute a request to find every documents that
contain the wanted meta-data. See the screen below for an example: if you select the Art/Archiecture couple in
the Subjects navigation tree, every documents that contains Art/Architecture in their Subjects meta-data will be
displayed on screen.
You have to create a vocabulary that will be used for the new Virtual Navigation, keep in mind that you can set
up a multi-level vocabulary, so you can imagine a vocabulary with parent and children. There is no limitation
on this side.
You have to select the element in a schema that will store your vocabulary data. In the basic Nuxeo 5
configuration it is dublincore:coverage and dublincore:subjects that store vocabularies data for Virtual
Navigation. If fact, when you select a country (coverage) in a document's meta-data, the country is stored in the
dublincore:coverage element. It's not mandatory to select a dublincore element, you can select an element
coming from your own schemas.
If you have a "only-one-level" vocabulary you can create a file to store it like this :
If you have a "multi-level" vocabulary you have to split him in sub vocabulary, and keep in mind that you have
to indicate which vocabulary element is the child of which parent vocabulary element, here is an example, the
full example can be found in Nuxeo 5 sources, check topic.csv and subtopic.csv:
When you have created your different files for vocabularies you have to register them in an extension point as
new vocabularies. In an xml file that you won't forget to place in a OSGI-INF directory and won't forget to
register in the MANIFEST.MF file of the package, you have to contribute the following extension point.
For a full example you can check the nxdirectories-contrib.xml file of the webapp-core package. Don't forget
to use the xvocabulary schema if your vocabulary is a child of another, if the vocabulary is the first parent or is
alone just use the vocabulary schema. Don't forget to indicate the parent if your vocabulary has more than one
level with the following tag <parentDirectory></parentDirectory>.
<extension target="org.nuxeo.ecm.directory.sql.SQLDirectoryFactory"
point="directories">
//here your new vocabularies contribution
</extension>
The query based search service of Nuxeo 5 requires that you create a document type that will be a base for a
document model to register data handled by the query. To understand it more here is an example:
You are browsing your documents by coverage. You are selecting the path Europe/France in the tree. The data
Europe/France is the base of the query and need to be registered in a document model created from a document
type. In this case the document type can be very simple, cause the query must register only one data at a time.
Creating a document type with only one schema that contains one field will be enough.
This document type will be referenced as query document type in this walkthrough. Create it and register it as
a normal document type. See Nuxeo Book part 6 to get more informations about document type creation.
Now you must contribute to another extension point to create a new navigation tree based on vocabularies you
contributed just before. See below for an example of the file you have to create. For a full example you can see
the directorytreemanager-contrib.xml file in the virtualNavigation package.
The new tree contribution needs many informations, a queryModel to indicate the query you will use to get
your documents, a schema and a field coming from the query document type you just set up in part 3, an
outcome that indicates the page where documents will be displayed after the request and a list of vocabularies
you can indicate with the tag <directory></directory>.
<require>
org.nuxeo.ecm.webapp.directory.DirectoryTreeService
</require>
<extension
target="org.nuxeo.ecm.webapp.directory.DirectoryTreeService"
point="trees">
//here your new navigation tree contribution
</extension>
Now you need to contribute to 2 extension points that will set up the query and the results provider. Here are
the 2 extension point you will need to contribute. For a full example see the file querymodel-contrib.xml and
resultsprovider-contrib.xml in the virtualNavigation package.
The query model contribution needs many information. In the docType parameter you have to put the name of
the documentType you created in part 3. In the <predicate></predicate> tag you must set up the name of the
element (field) that is the field storing the data used for virtual tree construction.
EXAMPLE :
You want to browse documents by coverage, each of your document have a coverage registered in dc:coverage
field. You have to use the dc:coverage parameter.
If there is no prefix set the name like this schema:field, if there is a prefix set it up like this prefix:field. In the
operator param put the value STARTSWITH. In the <field/> tag put the schema and the name of the field
coming from the query document type you set up in part 3.
<extension
target="org.nuxeo.ecm.core.search.api.client.querymodel.QueryModelService"
point="model">
//Here your new query model
</extension>
<extension
target="org.nuxeo.ecm.webapp.pagination.ResultsProviderService"
point="model">
//Here your new result provider
</extension>
Now you need to register a new indexing configuration in the search service. You will index the field that
register the data you want to apply the search on, it is the same data as saw at the end of part 5 . Here is the
extension point you have to contribute. For a full example see the nxsearch-contrib.xml file in the
search-core package.
You will have to create a new <resource></resource> tag if it doesn't exist for your schema. For the name
parameter you have to put the name of your schema, type param must be "schema" and indexAllFields param
must be "true". In a <field/> tag you have to indicate the indexing strategy, the type param must be "Path", if
the field that register your vocabulary is a complexType (list of String for example) you can add the parameter
multiple and set it at "true".
<extension
target="org.nuxeo.ecm.core.search.service.SearchServiceImpl"
point="resource">
//Here your new search configuration
</extension>
You must not forget to add some navigation cases in a deployment-frgament.xml file. Those navigation cases
must correspond at each outcome you set up in part 3. Each page named in navigation cases shall display
search result so you have to customize those pages with the good queryModel call. For a full example see
coverage_virtual_navigation.xml .
All theme contributions and tree navigation widget already exist in webapp-core and virtualNavigation
package, you can take a look for information purpose.
Indeed you have to add vitualNavigation to Nuxeo 5 plugin if you want a fully fonctional Virtual Navigation
immediatly.
23.1. Introduction
There are cases when some informations could be retrieved from attached files and keep this info as regular
Document properties. This info is refered to as metadata as it is a descriptive info refering the document (file)
from which it is extracted. For example given a MS Word document (file) we could retrieve info (metadata)
like author, title, creation date etc, simply by reading document headers with an appropriate library (that knows
how to parse and keep a MS Word document internal structure).
- nuxeo-platform-metadataext-api
- nuxeo-platform-metadataext-core
- nuxeo-platform-metadataext-facade
- nuxeo-platform-metadataext-plugins
The core part defines the metadata extraction component with a MetaDataExtractionManager implementation:
org.nuxeo.ecm.platform.metadataext.services.MetaDataExtractionService.
The service is normally invoked by a dedicated CoreListener that passes a DocumentModel for which an
extraction could be defined. Also the metadata could be extracted by direct invocation from a client code
through existing EJB3 facade.
The plugin part provides a plugin (more to come) which is specific to the MS-Word document type. The plugin
is a Transformation Plugin (as defined by the Transformation service) and gets invoked as part of a defined
transformation chain. The apropriate transformation will be called by the metadata extraction service if there is
a contribution for the given Document type, etc.
<extension
target="org.nuxeo.ecm.platform.metadataext.services.MetaDataExtraction"
point="extractions">
<meta-data-extraction inputField="file:content"
transformationName="MSWordMDExt">
<outputParams>
<param propertyName="dc:title">title</param>
<param propertyName="dc:description">comments</param>
<param propertyName="dc:created">creationDate</param>
<param propertyName="dc:modified">creationDate</param>
<param propertyName="dc:contributors">authors</param>
</outputParams>
<coreEvent>documentCreated</coreEvent>
<coreEvent>documentModified</coreEvent>
<docType>File</docType>
</meta-data-extraction>
</extension>
- specification of a source (blob) from where metadata will be extracted. This is defined by the value
inputField which should be a blob document property.
- the mapping of output parameters. This defines a corespondence between the map entries returned as the
result of transformation (extraction) and the Document properties names to which the results will be written
back.
- the list of core events for which the extraction will be performed.
- the list of document types for which the extraction can be applied.
A map with input parameters will be passed to the extraction plugin having the key the value of tag param (in
this case 'title').
Overriding the transform method is a must and in this case we will have only one TransformDocument as
source.
The blob from which metadata could be extracted can be retrieved from TransformDocument like: Blob
srcBlob = sources[0].getBlob().
After the useful information is extracted the properties should be set to a TransformDocument that will be
returned:
take place.
We need to define first the transformation plugin and the transformation contributions. After that the defined
transformation can be refered in the metadata extraction specific contribution.
<extension
target="org.nuxeo.ecm.platform.transform.service.TransformService"
point="plugins">
<documentation>
Set of default transformation plugins for metadata extraction.
</documentation>
<plugin name="MSWordMDExtPlugin"
class="org.nuxeo.ecm.platform.metadataext.plugins.MSWordMDExtractorPlugin"
destinationMimeType="application/msword">
<sourceMimeType>application/msword</sourceMimeType>
</plugin>
</extension>
<extension
target="org.nuxeo.ecm.platform.transform.service.TransformService"
point="transformers">
<documentation>
Set of default transformation chains for metadata extraction.
</documentation>
<transformer name="MSWordMDExt"
class="org.nuxeo.ecm.platform.transform.transformer.TransformerImpl">
<plugins>
<plugin name="MSWordMDExtPlugin" />
</plugins>
</transformer>
</extension>
<extension
target="org.nuxeo.ecm.platform.metadataext.services.MetaDataExtraction"
point="extractions">
<meta-data-extraction inputField="file:content"
transformationName="MSWordMDExt">
...
24.3. Configuration
How to configure unicity extension point. TODO: needs to be made clearer.
<enabled>
Default value is false. Use true if you want to enable this service.
</enabled>
<algo>
Default encoding algorithm is sha-256. You can choose every algorithm supported by java.security.MessageDigest.
</algo>
<field>
A field is an xpath expression giving a particular field of your schema.
It's the reference of a file on the server.
The type's field must be nxs:content.
You can use as many field as you want.
</field>
24.4. Snippets
Some code to get you started.
Example 24.1. Sample unicity extension point contribution to the FileManager service.
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.filemanager.service.FileManagerService.Plugins">
<extension
target="org.nuxeo.ecm.platform.filemanager.service.FileManagerService"
point="unicity">
<unicitySettings>
<enabled>true</enabled>
<algo>sha-256</algo>
<field>content</field>
</unicitySettings>
</extension>
</component>
return;
}
DocumentMessage doc = (DocumentMessage) obj;
String eventId = doc.getEventId();
if ("duplicatedFile".equals(eventId)){
Object[] documentLocations = (Object[]) doc.getEventInfo().get("duplicatedDocLocation");
for (Object documentLocation: documentLocations) {
log.debug(((DocumentLocation)documentLocation).getDocRef());
}
}
}
}
25.1. Presentation
Nuxeo mail service enables users to send and receive mail. It also makes it possible to apply a serie of actions
on messages located in folders.
The service is a wrapper around JavaMail . Knowledge of this API is necessary for advance use of the service.
25.2.1. Configuration
Use of the mail service is done via session. You need to configure a session factory that will be able to find the
correct server parameter to connect to. Then, the session can use the mail server. The simplest scenario is to
have mails sent and received using the credential of one user (see property fetcher for more complex use).
To pass the server parameter in your extension point, use a simple properties fetcher. For a server using TLS
connection with SMTP and IMAP, a configuration could look like:
<extension target="org.nuxeo.ecm.platform.MailService"
point="sessionFactory">
<sessionFactory name="mysession" fetcherName="simple">
<properties>
<property name="mail.store.protocol">imap</property>
<property name="mail.transport.protocol">smtp</property>
<property name="mail.smtp.port">587</property>
<property name="mail.host">mail.mycompany.com</property>
<property name="mail.smtp.host">mail.mycompany.com</property>
<property name="mail.imap.starttls.enable">true</property>
<property name="mail.imap.ssl.protocols">TLS</property>
<property name="mail.smtp.ssl.protocols">TLS</property>
<property name="mail.user">mailuser@mycompany.com</property>
<property name="mail.from">mailuserd@mycompany.com</property>
<property name="mail.imap.port">143</property>
<property name="password">myuserpassword</property>
<property name="user">maiuser@mycompany.com</property>
</properties>
</sessionFactory>
</extension>
With such a configuration you can send mails using the mail service, passing the text of the mail as a String.
MailService mailService = Framework.getService(MailService.class);
mailService.sendMail("My interesting mail.", "The subject of the mail",
"mysession", new Address {internetAddress});
To process mails in mail folders, you need to create a pipe and add actions to it. Each action in the pipe will be
applied to each mail in the folder. A typical pipe will include action to set the delete flag so the mail is deleted
when the pipe exits, transforms the mail, stores it, and maybe answers it. For all those actions to communicate
with each other, you can pass objects into the context. Context is passed from action to action.
The package org.nuxeo.ecm.platform.mail.action contains a set of Action classes you can use or extend.
Note that some actions expect objects to be in the map.
If the returned value of an action is false, then the processing of this message is stopped and the next message
will be processed.
The MailBoxAction enables using those service in a simple way, a typical use would be:
MailBoxActions mba = mailService.getMailBoxActions("mySimpleFactory", "INBOX");
mba.addAction(new ConvertToDocumentModelAction());
mba.addAction(new StoreDocumentModelAction());
mba.addAction(new SendMailReplyAction());
mba.execute();
The mail service comes with a simple fetcher. The simple fetcher enables passing the configuration for the mail
server using key/value pair. If you need more complex configuration, such as having specific properties for
each user fetched from a directory, you can contribute a new fetcher using the propertiesFetcher extension
point.
<extension target="org.nuxeo.ecm.platform.MailService"
point="propertiesFetcher">
<propertiesFetcher name="simple"
class="org.nuxeo.ecm.platform.mail.fetcher.SimplePropertiesFetcher"/>
</extension>
To test your mail service, jes mail server is a simple, light weight mail server you can use locally. You can
either start it manually or use the nuxeo-platform-mail-test bundle to do it.
26.1. Introduction
Nuxeo Platform Imaging provides picture management to Nuxeo Web Platform and RCP. It offers minor
picture transformation like rotation or resizing.
There are several Imaging addons for the Nuxeo platforms. They are listed in the following sections.
27.1. Overview
The preview addon is composed of several layers:
• UI part
• Transformer part
The Preview addon contributes transformers that are dedicated to generating HTML out of most file
formats.
The Preview addon includes services that allow you to define PreviewAdapters for each type of
Document. These adapters are responsible for defining how the preview will be generated:
Because a Nuxeo document contains a lot of differents fields, the adapter must determine what are the
default fields used to generate the preview.
Basically preview can be generated using some transformers or using some fields that already contains
pre-generated HTML preview
The services provided by the Preview addon let you configure the PreviewAdapter depending on your
document types and manage some cache for transformer based previews.
This transformer relies on the command line tool pdftohtml This tool can be found as package for most
linux distrib (for instance, included in poppler-utils in Ubuntu). This tool can also be installed on MS
Windows platform, see http://sourceforge.net/projects/pdftohtml/
Once you have installed pdftohtml on your server, you must configure Nuxeo to let him know where is
the command line tool. For this, you can use a extension point to define what is the temporary directory
to use and where is pdftohtml command.
Default settings should be ok for most Linuxes, but you will have to do the configuration if you use a MS
Windows box.
<?xml version="1.0"?>
<component name="myproject.pdftohtml.config.contrib">
<extension target="org.nuxeo.ecm.platform.preview.transformers.CLTransformerPluginParameterManagerComponent"
point="cltpParameters">
<CLTranformerPluginParameters name="Pdf2HtmlParams" targetTransformerPlugin="Pdf2Html">
<parameters>
<!-- default Linux parameters
<parameter name="commandString">/usr/bin/pdftohtml</parameter>
<parameter name="tmpDir">/tmp/</parameter> -->
<!-- MS Windows parameters -->
<parameter name="commandString">C:\\Program files\\pdftohtml\\pdftohtml.exe</parameter>
<parameter name="tmpDir">C:\\Temp\\</parameter>
</parameters>
</CLTranformerPluginParameters>
</extension>
</component>
• Any2Html
This Transformer is in fact a chain using Any2pdf and PDF2html. This means you need to have PDF2html
working but also OpenOffice.org in listen mode (this is documented earlier in this book).
doc.getAdapter(HtmlPreviewAdapter.class)
Defining custom preview adapter is done via a dedicated extension point: you register a Factory for a given
DocumentType. This factory will be used to create the implementation of the HtmlPreviewAdapter from the
DocumentModel. Here is example of such a contribution:
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.preview.adapter.contrib">
<extension target="org.nuxeo.ecm.platform.preview.adapter.PreviewAdapterManagerComponent"
point="AdapterFactory">
<previewAdapter name="notePreviewAdapter" enabled="true">
<typeName>Note</typeName>
<class>org.nuxeo.ecm.platform.preview.adapter.factories.NotePreviewAdapter</class>
</previewAdapter>
</extension>
</component>
This contribution defines the Adapter factory for the Note Document type. Contributed factories must
implement the PreviewAdapterFactory interface (that contains only one method!).
The implementation returned by the factory can be based on one of the 2 base classes provided inside the
addon.
• TransformerBasedHtmlPreviewAdapter
This base class provides all the build in features to create a preview adapter that uses the Transformation
Services to generate the html preview.
Default usage in Nuxeo is the generic adapter for the all the documents that contains the file schema.
• PreprocessedHtmlPreviewAdapter
This base class provides all the build in features to create a preview adapter that uses pre-processed
HTML preview that is stored inside the document.
Default usage in Nuxeo is the adapter for the Note Document type.
For most usages, you just be able to contribute a factory that create a new
PreprocessedHtmlPreviewAdapter with the good arguments.
It also provides you with a base class AbstractCommandLineBasedTransformer that can be used to easily make
another CommandLine based Transformer.
We also provide a CLTransformerPluginParameterManager service that is used by the base class to extact the
configurable parameters.
This restlet provides a RESTful access to the preview service. This means that you can retrieve the preview of a
document by just using a simple GET URL.
where:
• document_uuid
is the the document uuid that is already present in all Nuxeo urls
• previewfield
is the xpath of the field that should be used as main file for the preview
Depending on the underlying adapter implementation, this field may or may not be relevant. Use 'default'
to let the adapter implementation choose the right field for you.
Please note that the last / is important. As HTML preview can contain nested images, the base URL must end
with a /. The Restlet system handles a cache to avoid refetching the preview data at each call.
Tiling picture is usefull when you want to display in a web browser a very big image : accoring to the display
settings and to the zoom level, the picture tiling service will generate you the needed picture tiles.
28.1. Overview
The Tiling Service addOn is composed of several layers :
• REST API part
Provides a rest API to get the picture tiles from the server.
Depending of the URL, this API could be used to get the actual tiles (ie: picture), or to get information
about the picture and the tile grid (XML or JSON).
• Adapter part
Default adapter simply extract the blob from the DocumentModel using either the default file schema, or
using a provided xPath.
Other adapters can be provided to implement specific logic for source picture retrieveal (like
multi-view-pictures) or even extract pre-computed tiles from the DB.
• Tiling Service
Because picture tiling can be long, this service maintains a FileSystem based cache of the input
pictures, the generated tiles, and also some temporary resources used by the tiling process.
The service also manage a configurable garbage collector to clean up this cache.
• manage PictureTilers
• because it is faster
This ImageResource is usually provided via a DocumentModel adapter that will encapsulate the logic to
find the input picture and will create a DocumentImageResource.
This parameter represents the maximum number of tiles on the X or Y axis, that are needed to display the
complete picture.
Basically it represent the grid size you will need to display the complete image using this tiling.
As you may have noticed the zoom factor is not an input parameter. In fact it is an output parameter : the
service will compute the zoom factor that correspond to the tiling definition.
This choice may seem strange, but in most of the case, this is easier to use : you don't always know the size of
the underlying image, but you know the space you have to display the tiled image (total width/height and
number of tiles) and you know that you want to start with an image that is fully displayed.
From the tiling grid definition, the service will compute the needed input picture size.
When the needed picture size is really smaller that the real input image, it may be interesting to generate
a scalled down image.
This will slow down the process for the computation of the first tile, but will speed up the computation of
all other tiles.
This is particularly true for format like JPEG that support to easily extract from them a smaller image.
This speed up the reading part (less bytes to read from DB/Hard Drive) and speed up the final scalling.
When scaling down the input picture is possible, the resulting image will be stored into the cache.
Depending on the tiler implementation, it may generate one tile at a tile (default for ImageMagick based
tiler) or 9 tiles at a time (default for gimp tiler) or all tiles (possible for gimp tiler).
This behavior is transparent for the caller since, all generated tiles are cached.
28.3.1. Requirements
ImageMagick 6.3.7 or later is needed : the Tiling Service uses the ImageMagick stream command that is not
available in 6.2 (associated packages are libmagick and imagemagick).
If you want to use the Gimp tiler, you will need Gimp 2.x with python extensions.
28.3.3. Configuration
Configuration can be done using a extension point. Just create a file called pictures-tiles-config.xml in
nuxeo.ear/config.
http://{server}/nuxeo/restAPI/getTiles/{repoId}/{docUUID}/{tWidth}/{tHeight}/{maxTiles}?format={format}&x={x}&y=
where
• {server}
• {repoId}
is the id of the core repository that is used (use default if you don't know what it is).
• {docUUI}
The uuid of the document that contains the image. If you use the default adapter, this document must use
the fileschema and contain an image.
• {tWidth}
• {tHeight}
• {maxTiles}
• {format}
• {x}
• {y}
Will send you the tiling information in XML for the picture contained in the doc
950b0d27-2ca4-43e4-bb12-598ad6d64e86 with a tile size of 200x150x2.
http://server/nuxeo/restAPI/getTiles/default/950b0d27-2ca4-43e4-bb12-598ad6d64e86/200/150/2?format=JSON
Will send you the tiling information in JSON for the picture contained in the doc
950b0d27-2ca4-43e4-bb12-598ad6d64e86 with a tile size of 200x150x2.
http://server/nuxeo/restAPI/getTiles/default/950b0d27-2ca4-43e4-bb12-598ad6d64e86/200/150/2?x=1&y=1
Will send you the tile (1,1) for the picture contained in the doc 950b0d27-2ca4-43e4-bb12-598ad6d64e86 with
a tile size of 200x150x2.
You can access this test client via the rest API, just add the test=true to the URL.
http://server/nuxeo/restAPI/getTiles/default/950b0d27-2ca4-43e4-bb12-598ad6d64e86/200/150/2?test=true
29.1. Overview
There are several use cases that require having the same document available from several places. Typical use
cases include publication (same document visible in multiple sections), personnal workspaces (user want to
have some document into his workspace without copying them) ... Nuxeo Core provides a build-in feature
called Proxies that can be used in most cases. The current limitations of proxy system in Nuxeo Core 1.4.x
include :
• A proxy can only point to a checked-in DocumentModel (ie : a version)
• A proxy is always totally equivalent to the target DocumentModel (ie : same schemas and field values)
DocumentLink provides a proxy system implementation on top of the Core using the DocumentModel adapter
system. DocumentLink extends the Core proxy system to provide some additionnal features :
• A DocumentLink can point to a checked-in or checked-out DocumentModel
The reference of the target DocumentModel is stored in a dedicated schemas (named documentLink).
• target DocumentRepository
This means that for this schema the DocumentLink will always return the values stored in the target
DocumentModel even if the DocumentLink it self has these schemas.
The DocumentLink package also provides a indexing wrapper that will be used during indexing. This allows
In a way, it means that the real DocumentModels are "somewhere is space" and the user navigate via a
hierarchy of DocumentLinks.
For that purpous, the documentLink package provide a DocRepository service that will manage the storage of
the real DocumentModel.
"Real DocumentModel" won't be stored "in space" but in a hidden folder structure that will dispatch the
DocumentModels in a hidden tree.
On a stock Nuxeo EP, documentLink won't give you much visible features : it provides some new API, but
since the default Web Application does not use it, it won't be really usefull. The DocumentLink Addon must be
considered as a new API.
30.1. Overview
Nuxeo Runtime is the foundation of the Nuxeo infrastructure. It handles deployment and extensibility of
components to target platforms. This component allows the whole Nuxeo infrastructure to be easily ported
between Java platforms (Java EE, OSGi, etc.) and features an easy plug-in mechanism that any component can
use to declare extension points that can be used by other components to extend the former one.
Nuxeo Runtime uses the OSGi component model and a set of adapters to deploy POJO components to Java
host platforms, such as Eclipse/Equinox, or a Java EE 5 application server such as JBoss or WebLogic. When
deployed, Nuxeo Runtime components become actual host platform components. For example on JBoss the
component is seen as a MBean, while when deployed on Geronimo it is seen as a GBean and on Eclipse it is
seen as a native Eclipse plug-in. In short, Nuxeo Runtime offers a new and seamless way to make your Java EE
applications and components extensible (as Eclipse developers are already used to).
Nuxeo Runtime is not specific to the Nuxeo platform, it is a generic deployment and extension system that can
be used in any Java or Java EE application.
Forget specific build of your applications for a dedicated project or customer and enjoy “Code once, deploy
anywhere” for real!
To fulfill these needs, Nuxeo Runtime was developed as the foundation layer for all Nuxeo components. Nuxeo
Runtime is not a standalone framework. It is, basically, a component model running on top of an existing
platform and providing a common, platform-independent, model to power the applicative components of an
application. It is our component architecture allowing flexible and true componentization of applications.
Besides its component model, Nuxeo Runtime also defines a common model for packaging. The adopted model
is the OSGi bundle model. Actually, OSGi bundles are regular JARs containing an OSGi manifest file.
OSGi technology is more and more popular and is currently used by Eclipse, Geronimo and Jonas as their
runtime framework.
This way, applications based on Nuxeo Runtime can run on different platforms without modifications by using
an single component and packaging model - without having to care about platform specificity.
3. Adapters to support host platforms (JBoss and Eclipse support is built-in by default)
OSGi defines a modular and complete Java-based service framework. The deployment units used by this
framework are called bundles, so we will refer them as OSGi bundles.
OSGi bundles are normal Java libraries (JAR files) containing a special manifest file (META-INF/MANIFEST.MF)
describing all aspects related to the bundle, like: the bundle name, description, bundle dependencies, exported
packages, the bundle classpath, the bundle activator and many other OSGi-defined features.
The bundle activator is a Java object that is called whenever the bundle is started and stopped by the
framework. This is the only way available to the application to access the framework functionalities.
Besides bundles and bundle management, OSGi provides a service registry and an API to register the services
provided by a bundle and to lookup these services. Also, OSGi defines a Declarative Services specification that
significantly simplifies the service-oriented programming model. Through this model, services can be defined
in XML files inside the bundle and automatically deployed by the framework.
One of the main goals of Nuxeo Runtime is to natively support OSGi frameworks and to use the OSGi bundle
model for packaging and deployment. A second goal is to align the Nuxeo Runtime component model with the
OSGi Declarative Specifications.
On other platforms like JBoss, an adapter is required. Nuxeo Runtime eases the creation of such adapters by
providing an abstract OSGi adapter that can be customized for any platform.
Note: it doesn't mean you can transform any platform into a fully OSGi-compliant platform using Nuxeo
Runtime adapters. This is mainly due to the fact that the adapter is using in the background the host platform's
class-loading and deployment model that is incompatible with OSGi specifications.
Adapters only mimic an OSGi environment, using native host platform features, for applications running on top
of Nuxeo Runtime.
Many OSGi features are not yet provided by the adapter – but we hope to add more and more features. If you
are interested in helping on this, do not hesitate :-).
Currently, one of the most important features that is missing is OSGi service support, but we are working on
this and hope to provide it soon.
When running on OSGi-enabled platforms, no adapter is used and thus all OSGi features are available as given
by the host platform. Components are running natively on the OSGi platform without any alteration.
2. Test OSGi adapter – used for JUnit testing and can be used on any simple Java application that is not
using a complex class loading or deployment mechanism.
2. The manifest is loaded, the class path processed and the bundle activator instantiated
3. BundleActivator support
5. Fake Bundle and BundleContext implementations that adapts OSGi operations to native operations of
the host platform
6. Thus, Bundle Activators can use the common operations defined by the OSGi API
3. The OSGi class-loading specifications (the class-loading mechanism of the host platform is used)
4. Some methods of interfaces Bundle and BundleContext (unimplemented Methods will thrown an
UnsupportedOperationException exception)
2. Implementing the OSGi declarative services based on the runtime component model
A full support of OSGi declarative service specifications is planned in the mid-term future. Moreover, Nuxeo
components can describe any kind of components, not only services.
Components as defined by the Nuxeo model are logical units that may depend on and/or extend one another.
The Nuxeo Runtime is responsible for providing a common API to register, locate or extend components.
Components are commonly registered using XML descriptor files.
Components are commonly declared as part of an OSGi bundle through XML descriptors. To declare a
component, you need to create an XML description file, put it somewhere in the bundle and specify the
“Nuxeo-Component” header in the bundle manifest to load components at bundle activation.
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: HelloWorldExtension Plug-in
Bundle-SymbolicName: org.nuxeo.runtime.demo.HelloWorldExtension
Bundle-Version: 1.0.0
Bundle-Vendor: Nuxeo
Bundle-Localization: plugin
Require-Bundle: org.nuxeo.runtime.demo.HelloWorld
Nuxeo-Component: OSGI-INF/helloworld-extension.xml
2. Dependency between components: components are installed only when all their prerequisites are met. If
prerequisites are not met the components will be put in a pending state until their dependencies are
completely resolved. In the same manner when uninstalling a component all the components depending
on it will be moved to the pending state.
3. Extensibility through extension points: each component can let other components extend itself by
defining a set of extension points. Other components (or the component itself) may then plug extensions
into one of the declared extension points. This flexible extension mechanism draw inspiration from the
Eclipse extension points.
4. Life Cycle Events: component life cycle events are fired by the runtime to anyone interested in. See
Adaptable Components for a common use case.
5. OSGi integration: the component model is about to be fully integrated with OSGi and will be soon
compliant with the OSGi declarative service model.
6. Platform Independence: the component model can be used on any platform. It provides a single API to
register and look-up components – the Nuxeo Runtime native API may be used (and in the future, the
OSGi service API will be available too).
The JBoss runtime implementation is already doing this to adapt runtime components into JBoss MBean
services.
This way, components may be seamlessly integrated into the host platform, thus leveraging the host platform
functionalities (for example, MBean service management on JBoss)
The only limitation is that component objects must have a public constructor without arguments (the default
constructor) so that they can be instantiated via newInstance method on Class.
Anyway, if you want to benefit from the extension points mechanism or to respond to component life cycle
events like activation or deactivation, then you should either implement the Component interface, or define
some methods with a given signature in your object so that they will be called by using Java reflection. See
below for more details on this.
In conclusion, the component model is not limiting your objects in any way, it only gives you the capability to
register your components, extend and look them up in the same way on any platform supported by Nuxeo
Runtime.
2. ResolvedAll: dependencies of this component are satisfied. The component can be safely activated.
Other unresolved components waiting for a resolved component are notified and if they have no more
dependencies, they will be resolved too.
When an activated component is deactivated it is put back in the resolved state. If it is unregistered it
will be put in the resolved state then an unresolved event is fired and the component regresses to the
registered sate and then it is removed from the registry.
When a component is respectively activated or deactivated the runtime will invoke the activate,
respectively the deactivate method of the component, if any. Implementing life cycle methods or not is
the programmer's choice. These methods can be used to initialize and destroy the component in the
given context. Components are not forced to implement any one of these methods.
The activation of a component signifies the component is available to be used by other components so
that the component should be correctly initialized when it enters this state. Here is the complete life
cycle of a component:
How does it work? Imagine you have a component A that manages an action menu for the application. It wants
to let other components contribute actions in an easy and flexible way – for example by using XML files to
describe these actions.
To be able to do this, component A should declare an extension point, let's say “actions”. This way other
components willing to contribute some actions to the action menu managed by the component A can contribute
these actions to the extension point “actions” exposed by the component A.
Obviously one component may declare any number of extension points and any number of extensions
contributed to other components.
Components may declare extension points and extension contributions using a simple XML syntax like the
following:
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.core.schema.TypeService">
<implementation class="org.nuxeo.ecm.core.schema.TypeService"/>
<extension-point name="doctype">
<object class="org.nuxeo.ecm.core.schema.DocumentTypeDescriptor"/>
</extension-point>
<extension-point name="schema">
object class="org.nuxeo.ecm.core.schema.SchemaBindingDescriptor"/>
</extension-point>
<extension target="org.nuxeo.ecm.core.schema.TypeService" point="doctype">
<doctype name="File" extends="Document">
<schema name="common"/>
<schema name="file"/>
</doctype>
</extension
</component>
You can see here a component declaring two extension points and contributing an extension to its own doctype
extension point.
The content of an extension element is specific to the target extension point. The extension element content is
known only by the extension point. How are handled extensions not conforming to the extension point schema
is unpredictable – generally they will be ignored and some errors will be logged. There is, for now, no
mechanism of validating XML extensions like in Eclipse.
But Nuxeo Runtime provides an easy way to map XML extensions to real Java objects through an XML
mapping mechanism called XMap. You can see that each extension point in the example above is specifying an
object tag. This means the content of the XML extension should be mapped through XMap to an object
instance of this type. When no object tag is specified, extensions are returned as DOM elements and thus the
component should perform itself the DOM parsing of extension contributions.
For details on the XML mapping, see the XMap documentation and/or the JavaDoc.
Here is the list of some use cases of the extension mechanism identified in the context of the Nuxeo ECM
Platform:
1. to define actions and menus
4. to define content objects (associate a Content Schema with a class that will provide required methods for
the content object)
6. to define PageFlows for Seam that, optionally, can extend existing ones
8. to define content transformations (doc -> pdf, doc -> html, odf -> pdf, odf -> html, etc.)
9. to define rules for the rule engine (that can be bound to some objects to run a rule only in a specific
folder)
10.scriptable extensions that define scripts binded to interpreters like JavaScript, Groovy, Jython, JRuby,
etc
Any package (directory, .sar, .jar, .ear, .war or any other JBoss-supported archive) will be treated as an OSGi
bundle if it contains a valid OSGi manifest. In order for these bundles to be deployed, you need to have
NXRuntime.sar already deployed in JBoss.
Besides the OSGi adapter and the auto-registration of components through bundle manifest, the JBoss adapter
adds the capability to deploy runtime components as XML files located outside OSGi bundles through the
JBoss deployment mechanism. This feature can be useful to register components that provide extensions to
other components that can be described by plain XML without any code dependency.
b. provides information about deployed bundles and components through the JBoss JMX Console
30.5.1.1. Installation
Deploy the NXRuntime.sar in JBoss, then deploy your OSGi bundles as common JBoss packages.
When running on OSGi platforms, the main role of the runtime is to register components declared inside OSGi
bundles (as seen previously through their manifest).
Because Eclipse is not starting automatically OSGi bundles (it starts them only on demand or on class loading),
you need to update Eclipse's config.ini and configure it to start Nuxeo Runtime (i.e. org.nuxeo.runtime) when
Eclipse starts:
osgi.bundles=org.eclipse.equinox.common@2:start, org.eclipse.update.configurator@3:start, org.eclipse.core.runti
30.5.2.1. Installation
Update the config.ini file as described above, then copy NXRuntime.jar inside the Eclipse plugin directory.
A runtime context is the context where a component is registered. Contexts should be always associated to the
bundle containing the component classes. Through the context, a component can access the runtime service and
can load classes and retrieve resources from its bundle and other visible bundles.
loader. It should only be used in simple Java applications like JUnit tests that are not supporting OSGi
bundles.
This is the right context to use when using Nuxeo Runtime and it is working on any platform as long as
it is an OSGi platform or you have an OSGi adapter for it.
Once you have a runtime context object, you can start registering components.
Note
The current bundle object is usually retrieved from a BundleActivator in the
start(BundleContext context) method. You can also lookup other bundles by their symbolic
names given a Bundle object.
The context has several method of deploying (e.g. installing) components. For example, the method used
previously is identical to:
// load the component XML file given its location relative to the bundle root
URL url = context.getLocalResource(“OSGI-INF/MyComponent.xml”);
if (url != null) {
context.deploy(url);
}
Another, and the easiest way to deploy components is to let the bundle deploy them when started.
This can be done by specifying the local paths of the XML description files inside the bundle manifest by using
the Nuxeo-Component header as in the following example:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: HelloWorldExtension Plug-in
Bundle-SymbolicName: org.nuxeo.runtime.demo.HelloWorldExtension
Bundle-Version: 1.0.0
Bundle-Vendor: Nuxeo
Bundle-Localization: plugin
Require-Bundle: org.nuxeo.runtime.demo.HelloWorld
Nuxeo-Component: OSGI-INF/MyComponent.xml, OSGI-INF/MySecondComponent.xml
This way, as soon as the bundle is started, the component will be automatically deployed.
XML component are contained as resource files in that bundle and their path should be specified as relative to
the bundle root
This method of creating components is not recommended since it is internal to Nuxeo Runtime and it depends
on the implementation.
Here is an example on how you can use the API to manually register a component. We assume you are running
in an OSGi environment and you have a reference to the bundle object containing the component you want to
register.
// retrieve the current bundle
Bundle bundle = ...
RegistrationInfoImpl ri = new RegistrationInfoImpl();
// create a context associated to the current bundle
ri.context = new OSGiRuntimeContext(bundle);
ri.name = new ComponentName(“my.component”);
// set the class name of the component to register
ri.implementation= “org.nuxeo.runtime.example.MyComponent”;
// register the component
Framework.getRuntime().getComponentManager().register(ri);
When a component is deployed, Nuxeo Runtime will check its dependencies and if all of them are resolved, the
component is resolved and activated. (In the future, lazy activation or activation on demand will be supported
too). If component dependencies are not satisfied, the component will be put in a pending queue until all of its
dependencies are resolved.
When activating a component, the runtime will check if the component defines the activate life cycle method
and if true, it will call it to get a chance to the component to initialize itself.
The same thing is done when deactivating the component - the runtime will check if the component defines the
deactivate life cycle method and if true, it will call it to give a chance for the component to dispose itself
properly.
In this case, a cast to Component interface is performed and the life cycle methods are called.
public interface Component extends Extensible {
public void activate(RuntimeContext context) throws Exception;
public void deactivate(RuntimeContext context) throws Exception;
}
2. By simply declaring a public or protected methods on the component object using the right signature.
In this case the Java reflection mechanism is used to call the methods.
public class MyComponent {
...
public void activate(RuntimeContext context) throws Exception {
...
}
public void deactivate(RuntimeContext context) throws Exception {
...
}
...
}
After a component is activated, it can be retrieved using the Nuxeo Runtime API.
• Looking up the ComponentInstance object corresponding to this component. This object is a proxy to
the component object:
ComponentInstance ci = Framework.getRuntime().getComponentInstance(
"org.nuxeo.runtime.demo.HelloComponent");
if (ci != null) {
HelloComponent hc = (HelloComponent) ci.getInstance();
}
Now let's take a look at how a component may define an extension point and how other components may use
this extension point to contribute extensions.
Extension points are specified in the XML component descriptor using the extension-point tag. This tag has a
required attribute name and one or more optional object sub-tags.
1. The name attribute.
This should be unique relative to the parent component and is used to identify the extension points inside
a component.
2. The object sub-tag can be used to define what kind of objects are contributed by XML extensions.
These objects will be created from the extension XML fragment by using the XMap engine that maps
XML to Java objects through through Java annotations.
The object tag has a required class attribute that specifies the class name of the objects to contribute.
The object class will be loaded using the context of the bundle that defined the extension point.
2. asyncListeners
<?xml version="1.0"?>
<component name="org.nuxeo.runtime.EventService">
<implementation class="org.nuxeo.runtime.services.event.EventService"/>
<extension-point name="listeners">
<object class="org.nuxeo.runtime.services.event.ListenerDescriptor"/>
</extension-point>
<extension-point name="asyncListeners">
<object class="org.nuxeo.runtime.services.event.AsyncListenerDescriptor"/>
</extension-point>
</component>
To declare an extension, the extension tag is used. This tag must contains a target and a point attribute.
1. target
The target attribute specifies the name of the component providing the extension point
2. point
The extension element may contain arbitrary XML. The actual XML content is recognized only by the
extension point to where the extension is contributed. This means you should know the correct format for the
extension XML.
For this reason, it is important for components to document their extension points. If the extension point is
using XMap to map XML to Java objects, then you can use annotations existing on the contribution object class
to know the XML format. These annotations are easy to understand and can be used as well as a documentation
for the XML extension format.
If you are familiar with Eclipse extension points, you may wonder why Nuxeo Runtime is not using an XSD
schema to define the content of an XML extensions. The reason is simple: because inside our ECM project we
need to be able to define any type of XML content - even configuration files from external tools we use like for
example a Jackrabbit repository configuration. Defining and maintaining XSD schemas for this kind of
extensions would be painful.
Anyway, using XMap to map extensions to real Java objects makes it easy to use extensions.
Here is an example on how a component is declaring some contributions to the previously defined extension
points:
<?xml version="1.0"?>
<component name="my.component">
<implementation class=”MyComponent"/>
<extension target="org.nuxeo.runtime.EventService" point="listeners">
<listener class="org.nuxeo.runtime.jboss.RepositoryAdapter">
<topic>repository</topic>
</listener>
<listener class="org.nuxeo.runtime.jboss.ServiceAdapter">
<topic>service</topic>
</listener>
</extension>
</component>
You can see how the component is declaring an extension to the listeners extension point defined by the
component org.nuxeo.runtime.EventService
The result of this declaration is that the EventService will register two listeners, one listening on events from
the “repository” topic, the other on events from the “service” topic.
A component willing to declare extension points and accept contributed extensions should declare two
protected or public methods: registerExtension and unregisterExtension.
This can be done either by implementing the Component interface, or by declaring these methods with their
correct signatures on the component object (as we have seen before for the life cycle methods).
When an extension is contributed the registerExtension method is called with an argument that points to the
actual contributed extension as an Extension object.
Components should use this method to do something with the extension (usually to register it somewhere).
When the component contributing the extension is deactivated, the Runtime will call the unregisterExtension
method using the same Extension object as a parameter. This gives a chance to the extended component to
unregister extensions when they become inactive.
You can see how the contributed objects are fetched from the Extension object and then registered into a Java
Map. These contributions are objects of type HelloMessage as defined by the extension point (using the object
sub-element)
The contributions are also available as a DOM element so you can use this to retrieve contributions in the case
you don't use XMap to map XML extensions to Java objects. This DOM element is corresponding to the
extension element from the XML component descriptor.
So if you need to retrieve the DOM representation of the extension, you can do:
public void registerExtension(Extension extension) throws Exception {
Element element = extension.getElement();
// parse yourself the DOM element and extract extension data
...
}
Each component is defined inside its own file. As you can see the root element is component. This element has
a required attribute name. Apart this, all other sub-elements are optional.
This element is used to specify the component implementation class. The element is not required since
one may define plain XML components only for contributing some extensions to other components. We
will refer to these components as extension components.
2. require
This element can be used to specify dependencies on other components. The component will be resolved
and activated only after all these dependency are resolved.
3. property
This element can be used to define random properties that will be available later to the component when
it will be created.
4. extension-point
This element is used to declare extension points. A component may declare any number of extension
points.
5. extension
This element can be used to declare extensions to other components (or to the current component itself).
To load a whole OSGI bundle, use the deployBundle method, whose parameter is the bundle symbolic name,
as specified in its manifest.
Loading a whole bundle can be too heavy, or bring unwanted default configurations. Therefore, the
deployContrib method is provided to load just a resource (service definition, extension point contribution, etc.)
from a given bundle. It takes two arguments: the bundle symbolic name, and the path to the contrib from the
top of bundle.
For resources from the test packages, just make an OSGI bundle of the test package, which can be done by
creating the META-INF/MANIFEST.MF at the top of the target jar, and use deployContrib as above.
}
public void testServiceContribution() throws Exception {
// Lookup is ensured simply by making the 'test' sub-hierarchy a
// bundle of its own, with a MANIFEST file
deployContrib(OSGI_TEST_BUNDLE, "sample-booktitle-test.xml");
assertEquals("FOOBAR Test", service.correctTitle("foobar"));
}
}
The use-cases discussed below require basic knowledge of the Nuxeo ECM framework. Implicitely, it is
somehow assumed that the tested code has to interact with one service. In case of multiple target services, one
would have to choose a pattern for each of them.
We want to check that the API calls from the tested component to other components have the desired effect, but
we don't want to rewrite the tests each time the default configuration of the other components change.
Typically, this means that we need to deploy the xml contributions that define the services we need, together
with the minimal configuration to tie it up together.
Example: the search service is able to configure its indexes automatically from the schemas and core types
declaration. We don't want to have to update tests if someone changes the default config that ships with
nuxeo-core. Ideally, this test should use deployBundle to set up core services, test repository, etc. and then
work on dedicated schemas and core types that are loaded by deployContrib.
30.7.2.2. Integration test against base services and their default configuration
One can imagine here a core event listener that uses a given schema, a component that needs access to the
search service to manipulate some specific documents...
In this case, we need to load the base service and its configuration exactly as they are in the real application and
we do want the test to catch errors that are due to a change in said configuration. In this pattern, we'd use
deployBundle all over the place.
It's likely however that one does not want the test to rely on the default (if any) configuration of the module
being tested. If the tested component doesn't carry its configuration but still needs to be deployed within Nuxeo
Runtime, deployBundle can be used on itself, and then deployContrib for the test configuration, after the test
package has been upgraded to an OSGI bundle.
Variant: testing of a component and the configuration that comes along. Just think of your tested module as a
"base service."
This is usually neither possible nor recommended. Such situations do appear in the Nuxeo code base, and the
proper solution is to provide the wished resources or classes from a package, precisely like
org.nuxeo.ecm.runtime.test does.
30.9. References
1. Nuxeo.org website: http://www.nuxeo.org/
3. JSR 277, 291 and OSGi, Oh My! - OSGi and Java Modularity, presented by Richard S. Hall at
ApacheCon Europe 2006:
http://docs.safehaus.org/download/attachments/2995/osgi-apachecon-20060628.pdf
31.1. TODO: BS
Start from the previous documentation (OOo) and update it. :-)
This chapter targets developers that would like to use directly Nuxeo Core.
31.2. Overview
Nuxeo Core is the foundation of the Nuxeo ECM project. It defines and provides all the basic services and
functionalities needed to build a complete ECM platform and applications:
1. a repository model,
3. a query service,
4. a security model,
Like every Nuxeo ECM component, Nuxeo Core is running on top of Nuxeo Runtime which defines an
OSGi-compatible component model.
2. to be accessible both remotely or locally (i.e: to provide a common API accessible both from a remote
JVM or directly on the local one),
3. to be deployable anywhere without any modification (through Nuxeo Runtime): in a Java EE application
server like JBoss, or embedded in a desktop application like an RCP Eclipse application,
4. to be extensible and flexible; this is inherited from Nuxeo Runtime which provides an extensible
component model.
4. NXJCRConnector: JCR storage backend that leverages jackrabbit this is the default NXCore storage
backend
2. implementation layer,
3. facade layer.
There are also a number of services used by top-level components to provide them with common functionalities
like the schema service, query service, life cycle or security. These services are simple and cannot operate on
their own – they need a context to operate on. These services are exposed through top-level components and
may not follow the layering presented below.
The model provides a generic API that defines the concepts used by the service and that may have several
implementations (using different storage backends for example).
Usually this API cannot be accessed remotely since implementations may use local resources that cannot be
sent over the network.
For example, the Repository model defines objects like Document, Property, Session, etc. The JCR-based
implementation for the Repository model is directly wrapping JCR (Jackrabbit) nodes that cannot be detached
from the local JVM and sent over the network.
Implementations may use very specific resources and configuration, and are hidden by the common model
defined by the service. This means that implementation-specific objects or APIs are never used directly by
other Core components, they are only accessed by the implementation of the internal API.
This layer is also named the Public API because it defines the API exposed to clients. Any client, local or
remote, must use the public API of the component, and must not make calls to the internal API.
The main requirement of the public API is to use only serializable objects that can be sent over the network and
reconstructed on the client machine.
31.3.4. Deployment
The architecture presented above makes it possible to access the Core services when the Core is running inside
the same JVM as the client application (e.g., when embedded in a desktop application) but also when it is on a
remote JVM (e.g., deployed as a module inside an application server). In both cases the Core services are
accessed in the same way – through the public API.
While a client is connected to a Core service, the latter should track the client session and restore its state (if
any) at each client request. When the session is closed by the client, the Core service releases any resource held
by that session.
Any data passed between the client and the Core service is serializable and so it can safely be sent over the
network. In this way a client can operate identically when running on the same JVM or when running on a
remote one.
The repository model, as its name suggest, is describing a software component for managing repositories of
documents. Repositories store documents in a tree-like structure that enables grouping documents inside folders
in an hierarchic manner.
2. security management,
4. annotations,
5. SQL-like query.
The properties that a document may have and their types and constraints are defined through several schemas.
The repository model natively supports XML Schemas to define document schemas.
A schema is therefore the way the structure and contents of a document is defined. Through schemas you can
usually specify things like:
1. what properties are allowed,
In order to create and use documents, you first need to define their structure. For this, you have to define a
document type. Then you can create instances of documents of this type.
In some ways, document types and schemas are similar to Java classes and interfaces. A document type may
implement some schemas in the same way that Java classes implement interfaces, and a document type can
extend another document type in the same way that a Java class can extend another class.
Document Types define one or more schemas that the document structure must satisfy and some other extra
properties like facets which will be discussed later.
In conclusion, the unit of work in a repository is the document. To create a new document, you must specify the
document type and a path. You can either use existing document types or register new types as we will see in
the Extension Points section.
For more information on document types and schemas see the section XXX.
For now, facets are simple strings attached to a document type to specify a capability for documents of that
type. In the future, facets may evolve to more complex structures, for example to dynamically provide
interfaces to manipulate documents according to a capability they offer.
This is very useful for repository extensions that needs to store placeful (i.e., location-sensitive) information on
a document – information that cannot be specified by any document schema since its type is not necessarily
known in advance.
Annotations are not required to be stored through the same data storage as the document itself. For example one
may choose to store document in a Jackrabbit-based repository and to store annotations in a dedicated SQL
database.
These annotations usually keep some internal state or data about the document. For example, a tool that may
use annotations is the workflow service.
4. and generally on any rule that was defined over a particular location on the document parents.
Privileges are a standard example of extra information that need to be stored on the document in a placeful
manner, so it may be a perfect candidate for the annotation service.
But since privileges are very dynamic and may require expensive computations on every document that is
accessed, a separate Security Service exists to manage the storage as it sees fit - and not necessarily through
annotations on the document. This is more efficient from a performance point of view.
In the following subsections, we will see what type of information is stored on the document to enforce security
and how security checks are done. To ease comprehension of security concepts and evaluation, we will begin
the presentation from the smallest unit of security information to the largest one that is stored at the document
level.
This is the smallest unit specifying a security rule. It is a very simple object containing three fields:
1. principal: an authenticated entity. For example the user that opened the session on the repository is a
principal – but a principal may also be a group of users.
2. permission: the kind of action that may be granted or denied for a principal. This may also be a group of
permissions. This corresponds to the Java concept of privilege.
3. granting: specifies whether the given permission is granted or denied to the given principal.
Examples:
1. DENY, John, Read: an access entry that specifies that the reading is denied for the principal John.
2. GRANT, Developers, Drink: an access entry that specifies that drinking is granted for any principal
from the developer group.
An ACL is an ordered list of ACEs. This means it represents a set of access rules. Why ordered? Because
usually when evaluating access rules the order is important. This is because evaluation stops on the first DENY
or GRANT rule that match the criteria check.
Here is a simple example showing how ordering may influence the security checks. Suppose that we have a
principal John that belongs to the Readers group, and an ACL that contains the following two ACEs:
1. DENY, John, Read
Suppose we want to check whether principal John is granted reading. Every entry in the ACL is checked (in the
order they were defined) and if an entry matches the security check the evaluation stops. Using the example
above, John will be denied reading even if it is a member of the Readers group. But if you swap the order of
ACEs in the ACL, John will be granted reading.
An ACP is an ordered list of ACLs. Each ACL stored in the ACP is uniquely identified by a name. The
ordering is important when security is checked – ACLs at the beginning of the list will be checked first.
The ACP is the object containing the security information that is attached to a document.
Note that ACLs are inherited so that a document will inherit any defined ACLs from its parents in the
hierarchy. Inherited ACLs are evaluated after evaluating the local ACLs and from the nearest parent to the
remotely related parent.
You may wonder why an ACP is containing several ACLs? And what about ACL names? In a typical situation
where security information may only be changed by an administrator through a user interface, a single ACL is
enough.
But a complex application may have complex rules to set privileges according to the current document state or
context. This is the case for a workflow engine which may decide to revoke or grant privileges depending on
the document state or the context.
This means that access rules are changed not only by administrators but also by services like the workflow. To
avoid collisions, every tool that needs to change access rules may use its own (named) ACL for setting these
rules. If the workflow service considers that its rules are more important than the ones explicitly set by the
administrator, it simply places its ACL before the one reserved for the administrator so that it will be evaluated
first.
The local ACL is the only ACL an administrator may explicitly change through the User Interface.
This ACL is computed each time a security check is performed (unless caching is used). The inherited
ACL is the ACL obtained by merging all existing ACLs on the document's hierarchy. This ACL is
appended to the ACL list, so it will be evaluated last.
So from a simple security unit like the ACE we end up with a sophisticated structure like inheritable ACPs.
These use cases are not artificial, they are real use cases that a mature ECM product should satisfy.
The evaluation mechanism has been described above. Here is an example of how an evaluation is done.
Let's say the principal John is trying to edit the document D. Editing a document requires the Write permission.
Suppose the document D has the path /A/B/C/D – it is a child of the document C which is a child of the
document B which is the child of the document A.
To decide if the principal John can edit this document the following steps are taken:
1. The merged ACP for the document D is computed. This ACP is the local ACP set on the document D
merged with all parent ACPs. ACLs imported from the parents are appended to the local ACLs so that
they will be evaluated at last.
4. If an ACE match a security rule regarding the principal John (or a group which it belongs) and the
permission Write (or a permission group from which Write belongs) then the evaluation ends and the
access right of the matching ACE is returned
Nuxeo Core itself doesn't embed a workflow engine, or still a BPM engine, as such. It only provides a generic
way to define document life cycles, the way the document properties related the life cycle are stored and a way
to specify which document types follow which life cycles at deployment time.
Thus, the workflow engine that will be deployed along with Nuxeo Core will leverage the API exposed by
Nuxeo Core to set the life cycle properties.
The APIs defined in Nuxeo Core regarding life cycle are highly inspired from the JSR-283 specifications that
are still in a draft state at the time of writing this document.
Another advantage of such a design is the fact that the life cycle state of a document will be independent of the
application (i.e.: workflow variables) and will be embedded within the document itself at storage time, and thus
will be exported along with the document properties.
Nuxeo provides a BPM engine that knows how to leverages the Nuxeo Core life cycle API. See
http://www.nuxeo.org.
Nuxeo Core allows one to define life cycle using extension points. (See the Nuxeo Runtime documentation for
more information about extension points.). You will find at the end of this document the complete list of
extension points defined by the core, you will find an example of life cycle definition there using the life cycle
definition extension point.
The life cycle model defined by Nuxeo Core is simple stateful, or state-transition engine. Including the
following elements:
1. Life cycle definition
2. Current JCR specifications doesn't include a default policy model regarding life cycle so it appears
logical to not include this ourself at this layer of the architecture
This is important to note that the life cycle definition is fully independent from the document types themselves
which allows the reuse if life cycle for different document types.
The life cycle manager is responsible of the storage of the life cycle related properties. One could think of
storing the life cycle property within the JCR, which is the default implementation provided by
NXJCRConnector, or still one could think about storing it in a separated RDBMS apart from the content
storage.
Because of this, Nuxeo provides an abstraction for this storage allowing one to define a life cycle manager per
life cycle definition.
Let's take a look at the life cycle manager exposed by Nuxeo Core:
You can see that the interface is fairly simple. It basically, only specifies how to store and retrieve the state and
the life cycle policy of a given document.
http://fisheye.nuxeo.org/browse/~raw,r=4233/nuxeo/ECMPlatform/NXJCRConnector/trunk/src/org/nuxeo/ecm/core/jcr/JCR
Note this is how the JSR-283 current specifications specifies the life cycle storage repository side.
You can register your own life cycle managers using the lifecyclemanager extension point defined on the
Nuxeo Core side. See the extension points chapter of this document for an example.
When your life cycle definitions are defined and you did specify the life cycle managers which will take care of
the storage you will then need to specify associations in between document types and life cycle.
To achieve this, Nuxeo Core defines an extension point allowing one to specify, independently from the
document type definition, such an associations. Please, check the example at the end if this document.
Nuxeo Core defines a dedicated life cycle service that is used by the Nuxeo Core internals. This service is not
exposed at the facade layer because we don't need it there. This service is manipulating directly the repository
document themselves (not references and thus is not suitable for remoting purpose).
Actually, the document model itself has been extended so that you can directly invoke this service through the
document session itself at facade layer. See next chapter for an overview of the API.
31.4.5.6. The life cycle document API and the exposure at the facade layer
The document model exposes a life cycle related API. You can take advantage of this API from the document
itself if you are working at core level. Here is the API:
Nuxeo Core defines a service dedicated to core events. This service is only responsible of core events and
allows third party code to register listeners that will get notified when events occur (and that can take specific
actions themselves).
This service doesn't take advantage of event service such as JMS or still the NXRuntime event service at this
level because it needs ti be really fast at event processing to not decrease the repository performances for
instance.
By using event listener extensions, you can hook up and bridge on another synchronous or asynchronous
messaging systems. Let's take some examples.
1. Nuxeo Core defines a bridge to Nuxeo Runtime forwarding events on the NXRuntime event service in
an asynchronous way. It defines like that a local event loop shared by all components running on top of
NXRuntime.
2. The NXEvents component, not part of the Nuxeo Core, registers a JMS listener bridging Nuxeo Core
events to a dedicated JMS topic. It allows message driven beans in the Nuxeo Enterprise Platform to get
the Nuxeo Core events. (for instance NXAudit)
You could define whatever listeners you need to forward the Nuxeo Core events on an external messaging
system. See the end of this document for an example of such a registration.
NXQL offers standard SQL functionality to search records, but can also take advantage of the hierarchical
nature of the content repository to provide path-based searches. NXQL is used as the uniform query syntax to
access several kinds of repositories. The query engine itself must process and optimize the query, and dispatch
it to the different backends and tables that are referenced in the query.
Updates or creation statements are not covered and must be performed through the repository API.
For more information about the query engine, refer to the document about NXQL.
This public API has only one limitation: any object transferred between the client and the core must be
serializable. This way it can be sent over the network and restored on the client side.
So the public API is in fact is a serializable view of the repository model. This has a performance drawback
compared to the internal API since it should transform any model object like a Document into a serializable
form, but has the benefit of being totally independent from the JVM where the Core runs.
4. CoreInstance: the gateway to the Core. It uses session factories to create new sessions (connections) to
the Core.
31.4.7.1. DocumentModel
The document model is a data object that completely describes a document. You can see it as a serializable
view of document.
Apart from being a data object, this object also provides some logic. For example a document model is able to
lazily load data from the storage if not already loaded or it may check permissions for a given user on the
document it represent.
For each document schema, there is a DataModel that contains concrete data as specified by the corresponding
schema. You can see a DataModel as a data object described by a schema (i.e. a schema instance).
A document contains also data that is not defined by schemas like its internal ID, its name, its parent etc. Thus,
apart from these data models there is some information stored as members on the document model like the
document ID, the document name, a reference to the parent document, the ACP information (used for security
checks), the session ID etc.
Also the data model contains the list of facets that the document type defines.
One of the most important ability of the document model is to lazily load data the first time the data is required
from the client. This feature is important because the document may contain a lot of schemas and fields and it
will be a performance problem to load all these data from the storage each time a document model is created.
Usually, the client application is using only few DataModel fields like the Tile, Description, CreationDate,
etc. These are the fields commonly displayed by a tree – like explorer of the repository.
When the client is displaying or editing the document properties – then the document model will load missing
data models.
To achieve this, there are schemas or fields that are declared to be lazily loaded. When creating a document
model from a document, only the non-lazy schemas and fields are fetched from the storage. For example, a
blob field will be always lazy.
31.4.7.2. DataModel
As detailed above, the data model is an object containing the concrete data for a document schema.
Each data model is described by the schema name and the map of fields. The data model contains no logic, it is
a pure data object.
Apart from the fields map, the data model contains information about dirty fields (fields that have been
modified by the client), so that when saving changes to the repository only modified fields are saved.
31.4.7.3. CoreSession
The CoreSession is a session to the Nuxeo Core. The session is opened and closed by a client and gives the
client the possibility to interact with the Core.
The Core a session connects to can be located in a separate JVM or in the current one. To create remote or local
sessions, you need to use a specific CoreSessionFactory object. These objects are usually specified using
extension points but you can also use them programatically.
After creating a session, you can begin to retrieve and modify documents through the API exposed by the
CoreSession interface.
31.4.7.4. CoreInstance
This is the gateway to a Core instance. As mentioned above, the Core may be located in a remote JVM. The
CoreInstance uses CoreSessionFactory objects (declared through extension points) to connect to a Core
instance and to create a session.
The resource adapter is write over the repository model so it is not dependent on the repository implementation
(like for example JackRabbit).
The resource adapter enables the repository to take part on transactions managed by the application server.
This extension points is for registering new session factories. Session factories are used to create new Core
Sessions.
2. a remote session factory – that create sessions to a remote Core (running in a remote Application Server)
This extension points is for registering new life cycle managers. A life cycle manager is responsible for
managing and storing document life cycle information.
The service can also be used to create in batch document trees from valid import archives or to provide a simple
solution of creating and retrieving repository data. This could be used for example to expose repository data
through REST or raw HTTP requests.
Export and import mechanism is extensible so that you can easily create you custom format for exported data.
The default format provided by Nuxeo EP is described below.
The import / export module is part of the nuxeo-core-api bundle and it is located under the
org.nuxeo.ecm.core.api.io package.
When exporting trees document children are put as subdirectories inside the document parent directory.
Optionally each service in nuxeo that store persistent data related to documents like the workflow, relation or
annotation services may also export their own data inside the document folder as XML files.
A document tree will be exported as directory tree. Here is an example of an export tree containing relations
information for a workspace named workspace1:
+ workspace1
+ document.xml
+ relations.xml
+ doc1
+ document.xml
+ relations.xml
+ doc2
+ document.xml
+ relations.xml
+ file1.blob
+ doc3
+ document.xml
You can see that the generated document is containing one [system] section and one or more [schema] sections.
The system section contains all system (internal) document properties like document type, path, lifecycle state
and access control configuration. For each schema defined by the document type there is a schema entry which
contains the document properties belonging to that schema. The XSD schema that correspond to that schema
can be used to validate the content of the schema section. Anyway this is true only in the case of inlined blobs.
By default, for performance reasons, the blobs are put outside the XML file in their own file.
So instead of encoding the blob in the XML file a reference to an external file is preserved: cd1f161f.blob
Here is how the same blob will be serialized when inlining blobs (an option of the repository reader):
Of course this is less optimized than writing the raw blob data in external files but provides a way to encode the
entire document content in a single file and in a well known and validated format.
By default when exporting documents from the repository blobs are not inlined. To activate the inlining option
you must set call the method on the DocumentModelReader you are using to fetch data from the repository:
reader.setInlineBlobs(boolean inlineBlobs);
In the same way an import can be defined as a chain of three sub processes:
1. fetching data from external sources
We will name the process chain used to perform imports and exports as a Document Pipe.
In both cases (imports and exports) a document pipe is dealing with the same type of objects:
1. A document reader
3. A document writer
So the DocumentPipe will use a reader to fetch data that will be passed through registered transformers and
then written down using a document writer.
See the API Examples for examples on how to use a Document Pipe.
For example a reader may extract documents from the repository and to output it as XML DOM objects. Or it
may be used to read files from a file system and convert them into DOM objects to be able to import them in a
Nuxeo repository.
To change the way document are extracted and transformed to a DOM representation you can implement your
own Document Reader. Currently Nuxeo provides several flavors of document readers:
1. Repository readers - these category of readers are used to extract data from the repository as DOM
objects. All of these readers are extending DocumentModelReader:
• SingleDocumentReader - this one reads a single document given its ID and export it as a dom4j
Document.
• DocumentChildrenReader - this one reads the children of a given document and export each one as
dom4j Document.
• DocumentTreeReader - this one reads the entire subtree rooted in the given document and export each
node in the tree as a dom4j Document.
• DocumentListReader - this one is taking as input a list of document models and export them as domj
Documents. This is useful when wanting to export a search result for example.
2. External readers used to read data as DOM objects from external sources like file systems or databases.
The following readers are provided:
• XMLDirectoryReader - read a directory tree in the format supported by Nuxeo (as described in Export
Format section). This can be used to import deflated nuxeo archives or hand created document
directories.
• NuxeoArchiveReader - read Nuxeo EP exported archives to import them in a repository. Note that
only zip archives created by nuxeo exporter are supported.
• ZipReader - read a zip archive and output DOM objects. This reader can read both Nuxeo zip archives
and regular zip archives (hand made). Reading a Nuxeo archive is more optimized - because Nuxeo
zip archives entries are added to the archive in a predefined order that makes possible reading the
entire archive tree on the fly without unziping the content of the archive on the filesystem first. If the
zip archive is not recognized as a Nuxeo archive the zip will be deflated in a temporary folder on the
file system and the XMLDirectoryReader will be used to read the content.
1. Repository Writers - These ones are writing documents to a Nuxeo repository. They are useful to
perform imports into the repository.
• DocumentModelWriter - writes documents inside a Nuxeo Repository. This writer is creating new
document models for each one of the imported documents.
2. External Writers - are writers that write documents on an external storage. They are useful to perform
exports from the repository.
• XMLDocumentTreeWriter - writes a list of documents inside a unique XML file with blobs inlined.
The document tags will be included in a root tag
<documents> .. </documents>
• XMLDirectoryWriter - writes documents as a folder tree on the file system. To read back the exported
tree you may use XMLDirectoryReader
• NuxeoArchiveWriter - writes documents inside a Nuxeo azip archive. To read back the archive you
may use the NuxeoArchiveReader
As documents are expressed as XML DOM objects you can also use XSLT transformations inside your
transformer.
The page size argument is important when you are running the pipe on a machine different than the one
containing the source of the data (the one from where the reader will fetch data). This way you can fetch
several documents at once improving performances.
2. Create a new DocumentReader that will be used to fetch data and put it into the pipe. Depending on the
data you want to import you can choose between existing DocumentReader implementation or you may
write your own if needed:
In this example we use a DocumentTreeReader which will read an entire sub-tree form the repository
rooted in 'src' document.
The docMgr argument represent a session to the repository, the 'src' is the root of the tree to export and
the 'true' flag means to exclude the root from the exported tree.
3. Create a DocumentWriter that will be used to write down the outputed by the pipe.
In this example we instantiate a writer that will write exported data onto the file system as a folder tree.
4. Optionally you may add one or more Document Transformers to transform documents that enters the
pipe.
pipe.run();
33.1. Introduction
This chapter is provided to document experimental features that are being introduced in the current
development branch (Nuxeo 5.2-SNAPSHOT).
Note
Your feedback is welcome! If you have use cases and want to help with the development of these
technologies, please join the ECM mailing list or the forum.
33.2.1. Introduction
Preliminary, yet powerful, support for scripting in Nuxeo Runtime has been recently added. This makes
scripting available from all Nuxeo’s platform. Thanks to this new feature, you can easily uses script from your
custom components. This can be very useful for a lot of use cases, like:
• dynamic rules (scripting language as DSLs)
• etc.
Scripts have access to the whole API thanks to Java scripting integration (JSR-223).
Moreover, scripts can also be run remotely thanks to the Nuxeo Runtime command line. This allows you to
create a script on your administration machine, launch it on the remote platform and get the result back. It
makes scripting a killer-feature for administration scripts (ex: expire content, bulk content modification, bulk
refactoring of the content repository layout, etc.).
• JRuby
• Jython
• Groovy
• JavaScript (Rhino)
• Let the script inside your .jar and register it under using name as a script component. Then you can run
the script as follow:
Framework.getService(ScriptingService.class).getScript("myScript").eval();
This method chaches the compiled script so it is only supported for languages that support compilation
(currently all the engines that comes in Nuxeo).
This can be used for debug, testing or administration. You write a script locally then you run it against a
remote Nuxeo EP server. The script will be send to the server and executed on the server then the server
will return the result (including STDOUT and STDERR) to the client.
For security reason this feature can be disabled using a runtime property on the server.
service:org.nuxeo.ecm.core.api.DocumentAdapterService
service:org.nuxeo.ecm.core.repository.RepositoryService
service:org.nuxeo.runtime.remoting.RemotingService
service:org.nuxeo.runtime.EventService
service:org.nuxeo.ecm.platform.login.LoginConfig
...
So, this new feature can be used to write pure script based Nuxeo components. Also in future I will try to
configure Tomcat to be able to run scripts inside servlets. This means to be able to write web pages in PHP or
other supported language for Nuxeo EP ;-)
If you're willing to leverage these new scripting features in your own applications and explore new ways to use
scripting to make balance "enterprise" with "agile" in the ECM field, please join the list or the forum and start
contributing.
No integration with Seam and no state management. This is the original logic of Restlet.
You can use BaseStalessNuxeoRestlet as base class that provides helpers for accessing main services
(like the repository).
• Seam-aware restlets
For restlet declared as Seam-aware, the Nuxeo Restlet servlet initializes the Seam context before
executing the restlet. Thanks to this initialization your restlet can use injection (@In) to access the Seam
context. This solution gives you the possibility of using existing Seam components. You don't have to
use Service Platform API to access the service since you can access Seam delegates for that.
You can use BaseNuxeoRestlet that provides helper API for error handling, security and URL
management.
• Conversation-aware restlets
The are Seam restlets that are tied to a Seam conversation. For these restlets, the Seam context
initialization is done in order to setup the current Conversation. Conversationnal restlets must be called
with a convesationId as parameter.
Convesationnal restlet can be used if you need to access the current Seam context associated with a
browser session. Typically this is what is used by the Firefox helper to upload files.
You can use BaseNuxeoRestlet that provides helper API for error handling, security and URL
management.
When defining URLs you can use {} to have parts of the URL that will be converted as parameters.
For example :
/{repo}/{docid}/{filename}/upload
will define a URL pattern with 4 parts and you will have access from withing your code to 3 parameters: repo,
docId and filename.
req.getAttributes().get("repo");
req.getResourceRef().getQueryAsForm().getFirstValue("SomeParameter");
The first thing to do is to write a new restlet: you can either implement the Restlet Interface "by hand" or just
inherit from BaseNuxeoRestlet or BaseStatelessNuxeoRestet.
Once your class is written, you need to contribute to the restlets extension point exposed by
org.nuxeo.ecm.platform.ui.web.restAPI.service.PluggableRestletService.
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.ui.web.restAPI.contrib">
<extension
target="org.nuxeo.ecm.platform.ui.web.restAPI.service.PluggableRestletService"
point="restlets">
<restletPlugin
name="upload"
class="org.nuxeo.ecm.platform.ui.web.restAPI.UploadRestlet"
enabled="true"
useSeam="true"
useConversation="false">
<urlPatterns>
<urlPattern>/{repo}/{docid}/{filename}/upload</urlPattern>
</urlPatterns>
</restletPlugin>
...
The useSeam and useConversation flags define how the Nuxeo Restlet servlet will handle the call.
id="2fbf878d-9c2f-42c6-acbb-ea339ce15615"
url="/default/2fbf878d-9c2f-42c6-acbb-ea339ce15615">
<document title="Default domain"
type="Domain"
id="95ce52b2-6959-4afa-bc63-396096b376b4"
url="/default/95ce52b2-6959-4afa-bc63-396096b376b4"/>
</document>
This restlet uses Seam in order to have documentManager injected (this is not a need but rather a simple way of
accessing the repository without using the Service API)
• http://127.0.0.1:8080/nuxeo/restAPI/default/2fbf878d-9c2f-42c6-acbb-ea339ce15615/export?format=ZIP
• http://127.0.0.1:8080/nuxeo/restAPI/default/2fbf878d-9c2f-42c6-acbb-ea339ce15615/exportTree
to export the document 2fbf878d-9c2f-42c6-acbb-ea339ce15615 and all its children as a Zip Archive
• http://127.0.0.1:8080/nuxeo/restAPI/default/2fbf878d-9c2f-42c6-acbb-ea339ce15615/exportTree?format=XML
This restlet uses Seam in order to have documentManager injected (this is not a need but rather a simple way of
accessing the repository without using the Service API)
• GET http://127.0.0.1:8080/nuxeo/restAPI/default/2fbf878d-9c2f-42c6-acbb-ea339ce15615/Locking/lock
or
LOCK http://127.0.0.1:8080/nuxeo/restAPI/default/2fbf878d-9c2f-42c6-acbb-ea339ce15615/Locking
• GET
http://127.0.0.1:8080/nuxeo/restAPI/default/2fbf878d-9c2f-42c6-acbb-ea339ce15615/Locking/unlock
or
UNLOCK http://127.0.0.1:8080/nuxeo/restAPI/default/2fbf878d-9c2f-42c6-acbb-ea339ce15615/Locking
to upload a file
The primary goal of the RestPack is to provide the needed RestAPI to build simple JSR168 portlets (like search
portlets) that communicate with Nuxeo via HTTP/XML.
To deploy the module, just copy the jar file in nuxeo.ear/system folder and restart you JBoss
Sample call :
GET http://127.0.0.1:8080/nuxeo/restAPI/vocabulary/{vocName}/
parameters
• {vocName}
• lang
GET (QueryString) parameter used to set language used to generate the lables
Sample calls
• http://127.0.0.1:8080/nuxeo/restAPI/vocabulary/subject?lang=en
<entries>
<entry id="arts" label="label.directories.subject.arts" translatedLabel="Arts"/>
<entry id="business" label="label.directories.subject.business" translatedLabel="Business"/>
<entry id="computers" label="label.directories.subject.computers" translatedLabel="Computers"/>
...
• http://127.0.0.1:8080/nuxeo/restAPI/vocabulary/continent_country?lang=en
<entries>
<entry id="africa" label="label.directories.continent.africa" translatedLabel="Africa">
<entry id="Algeria" label="label.directories.country.Algeria" translatedLabel="Algeria" parent="africa"/>
<entry id="Angola" label="label.directories.country.Angola" translatedLabel="Angola" parent="africa"/>
<entry id="Benin" label="label.directories.country.Benin" translatedLabel="Benin" parent="africa"/>
<entry id="Botswana" label="label.directories.country.Botswana" translatedLabel="Botswana" parent="africa"/>
<entry id="Burkina_Faso" label="label.directories.country.Burkina_Faso" translatedLabel="Burkina Faso" parent="a
<entry id="Burundi" label="label.directories.country.Burundi" translatedLabel="Burundi" parent="africa"/>
<entry id="Cameroon" label="label.directories.country.Cameroon" translatedLabel="Cameroon" parent="africa"/>
...
Export as RSS or ATOM the list of children document of the targeted container
Sample call:
http://127.0.0.1:8080/nuxeo/restAPI/{repoId}/{docId}/{format}
Parameters:
• {repoId}
• {docId}
DocumentRef of the target container (use the docId present in the standard nuxeo URL when browsing
the webapp)
• {format}
Sample call:
http://127.0.0.1:8080/nuxeo/restAPI/workflowTasks/{repoId}/?format=XML/ATOM
Parameters:
• {repoId}
• format
Sample call:
http://127.0.0.1:8080/nuxeo/restAPI/workflowTasks/default/?format=xml
<nxt:tasks>
<nxt:category category="None">
<nxt:task name="review" workflowType="document_review_approbation" author="Administrator" startDate="2007-10-0
</nxt:category>
</nxt:tasks>
Export user's workflow tasks that match the query as XML or ATOM
Sample call:
http://127.0.0.1:8080/nuxeo/restAPI/queryForWorkflowTask/{repoId}/?format=XML/ATOM&workflowRequest=author&
Parameters:
• {repoId}
• format
• workflowRequest
Defines the worflow item attribute to compare for the query. Please refer to the WorkItem Filter.
• comparisonType
• workItemFromUser
• canManage
If true add filter that match workItems that the participant has a direct action to perform
Sample call:
http://127.0.0.1:8080/nuxeo/restAPI/workflowTasks/default/?format=xml
<nxt:tasks>
<nxt:category category="None">
<nxt:task name="review" workflowType="document_review_approbation" author="Administrator" startDate="2007-10-0
</nxt:category>
</nxt:tasks>
http://localhost:8080/nuxeo/restAPI/queryForWorkflowTask/default/?workflowRequest=author&comparisonType=0&work
Get work item tasks that the user initiate and is pending for the next user.
Sample call :
http://127.0.0.1:8080/nuxeo/restAPI/execQueryModel/{queryModelName}
Parameters:
• {queryModelName}
• format
Defines the export format: XML, Atom, RSS or JSON. This parameter is set as a QueryString parameter
• page
Defines the page number you want in the result. This parameter is set as a QueryString parameter
• ascending
• criteria
Defines ordering columns used for sorting. This parameter is set as a QueryString parameter
• columns
Defines the columns you want to be included in the resultset. This parameter is set as a QueryString
parameter
This parameter is a simple string containing schema.fieldName tokens separated by "," . The only special
field is url. Default value is : dublincore.title,dublincore.description,url
• QueryModel parameters
For stateless QueryModels, you have to specify the parameters via QP1, QP2 ... (only ordering is
important)
The value $USER is automatically replaced by the name of the current User :
http://127.0.0.1:8080/nuxeo/restAPI/execQueryModel/USER_DOCUMENTS?QP1=$USER
Sample call:
http://127.0.0.1:8080/nuxeo/restAPI/execQueryModel/USER_DOCUMENTS?QP1=$USER&format=JSON
[
{
"title": "nouveau-fichier",
"description": null,
"url": "nxdoc/default/452c122d-07de-422b-b448-b0fef9534a62/view_documents",
"id": "452c122d-07de-422b-b448-b0fef9534a62"
},
{
"title": "Setup",
"description": null,
"url": "nxdoc/default/ae03f7bf-9967-4b5b-b37b-807fd40a6ec7/view_documents",
"id": "ae03f7bf-9967-4b5b-b37b-807fd40a6ec7"
},
{
"title": "cps",
"description": null,
"url": "nxdoc/default/2bad93ca-188f-4ea0-a585-9540a6ed6581/view_documents",
"id": "2bad93ca-188f-4ea0-a585-9540a6ed6581"
},
{
"title": "testMe",
"description": null,
"url": "nxdoc/default/e4de81a9-95e8-49ff-9146-e020f99b8bb8/view_documents",
"id": "e4de81a9-95e8-49ff-9146-e020f99b8bb8"
} ... ]
http://127.0.0.1:8080/nuxeo/restAPI/execQueryModel/USER_DOCUMENTS?QP1=$USER&format=XML&page=1
<results>
<pages pages="3" pageNumber="1"/>
<document id="a3154f03-6baa-4d7d-8bac-579d52a8d304" title="ssl-uil2-service"
url="nxdoc/default/a3154f03-6baa-4d7d-8bac-579d52a8d304/view_documents"/>
<document id="e2b26d9a-9140-44f1-ae23-4ad7866911c0" title="build"
url="nxdoc/default/e2b26d9a-9140-44f1-ae23-4ad7866911c0/view_documents"/>
<document id="bf7de6df-bca1-4d29-8e26-5019ced696fd" title="jboss-service"
url="nxdoc/default/bf7de6df-bca1-4d29-8e26-5019ced696fd/view_documents"/>
<document id="439a11e8-642d-41f7-ae0f-4ef4d7303d79" title="postgres-jdbc2-service"
url="nxdoc/default/439a11e8-642d-41f7-ae0f-4ef4d7303d79/view_documents"/>
<document id="467745db-565d-4350-953f-b2fa03733406" title="oil-service"
url="nxdoc/default/467745db-565d-4350-953f-b2fa03733406/view_documents"/>
<document id="44a2441e-e265-4fee-ad17-9ec24245cccf" title="jbossmq-state"
url="nxdoc/default/44a2441e-e265-4fee-ad17-9ec24245cccf/view_documents"/>
<document id="8d7e3077-a3be-4d6c-806c-aa97816821e2" title="oracle-jdbc2-service"
url="nxdoc/default/8d7e3077-a3be-4d6c-806c-aa97816821e2/view_documents"/>
<document id="b431ad04-3439-441e-826b-48b5f0f0c1c8" title="as400-jdbc2-service"
url="nxdoc/default/b431ad04-3439-441e-826b-48b5f0f0c1c8/view_documents"/>
<document id="e146c6b2-a315-4788-9bbe-5f1fc79c18ac" title="mssql-jdbc2-service"
url="nxdoc/default/e146c6b2-a315-4788-9bbe-5f1fc79c18ac/view_documents"/>
<document id="b109a672-5ebd-4a01-8a86-79e2430c2f07" title="file-state-service"
url="nxdoc/default/b109a672-5ebd-4a01-8a86-79e2430c2f07/view_documents"/>
</results>
http://127.0.0.1:8080/nuxeo/restAPI/execQueryModel/USER_DOCUMENTS?QP1=$USER&format=XML&page=1&colu
<results>
<pages pages="3" pageNumber="1"/>
<document id="a3154f03-6baa-4d7d-8bac-579d52a8d304" icon="/icons/note.gif" title="ssl-uil2-service"/>
<document id="e2b26d9a-9140-44f1-ae23-4ad7866911c0" icon="/icons/note.gif" title="build"/>
<document id="bf7de6df-bca1-4d29-8e26-5019ced696fd" icon="/icons/note.gif" title="jboss-service"/>
<document id="439a11e8-642d-41f7-ae0f-4ef4d7303d79" icon="/icons/note.gif" title="postgres-jdbc2-service"/>
<document id="467745db-565d-4350-953f-b2fa03733406" icon="/icons/note.gif" title="oil-service"/>
<document id="44a2441e-e265-4fee-ad17-9ec24245cccf" icon="/icons/note.gif" title="jbossmq-state"/>
<document id="8d7e3077-a3be-4d6c-806c-aa97816821e2" icon="/icons/note.gif" title="oracle-jdbc2-service"/>
<document id="b431ad04-3439-441e-826b-48b5f0f0c1c8" icon="/icons/note.gif" title="as400-jdbc2-service"/>
<document id="e146c6b2-a315-4788-9bbe-5f1fc79c18ac" icon="/icons/note.gif" title="mssql-jdbc2-service"/>
<document id="b109a672-5ebd-4a01-8a86-79e2430c2f07" icon="/icons/note.gif" title="file-state-service"/>
</results>
Even if you can easily use restlet by directly manipulating HTTP Request (this is one of the purpous of REST),
if you use Java on the client side, you may choose to use Nuxeo HTTP client.
The main service object is a NuxeoServer that provides attribute configuration and call methods:
NuxeoServer nxServer = new NuxeoServer("http://127.0.0.1:8080/nuxeo");
nxServer.setAuthType(NuxeoServer.AUTH_TYPE_BASIC);
nxServer.setBasicAuthentication("Administrator", "Administrator");
List<String> pathParams = Arrays.asList("vocabulary", "country");
Representation res = nxServer.doRestletGetCall(pathParams, null);
The shared Secret Authentication depends on an additionnal authentication plugin that needs to be deployed on
Nuxeo side: nuxeo-platform-login-portal-sso.
This authentication system is based on a shared secret between the client and Nuxeo server: you need to
configure this shared secret in the configuration file of the server side module, and also to pass this secret to the
client http lib. Thanks to this shared secret the client will send the login name and a digest token that will be
used to execute the request on the behalf of the login user
A typical use case is a JSR 168 portlet that fetches data from Nuxeo EP. The data retrieval must be done on
behalf of the connected user (request.getPrincipal()). This allows a portlet to display user's workspaces list,
or last documents without the portlet having to know the password of the user.
The authentication token sent between the client is based on the shared secret, the user login, a random data and
a timestamp. Althought this should be secure enought for most needs, this trusted communication between a
client application and a Nuxeo server should not be done on a HTTP connection that uses public Internet.
nxServer.setSharedSecretAuthentication("JDoe","nuxeo5secretkey");
List<String> pathParams = Arrays.asList("execQueryModel","USER_DOCUMENTS");
Map<String, String> queryParams = new HashMap<String, String>();
queryParams.put("QP1", "$USER");
queryParams.put("format", "JSON");
Representation res = nxServer.doRestletGetCall(pathParams, queryParams);
TODO: verbose
The data exchange between the external indexer and Nuxeo is done in a pull mode: the external indexer queries
Nuxeo for new documents to index.
Here is a simple summary of the responsibilities between the external indexer and Nuxeo server:
Externalindexer:
• query nuxeo to know what document may be indexed
Nuxeo:
• provides an API to browse repository
• external indexer easier (like ACP/ACL/ACE preprocessing) may notify external indexer that some data
need to be (re)indexed
37.1. Overview
37.2.1. Prerequisites
We will assume that a JBoss is installed in /opt/jboss, and a Nuxeo platform is deployed on it.
The nuxeo-platform-restPack project contains some useful restlets, including the sample restlet used in the
following project. You need to install it in your deployed Nuxeo EP.
Then, go into the newly created folder, build the project and copy the new .jar:
Restart JBoss
37.2.1.2. Authentication
To enable the authentication between the portlet and the Nuxeo server, you need to install the
nuxeo-platform-login-portal-sso project in your deployed Nuxeo EP.
svn co https://svn.nuxeo.org/nuxeo/nuxeo-addons/nuxeo-platform-login-portal-sso/trunk \
nuxeo-platform-login-portal-sso
cp Sample/Sample-Portal-SSO-descriptor-bundle.xml /opt/jboss/server/default/deployed/nuxeo.ear/config
Then, go into the newly created folder, build the project and copy the new .jar:
Restart JBoss
Before creating a new project, you first need to install nuxeo-archetype-portlet on your local repository.
svn co http://svn.nuxeo.org/nuxeo/org.nuxeo.archetypes/nuxeo-archetype-portlet/trunk \
nuxeo-archetype-portlet
• ArtifactId: usually the name of your project, with '-' to separate the words if there are many
• GroupId: the domain name of your project. Usually the package parent name of your classes.
my-portlet
|-- pom.xml
`-- src
`-- main
|-- java
| `-- com
| `-- company
| `-- sandbox
| `-- portlet
| `-- sample
| `-- NuxeoSamplePortlet
|-- resources
| `-- org
| `-- nuxeo
| `-- portlet
| `-- sample
| `-- i18n
| |-- SamplePortletMessages_en.properties
| `-- SamplePortletMessages_fr.properties
|
`-- webapp
|-- index.jsp
`-- WEB-INF
|-- portlet.xml
|-- web.xml
|-- jsp
| |-- edit.jsp
| |-- sampleHelp.jsp
| `-- sampleView.jsp
`-- tld
|-- c.tld
|-- fmt.tld
`-- fn.tld
In our example, the class is now in the package com.company.sandbox.portlet.sample, change the line to
reference the right class:
<portlet-class>com.company.sandbox.portlet.sample.NuxeoSamplePortlet</portlet-class>
You can now build the portlet and deploy it on Jahia. In your project folder:
Just wait a few seconds that Jahia finds your new portlet and deploys it. Then, go to Jahia and add the portlet.
Go to the edit page of the portlet and fill the different informations (like Nuxeo Server URL, UserName,
Password, ...).
When all is configured, write your name and send it. You should have "Hello your_name!" as response.
• error handling
• global preferences handling (informations stored in global preferences are shared with all the users of the
portlet)
• an administrator role is already defined (in web.xml and portlet.xml), so you can link, for instance, Jahia
administrator role to your portlet administrator role. In your code, use the method isAdministrator()
from NuxeoPortlet class to know if the current user is an administrator or not.
• a basic, but with all the informations needed to connect to a Nuxeo EP, edition page for the portlet is
already done. All the informations are stored in global preferences for the portlet (through the
getGlobalPreferences() and saveGlobalPreferences() methods). That means you can allow only
administrators to modify these preferences, but they will be shared with all the users. You can of course
create your own edition page and override the default behaviour.
• the action requests are dispatched in convenient methods, depending of the portlet mode. For instance, in
the class NuxeoSamplePortlet, we don't use the processAction() method, but processViewAction()
when portlet is in VIEW mode and processHelpAction() when in HELP mode. You don't have to deal
with the different portlet modes. Need to process a view action request? just use processViewAction().
See the nuxeo-portlet-search project as an example of how to use or override the defaults behaviours. This
portlet allows the user to make a simple search or an advanced one (like in Nuxeo EP) on the configured Nuxeo
server.
37.3.3. portlet.xml
Beside the portlet class, portlet.xml lets you customize the portlet description, name, title.
There are also some default init parameters which are used to build the global preferences of the portlet. All the
init parameters will be stored in the global preferences, so you can add the parameters you want and then use
them in your portlet through the global preferences.
All the init parameters already in portlet.xml are needed by NuxeoPortlet to behave correctly.
37.3.4. Restlets
The portlet communicates with the Nuxeo server through some restlets. In our example, we call the restlet
sample with a name as parameter.
To do a restlet call, use the method doRestletCall() from NuxeoPortlet, it makes your call and returns the
result as a Representation (use getText() on it to have a String containing the result). The doRestletCall()
method takes a RestletCall object as argument which contains the different parameters to do the restlet call:
the restlet name, the path parameters and the query parameters.
The doRestletCall() builds the URL to call from the configured Nuxeo server in the global preferences and
from the parameters of the RestletCall object. In our example:
http://localhost:8080/nuxeo/sample?name=my_name
--------------------------- ------ ------------
| | `-- the query parameters of the RestletCall object
| `-- the restlet name of the RestletCall object which will be
| in the path parameters
`-- the Nuxeo server URL from the global preferences
If all is well configured in the global preferences, you don't need to bother with authentication, just call the
restlet.
To make a specific work, you have to develop your own restlets and contrib them to the
nuxeo-platform-restPack project. Then rebuild the project, copy the .jar in your deployed Nuxeo EP and
restart JBoss. You can now call your newly created restlets in your portlet.
37.4.1.1. Installation
You just have to compile the portlet and install it into a portlet container, Jahia for instance, as explained in the
previous sections.
After the installation of the prerequisites, you need to deploy on your portlet container the packaged portlet.
If you don't have already a packaged portlet, checkout the sources and package it:
svn co https://svn.nuxeo.org/nuxeo/nuxeo-addons/nuxeo-portlets/trunk/nuxeo-portlet-search \
nuxeo-portlet-search
cd nuxeo-portlet-search
mvn package
cp target/nuxeo-portlet-search.war /opt/jahia/tomcat/webapps/jahia/WEB-INF/var/new_webapps
Before making any test, a Nuxeo EP should be running and you must configure the portlet for a Nuxeo EP:
• Attach a group or a user from Jahia (the one who will be considered as Administrator for the portlet) to
the Administrator role of the portlet.
• Then, go to the edit view of the portlet and fill the different fields, so that the portlet can communicate
with your Nuxeo EP.
This portlet provides the two search methods of Nuxeo EP, the simple one and the advanced one. They have the
same behavior than the ones in Nuxeo EP.
Packaged installers for those extensions are made available on the http://updates.nuxeo.org URL.
TODO: add here the list of remote interfaces to the FileManagerService API (REST, SOAP?).
TODO: give technical details on the WS protocol used to perform the file uploads.
The firefox plugin is packaged as a regular XPI and is platform-independent and uses the built-in Javascript
API of Mozilla to upload the content of the files as POST request to a RESTful web service URL.
TODO: give details on the remote RESTlets used for this implementation.
When saving changes, the new version of the file is automatically re-uploaded to the Nuxeo server through a
SOAP or RESTful web service to update the original document content and make the changes available to the
The version number can be incremented upon LiveEdit editing. A lock can be optionally set on the original
document so that two users do not overwrite each other changes in concurrent LiveEdit sessions.
• creation of a new Nuxeo document with an client-side generated attachment preinitialized with the copy
of an existing binary attachment stored elsewhere.
The following introduce the details of the 3 use cases implemented by the LiveEdit system along with the
technical components at work.
The user wants to a edit a non-empty Office file stored as a property of a Nuxeo document.
The user authenticate to Nuxeo (login/password or some implementation of SSO) with her web browser (IE or
Firefox) and browse her workspaces till the summary view of a document she has the rights to edit.
If the document has Blob storing property containing a non-empty office file with a mimetype flagged as live
editable (MS Office and OpenOffice related mimetypes), the web interface generate a link (marked 'edit
online') that automatically opens the right desktop application with the content of the attachment in opened in
the editing window.
Upon saving, the desktop editor opens popup leaving the user with the option either to save a local copy or the
save on the Nuxeo server. For the latter the user can also choose to increment the minor or major version
number or to overwrite the current version.
Upon completion the user can check by browsing back to her document that attachment has be updated with her
changes on the Nuxeo server.
The user wants to create a new Nuxeo document from scratch directly from an Office productivity application.
The user authenticate to Nuxeo (login/password or some implementation of SSO) with her web browser (IE or
Firefox) and has the possibility to click on a document creation menu with the following items:
• New Word document
Clicking one of those links automatically opens the right desktop application with a new empty document
opened in the editing window.
Upon saving, the desktop editor opens popup leaving the user with the option either to save a local copy or the
save on the Nuxeo server. For the latter the user has to choose among a flat list of candidate remote locations
selected are labeled with both a title (e.g. 'My Workspace') and a path to the location (e.g.
/default-domain/workspace/my-workspace )
Upon completion the user can check by browsing to her new document at the selected location. The file
attachment of that document has the content of the saved Office file.
The user wants to create a new Nuxeo document directly from an Office productivity application but reusing
the content of an attachment stored as a property of an existing Nuxeo document.
The user authenticate to Nuxeo (login/password or some implementation of SSO) with her web browser (IE or
Firefox) and browse her workspaces till reaching a document with a non-empty Office file attachment. The
view of that file carries a link labeled 'edit online as a new document' .
Clicking on that link opens the right desktop application preinitialized with a copy of the content of the
template document attachment.
Upon saving, the desktop editor opens popup leaving the user with the option either to save a local copy or the
save on the Nuxeo server. For the latter the user has to choose among a flat list of candidate remote locations
selected are labeled with both a title (e.g. 'My Workspace' ) and a path to the location (e.g.
/default-domain/workspace/my-workspace )
Upon completion the user can check by browsing to her new document at the selected location. The file
attachment of that document has the content of the saved Office file.
The LiveEdit feature is built upon a set of the following interacting components:
• webapp components to generate a link to a generated XML bootstrap file describing the file to edit
remotely along with connection parameters. Such a sample bootstrap file is provided in the technical
annexes of this specification file.
• a browser protocol handler (i.e. plugin for Internet Explorer or Firefox) that parses the xml bootstrap size
and launch the relevant desktop application according to the mimetype
• an editor plugin for each desktop application (MS Office, OpenOffice) to be able to make the desktop
application fetch the file from Nuxeo through SOAP or REST GET with connection parameters provided
in the bootstrap file and save it back to the server using SOAP or REST POST as well.
Before and after editing the document, the editor plugin can fetches a list of configurable actions to call
on the server through SOAP or REST GET (lock/unlock, check in/ check out, validate, etc.).
• a web service component (EJB3 stateful bean with JAXWS extensions) to implement the required
methods along with the WSDL definition need by the desktop client
The source code of the various LiveEdit client side components is available at:
https://svn.nuxeo.org/nuxeo/desktop-intergration/nuxeo-liveedit-* .
The Bootstrap module must intercept the click on the "online edit" link using a dedicated protocol handler
packaged as a browser plugin.
nxedit:http://localhost:8080/nuxeo/nxliveedit.faces?action=edit&repoID=[repoID]&docRef=[docRef]&schema=[
The protocol handler will be called by the OS/Browser and receive the URL. In turn it will receive the XML
bootstrap file.
• nxedit:http://localhost:8080/nuxeo/nxliveedit.faces?action=create&repoID=[repoID]&mimetype=[mime
• user case#3: docid and field path of the original blob AND doctype and fieldpath to of the document that
will host the result:
• nxedit:http://localhost:8080/nuxeo/nxliveedit.faces?action=createFromTemplate&templateRepoID=[te
The Bootstrap client module rewrites each of those URIs as valid HTTP GET by swapping the prefix:
• http://localhost:8080/nuxeo/nxliveedit.faces?[query_parameters]
The Bootstrap client protocol must strip the "nxedit:" prefix of the URI to get the HTTP URL and thus work
either with SSL or not:
• nxedit:http://localhost:8080/nuxeo/nxliveedit.faces?[query_parameters]
while:
• nxedit:https://localhost:8080/nuxeo/nxliveedit.faces?[query_parameters]
In order to generate valid nxedit: URIs it is strongly advised to use the JSF functions defined in the
org.nuxeo.ecm.platform.ui.web.tag.fn.DocumentModelFunctions class.
• liveEditUrl(DocumentModel, String, String, String) : get the nxedit: URI to edit a document
providing schema, blob field and filename field names
• liveCreateUrl(String) : get the nxedit: URI to create a new document of type File providing the
mimetype as argument
• liveCreateUrl(String, String, String, String, String) : get the nxedit: URI to create a new
document with parameters: mimetype, doctype, schema, blob and filename field names
The Bootstrap server module will be a simple Seam component called using the info passed as request
parameters to generate the XML payload of the bootstrap file.
The HTTP response to that GET URL is a bootstrap file containing an XML payload. This file contains:
• the action ID so the client knows to interpret it
• repo name
• document unique reference (in case of editing) or template reference (new from template)
• associated filename
• associated mime-type
• principal name
• whether the result of this editing session should be saved as a new Nuxeo document else where in the
repository (creation use cases)
• the fieldpath hosting the binary attachment to initialized the editor with
The XML payload further contain a copy of all the HTTP request headers and cookies + basic auth credentials
and the adress of the WSDL description of the LiveEdit webservice.
Please refer to the sample XML bootstrap file in the annexes for more details on the syntax. Some fields (eg.
document reference) might be empty or missing in case of document creation (use cases #2 and #3).
The bootstrap module receives and parses the content of the XML bootstrap file. According to a set of
configurable rules the bootstrap module launch the right editor with bootstrap file as command line parameter.
The Bootstrap client will need to do an http call to get the xml file from the server. This call must be
authenticated. So the protocol handler must reuse the browser session.
All WS requests (SOAP and RESTful) from the editor plugin back to the WS server should reuse all the HTTP
cookies along with any basic auth parameters to ensure the request will pass through any authenticating reverse
proxies (e.g. CAS, mod_sso, ...) as if they were the original browser.
In particular the list of candidate locations to 'save as new document' is provided by the WS server-side API to
the LiveEdit client. The list should default to the list of Workspaces the user currently has the "AddChildren"
permission. The WS server should dynamically compute that list according to an extensible service (i.e.
overridable by a extension point) so that customer project can register a custom Java class that is responsible to
implement the custom business logics in case the list of workspaces is not enough.
The location selected by default should also be defined on the server side and overridable by the same
extension point.
This is very important that bootstrap client can be separated from the editors plugins, because there will plugins
contributed for specific editors. The simplest and most neutral way of launching an editor plugin is just
executing the editor plugin passing it a copy of the bootstrap file. This file will be the same as the one returned
by Nuxeo server with additional authentication information: cookies and Login/Password.
So the idea is that the WebService will provide the client editor plugin a simple list of actions, the client will
simply display available actions, and eventually ask the server to execute them without knowing the underlying
logic.
This "action logic" is somehow close to what we already do in the web layer, an action defines an action id and
a label.
If several actions can be done (like checkout + lock), then they will be combined as compound actions:
checkout, lock, checkout_and_lock. This way we won't have to handle associations conditions on the client
side: the user can always select at most one action: none / lock / checkout / checkout_and_lock
This means you can define on what kind of documents the liveEdit links will be displayed.
The client browser tells the server what kind of documents can be "live edited".
Links are only displayed on mime-types that are configured on the server to be live editable.
• Mixed configuration
Links are only when server and client policy both apply.
For that the firefox and msie plugins add the live editable mime-types to the accept header sent by the browser.
This way, the server can decide on what type of file the liveedit link must be displayed. If you use Firefox, just
upgrade to the last version and use the configuration pannel to tell on what mime-types you want liveedit links.
The main advantage of this policy is that only the users that have LiveEdit plugin installed will see the liveEdit
links.
The accept header send by a LiveEdit enabled client looks like that :
...,application/x-nuxeo-liveedit;application/vnd.oasis.opendocument.text;application/vnd.oasis.opendocument.pres
In this case the server will only display the LiveEdit links for files that have a mime-types that is declared
"LiveEditable" in the MimeTypesRegistry service.
Simply display links on mimetypes that are livededitable on the client side and on the server.
Inside Nuxeo 5, the path (id) and the display name (title) are clearly separated:
• id/path is generated from the title but is not equal: non standard characters are escaped and length is
limited to avoid having too long URLs.
Foe example, a folder with title "New Folder" will have "new-folder" as a name.
• The name has to be unique within a given container, but 2 documents can have the same title.
For a complete bug listing of Web Folders depending on version please see
http://www.greenbytes.de/tech/webdav/webdavfaq.html,
http://www.greenbytes.de/tech/webdav/webfolder-client-list.html,
http://www.greenbytes.de/tech/webdav/webdav-redirector-list.html
This hacks can be activated of not depending on the User-Agent header provided by each client.
Because most client uses the last part of the URL to get the displayName, a simple hack to have
resources displayed correctly is to have a specific URL for them.
Getting the good displayName for collection resources is a little bit harder, previous hacks won't work.
In some webdav client, the url parsing is so weak, that we can use URLs like :
http://nxServer/nuxeo/dav/default/default-domain/workspaces/my-workspace-1?displayName=/My%20Workspace%
to have the collection correctly displayed as "My Workspace 1". Although this hack does not work with
most clients, it works with most Web Folders version.
• If your client needs MS specific DAV Headers (required for some versions of Web Folders)
Configuration can be contributed using a simple Extension Point.
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.platform.webdav.config.defaultContrib">
<extension target="org.nuxeo.ecm.platform.webdav.config.WebDavConfigurationService" point="DavClientConfigurat
<documentation>
Configuration for MS Web Folders (both versions) and WebDrive.
</documentation>
<davClientConfiguration name="MSWebFolders" enabled="true">
<needGetParameterForCollectionNamming>true</needGetParameterForCollectionNamming>
<needVirtualPathForLief>false</needVirtualPathForLief>
<useFileNameAsRessourceName>true</useFileNameAsRessourceName>
<needFullURLs>true</needFullURLs>
<needMSDavHeader>true</needMSDavHeader>
<userAgentPatterns>
<pattern>Microsoft-WebDAV</pattern>
<pattern>Microsoft Data Access Internet Publishing Provider</pattern>
</userAgentPatterns>
</davClientConfiguration>
...
As you can see, each configuration set is attached to one or more User-Agent substring. Unfortunately, Web
Folders clients do not send information about their version, even if it would be very helpful.
When URLs hack are activated, the clients will be able to display the resources with their displayName (title)
instead of the name. But some of them will be fooled enough to use this displayName in URLs to do propfind
or move calls. Because of that, the server must be able to resolve URLs that are constitued partially of path and
partially of displayNames like:
http://nxServer/nuxeo/dav/default/default-domain/workspaces/My%20Workspace%201.
The Nuxeo EP WebDAV connector implements a custom URL resolver that is able to resolve these URLs. In
also maintains a cache of these virtual URLs to speed up resolution (and also make it more consistent is case of
name collisions).
Displaying your Nuxeo documents as simple files (that's what will do most WebDAV client) can be very
restrictive.
The Nuxeo WebDAV connector uses DocumentModel adapters to define how a Nuxeo Document must be
mapped to a WebDAV resources. This adapter defines:
• How the display name of your resource will be generated.
The default built-in behavior will be to return the filename for each document that has the file schema
and otherwise return the title.
• the binary file for Documents that uses the file schema
New DavAdapters can be contributed to define specific WebDAV mapping for your document types. For that,
the Nuxeo WebDAV connector provides an extension point to let you register a new class
implementingorg.nuxeo.ecm.platform.webdav.adapters.DavResourceAdapter and associate it to a
document type.
<extension target="org.nuxeo.ecm.platform.webdav.config.WebDavConfigurationService"
point="DavAdapter">
<davAdapter name="NoteDavAdapter" enabled="true">
<typeName>Note</typeName>
class>org.nuxeo.ecm.platform.webdav.adapters.NoteDavResourceAdapter</class>
</davAdapter>
</extension>
Propset is for now not implemented as none of the used WebDAV client seems to use this method.
In order to install the WebDAV features, you need to download the WebDAV Connector jar (or build it from
source), copy the jar in nuxeo.ear/system and restart your server.
The WebDAV connector works for both 5.1 and 5.2 versions of Nuxeo EP, you just need to download the
associated version or build it using the right POM file.
In order to use MS Web Folders, you just need to go to "My Network Places" and choose "Add a new Network
place". You can then enter the Nuxeo WebDAV URL and login/password.
40.1. Overview
BIRT is an open source Eclipse-based reporting system that integrates with Java/J2EE application to produce
compelling reports. The BIRT driver for Nuxeo enables BIRT to be used as reporting engine for the Nuxeo
Content Repository. It basically gives an easy way to query the repository and create report from the results.
Thanks to BIRT, reports can be run inside Eclipse or as servlets on the server side.
• org.nuxeo.ecm.client
• org.nuxeo.ecm.core.api
• org.nuxeo.ecm.core.query
• org.nuxeo.ecm.core.schema
• org.nuxeo.ecm.jboss_connector
• org.nuxeo.ecm.platform.search.api
• org.nuxeo.ecm.platform.usermanager.api
• org.nuxeo.logging
• org.nuxeo.runtime
• org.nuxeo.runtime.config
• org.nuxeo.birt.oda.nuxeoep
• org.nuxeo.birt.oda.nxueoep.ui
Figure 40.1. In the Data Source Type screen, select "Nuxeo Data Source":
Figure 40.3. In the Data Set dialog, type NXQL query and select fields & schemas you would like to use
in the report
Figure 40.5.
Figure 40.6.
So the numbers below are given for the one needed high-end server.
• RAM: 2Gb is the minimum requirement for using Nuxeo EP
You might be better avoiding machines from the Intel Pentium 4 Xeon series since some models have a
too small amount of cache. This impairs performance greatly compared to other CPU architecture of the
same generation. Intel Pentium 4 servers are quite widespread because of an attractive price politic.
• Storage (disk) space: the minimum Nuxeo installation, along with the needed JBoss and libs, takes
something between 200Mb and 250Mb on a filesystem. Then the final size will of course depend on the
amount of data that will be stored in Nuxeo. A safe bet (until we provide better numbers) is to consider
data space ratio of 1.5 to 2.
• Linux
• PostgreSQL 8.2
Use PostgreSQL for document repository and all other services except for Compass search engine
(nxsearch-compass-ds.xml) which should be set to use filesystem.
41.3.1. OS
• Debian GNU/Linux 5.0 Lenny
• Unix
41.3.2. JVM
• Sun JDK 1.5 update 14, 15, 16
• PostgreSQL 8.3
This version need a workaround to be applied as it is much stricter than PostgreSQL 8.2 with respect to
value casting.
• MySQL
• Oracle 10
• MsSQL 2005
• HSQL
• Derby
41.3.4. LDAP
• OpenLDAP
• Ms Active directory
• configure the Nuxeo Core storage, modifying the default repository configuration,
• You may also add a new repository configuration (and optionally disable the old one).
Each type of data has its own storage engine and can be configured separately. All storages can use RDBMS
but some of them can also use the filesystem.
This means you can have a RDBMS only configuration or a mixed configuration using RDBMS and
filesystem. You can even use several RDBMS if you find a use case for that.
For a lot of services, RDBMS access are encapsulated by JPA or hibernate calls, so the storage should be
RDBMS agnostic as long as there is a hibernate dialect for the DB.
This documentation will use PostgreSQL 8.x as an example. The setup for other RDBMS should be very
similar.
<?xml version="1.0"?>
<component name="default-repository-config">
<documentation>
Defines the default JackRabbit repository used for development and testing.
</documentation>
<extension target="org.nuxeo.ecm.core.repository.RepositoryService"
point="repository">
<documentation>
Declare a JackRabbit repository to be used for development and tests. The
extension content is the Jackrabbit XML configuration of the repository.
</documentation>
<repository name="default"
factory="org.nuxeo.ecm.core.repository.jcr.JCRRepositoryFactory"
securityManager="org.nuxeo.ecm.core.repository.jcr.JCRSecurityManager"
forceReloadTypes="false">
<Repository>
<!--
virtual file system where the repository stores global state
(e.g. registered namespaces, custom node types, etc.)
-->
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
<param name="path" value="${rep.home}/repository" />
</FileSystem>
<!--
security configuration
-->
<Security appName="Jackrabbit">
<!--
access manager:
class: FQN of class implementing the AccessManager interface
-->
<AccessManager class="org.apache.jackrabbit.core.security.SimpleAccessManager">
<!-- <param name="config" value="${rep.home}/access.xml"/> -->
</AccessManager>
<LoginModule class="org.apache.jackrabbit.core.security.SimpleLoginModule">
<!-- anonymous user name ('anonymous' is the default value) -->
<param name="anonymousId" value="anonymous" />
<!--
default user name to be used instead of the anonymous user
when no login credentials are provided (unset by default)
-->
<!-- <param name="defaultUserId" value="superuser"/> -->
</LoginModule>
</Security>
<!--
location of workspaces root directory and name of default workspace
-->
<Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default" />
<!--
workspace configuration template:
used to create the initial workspace if there's no workspace yet
-->
<Workspace name="${wsp.name}">
<!--
virtual file system of the workspace:
class: FQN of class implementing the FileSystem interface
-->
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
<param name="path" value="${wsp.home}" />
</FileSystem>
<!--
persistence manager of the workspace:
class: FQN of class implementing the PersistenceManager interface
-->
<PersistenceManager class="org.apache.jackrabbit.core.state.obj.ObjectPersistenceManager">
</PersistenceManager>
<!--
Search index and the file system it uses.
class: FQN of class implementing the QueryHandler interface
-->
<SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
<param name="path" value="${wsp.home}/index" />
</SearchIndex>
</Workspace>
<!--
Configures the versioning
-->
<Versioning rootPath="${rep.home}/version">
<!--
Configures the filesystem to use for versioning for the respective
persistence manager
-->
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
<param name="path" value="${rep.home}/version" />
</FileSystem>
<!--
Configures the persistence manager to be used for persisting version state.
Please note that the current versioning implementation is based on
a 'normal' persistence manager, but this could change in future
implementations.
-->
<PersistenceManager class="org.apache.jackrabbit.core.state.obj.ObjectPersistenceManager">
</PersistenceManager>
</Versioning>
<!--
Search index for content that is shared repository wide
(/jcr:system tree, contains mainly versions)
-->
<SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
<param name="path" value="${rep.home}/repository/index" />
</SearchIndex>
</Repository>
</repository>
</extension>
</component>
Change the two PersistenceManager sections defining various database connection settings.
Refer to the Jackrabbit documentation for more information, and to the Jackrabbit Javadoc for details about
configuring the PersistenceManager.
In particular, decide if you want the binary blobs stored inside the database or in the filesystem (change
externalBLOBs to true if you want them outside the database, in the filesystem).
Using externalized Blobs can provide a performance improvement in particular if you need to store a lot of big
files. Depending on the RDBMS used, there may also be a max size limit for blob if you store them in the
RDBMS (for PostgeSQL 8.2 blobs are limited to 1 GB).
<!-- Workspaces configuration. Nuxeo only uses the default workspace. -->
(...)
<PersistenceManager class="org.apache.jackrabbit.core.persistence.bundle.PostgreSQLPersistenceManager"
<param name="driver" value="org.postgresql.Driver"/>
<param name="url" value="jdbc:postgresql://localhost/nuxeo"/>
<param name="user" value="postgres"/>
<param name="password" value="password"/>
<param name="schema" value="postgresql"/>
<param name="schemaObjectPrefix" value="jcr_${wsp.name}_"/>
<param name="externalBLOBs" value="false"/>
</PersistenceManager>
(...)
<!-- Versioning configuration. -->
(...)
<PersistenceManager class="org.apache.jackrabbit.core.persistence.bundle.PostgreSQLPersistenceManager"
<param name="driver" value="org.postgresql.Driver"/>
<param name="url" value="jdbc:postgresql://localhost/nuxeo"/>
<param name="user" value="postgres"/>
<param name="password" value="password"/>
<param name="schema" value="postgresql"/>
<param name="schemaObjectPrefix" value="jcr_ver_"/>
<param name="externalBLOBs" value="false"/>
</PersistenceManager>
<!-- Workspaces configuration. Nuxeo only uses the default workspace. -->
(...)
<PersistenceManager class="org.apache.jackrabbit.core.persistence.bundle.MySqlPersistenceManager">
<param name="driver" value="com.mysql.jdbc.Driver"/>
<param name="url" value="jdbc:mysql://localhost/nuxeo"/>
<param name="user" value="mysql"/>
<param name="password" value="password"/>
<param name="schema" value="mysql"/>
<param name="schemaObjectPrefix" value="jcr_${wsp.name}_"/>
<param name="externalBLOBs" value="true"/>
</PersistenceManager>
(...)
<!-- Versioning configuration. -->
(...)
<PersistenceManager class="org.apache.jackrabbit.core.persistence.bundle.MySqlPersistenceManager">
<param name="driver" value="com.mysql.jdbc.Driver"/>
<param name="url" value="jdbc:mysql://localhost/nuxeo"/>
<param name="user" value="mysql"/>
<param name="password" value="password"/>
<param name="schema" value="mysql"/>
<param name="schemaObjectPrefix" value="jcr_ver_"/>
<param name="externalBLOBs" value="true"/>
</PersistenceManager>
For JackRabbit, there are some persistence manager specific to each RDBMS :
• for PostgreSQL: you may use
org.apache.jackrabbit.core.persistence.bundle.PostgreSQLPersistenceManager
$JBOSS_HOME/server/default/deploy/nuxeo.ear/datasources/unified-nuxeo-ds.xml.
To use a dedicated datasource for a service, you need to modify its configuration file. If you would like to store
audit logs in PostgreSQL using its own datasource, remove the NamingAlias in nxaudit-logs-ds.xml and
replace it with the datasource configuration example:
<?xml version="1.0"?>
<datasources>
<local-tx-datasource>
<jndi-name>nxaudit-logs</jndi-name>
<connection-url>jdbc:postgresql://localhost/logs</connection-url>
<driver-class>org.postgresql.Driver</driver-class>
<user-name>username</user-name>
<password>password</password>
</local-tx-datasource>
</datasources>
We recommend to enable XA transactions if your database server support it (for PostgreSQL, you have to use
8.x versions). The following datasource definition example enables XA transaction for the Audit Service using
PostgreSQL.
Example 43.5. Datasource for the Audit Service using PostgreSQL with XA transactions
<?xml version="1.0"?>
<datasources>
<xa-datasource>
<jndi-name>nxaudit-logs</jndi-name>
<xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
<xa-datasource-property name="ServerName">localhost</xa-datasource-property>
<xa-datasource-property name="PortNumber">5432</xa-datasource-property>
<xa-datasource-property name="DatabaseName">logs</xa-datasource-property>
<xa-datasource-property name="User">postgres</xa-datasource-property>
<xa-datasource-property name="Password">password</xa-datasource-property>
<track-connection-by-tx/>
</xa-datasource>
</datasources>
See Datasources Configuration on the JBoss wiki for more examples of datasources.
• Relations: nxrelations-default-jena-ds.xml
MySQL is a quirky database which sometimes needs very specific options to function properly.
<connection-url>
jdbc:mysql://localhost/nuxeo?relaxAutoCommit=true
</connection-url>
The datasource used by Compass (nxsearch-compass-ds.xml) needs to be put in "relax autocommit" too, and
in addition it needs an "emulate locators" option:
<connection-url>
jdbc:mysql://localhost/nuxeo?relaxAutoCommit=true&emulateLocators=true
</connection-url>
Note the & syntax for the URL parameters instead of just & because the URL is embedded in an XML file.
• HSQL
• MsSQL
• MySQL
• Oracle
• PostgreSQL
Please refer to the Jena Site for more information about database support.
The value of the above properties are used as variables by the extension point defining the Jena configuration,
so that they only have to be changed in one place.
<connection>
<jdbc managed="true"
dialectClass="org.apache.lucene.store.jdbc.dialect.HSQLDialect"
deleteMarkDeletedDelta="3600000">
<dataSourceProvider>
<jndi lookup="java:/nxsearch-compass" />
</dataSourceProvider>
</jdbc>
</connection>
The dialectClass has to be changed according to your datasource configuration. The possible values end with
MySQLDialect, PostgreSQLDialect , etc. They are documented in the Compass documention about SQL
dialects .
• default-repository-config.xml
• platform-config.xml
TODO: Nuxeo configuration has changed, the two above sections need to be updated.
• the name of the repository (<repository name="MyRepo">), which is used to refer to it from your
application and must also be unique among repository names,
• the various database connection settings (driver, user, password, schema, etc.),
• decide if you want the binary blobs stored inside the database or in the filesystem (change
externalBLOBs to true if you want them outside the database).
Refer to the Jackrabbit documentation for more information, and to the Jackrabbit Javadoc for details about
configuring the BundleDbPersistenceManager.
<?xml version="1.0"?>
<component name="org.nuxeo.project.sample.repository.MyRepo">
<extension target="org.nuxeo.ecm.core.repository.RepositoryService" point="repository">
<repository name="MyRepo"
factory="org.nuxeo.ecm.core.repository.jcr.JCRRepositoryFactory"
securityManager="org.nuxeo.ecm.core.repository.jcr.JCRSecurityManager"
forceReloadTypes="false">
<Repository>
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
<param name="path" value="${rep.home}/repository"/>
</FileSystem>
<Security appName="Jackrabbit">
<AccessManager class="org.apache.jackrabbit.core.security.SimpleAccessManager">
</AccessManager>
<LoginModule class="org.apache.jackrabbit.core.security.SimpleLoginModule">
<param name="anonymousId" value="anonymous"/>
</LoginModule>
</Security>
<!-- Workspaces configuration. Nuxeo only uses the default workspace. -->
<Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default"/>
<Workspace name="${wsp.name}">
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
<param name="path" value="${wsp.home}"/>
</FileSystem>
<PersistenceManager class="org.apache.jackrabbit.core.persistence.bundle.PostgreSQLPersistenceManager"
<param name="driver" value="org.postgresql.Driver"/>
<param name="url" value="jdbc:postgresql://localhost/nuxeo"/>
<param name="user" value="postgres"/>
<param name="password" value="password"/>
<param name="schema" value="postgresql"/>
<param name="schemaObjectPrefix" value="jcr_${wsp.name}_"/>
<param name="externalBLOBs" value="false"/>
</PersistenceManager>
<SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
<param name="path" value="${wsp.home}/index"/>
</SearchIndex>
</Workspace>
<!-- Versioning configuration. -->
<Versioning rootPath="${rep.home}/version">
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
<param name="path" value="${rep.home}/version"/>
</FileSystem>
<PersistenceManager class="org.apache.jackrabbit.core.persistence.bundle.PostgreSQLPersistenceManager"
<param name="driver" value="org.postgresql.Driver"/>
<param name="url" value="jdbc:postgresql://localhost/nuxeo"/>
<param name="user" value="postgres"/>
<param name="password" value="password"/>
<param name="schema" value="postgresql"/>
<param name="schemaObjectPrefix" value="jcr_ver_"/>
<param name="externalBLOBs" value="false"/>
</PersistenceManager>
</Versioning>
<!-- Index for repository-wide information, mainly versions. -->
<SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
<param name="path" value="${rep.home}/repository/index"/>
</SearchIndex>
</Repository>
</repository>
</extension>
</component>
You have now replaced the default repository (demo) with your newly defined one (MyRepo). To actually use it,
create or edit the file MyPlatform-Layout-config.xml in
$JBOSS_HOME/server/default/deploy/nuxeo.ear/config/ and configure the parameters as shown in the
following example.
<?xml version="1.0"?>
<component name="MyPlatformLayout">
<require>org.nuxeo.ecm.platform.api.DefaultPlatform</require>
<extension target="org.nuxeo.ecm.platform.util.LocationManagerService" point="location">
<locationManagerPlugin> <!-- This disable the default (demo) repository -->
<locationEnabled>false</locationEnabled>
<locationName>demo</locationName>
</locationManagerPlugin>
<locationManagerPlugin> <!-- This enable your new repository -->
<locationEnabled>true</locationEnabled>
<locationName>MyRepo</locationName> <!-- Use the name of your repository -->
</locationManagerPlugin>
</extension>
<extension target="org.nuxeo.ecm.core.api.repository.RepositoryManager"
point="repositories">
<documentation>The default repository</documentation>
<repository name="MyRepo" label="My Repository"/>
</extension>
This sample configuration creates a new repository in the core Group that will be assigned to the default server.
If you want to have it located on an other server you can use:
45.1. Installation
See http://www.openoffice.org/ for installation procedure if not provided by your OS.
45.1.2. Parameters
45.1.2.1. -headless
The -headless switch lets OOo manage to answer every question that may occur with a default response
avoiding dead-locks. It also hides the OOo windows if a display is available by default (eg. Ms Windows).
Note that the -invisible switch is not used as it is redundant and errors on PDF exports may occur.
45.1.2.2. -nofirststartwizard
The -nofirststartwizard skips any post-install wizard, allowing to use OOo directly after the installation
without any user parameterization.
45.1.2.3. -accept
The UNO protocol implies that OOo is opened in listening mode on a network interface. This parameter let OOo
listen on the right interface and port.
Alternate method: this could also be done by adding this connection info in OOo Setup.xcu configuration file.
<node oor:name="Office">
<prop oor:name="ooSetupConnectionURL">
<value>socket,host=localhost,port=8100;urp;StarOffice.Service</value>
</prop>
</node>
45.1.4. Notes
45.1.4.1. Multi-machine
On multi-machine deployment, we recommend to install OOo on the server running the webapp (ie stateless
server on a bi-machine "stateless/stateful" installation).
For OOo versions lower than 2.3, a graphical interface is needed. If the server that hosts OOo is Linux server
that has no X installed, then the X virtual frame buffer tool Xvfb (or xvfb-run depending of your distribution)
can be used to create a suitable display.
Note that the platform used for both the JVM and OOo have to be consistent to allow UNO protocol to work:
with a 32 bits JVM, you'll have to use the 32 bits OOo version while the 64 bits one will be mandatory for a 64
bits JVM.
• in $JBOSS_HOME/server/default/deploy/nuxeo.ear/config/platform-config.xml
multi-server: either use IP or set DNS aliases in your hosts file (default names in Nuxeo packages are
usually something like nxcoreserver, nxsearchserver, nxplatformserver, nxjmsserver, nxwebserver,
nxdbserver).
• in $JBOSS_HOME/server/default/deploy/nuxeo.ear/config/datasources/core-events-ds.xml
replace, if enabled, the 127.0.0.1 value of java.naming.provider.url by the IP address of the Jboss
server,
./run.sh -b server_IP_address
For obvious security reasons, JBoss 4.2 (version required by Nuxeo EP 5.2) has a different
behavior. If you still want this, use -b 0.0.0.0
47.1. Backup
Important
It is highly recommended to stop nuxeo during backup.
If you use a database, you may switch it in a "hot backup" mode to ensure that all new incoming
requests will wait the end of the backup process.
$JBOSS/server/default/lib/nuxeo*
$JBOSS/server/default/deploy/nuxeo.ear/
$JBOSS/server/default/data/
$JBOSS/server/default/log/
Finally, backup any file you have customized. Here is a short list of such files :
• if you changed JVM startup parameters (run.bat, run.conf) or use Nuxeo's startup script (jbossctl,
jbossctl.conf, bind.conf)
$JBOSS/server/bin
$JBOSS/server/default/conf
$JBOSS/server/default/lib
$JBOSS/server/default/deploy
If you plan an upgrade, you may backup separately the configuration files in order to easily apply again your
configuration on the default one (take care not to loose any evolution on these files):
• Main configuration is in $JBOSS/server/default/deploy/nuxeo.ear/config
47.3. Reset
You can simply reset Nuxeo by removing its data: delete $JBOSS/server/default/data/ and empty all used
databases. On start, Nuxeo EP recreates all its tables, files and directories.
47.4. Restore
All you need is to restore previously saved database(s), files and directories.
48.1. Overview
The Nuxeo Shell is a command line tool for everyone who needs simple and quick remote access to a Nuxeo
EP server. You can see it as the swiss army knife for the Nuxeo EP world. It can be used by regular users to
browse or modify content, by advanced users to administrate nuxeo servers, or by programmers to test and
debug.
The Nuxeo Shell is designed as an intuitive and extensible command line application. It can both serve as a
user, administrator or programmer tool, or as a framework to develop new Nuxeo command line clients.
The application is based on the Nuxeo Runtime framework and thus offers the same extension point mechanism
you can find on the server application.
• auto-completion
• command history
• JSR223 scripting integration. You can thus connect and execute commands on a Nuxeo server in pure
scripting mode.
The advanced command line handling is done using the JLine library.
The only requirement is Java 5+. The tool works on any Unix-like OS, on Windows and on Mac OS X.
• The lib folder contains JARs needed by the application launcher. The Launcher.class is the application
launcher and launcher.properties contains configuration for the launcher.
• app/bundles contains the application OSGi bundles. These bundles will be deployed using a minimal
implementation of OSGi (the same that is used on the server side). We may replace the default
implementation by equinox later.
The only file you may need to change is the nxshell.sh script. Here is the content of this file:
#!/bin/bash
#JAVA_OPTS="$JAVA_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=127.0.0.1:8788,server=y,suspend=y"
JAVA_OPTS="$JAVA_OPTS -Dorg.nuxeo.runtime.1.3.3.streaming.port=3233"
java $JAVA_OPTS Launcher launcher.properties $@
If you uncomment the first line, you will be able to run Nuxeo Shell in debug mode.
The second line [must] be commented out if on the server you are running on a nuxeo runtime
1.4.SNAPSHOT. When running against Nuxeo EP 5.1 you need to let this uncommented.
2. In interactive mode - in this mode you can share a single session to run multiple commands against the
remote Nuxeo EP. To start the application in that mode you should run the "interactive" command as
follows:
./nxshell.sh interactive
After entering in interactive mode a command prompt will be displayed. At this prompt you can enter
commands in the same way you specify them on the command line in batch mode.
|>
After connecting to a server named, let's say "nuxeo-platform", the prompt will be
displayed as:
|nuxeo-platform>
So, as we've seen in the above examples, executing a command is done by using the the following syntax:
where options and parameters are optional and are specific to the command you run.
Example:
In this case "import" is the command, "-u" is a flag option and "path/to/doc" and "/path/to/local_file" are both
parameters
Parameters are stand alone arguments (they are not bound to a specific option) and can be retrieved
programatically using their index. In the example above, the first parameter will have the index 0 while the
second the index 1.
Options may take values or may be used as flags turning on / off a specific option by their simple presence.
When using long names you should specify the option values immediately after the option. However when
using short names you can group options together. Let say for example we have a command that support 4
options: a, v, i, o. a and v are flag options and both i and o takes an argument as value. In this case the you can
group options like the following:
or
command -avio in_file out_file
or anyhow you want. You should keep in mind that when grouping options that take arguments, these
arguments will be assigned to each of this options in the order they were specified on the command line.
Besides the specific options that commands may define, there are several global options that apply to all
commands. These are:
• host (--host or -h) the Nuxeo EP host where to connect - defaults to localhost
• port (--port or -p) the Nuxeo EP port where to connect - defaults to 62474
• username (--username or -u) the username to use - defaults to the "system" user
48.2.2. Commands
There is the list of all built-in commands of nuxeo shell.
Notes:
1. Many of these built-in commands are not yet implemented at the time of writing this document. They
will be marked with an asterisk (*).
• offline commands - command that doesn't need a connection to work. Example: help, log etc.
• online commands - commands that requires a connection to work. These commands are automatically
connecting if no connection is currently established.
3. Some commands make no sense and are not available in both modes (batch and interactive). This will be
specified by each command if it is the case.
48.2.2.1. interactive
Runs in the interactive mode. This command is not available when already in interactive mode.
./nxshell.sh interactive
48.2.2.2. help
By default, displays the global help page. If a command is given as an argument, displays the help page for that
command.
./nxshell.sh help ls
48.2.2.3. log *
48.2.2.4. connect *
48.2.2.5. disconnect *
Disconnects from the current connected server. If no connection exists, does nothing.
48.2.2.6. ls
Note that the ls directory-name command is accepted but won't list the content of directory-name, it will
only list the content of the current directory. This might be improved in the future to provide a behavior alike
the one of the Unix ls command.
48.2.2.7. tree
Displays the complete tree structure of the documents as it would be returned by the Unix tree command.
48.2.2.8. cd
48.2.2.9. pwd
48.2.2.10. view
Views info about document. The information displayed can be controlled using these command options:
48.2.2.11. rm
48.2.2.12. mkdir
48.2.2.13. put
Uploads a blob to a file document. If the target document doesn't exists, creates it.
48.2.2.14. putx
Creates a document other than a file. Metadata (and blobs) are specified in the Nuxeo export format.
48.2.2.15. get
48.2.2.16. getx
Gets a document as an XML file (as export, but only for a document).
48.2.2.17. export
48.2.2.18. import
48.2.2.19. script
Makes it possible to run external scripts. Here are examples of such scripts.
48.2.2.20. chperm *
48.2.2.21. adduser *
48.2.2.22. addgroup *
48.2.2.23. select
Search the repository using the NXQL query language. For example:
This command can be used as a starting point to write another script to perform search service queries if
needed.
48.2.2.24. lstypes *
48.2.2.25. viewtype *
48.2.2.26. lsusers *
48.2.2.27. lsgroups *
48.2.2.28. viewuser *
48.2.2.29. viewgroup *
48.3. Troubleshooting
192.0.0.10> view
--------------------------------------------------------------------
UID: 2a13db70-f133-473c-9a90-6838d01610aa
Path: /
Type: Root
--------------------------------------------------------------------
Title: 2a13db70-f133-473c-9a90-6838d01610aa
Author: null
Created: null
Last Modified: null
--------------------------------------------------------------------
Description: 2a13db70-f133-473c-9a90-6838d01610aa
--------------------------------------------------------------------
49.1. Introduction
"Follow a common coding standard, so that all the code in the system looks as if it was written
by a single — very competent — individual. The specifics of the standard are not important:
what is important is that all the code looks familiar, in support of collective ownership."
—Ron Jeffries XProgramming.com
Coding Standards are a good idea. Every team should adopt a coding style and standard, and
stick to it. The code produced by that team should have a consistent look and feel that is devoid
of individual preferences and fetishes.
—"Uncle" Bob Martin ObjectMentor website
The primary goal of this chapter is to provide common conventions, as well as guidelines that aim at ensuring a
high level of quality (with respect to maintainability, extensibility, modularity, security, testability,
documentation, etc.) throughout the Nuxeo project.
As such, it is primarily written for the platform's developers, both "core" developers and contributors, and
developers that write extension modules that will someday find a place in the Nuxeo codebase.
If you're working on your own project with no plan to contribute code to the Nuxeo platform, you will probably
be still interested in reading this chapter, and adapt the conventions stated here to your company's or
organization's own coding standards.
2. propose default settings for common Eclipse tools (including the built-in code formatter and the
CheckStyle plugin), tuned to comply to these conventions.
The official coding standard for the project is: "The Elements of Java Style" [Vermeulen2000] which is a little
book that can be bought from Amazon or other book dealers.
If a PDF suits you better, you can download an earlier version of the book from here.
Note however that these guidelines have been written in 2000 (last century!) and some of the recommendations
need the be updated in light of this millenium's experience.
There are also architectural guidelines that need to be followed. At this moment, they are written here (section 4
- "Guidelines")
Java code should be formatted using Sun's Code Conventions for the Java Programming Language.
Regarding code formatting (which is just one small part of the book and PDF mentioned above), we'll be using
the standard Eclipse default style, with a couple of minor tweaks (see this README.txt for how to configure
Eclipse to support this style).
• spaces after ,
• regarding code block, we are using 1TBS ("One True Brace") style:
Bad:
if (xxx)
{
yyy
}
Good:
if (xxx) {
yyy
}
Bad:
if someThing() doSomething;
if someThing()
doSomething;
Good:
if someTest() {
doSomething;
}
• Don't prefix your instance variables by "this." when it's not mandatory.
Why? Because with modern IDEs, instance variables are typeset in a different color than local variables,
hence you don't need the extra information provided by the "this." prefix to recognize which is which.)
• etc.
Badly formatted XML code can be reformatted using the Eclipse formatter.
Always check that the result is better than the original, and tweak afterwards if needed.
49.3.3. Design
1. Use interfaces every time it is possible instead of concrete classes.
Why? Using interfaces makes it possible to use several different implementations. Actually, interfaces
are the basis of component-oriented programming.
Why? The more code we have, the more expensive maintainance is.
Why? This is a common design principle, called the "Acyclic Dependency Principle" or "ADP".
How? use JDepend or a JDepend plugin for you IDE (ex: http://andrei.gmxhome.de/jdepend4eclipse/) or
look at the JDepend report by Maven.
More info:
• Uncle Bob Martin's "Design Principles and Design Patterns" (pages 18-22, "The Acyclic
Dependencies Principle (ADP)")
How? Learn how to use JUnit. Use tests in existing modules as examples. Remember that well-tested
modules have at least as many lines of test code than not test code.
2. Check that your unit tests have a coverage of most of the critical parts of your code.
How? Look at the Cobertura report by Maven, or use the TPTP or EMMA (http://www.eclemma.org/)
plugins for Eclipse or use the native code-coverage function of IDEA.
49.3.5. Security
1. Read Graff and van Vyk's "Secure Coding" [Graff2003] book. More info on the securecoding.org
website.
2. If you don't have access to the book, read at least Secure Coding: The State of the Practice (including the
Architectural Principles, the Design Ideas and the Java Tips sections) from one of the two authors.
2. Check some good articles on naming things, like Ottinger's Rules for Variable and Class Naming.
It is especially important that we all use the same words (in case there is an ambiguity) to refer to the
same concepts (like "fetch" vs. "retrieve", for instance).
Why? This will improve the amount of static checks done by the compiler.
49.3.9. Logging
Follow this piece of advice from Alex Miller:
My opinion is that there are 4 log levels that matter for general program logging (specialized logs like audit and
performance have their own rules). I dont care what you call them so feel free to replace these with the names
of your choice.
• ERROR - an error has occurred in the program (usually an exception). All internal or unexpected failures
should be logged at ERROR level. Programmers should care about anything logged as an error. User
errors (like input validation) should generally NOT be logged at ERROR level. These are expected (if
invalid) inputs and you should respond to the user appropriately (another post altogether), but this is not
a program error. Exceptions may be made for things like repeated invalid password attempts that are
likely security problems.
• WARNING - an anomalous condition has been detected and the program will attempt to deal with it. No
action needs to be taken externally but you might be interested if things go down in flames 5 minutes
later. The conditions for warnings are relatively rare but an example would be reaching a warning
threshold on a connection pool or a loss of connectivity that can be repaired by reconnecting to the same
or different source.
• INFO - an interesting piece of information that helps to give context to a log, often when things are
starting or stopping. INFO messages should never be printed on a per transaction (whatever that means
to you) path through the code. That is, INFO messages should be 1-time or very occasional messages.
The first three levels (ERROR, WARNING, and INFO) should always be on. If your logs grow rapidly with
these levels on, something is wrong. You should be able to run in production with these levels for weeks
without a problem (but you should really use a rolling log writer just in case). DEBUG should be used only
during development for debugging.
Why? Otherwise, it will be harder for others (even you!) to maintain or extend your code. Some people
may even end up refactoring your code under wrong assumptions.
See for instance item #28 of Bloch's "Effective Java" [Bloch2001] for more insight, including this most
important principle:
The doc comment for a method should describe succinctly the contract between the method
and its client. With the exception of methods in classes designed for inheritance, the contract
should say what the method does rather than how it does its job.
2. Write javadocs according to the javadoc manual and the official Sun javadocs guidelines.
Why? Because javadocs are compiled by a standard tool, you must conform to the syntax mandated by
this tool.
• Don't include XML tags without escaping the < and > characters.
• Remember that the first sentence (i.e. everything until the first dot) is used as a short description of
the package / class / method / etc. it documents. Hence end all your sentences with a dot.
• Don't use </p> in the source though as it is useless and rather bad looking.
• If your javadoc starts with "This class" or "This methods", this is most probably wrong.
4. Proofread carefully (including checks for spelling and grammar errors) and check that your javadocs
provide useful information for third-party developers (or your teammates) by actually reading the
generated doc.
How? See either "Project -> Generate Javadocs" from Eclipse, or "mvn javadoc:javadoc" from the
project root and browse the docs.
Remember that you can see a preview of how your javadoc will look like from Eclipse by using the
"Javadoc" tab in the Java perspective.
Javadocs comments are intended to be read by clients of the API, implementation comments by people
who need to review you code or who will end up maintaining it (including yourself).
For instance, don't put "TODO" markers in your javadoc because they are intended for yourself or other
people co-developing or maintaining your code, not clients of the API.
If you don't have enough time to document everything, document first the API part of your code
(usually, the interfaces), because that's what third-party developers are going to use.
Also documenting the non-obvious (like giving an overview of a package or an important class) is more
important than writing for instance, that a "getTitle()" method "@returns the title".
Package-level Javadoc (just package.html files in your source code) are the most important javadocs
you have to write, because that's what people reading the javadoc-generated site will read first, and
because that's where the information is usually the less obvious.
It is very important that people who will read / maintain your code know that you are the author, so that
they can ask you questions, give you feedback, etc.
9. When you've borrowed code from another open source project, always document it so that:
• we are sure that there is no license conflict with the original code
• we understand why the code in question doesn't follow our own coding conventions or isn't unit-tested
as it should (it should anyway, but this is another story)
10.Put markers as (inline) comments to document parts of your code that will need further work.
• use BBB to mark code used to ensure compatibility with a deprecated feature, that will be removed
after a certain release.
• do not leave TODO markers behind when they are not relevant (for instance for dummy classes used
in tests and auto-generated by the IDE from an interface or an abstract class).
Either go to http://maven.nuxeo.com/apidocs/ and check the apidoc for your project, or run mvn
javadoc:javadoc locally and browse the generated apidoc, and ask yourself the simple question: "if I was
a third-party developer, would I understand how to use the API by reading this".
49.3.11. Deprecation
1. Don't use deprecated features (= API), either from Java or third-party libraries, or from the Nuxeo
framework.
2. Deprecate your own API when you have to, but make sure you write comments that explain to your
API's clients how to migrate from the old API to the new one. Use BBB markers (see above) if needed.
3. Deprecated APIs should be maintained at least for 1 release cycle, at least for external clients of the
APIs, but we should strive to switch to the new APIs internally in 1 release cycle.
How?
• Enable all the rules, and run the analysis on your module.
• Fix the issues that appear serious (you still have to think).
2. You should also use the Checkstyle and FindBugs Eclipse plugins to ensure minimal bug count and
maximal coding style coherence.
3. Read Improving code with Eclipse plugins on developerWorks for more background information on the
subject.
This article covers what I consider to be the "big five" code analysis areas:
• Coding standards
• Code duplication
• Code coverage
• Dependency analysis
• Complexity monitoring
These analysis areas can be uncovered using a number of the following slick Eclipse plugins:
NB: Coverlipse may or may not work correctly, an alternative is EclEmma which is very similar.
49.4.2. Refactor
1. When something looks wrong in the code ("smell"), refactor it, but make sure you don't break anything.
How?
• Check that the code you are refactoring is covered by some unit tests.
• Refactor. (Tip: a modern IDE (Eclipse and IDEA for instance) has some functions that may help.)
• Check that the code still compiles (including dependent modules) and all the unit tests are still
passing, and commit.
3. ObjectMentor's design articles (click on "Design Principles"), the most important one being Principles
and Patterns.
If you are already familiar with the Checkstyle Eclipse plugin, just configure it to use the checkstyle.xml at
the root of the SVN directory for nuxeo-ep:
http://svn.nuxeo.org/trac/nuxeo/browser/nuxeo-ep/trunk/checkstyle.xml
TODO
TODO
The tool is written in Python and the following libraries are needed:
<component ...>
<!--############## Component configuration ############-->
<!-- implementation class (optional) -->
<implementation>...</implementation>
<!-- component properties (optional) -->
<property name="..." value="..."/>
...
<property name="..." value="..."/>
<!--############### Extension points ################-->
<!-- extension points are optional -->
<extension-point ...>
...
</extension-point>
...
<!--############### Contributions ################-->
<!-- contributions are optional -->
<extension ...>
...
</extension>
...
</component>
We can see that the only required element is the component element (although it is useless to have an empty
component). So there are 3 main sections (any of these sections are optional).
• Component configuration: This section defines the component implementation class and some properties
to initialize the component (This section content may be modified in future especially when aligning
nuxeo components with OSGi services).
• Extension points: This sections contains all the extension point declared by the component.
• Contributions (extension tag): This section contains all the contributions made by this component to
other components.
To add documentation to these elements a <documentation> tag will be used. An element may have different
content depending on what it is documenting. While some information is already available in other XML
elements in the file, there is no need to duplicate these information inside the documentation provided though
the element. For example the name of the component can be retrieved from the name attribute of the
component element, the implementation class name from the implementation element etc.
To format the description text, we can use XHTML tags and javadoc-like markers such as @property, @schema
etc. Javadoc-like links are also supported: @see points on the javadoc, @component points on another
component documentation. For example, {@see org.nuxeo.ecm.core.schema.types.Type} will point on the
http://maven.nuxeo.org/apidocs corresponding page while {@component
org.nuxeo.ecm.webapp.directory.DirectoryTreeService} will explicitly insert a link to the related
NxPointDoc page. Code colorization is also supported through the <code language='xml'> ... </code> tags.
If no language is given, xml is taken by default. Java (language='java') and many other languages are
supported (see Pygments pages).
• @version
• @property
• @deprecated
• @schema
• @deprecated
• @see
• @component
• If the extension point is using object sub-elements, the DTD should be extracted from the XMap
annotated class, otherwise the user may specify the DTD using the @schema marker inside the
documentation element
• @see
• @component
• @deprecated
• component/extension-point/documentation description
Here is a short example of what a component xml file may look like.
<?xml version="1.0"?>
<component name="org.nuxeo.ecm.MyService">
<documentation>
My demo service
<p/>
This service does nothing
@property home home directory to be used to create temp files
Feel free to browse the NxPointDoc site and teh corresponding xml file to go deeper. The systematic link to the
source code svn repository may help you.
$ ./nxpointdoc.py -h
usage: nxpointdoc.py [options]
options:
--version show program's version number and exit
-h, --help show this help message and exit
--source=SOURCE_DIR Source root directory containing xml component files
--target=TARGET_DIR Target directory for the generated documentation
--template=TEMPLATE Genshi template for component html file
--template-index=TEMPLATE_INDEX Genshi template for index html file
--allow-xhtml-comment=ALLOW_XHTML_COMMENT 'no' to not interpret xhtml tags in comment
--color=COLOR_CODE 'no' to not color <code> contents
Valid SOURCE_DIR and TARGET_DIR are mandatory. Template files have to exist. The one delivered have the
.template extension and can be used as is.
The statistic gives some rough indicators on the documentation coverage, globally or for each component file.
The G.D.C stands for Global Documentation Coverage while the I.D.C stands for Individual Documentation
Coverage. They show the ratio between all the information/documentation that is written over all the entries
that are considered as mandatory (like author, documentation, etc.). The higher these indicators are, the better it
is.
their work frequently, usually each person integrates at least daily - leading to multiple
integrations per day. Each integration is verified by an automated build (including test) to
detect integration errors as quickly as possible. Many teams find that this approach leads to
significantly reduced integration problems and allows a team to develop cohesive software
more rapidly."
—Martin Fowler - Continuous Integration (an introduction)
See http://en.wikipedia.org/wiki/Continuous_Integration
It's as important to follow quality processes on development as to maintain this quality among time. Nuxeo is
involved in such practices that will guarantee or reinforce its products quality.
Nuxeo sources repositories hg.nuxeo.org and svn.nuxeo.org are under continuous integration.
This includes Nuxeo EP, Nuxeo addons, Nuxeo RCP, Nuxeo WebEngine, Nuxeo Books, tools and
plugins and of course all our customers' projects.
This is done by Hudson on Nuxeo QA Unit tests and on Nuxeo QA Functional tests.
When code is committed, target project is built, as all projects depending on it. The full chain is verified,
from build to deployment.
Mainlines on Nuxeo EP and addons are the main branches in development: 5.1 and 5.2 (resp. 1.4 and 1.5
for associated subtrees). For projects under SubVersion, that means the trunk and, if exists, 5.1 branch.
Hudson plugins ensure to warn potential responsible(s) of build fail by mail and jabber, so they can react
quickly.
Nightly builds are done. Produced artifacts are published on our Maven repositories archiva.nuxeo.org.
Currently managed with Archiva, our repositories store all released artifacts and recent snapshots.
Continuous Integration is done on multiple servers, more or less powerful, using slaves in order to
distribute the load.
Thanks to Maven and to Nuxeo modularity, each module is built separately and as a consequence,
quickly.
First level checks code compilation and runs Unit tests. A lot of Unit tests simulate target environments
(with mock objects). Dependent projects/modules are then added to the CI chain.
Second level runs packaging tools and automated deployment against multiple environments (we aim at
covering JVM versions, SQL backends, OS, browsers, performance, ...). Finally we use Selenium tests to
check functional integrity. This also indirectly provides a continuous integration on our tools (packaging,
convenient scripts, ...).
Except for prototype and spike solutions (sandbox projects or temporary branches), all projects must be
under CI. If not, ask for it to the QA team, providing the informations mentionned in the following
Hudson part.
Think about QA tools that will have to test the project without any human intervention. Provide Maven,
Ant or, in the worse case, Shell autonomous configuration.
Think "test-driven development". Simply building a project/module and running its Unit tests should be a
valuable measurement of the code stability. Unit tests code coverage often needs to be increased.
Smaller are the commits, lower is the risk of conflicting changes and easier is the bug analysis.
• Stay tuned
Be aware of CI builds, particularly failed builds. Subscribe to ECM QA mailing list. Use mail filters to
quickly catch and fix problems. Hudson will send you a mail if it detects one of your commits between
succeed and failed tests.
Check regurlarly your projects health on our QA sites. Inform QA team if you notice any issue.
Maven Parent POM file gives a lot of useful information. Take care to fill in you project's pom.xml file:
• main tags
<name>Nuxeo ECM Projects</name>
<description>Nuxeo ECM Platform and related components</description>
<organization>
<name>Nuxeo SAS</name>
<url>http://www.nuxeo.com/</url>
</organization>
<licenses>
<license>
<name>GNU LESSER GENERAL PUBLIC LICENSE, Version 2.1</name>
<url>http://www.gnu.org/copyleft/lesser.txt</url>
</license>
</licenses>
<mailingLists>
<mailingList>
<name>Nuxeo ECM list</name>
<subscribe>http://lists.nuxeo.com/mailman/listinfo/ECM</subscribe>
<unsubscribe>http://lists.nuxeo.com/mailman/listinfo/ECM</unsubscribe>
<archive>http://lists.nuxeo.com/pipermail/ecm/</archive>
</mailingList>
</mailingLists>
<issueManagement>
<system>jira</system>
<url>http://jira.nuxeo.org/browse/NXP</url>
</issueManagement>
<ciManagement>
<system>Hudson</system>
<url>http://qa.nuxeo.org/hudson/</url>
</ciManagement>
• "scm" tag
<scm>
<connection>scm:hg:http://hg.nuxeo.org/addons/nuxeo-samples</connection>
<developerConnection>scm:hg:https://hg.nuxeo.org/addons/nuxeo-samples</developerConnection>
<url>http://trac.nuxeo.org/nuxeo/browser/nuxeo_samples</url>
</scm>
• "developers" tag (there's no rule for tags within the "developer" tag, feel free to add useful information
such as "role", "url", "organization" or "module")
<developers>
<developer>
<name>John Doe</name>
<email>jdoe@nuxeo.com</email>
</developer>
</developers>
• You also have to add <repositories> section in the project's parent POM in order to make your project
fully autonomous.
<repositories>
<repository>
<id>nuxeo_release</id>
<url>http://archiva.nuxeo.org/archiva/repository/nuxeo_release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>nuxeo_snapshot</id>
<url>http://archiva.nuxeo.org/archiva/repository/nuxeo_snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
All these tags are intelligently inherited so that, if you're project's Maven parent is nuxeo-ecm or one of its
children, you don't have to repeat informations such as "organization", "licenses", "mailingLists",
"issueManagement". Also, when working on a project with sub-modules, it's only necessary to set "scm" on the
parent POM.
• Build command
In case of Maven, it's the goals to run (usually, it will be "clean install"). Consider using Maven
"Profiles" to manage different behaviors like development versus production environment.
In case of Ant, you may need to provide some parameters on the command line (equivalent to what can
In case of Shell (avoid it as much as possible), it's a simple command with working default values in case
of required parameters. If needed, some environment constants may be set.
51.1. Introduction
Goal of this chapter is to help creating projects over Nuxeo. How to create a project structure including a
packaging module that builds one or more Enterprise ARchive (EAR), ready for deployment. Ant scripts,
Maven profiles and Nuxeo assemblies allow defining simple commands to build packages that will fit each
target environments.
❶ Ant sample properties. Copy this file to build.properties and personalize it to fit your installation.
❷ Ant script. It's aimed to ease the use of maven, giving targets to automatize common tasks on project (test,
compile, deploy, package, ...).
❸ OS dependent, will only work on Linux/Unix and Mac OS X. Copy it from Nuxeo EP root. It's a shell
utility script that calls maven-eclipse-plugin to create or update Eclipse's .project and .classpath files,
taking care of setting different build directories for Maven and Eclipse. The script gives precedence to any
present .project.ok or .classpath.ok.
❹ Maven project parent file.
❺ Required to import this project into eclipse as a whole resources project (each module will also be
individually imported as a java project).
❻ This module contains common code usable in all other modules, such as interfaces, adaptors, constants,
Example 51.1. Sample of customized assembly for a standard EAR, configured for development
<!-- This is a template assembly file that generates a mono Nuxeo-EP server.
Configuration is designed for development usage -->
<assembly>
<!-- RESOURCES -->
<assemble>
<set>resources</set>
<outputFile>/</outputFile>
</assemble>
<zipEntrySet id="resources">
<artifact>
org.nuxeo.ecm.platform:nuxeo-platform-ear:${nuxeo.platform.version}:zip:resources-mono
</artifact>
</zipEntrySet>
<!-- ARTIFACTS -->
<assemble>
<outputFile>/</outputFile>
<set>root-artifacts</set>
</assemble>
<artifactSet id="root-artifacts">
<import>**</import>
<includeDependencies>false</includeDependencies>
<includes>
<artifact name="nuxeo-platform-webapp" />
<artifact name="nuxeo-platform-webapp-core" />
</includes>
</artifactSet>
<assemble>
<outputFile>system</outputFile>
<set>system</set>
</assemble>
<artifactSet id="system">
<import>**</import>
<includeDependencies>true</includeDependencies>
<includes>
<artifact group="org.nuxeo.*" category="runtime,jboss4"
includeDependsOnCategory="false" />
<artifact group="org.nuxeo.*" category="core,search,web" />
<!-- add here nuxeo-addons -->
</includes>
<excludes>
<artifact group="!org.nuxeo*" />
<artifact group="org.nuxeo.common" />
<artifact name="nuxeo-runtime-jboss-extensions" />
<artifact name="nuxeo-platform-webapp" />
<artifact name="nuxeo-platform-webapp-core" />
<artifact name="nuxeo-platform-audit-facade" />
<artifact name="nuxeo-platform-placeful-facade" />
<artifact name="nuxeo-platform-search-compass-plugin" />
<artifact name="nuxeo-platform-ear" />
<artifact name="nuxeo-apt-extensions" />
<artifact group="org.nuxeo.projects.template" />
</excludes>
</artifactSet>
<assemble>
<outputFile>system</outputFile>
<unpack>true</unpack>
<unpackInNewDirectory>true</unpackInNewDirectory>
<set>nuxeo-platform-unpacked</set>
</assemble>
<artifactSet id="nuxeo-platform-unpacked">
<import>**</import>
<includes>
<artifact name="nuxeo-platform-audit-facade" />
<artifact name="nuxeo-platform-placeful-facade" />
<artifact name="nuxeo-platform-search-compass-plugin" />
</includes>
</artifactSet>
<!-- third party libraries embedded in the ear -->
<assemble>
<outputFile>lib</outputFile>
<set>nuxeo-fixed-libs</set>
</assemble>
<artifactSet id="nuxeo-fixed-libs">
<artifacts>
<artifact group="org.freemarker" name="freemarker" version="2.3.11" />
<artifact group="org.osgi" name="osgi-core" version="4.1" />
<artifact group="commons-collections" name="commons-collections"
version="3.1" />
<artifact group="commons-io" name="commons-io" version="1.2" />
<artifact group="commons-lang" name="commons-lang" version="2.2" />
<artifact group="commons-fileupload" name="commons-fileupload"
version="1.1.1" />
<artifact group="cssparser" name="cssparser" version="0.9.4-fix" />
<artifact group="net.sf.ehcache" name="ehcache" version="1.2.3" />
<artifact group="net.sf.ezmorph" name="ezmorph" version="0.9" />
<artifact group="org.hibernate" name="hibernate" version="3.2.0.ga" />
<artifact group="org.hibernate" name="hibernate-annotations"
version="3.2.0.ga" />
<artifact group="org.hibernate" name="hibernate-entitymanager"
version="3.2.0.ga" />
<artifact group="jboss" name="jboss-cache-jdk50" version="1.4.0.SP1" />
<artifact group="org.jboss.seam" name="jboss-seam" version="1.1.5.NX3" />
<artifact group="jboss" name="jbpm" version="3.1.2" />
<artifact group="jboss" name="jgroups" version="2.2.9" />
<artifact group="net.sf.json-lib" name="json-lib" version="0.9" />
<artifact group="org.apache.lucene" name="lucene-core" version="2.0.0" />
<artifact group="net.sf.opencsv" name="opencsv" version="1.7" />
<artifact group="org.slf4j" name="slf4j-api" version="1.3.0" />
<artifact group="org.slf4j" name="slf4j-log4j12" version="1.3.0" />
<artifact group="org.apache.myfaces.tomahawk" name="tomahawk"
version="1.1.5" />
<artifact group="org.apache.myfaces.tomahawk" name="tomahawk-sandbox"
version="1.1.5" />
<artifact group="org.apache.myfaces.trinidad" name="trinidad-api"
version="1.0.1-incubating-NXEP51M2" />
<artifact group="org.apache.myfaces.trinidad" name="trinidad-impl"
version="1.0.1-incubating-NXEP51M2" />
<artifact group="org.apache.directory.server"
name="apacheds-protocol-shared" version="1.5.1" />
<artifact group="org.apache.directory.shared" name="shared-ldap"
version="0.9.7" />
<artifact group="com.sun.facelets" name="jsf-facelets" version="1.1.11" />
<artifact group="org.nuxeo.ecm.platform" name="nuxeo-jbossws-wrapper"
version="4.0.5.GA" />
</artifacts>
</artifactSet>
<!-- template project's artifacts -->
<assemble>
<outputFile>plugins</outputFile>
<set>template-plugins</set>
</assemble>
<artifactSet id="template-plugins">
<import>**</import>
<includeDependencies>false</includeDependencies>
<includes>
<artifact group="org.nuxeo.projects.template" />
</includes>
</artifactSet>
<!-- template project's resources -->
<assemble>
<outputFile>/</outputFile>
<set>template-resources</set>
</assemble>
<fileSet id="template-resources">
<directory>src/main/resources_template_common</directory>
<excludes>
<exclude>README.txt</exclude>
</excludes>
</fileSet>
<assemble>
<outputFile>/</outputFile>
<set>template-resources-dev</set>
</assemble>
<fileSet id="template-resources-dev">
<directory>src/main/resources_template_dev</directory>
<excludes>
<exclude>README.txt</exclude>
</excludes>
</fileSet>
</assembly>
Create an XML file in template-ear/src/main/assemble/ that will describe the EAR to build. Here's the file
content:
• assembly: the main tag. For now, you can only define one assembly per file (meaning one packaging
definition by assembly descriptor file).
• assemble: part of an assembly. An assemble must be associated with a set (different sets are available:
zipEntrySet, fileSet, artifactSet).
• outputFile: output directory string. "/" represents the building EAR root.
• unpackInNewDirectory: true or false. if unpack is true, whether to create new directories or not in the
output directory.
• zipEntrySet
This set allow to retrieve artifacts from outside the project dependencies and unzip them.
• profile: if specified, will only be treated if the given maven profile is active.
• artifactSet
This set retrieves all artifacts from the project's dependencies tree, depending on conditions
• includeDependencies: true or false. If true, all dependencies of selected artifacts by this set will be
added to the set.
• excludeDependencies: true or false. If true, all dependencies of selected artifacts by this set will be
removed from the set.
• extends: deprecated. Was aimed to allow assemblies inheritance and sets overriding.
• includes (or excludes): artifacts list to include (resp. exclude). Each artifact accept these parameters:
• group: artifact's group id. Accept wildcards '*' and negative '!' to match multiple artifacts.
• name: artifact's id. Accept wildcards '*' and negative '!' to match multiple artifacts.
• file: artifact's filename. Accept wildcards '*' and negative '!' to match multiple artifacts.
• category: artifact's category. Concept introduced by Nuxeo to "tag" an artifact with a "category".
This is done in the artifact's Manifest as "Bundle-Category". Multiple categories, comma separated,
is interpreted as an union of matching artifacts.
• profile: if specified, the artifact will only be treated (included or excluded) if the given maven
profile is active.
• fileSet
• directory: root directory of resources to copy. For now, only one directory per set is allowed
• include: string. File, directory or pattern file/directory to include. "**" means multiple directories.
• excludes: files and directories list to exclude. By default, these patterns are already excluded: "**/.svn"
and "**/.hg".
• exclude: string. File, directory or pattern file/directory to exclude. "**" means multiple directories.
• resources_template_dev where you put your resources configured for development purpose (filesystem
backend, ...).
The module-ear pom.xml file must have in its dependencies all wanted artifacts that will then be filtered by the
assembly descriptor. Including nuxeo-platform-ear bring all its dependencies, there's no need to copy Nuxeo's
artifacts list.
In order to call the assembly descriptor, it must contain the following plugin declaration.
"template.ear.assembly" is used to parameterize the assembly descriptor name. Set a default value with a
property.
<properties>
<!-- default assembly descriptor to use -->
<template.ear.assembly>template-dev</template.ear.assembly>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-nuxeo-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<runPreprocessor>false</runPreprocessor>
<format>directory</format>
<outputDirectory>../target</outputDirectory>
<targetFile>${template.ear.assembly}</targetFile>
<descriptor>
${basedir}/src/main/assemble/${template.ear.assembly}.xml
</descriptor>
</configuration>
<executions>
<execution>
<id>assemble-ear</id>
<phase>package</phase>
<goals>
<goal>assembly</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Maven parameters may be set in command-line. For example, in the above template, "mvn clean package" will
package a Nuxeo EAR for developers and "mvn clean package -Dtemplate.ear.assembly=template-ldap-pg"
will call template-ldap-pg.xml instead of template-dev.xml and so package a Nuxeo EAR configured for LDAP
and PostgreSQL.
<?xml version="1.0"?>
<project name="template" default="usage" basedir=".">
<!-- Create a build.properties file from build.properties.sample
if you wish to override the JBoss paths -->
<property file="build.properties" />
<property name="jboss.dir" value="/opt/jboss" />
<property name="jboss.config" value="default" />
<property name="mvn.opts" value="" />
<property name="javac.debug" value="true" />
<property name="javac.deprecation" value="false" />
<!-- Boilerplate configuration -->
<property name="build.dir" value="${basedir}/target" />
<property name="deploy.dir" value="${jboss.dir}/server/${jboss.config}/deploy" />
<property name="template.ear.root" value="template-ear" />
<property name="nuxeo.ear" value="nuxeo.ear" />
<property name="deploy.lib.dir" value="${jboss.dir}/server/${jboss.config}/lib" />
<property name="build.lib.dir" value="lib" />
51.5.1.1. Introduction
Stateful/Stateless packaging gives a good solution to a lot of production requirements. Principle is to split
Nuxeo in two parts: one that contains the core and every services that persist data, and another that provides
web services. Stateless part may be duplicated.
51.5.1.2. Packaging
Bi-machine packaging is done by two assembly descriptors, each one building an EAR to deploy on a separate
server.
To build your own bi-machine packaging, copy the template-project structure. Copy and adapt Ant, Maven and
descriptor files. Then, configure artifactSet and fileSet that will deploy your project's artifacts into the plugin
directory and the wanted resources to replace those deployed by default.
<!-- This is a template assembly file that generates a "stateful" part of Nuxeo-EP server -->
<assembly>
<!-- RESOURCES -->
<assemble>
<set>resources</set>
<outputFile>/</outputFile>
</assemble>
<zipEntrySet id="resources">
<artifact>
org.nuxeo.ecm.platform:nuxeo-platform-ear:${nuxeo.platform.version}:zip:resources-platform-stateful
</artifact>
</zipEntrySet>
<!-- ARTIFACTS -->
<assemble>
<outputFile>system</outputFile>
<set>system</set>
</assemble>
<artifactSet id="system">
<import>**</import>
<includeDependencies>true</includeDependencies>
<includes>
<artifact group="org.nuxeo.*" category="runtime,jboss4"
includeDependsOnCategory="false" />
<artifact group="org.nuxeo.*" category="core,stateful" />
<!-- add here core and stateful nuxeo-addons -->
</includes>
<excludes>
<artifact group="!org.nuxeo*" />
<artifact group="org.nuxeo.common" />
<artifact name="nuxeo-runtime-jboss-extensions" />
<artifact name="nuxeo-platform-webapp" />
<artifact name="nuxeo-platform-webapp-core" />
<artifact name="nuxeo-platform-audit-facade" />
<artifact name="nuxeo-platform-placeful-facade" />
<artifact name="nuxeo-platform-search-compass-plugin" />
<artifact name="nuxeo-platform-ear" />
<artifact group="org.nuxeo.projects.template" />
</excludes>
</artifactSet>
<assemble>
<outputFile>system</outputFile>
<unpack>true</unpack>
<unpackInNewDirectory>true</unpackInNewDirectory>
<set>nuxeo-platform-unpacked</set>
</assemble>
<artifactSet id="nuxeo-platform-unpacked">
<import>**</import>
<includes>
<artifact name="nuxeo-platform-audit-facade" />
<artifact name="nuxeo-platform-placeful-facade" />
<artifact name="nuxeo-platform-search-compass-plugin" />
</includes>
</artifactSet>
<!-- third party libraries embedded in the ear -->
<assemble>
<outputFile>lib</outputFile>
<set>nuxeo-fixed-libs</set>
</assemble>
<artifactSet id="nuxeo-fixed-libs">
<artifacts>
<artifact group="org.freemarker" name="freemarker" version="2.3.11" />
<artifact group="org.osgi" name="osgi-core" version="4.1" />
<artifact group="commons-collections" name="commons-collections"
version="3.1" />
<artifact group="commons-io" name="commons-io" version="1.2" />
<artifact group="commons-lang" name="commons-lang" version="2.2" />
<artifact group="commons-fileupload" name="commons-fileupload"
version="1.1.1" />
<artifact group="cssparser" name="cssparser" version="0.9.4-fix" />
<artifact group="net.sf.ehcache" name="ehcache" version="1.2.3" />
<artifact group="net.sf.ezmorph" name="ezmorph" version="0.9" />
<artifact group="org.hibernate" name="hibernate" version="3.2.0.ga" />
<artifact group="org.hibernate" name="hibernate-annotations"
version="3.2.0.ga" />
<artifact group="org.hibernate" name="hibernate-entitymanager"
version="3.2.0.ga" />
<artifact group="jboss" name="jboss-cache-jdk50" version="1.4.0.SP1" />
<artifact group="org.jboss.seam" name="jboss-seam" version="1.1.5.NX3" />
<artifact group="jboss" name="jbpm" version="3.1.2" />
<artifact group="jboss" name="jgroups" version="2.2.9" />
<artifact group="net.sf.json-lib" name="json-lib" version="0.9" />
<artifact group="org.apache.lucene" name="lucene-core" version="2.0.0" />
<artifact group="net.sf.opencsv" name="opencsv" version="1.7" />
<artifact group="org.slf4j" name="slf4j-api" version="1.3.0" />
<artifact group="org.slf4j" name="slf4j-log4j12" version="1.3.0" />
<artifact group="org.apache.myfaces.tomahawk" name="tomahawk"
version="1.1.5" />
<artifact group="org.apache.myfaces.tomahawk" name="tomahawk-sandbox"
version="1.1.5" />
<artifact group="org.apache.myfaces.trinidad" name="trinidad-api"
version="1.0.1-incubating-NXEP51M2" />
<artifact group="org.apache.myfaces.trinidad" name="trinidad-impl"
version="1.0.1-incubating-NXEP51M2" />
<artifact group="org.apache.directory.server"
name="apacheds-protocol-shared" version="1.5.1" />
<artifact group="org.apache.directory.shared" name="shared-ldap"
version="0.9.7" />
<artifact group="com.sun.facelets" name="jsf-facelets" version="1.1.11" />
<artifact group="org.nuxeo.ecm.platform" name="nuxeo-jbossws-wrapper"
version="4.0.5.GA" />
</artifacts>
</artifactSet>
<!-- template project's artifacts -->
<assemble>
<outputFile>plugins</outputFile>
<set>template-plugins</set>
</assemble>
<artifactSet id="template-plugins">
<import>**</import>
<includeDependencies>true</includeDependencies>
<includes>
<artifact name="template-core" />
<artifact name="template-stateful-services" />
</includes>
<excludes>
<artifact group="!org.nuxeo.projects.template" />
</excludes>
</artifactSet>
<!-- template project's resources -->
<assemble>
<outputFile>/</outputFile>
<set>template-resources</set>
</assemble>
<fileSet id="template-resources">
<directory>src/main/resources_template_common</directory>
<excludes>
<exclude>README.txt</exclude>
</excludes>
</fileSet>
<assemble>
<outputFile>/</outputFile>
<set>template-resources-stateful</set>
</assemble>
<fileSet id="template-resources-stateful">
<directory>src/main/resources_template_stateful</directory>
</fileSet>
</assembly>
<!-- This is a template assembly file that generates a "stateless" part of Nuxeo-EP server -->
<assembly>
<!-- RESOURCES -->
<assemble>
<set>resources</set>
<outputFile>/</outputFile>
</assemble>
<zipEntrySet id="resources">
<artifact>
org.nuxeo.ecm.platform:nuxeo-platform-ear:${nuxeo.platform.version}:zip:resources-web-stateless
</artifact>
</zipEntrySet>
<!-- ARTIFACTS -->
<assemble>
<outputFile>/</outputFile>
<set>root-artifacts</set>
</assemble>
<artifactSet id="root-artifacts">
<import>**</import>
<includeDependencies>false</includeDependencies>
<includes>
<artifact name="nuxeo-platform-webapp" />
<artifact name="nuxeo-platform-webapp-core" />
</includes>
</artifactSet>
<assemble>
<outputFile>system</outputFile>
<set>system</set>
</assemble>
<artifactSet id="system">
<import>**</import>
<includeDependencies>true</includeDependencies>
<includes>
<artifact group="org.nuxeo.*" category="runtime,jboss4"
includeDependsOnCategory="false" />
<artifact group="org.nuxeo.*" category="stateless" />
<!-- add here web and stateless nuxeo-addons -->
</includes>
<excludes>
<artifact group="!org.nuxeo*" />
<artifact group="org.nuxeo.common" />
<artifact name="nuxeo-runtime-jboss-extensions" />
<artifact name="nuxeo-platform-webapp" />
<artifact name="nuxeo-platform-webapp-core" />
<artifact name="nuxeo-platform-ear" />
<artifact name="nuxeo-apt-extensions" />
<artifact group="org.nuxeo.projects.template" />
</excludes>
</artifactSet>
<!-- third party libraries embedded in the ear -->
<assemble>
<outputFile>lib</outputFile>
<set>nuxeo-fixed-libs</set>
</assemble>
<artifactSet id="nuxeo-fixed-libs">
<artifacts>
<artifact group="org.freemarker" name="freemarker" version="2.3.11" />
<artifact group="org.osgi" name="osgi-core" version="4.1" />
<artifact group="commons-collections" name="commons-collections"
version="3.1" />
<artifact group="commons-io" name="commons-io" version="1.2" />
<artifact group="commons-lang" name="commons-lang" version="2.2" />
<artifact group="commons-fileupload" name="commons-fileupload"
version="1.1.1" />
<artifact group="cssparser" name="cssparser" version="0.9.4-fix" />
<artifact group="net.sf.ehcache" name="ehcache" version="1.2.3" />
<artifact group="net.sf.ezmorph" name="ezmorph" version="0.9" />
<artifact group="org.hibernate" name="hibernate" version="3.2.0.ga" />
<artifact group="org.hibernate" name="hibernate-annotations"
version="3.2.0.ga" />
<artifact group="org.hibernate" name="hibernate-entitymanager"
version="3.2.0.ga" />
<artifact group="jboss" name="jboss-cache-jdk50" version="1.4.0.SP1" />
<artifact group="org.jboss.seam" name="jboss-seam" version="1.1.5.NX3" />
<artifact group="jboss" name="jbpm" version="3.1.2" />
<artifact group="jboss" name="jgroups" version="2.2.9" />
<artifact group="net.sf.json-lib" name="json-lib" version="0.9" />
<artifact group="org.apache.lucene" name="lucene-core" version="2.0.0" />
<artifact group="net.sf.opencsv" name="opencsv" version="1.7" />
<artifact group="org.slf4j" name="slf4j-api" version="1.3.0" />
<artifact group="org.slf4j" name="slf4j-log4j12" version="1.3.0" />
<artifact group="org.apache.myfaces.tomahawk" name="tomahawk"
version="1.1.5" />
<artifact group="org.apache.myfaces.tomahawk" name="tomahawk-sandbox"
version="1.1.5" />
<artifact group="org.apache.myfaces.trinidad" name="trinidad-api"
version="1.0.1-incubating-NXEP51M2" />
<artifact group="org.apache.myfaces.trinidad" name="trinidad-impl"
version="1.0.1-incubating-NXEP51M2" />
<artifact group="org.apache.directory.server"
name="apacheds-protocol-shared" version="1.5.1" />
<artifact group="org.apache.directory.shared" name="shared-ldap"
version="0.9.7" />
<artifact group="com.sun.facelets" name="jsf-facelets" version="1.1.11" />
<artifact group="org.nuxeo.ecm.platform" name="nuxeo-jbossws-wrapper"
version="4.0.5.GA" />
</artifacts>
</artifactSet>
<!-- template project's artifacts -->
<assemble>
<outputFile>plugins</outputFile>
<set>template-plugins</set>
</assemble>
<artifactSet id="template-plugins">
<import>**</import>
<includeDependencies>true</includeDependencies>
<includes>
<artifact name="template-web" />
<artifact name="template-stateless-services" />
</includes>
<excludes>
<artifact group="!org.nuxeo.projects.template" />
</excludes>
</artifactSet>
<!-- template project's resources -->
<assemble>
<outputFile>/</outputFile>
<set>template-resources</set>
</assemble>
<fileSet id="template-resources">
<directory>src/main/resources_template_common</directory>
<excludes>
<exclude>README.txt</exclude>
</excludes>
</fileSet>
<assemble>
<outputFile>/</outputFile>
<set>template-resources-stateless</set>
</assemble>
<fileSet id="template-resources-stateless">
<directory>src/main/resources_template_stateless</directory>
</fileSet>
</assembly>
If you deploy multiple stateless servers, the unique (for now it cannot be duplicated) stateful server only need to
know one stateless server. Stateless servers do not need to communicate between each other.
Note
There is misconfiguration in Nuxeo EP 5.1.6 and 5.2.M3 (http://jira.nuxeo.org/browse/NXP-2838).
Replace in nuxeo-web-stateless.ear/datasources/core-events-ds.xml
java.naming.provider.url=${jboss.bind.address}:1099
with
java.naming.provider.url=nxjmsserver:1099
52.1. Introduction
Nuxeo EP and related components can use the maven release plugin to prepare and perform releases of Nuxeo
components.
• update version number of the trunk to start the new development cycle
The maven release plugin streamlines and automates the release process of software components.
To get more background about maven release, you can read following pages:
• mini-guide on Maven Release
This process has to be as easy as possible. You will be able to find bellow the basic process.
The first step is then to remove all dependencies on SNAPSHOT versions. Of course, as you may not be able to
release all your dependencies as long as your software, you can create specific versions of your dependencies
for the release.
❶❷ Informations of the repository you are using. For Nuxeo's software, use:
• repositoryId: central
• url: http://tamise.nuxeo.com:8080/archiva/repository/central>
Of course, you will need write permission on the repository. You can request it from your repository's
manager.
❸❹❺❻
• groupId: the group id for the artifact, you should get it for the pom of this artifact
• artifactId: idem
• packaging: usually jar (or pom if you are only deploying a pom file)
❼ Point to the file (usually jar or pom) you want to upload.
To be sure that everything is clean and that you will test in the same environment as new users, remove your
repository. On windows, remove your repository using your preferred tool. On Linux/Unix/MacOS, you can
perform:
$ rm ~/.m2/repository
This should download all required dependencies (since you've clean your maven repository in the previous
paragraph) and build the software.
Answer, now to the questions maven will ask about version numbers and tag name. This will simulate a release
preparation, especially the svn tags.
If everything went well until that point, we can actually perform the release...
This will tag your sources in the SCM, build the software, build the site upload your artifact to the defined
repository and deploy your site.
In Nuxeo's case, you need to release first the global master POM before releasing other component, if the
master POM is using a SNAPSHOT version. To achieve this, go to a checkout of the master POM and do a
non-recursive release using:
mvn -N release:prepare release:perform
Then you can set master POM's new version number into each component you want to release and start the
release process.
53.1. Introduction
Add-ons are modules you can add to a Nuxeo instance. They provide new functionnalities. To install an
add-ons, download the modules from http://hg.nuxeo.org/ and copy it in the plugins directory. For additional
set-up, refer to the add-ons documentation.
54.1. Introduction
However the annotea specification is quite old and does not handle all use cases for annotating documents in
the context of Nuxeo ECM. Therefore, we propose to provide some extensions to annotea. Using the repository
plugin, it provides a plugable system to be able to keep track of the relationship between an annotated URL and
the underlying nuxeo document.
Inside Nuxeo EP we need to be able to annotate the HTML preview of a document. This preview may include
images. This images are annotable as the rest of the document, but it requires an extension to the Xpointer
specification since user may want to annotate just a portion (zone) of the image.
Provides Java API and all the needed Java artifacts needed to call the Annotation service remotely.
Provides the Nuxeo Service that exposes the required annotation Java Interface. This service is
implemented as a Nuxeo Runtime component and will provide the needed extension points .
The HTTP Gateway implements the Annotea HTTP protocol. It is implemented as a servlet that enable
access to the annotation service via http GET/POST requests as defined in the W3C Annotea
specification.
54.2.1. Overview
This is the main component of NXAS, the one that contains all the logic for managing RDF based annotations.
The Core Service is not document aware and manages only URIs.
This component is also responsible for exposing all the needed extension points that will be used for
configuration and integration.
54.2.2. Implementation
The service is implemented as a Runtime service on top of a Nuxeo Runtime component. The runtime
component provides the extension point mechanisms. The API provided by the service will target managing
annotations on URLs.
As any Nuxeo Service, the Annotation Service is accessible via the Runtime lookup method :
Framework.getLocalService(AnnotationService.class)
Implementation Note
The core function of the annotation service is to add/remove/query annotations from the RDF
Graph. It also has to provide security and add configurability. The implementation separates those
2 functions in 2 classes: The AnnotationServiceProxy class implements all the configuration, event
management and security function and let the AnnotationServiceImpl provides the core RDF
service.
54.2.3. Storage
The Annotation service stores the annotations as a RDF graph. Nuxeo Relation Service is responsible for
storing and managing the RDF data. The service uses a graph named "annotations". The default contribution is:
<component name="org.nuxeo.ecm.annotations.graph">
<require>org.nuxeo.ecm.platform.relations.jena</require>
<extension target="org.nuxeo.ecm.platform.relations.services.RelationService"
point="graphs">
<graph name="annotations" type="jena">
<option name="backend">sql</option>
<option name="databaseType">${org.nuxeo.ecm.sql.jena.databaseType}</option>
<option name="databaseTransactionEnabled">
${org.nuxeo.ecm.sql.jena.databaseTransactionEnabled}
</option>
<option name="datasource">java:/nxrelations-default-jena</option>
<namespaces>
<namespace name="rdf">http://www.w3.org/1999/02/22-rdf-syntax-ns#</namespace>
<namespace name="dcterms">http://purl.org/dc/terms/</namespace>
<namespace name="nuxeo">http://www.nuxeo.org/document/uid/</namespace>
</namespaces>
</graph>
</extension>
</component>
Refer to the Relation Service documentation ( Chapter 20, Relations ) for more information on configuration
options.
54.2.4. uriResolver
The uriResolver extension point allows to contribute a class that is responsible for resolving uri. It maps URI
sent by the client to URI stored in the graph. It also allows to translate a URI in a list of URIs when querying
the graph.
This allows to consider annotations on different URLs to be considered as annotations on the same document,
therefore being able to see those annotations on both URLs.
To contribute a uriResolver contribute to the extension point a class that implements the UriResolver interface.
The default contribution is:
<extension
target="org.nuxeo.ecm.platform.annotations.services.AnnotationsService"
point="uriResolver">
<urlResolver
class="org.nuxeo.ecm.platform.annotations.service.DefaultUriResolver"/>
</extension>
The DefaultUriResolver maps URI without changing them, unless it includes "nuxeo/Annotations" in its URL.
In such a case, it transform the URL in a URN of the form "urn:annotation:***".
54.2.5. urlPatternFilter
The urlPatternFilter extension point allows to contribute regular expression pattern to the list of allowed URL
pattern or disallowed URL pattern. When a request is made to get/set annotations on an URL, the server check
the list.
The check is done before any transformation on the URL. Because the check on URL is done very early in the
processing of a request, to increase performance, it is recommended to use it whenever possible.
The filter follows Apache's mod_access convention to filter URL. The default contribution is:
<extension
target="org.nuxeo.ecm.platform.annotations.services.AnnotationsService"
point="urlPatternFilter">
<urlPatternFilter order="Deny,Allow">
<deny>.*</deny>
<allow>.*</allow>
</urlPatternFilter>
</extension>
54.2.6. metadata
The metadata extension point allows to add metadata to an annotations when it is created. The contribution
needs to implement the MetadataMapper interface. It is passed the annotation to which it can add the metadata.
The default contribution:
<extension
target="org.nuxeo.ecm.platform.annotations.services.AnnotationsService"
point="metadataMapper">
<metadataMapper
class="org.nuxeo.ecm.platform.annotations.service.DefaultMetadataMapper">
</extension>
This contribution adds the date and creator to the annotation. Note that you can add metadata and modify the
annotation before sending it to the service. You could choose to add the creation time using the local time of
the client and add it to the annotation without using the metadatMapper.
The permissionManager extension point allows to contribute a class that checks before CRUD operations. The
permissionMapper will maps permission name with operation.
The permissionMapper extension point allows to contribute the name of the permission to check before a
CRUD operation. The default contribution is:
<extension
target="org.nuxeo.ecm.platform.annotations.services.AnnotationsService"
point="permissionMapper">
<permissionMapper>
<createAnnotation>createAnnotation</createAnnotation>
<readAnnotation>readAnnotation</readAnnotation>
<updateAnnotation>updateAnnotation</updateAnnotation>
<deleteAnnotation>deleteAnnotation</deleteAnnotation>
</permissionMapper>
</extension>
The permissionManager extension point allows to contribute a class to check before CRUD operations. The
default permission manager allows all operations to everybody:
<extension
target="org.nuxeo.ecm.platform.annotations.services.AnnotationsService"
point="permissionManager">
<permissionManager
class="org.nuxeo.ecm.platform.annotations.service.DefaultPermissionManager"/>
</extension>
54.2.8. annotabilityManager
The annotabilityManager extension point allows to contribute a class to fine grain which documents can be
annotated. An implementation needs to implement the interface AnnotabilityManager . The default
annotability manager is:
<extension
target="org.nuxeo.ecm.platform.annotations.services.AnnotationsService"
point="annotabilityManager">
<annotabilityManager
class="org.nuxeo.ecm.platform.annotations.service.DefaultAnnotabilityManager"/>
</extension>
54.3.1. Overview
NXAS core offers an implementation of an Annotea Server. It is "pure" RDF and relies on URI for resource
identification. However, Nuxeo deals with versionned document. The repository plugin allows to contribute to
the core module using Nuxeo's object of DocumentModel, ACP and Version.
The repository plugin contribute to Core NXAS, replacing most of the default contribution. You can then
contribute to the repository plugin to tune security, annotability and more.
You can then use the uri for the query method.
54.3.2.1. permissionMapper
54.3.2.2. uriResolver
The defaultNuxeoUriResolver transform the URI if it points to a Nuxeo document. The URI becomes a URN
using the docId and repository name.
54.3.2.3. metadataMapper
In addition to the default mapper, the DefaultNuxeoMetadataMapper add the company of the user.
54.3.2.4. permissionMapper
54.3.2.5. annotabilityManager
54.3.2.6. eventListener
54.3.3.1. documentAnnotability
The documentAnnotability extension point allows to choose which document are annotable. To contribute to
this point, create a class that implement DocumentAnnotability . The default implementation is (
DefaultDocumentAnnotability ), allows annotations on all documents.
54.3.4. documentEventListener
The documentEventListener allows to contribute class that would be notify when an annotation is create,
updated, read and deleted. Notification comes synchronously before and after the event. It is passed as
parameter the annotation and the DocumentLocation of the the document being annoted.
54.3.5. securityManager
The security manager extension point allows to contribute a class that implements the SecurityManager
interface. It fine grains security using NuxeoPrincipal, the permission checked and the DocumentMode.
54.4.1. Overview
The NXAS HTML Client is the a web interface that can be used by the end user. It is written in GWT. It can be
configured in two ways. The "normal" Nuxeo way of extension point, or adding attribute or object to the html
page to be read by the gwt module.
54.4.3. Configuration
The client uses div to add the annotations panel. The panel for the list of annotation uses a div with id
"annotation_list_id". The panel for the functions button uses a div with id "annotation_functions_id".
To allow integration with the preview module, additional configuration is possible via a div with an id of
"preview-setting". Each configuration is an attribute of the div.
• imageOnly value should be true or false. If only the image should be annotated in the frame.
• multipleImageAnnotation should be true or false. If more than one image should be annotated.
• The annotation module uses 2 functions to transform the xpointer gotten from clicking on an image, and
to transform a xpointer to a xpointer that make sens in the previewed image. It get the function from
top[xPointerFilterPath] and top [pointerAdapter]. xPointerFilterPath and pointerAdapter are the value of
the same named attribute.
Framework.getService(AnnotationService.class)
54.6.1. Overview
The Annotation Service HTTP gateway provides the http API on top of the java APi of the Annotation Service
It is a servlet that implements the annotea protocol.
54.7. References
• W3C Annotea Protocol
• XPointer
Hardware:
• Intel/AMD 32-bit & 64-bit
Operating Systems:
• RedHat 3.x, 4.x, 5.x
• Solaris 10
• MacOS X 10.4.x
RDBMS:
• PostgreSQL 8.x
• MySQL 5.x
The most used configuration is JBoss AS 4.0.4 GA using JRE 1.5.0_11 on RedHat AS 4.x running on Intel x86
hardware.
Intel-based hardware (bi-Dual Core, Quad Core or bi-Quad Core), 4GB of RAM. The required disk space only
depends on the data volume to store (raw requirements to be secure: size of files to manage * 2).
Each release (major and minor) is delivered with an upgrade procedure, new features list, improvements list,
fixed bugs list and known bugs/limitations list. Moreover the issue tracker is public (it allows everybody to see
the status of the software, known/ongoing bugs and issues, features/improvement roadmap, etc.).
An installation guide is available. Upgrade procedures are delivered along each release.
The reference manual assembles all the documentation available for users, developers, operation teams, etc.
Nuxeo provides several other documents/resources such as the API (Javadoc), some tutorials, specific
Archetypes for Maven 2 (useful to quickly bootstrap new plugins / projects), etc.
The documentation is available in the English language. Translation to French, Spanish, German and Italian are
supported/provided by the community (if you require a specific language, you can order it from Nuxeo).
The documentation of included software are either included in the reference documentation if it's useful for
common operations, either linked if not. All the documentation of Nuxeo EP and bundled software packages is
freely available.
Yearly major release and quarterly minor release. The high-level roadmap is published by Nuxeo and updated
frequently. Detailed roadmap is available from the issue tracker (all details are available on each issue such as
comments, status, votes, related commit in the SCM, etc.).
The "Administration and Operation Guide" describes available monitoring points. In short, Nuxeo EP offers a
set of JMX services to monitor all critical points of the application (standard Java EE applications monitoring
system). Moreover, logs can be broadcasted using log4j capabilities (SNMP, email, etc.). Both should be usable
by all major monitoring software.
Nuxeo EP is fully based on Java EE 5 and supports related clustering and HA features. JBoss Clustering is the
recommended clustering and HA solution for Nuxeo EP's services. Nuxeo EP services can be configured for
performance clustering and/or HA clustering (depending on the capabilities and requirements of each service).
This is possible through configuration of the application server (ex: JBoss AS / Tomcat). Nuxeo EP relies on
the application server for all the network configuration.
Nuxeo EP entirely depends on the Java EE application server for all network related configuration. It is not
bind in any way to the physical network configuration of the server. Hence it is possible to change the
hostname of the server and restart the machine without causing any problem to Nuxeo EP.
Fail-over relies on JBoss Clustering for Nuxeo EP services. Here is the HA system used for each category of
services:
• Nuxeo Core (Content Repository): HA clustering only. It relies on the native RDMBS replication system
(Oracle RAC or PostgreSQL replication solutions). Data integrity has to be trustable and enforced.
• Nuxeo Search (Search Engine): HA and performance clustering. It can use a shared filesystem (if indexes
are stored on the filesystem) or can rely on the RDMBS replication solution. If data integrity is corrupt, a
reindexing of the content is be sufficient to restore it.
• EJB3-based Services: HA and performance clustering. Use native EJB3 clustering and load-balancing
from JBoss Clustering. Services using data persistence rely on RDMBS replication (for HA) that needs
to be trustable and enforced.
• Web Client/App: can use HA and performance clustering (using JBoss Clustering). Does not need data
sync.
To achieve the highest level of data integrity, Nuxeo recommends storing binary files as BLOBs directly in the
database (hence use a RDBMS offering optimized BLOBs storage (such as Oracle or PostgreSQL). Using this
mechanism, Nuxeo EP can store all its data into the RDBMS (including request/search engine indexes) and
relies on it to enforce data integrity. Moreover, Nuxeo EP is fully transactional and relies on JTA (+ XA) for
transaction management (that enforce data integrity across data sources).
Nuxeo EP has been designed to completely rely on RDBMS data integrity (that can be considered trustable
nowadays). One can use RDBMS tools to check data integrity/consistency and data failure if any. If the data
model is corrupted, Nuxeo EP warns about it when the repository starts. Indexes (from Nuxeo Search) can be
verified and easily be rebuilt by reindexing the content if any problem occurs.
The maximal impact is service downtime and data restoration from backups. Data integrity errors are reported
in the logs and can then be sent via email notifications, SNMP and any log4j capabilities.
Nuxeo EP offers an applicative data import/export service (using XML serialization of documents) that can be
used as an incremental backup/restore system. For an efficient backup system, Nuxeo recommends using native
RDBMS tools (that can offer incremental backups, snapshots, hot restore, etc.).
The restoration speed is the native database write performances. We do not have more statistics yet (but should
be available by July 2007, benchmark are in progress on this point).
When all datasources for storage are using the same database (the recommended setup), the RDBMS can
achieve a consistent backup (usually at low cost for the user service). Restore can only be launched when the
system is stopped.
Content object restore can be done using the import/export service. Here is the procedure to achieve this:
1. Get IDs of content object to restore using, for example, the audit service/log (ex: get all DocIds from
"CreateObject" log entries for a particular user).
2. Get those document from a backup (done via the export service) and copy them in a directory (the
standard export format use one directory per content object which is easing a lot this operation).
4. You're done.
RDBMS backup can be handled as usual using legacy backup scripts for this RDBMS. Applicative backups can
be launched using the import/export client CLI. There is not supported scripts at the moment (but they could
easily be written).
[TODO: refactor this chapter as in many cases, the packages may be installed using some package management
system (for instance on Mac OS: port install apache-ant maven2).]
You may already have the right Java development kit install can enter in the command line:
javac -version
If the java version is 1.5.x, you can skip the "Installing java 5" section.
Warning
Nuxeo EP doesn't currently compile under Java 6 (the 1.6.0 JDK from Sun). Even if it did, it is not
clear that JBoss, the application server we are targeting, works under Java 6.
Note
Java 5 is also sometimes called Java 1.5 or 1.5.0.
B.1.1. Using the Sun Java Development Kit (Windows and linux)
Sun Microsystems provides freely downloadable version of the Java Development Kit (JDK), that is needed to
compile the Nuxeo platform.
For the purpose of Nuxeo development, you should download the latest release of the JDK 5.0 (JDK 5.0
Update 11 at the time of this writing) from http://java.sun.com/javase/downloads/index_jdk5.jsp
• ensure Java 5 is now the default JVM on your system (instead of gcj and friends by default):
$ sudo update-alternatives --set java /usr/lib/jvm/java-1.5.0-sun/jre/bin/java
B.1.4.1. Windows
• click on new and enter "JAVA_HOME" for variable name and "C:\Program Files\Java\jdk1.5.0_10"
(adapt to your own JDK install)
B.1.4.2. Linux
B.1.4.3. Mac OS
Under Mac OS X, if you have properly installed a JDK (XXX: check how), you will need to put in your your
.bashrc (or .zshrc, ...) add something like:
export JAVA_HOME=/Library/Java/Home
Then need to have Ant setup and on your PATH environment variable.
For linux:
export PATH=/opt/apache-ant-1.7.0/bin:$PATH
For Windows:
• add something like this at the end of the PATH definition: ;c:\program files\apache-ant-1.7.0\bin
Maven is a software tool for Java programming language project management and automated
software build. It is similar in functionality to the Apache Ant tool, but has a simpler build
configuration model, based on an XML format. Maven is hosted by the Apache Software
Foundation, where it was formerly part of the Jakarta Project.
Maven uses a construct known as a Project Object Model (POM) to describe the software
project being built, its dependencies on other external modules and components, and the build
order. It comes with pre-defined targets for performing certain well defined tasks such as
compilation of code and its packaging.
A key feature of Maven is that it is network-ready. The core engine can dynamically download
plugins from a repository, the same repository that provides access to many versions of
different Open Source Java projects, from Apache and other organizations and developers. This
repository and its reorganized successor the Maven 2 repository are the de facto distribution
mechanism for Java applications. Maven provides built in support not just for retrieving files
from this repository, but to upload artifacts at the end of the build. A local cache of
downloaded artifacts acts as the primary means of synchronizing the output of projects on a
local system.
We recommend that you use the latest version of Maven (2.0.8 at the time of this writing).
As usual, you have to put the mvn executable into the path of your environment (cf. Ant)
Then add the bin/ subdir in your PATH by adding something like the following in your .bashrc:
export PATH=/opt/maven-2.0.8/bin:$PATH
The default code provides: a maven layout for sources, tests and dependencies, a Ant target for deployment. It
also customizes the web application a litte bit.
• artifactId : the name of your project, usually with '-' to separate the words if there are many.
• groupId : the domain name of your project. Usually it is the package parent name of your classes, you
should use '.' to separate the words ('-' is not supported here).
• archetypeArtifactId : the maven archetype artifact id. To generate a new project, Nuxeo provides you
with nuxeo-archetype-start
• archetypeVersion : the version of the archetype which is equivalent to the version of Nuxeo EP without
the GA or RC part. (5.1.6, 5.1.5 ...).
While executing the installation wizard, you must select ejb3 install. You can leave all other parameters to their
default values.
You would get PermGenSpace errors if you run JBoss without this configuration:
• Linux:
Edit /opt/jboss/bin/run.conf and add the following line at the end of the file
JAVA_OPTS="$JAVA_OPTS -XX:MaxPermSize=128m"
Restart JBoss.
• Windows:
Edit $JBOSS/bin/run.bat and add the following line after the line : set JAVA_OPTS=%JAVA_OPTS%
-Dprogram.name=%PROGNAME%
Restart JBoss.
JBoss is shipped with built-in Tomcat web server. This server is configured in
'deploy/jbossweb-tomcat55.sar/server.xml' By default only two connectors are enabled: HTTP connector (port
8080) and AJP connector (port 8009). Generally speaking you need only one of them. The former connector is
needed if standalone HTTP server built in JBoss is used. You may want to configure it to listen the default
HTTP port 80. The latter connector is needed only if you want to couple JBoss server with external web server
like Apache, in this case it is reasonable, for security issues to change the binding address to 'localhost' (of
course if Apache runs on the same host).
The JBoss default configuration deploys a special service that can be used to expose different JBoss services in
the HTTP server. It is located in 'deploy/http-invoker.sar'. The configuration file
'deploy/http-invoker.sar/META-INF/jboss-service.xml' may be tweaked to tune the AS to specific needs. By
default the service provides HTTP invoker MBean for EJB ('jboss:service=invoker,type=http') and two HTTP
proxy MBeans that marshal the requests to the Naming service MBean
('jboss:service=invoker,type=http,target=Naming' and
'jboss:service=invoker,type=http,target=Naming,readonly=true'). If you need to provide HTTP interface to
another MBeans, you also may specify the proxy services in
'deploy\http-invoker.sar\META-INF\jboss-service.xml'. For instance the SRP service for JBoss authentication
may be exposed here.
The service also deploys web application 'deploy/http-invoker.sar/invoker.war', that configures the servlets that
convert HTTP requests into invocation of MBeans/EJB methods. If you add HTTP proxies to MBeans, you
may need to add servlets that handle the corresponding URI.
Important note. If HTTPS protocol is used the configuration should not use the default host name because the
virtual host name used in the URL (say 'www.nuxeo.org') and exposed in SSL certificates usually differs from
the computer name where JBoss is running. To accomplish this get rid of the following attributes:
InvokerURLPrefix, InvokerURLSuffix, UseHostName, replacing them with a single InvokerURL attribute, like
this:
<mbean code="org.jboss.invocation.http.server.HttpProxyFactory"
name="jboss:service=invoker,type=https,target=Naming">
<!-- Compose the invoker URL from the cluster node address -->
<attribute name="InvokerURL">
https://www.nuxeo.org/invoker/JMXInvokerServlet
</attribute>
<attribute name="ExportedInterface">
org.jnp.interfaces.Naming
</attribute>
<attribute name="JndiName"></attribute>
<attribute name="ClientInterceptors">
<interceptors>
<interceptor>org.jboss.proxy.ClientMethodInterceptor</interceptor>
<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>
<interceptor>org.jboss.naming.interceptors.ExceptionInterceptor</interceptor>
<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>
</interceptors>
</attribute>
<depends>jboss:service=invoker,type=https</depends>
</mbean>
<!-- The rest MBeans should also use InvokerURL attribute only,
make sure you specify the right host name
-->
This is the core service of JBoss and should never be disabled. Nevertheless this service does not need own
listening port (1099,1098), so just change the '1099' to '-1':
<mbean code="org.jboss.naming.NamingService"
name="jboss:service=Naming"
xmbean-dd="resource:xmdesc/NamingService-xmbean.xml">
<!-- The call by value mode. true if all lookups are unmarshalled using
the caller's TCL, false if in VM lookups return the value by reference.
-->
<attribute name="CallByValue">false</attribute>
<!-- The listening port for the bootstrap JNP service. Set this to -1
to run the NamingService without the JNP invoker listening port.
-->
<attribute name="Port">-1</attribute>
<!-- The bootstrap JNP server bind address. This also sets the default
RMI service bind address. Empty == all addresses, use localhost to hide this from
network
-->
<attribute name="BindAddress">localhost</attribute>
<!-- The port of the RMI naming service, 0 == anonymous, you cannot use -1 here -->
<attribute name="RmiPort">1098</attribute>
<!-- The RMI service bind address. Empty == all addresses, use localhost to hide this from
network
-->
<attribute name="RmiBindAddress">localhost</attribute>
<!-- The thread pool service used to control the bootstrap lookups -->
<depends optional-attribute-name="LookupPool"
proxy-type="attribute">jboss.system:service=ThreadPool</depends>
</mbean>
You may deinstall the JRMP and Pooled invokers completely. Just comment out the MBeans that provide the
corresponding services in 'conf/jboss-service.xml'.
Important note. The JBoss specifies the invokers for EJB in 'conf/standardjboss.xml' file. The default is
'jboss:service=invoker,type=jrmp' invoker. To change it to HTTP invoker you need to add invoker bindings for
all EJB types deployed in your applications. Generally it means you need to copy all "*-rmi-invoker" bindings
into "*-http-invoker" bindings, replacing
"<invoker-mbean>jboss:service=invoker,type=jrmp</invoker-mbean>" with
"<invoker-mbean>jboss:service=invoker,type=http</invoker-mbean>" for the new bindings. Also you will
need to make the HTTP invoker default for all EJB container configurations replacing
"<invoker-proxy-binding-name>*-rmi-invoker</invoker-proxy-binding-name>" with
"<invoker-proxy-binding-name>*-http-invoker</invoker-proxy-binding-name>" correspondingly.
The easiest (but probably not the right) way for JBoss 4.0.x is to replace the string
'jboss:service=invoker,type=jrmp' with 'jboss:service=invoker,type=http' in this file by any text editor. It may
be not correct if you want to mix both invokers for your EJBs.
The Client User Transaction service depends on two JRMP Proxy Factories described in the nested MBeans.
Every JRMP proxy factory should be replaced with HTTP proxy factory:
<mbean
code="org.jboss.tm.usertx.server.ClientUserTransactionService"
name="jboss:service=ClientUserTransaction"
xmbean-dd="resource:xmdesc/ClientUserTransaction-xmbean.xml">
<depends>
<mbean code="org.jboss.invocation.http.server.HttpProxyFactory"
name="jboss:service=proxyFactory,target=ClientUserTransactionFactory">
<attribute name="InvokerName">jboss:service=invoker,type=http</attribute>
<attribute name="JndiName">UserTransactionSessionFactory</attribute>
<attribute name="ExportedInterface">
org.jboss.tm.usertx.interfaces.UserTransactionSessionFactory
</attribute>
<attribute name="ClientInterceptors">
<interceptors>
<interceptor>org.jboss.proxy.ClientMethodInterceptor</interceptor>
<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>
</interceptors>
</attribute>
<depends>jboss:service=invoker,type=http</depends>
</mbean>
</depends>
<depends optional-attribute-name="TxProxyName">
<mbean code="org.jboss.invocation.http.server.HttpProxyFactory"
name="jboss:service=proxyFactory,target=ClientUserTransaction">
<attribute name="InvokerName">jboss:service=invoker,type=http</attribute>
<attribute name="JndiName"></attribute>
<attribute name="ExportedInterface">
org.jboss.tm.usertx.interfaces.UserTransactionSession
</attribute>
<attribute name="ClientInterceptors">
<interceptors>
<interceptor>org.jboss.proxy.ClientMethodInterceptor</interceptor>
<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>
</interceptors>
</attribute>
<depends>jboss:service=invoker,type=http</depends>
</mbean>
</depends>
</mbean>
Note that JRMP Proxy factory attributes differ from attributes of HTTP proxy factory.
and ('deploy/console-mgr.sar/META-INF/jboss-service.xml'):
<mbean code="org.jboss.console.manager.PluginManager"
name="jboss.admin:service=PluginManager">
<depends>jboss.jmx:type=adaptor,name=Invoker,protocol=http,service=proxyFactory</depends>
<!-- the rest stays intact -->
</mbean>
You need to set the invoker explicitly for all deployed data sources. The element <jmx-invoker-name> should
be added to all <local-tx-datasource> and <xa-datasource> elements. Otherwise the server will complain about
missing JRMP invoker which is used by default:
<datasources>
...
<local-tx-datasource>
<!-- specify explicitly the invoker to use -->
<jmx-invoker-name>jboss:service=invoker,type=https</jmx-invoker-name>
<!-- the rest stays intact -->
...
</local-tx-datasource>
...
</datasources>
B.5.2. Windows
For MS Windows, we recommend to use the TortoiseSVN Subversion client. You can also directly use the
Subversion command-line client from Subversion website.
B.6.1. Linux
To install the hg command under Ubuntu / Debian :
$ sudo apt-get install mercurial
B.6.2. Windows
For MS Windows, we recommend to use the all in one tortoise bundle provided to you by selenic.
B.6.3. Mac OS
For Mac OS, our preferred method is to use the darwin ports environment.
$ port install mercurial
...
[ui]
...
username = firstname lastname <you@your-domain>
...
...
[extensions]
...
hgext.mq =
hgext.parentrevspec =
hgext.graphlog =
patchbomb =
transplant =
...
. The next step is to declare the plugin into you .hgrc file in the extension section.
[extensions]
...
hgext.forest = [your installation path]/forest.py
...
• how to install Ant and Maven, two mandatory tools for building and deploying your own projects on top
of the Nuxeo platform
• how to install the JBoss AS 4 application server that will act as a container for the Nuxeo application
Through our commercial offer, Nuxeo Connect, we deliver enterprise-grade functional and technical support,
certified software patches and updates, and management tools that assist you during every stage of the
application life-cycle - from design and development, throughout testing and deployment, to operations and
monitoring. Nuxeo Connect helps reduce business and technical risks, increase productivity, speed time to
deployment and improve your success rate for all Nuxeo-based projects.
We're also happy to work with partners, IT Integrators or ISVs, to deliver the best possible applications to
customers.
C.2.1. General
Web: www.nuxeo.com
E-mail: contact@nuxeo.com
C.2.2. France
18-20, rue Soleillet
Tel: +33 1 40 33 79 87
Fax: +33 1 43 58 14 15
C.2.3. UK
Garden Studios, 11-15 Betterton Street, London WC2H 9BP
Tel: +44-207-043-7933